This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
In buildtoc, remove whitespace only lines just before output.
[perl5.git] / vms / vms.c
CommitLineData
b429d381 1/* vms.c
a0d0e21e 2 *
82dd182c 3 * VMS-specific routines for perl5
748a9306 4 *
82dd182c
CB
5 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6 * 2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
7 *
8 * You may distribute under the terms of either the GNU General Public
9 * License or the Artistic License, as specified in the README file.
10 *
11 * Please see Changes*.* or the Perl Repository Browser for revision history.
a0d0e21e
LW
12 */
13
7c884029 14/*
4ac71550
TC
15 * Yet small as was their hunted band
16 * still fell and fearless was each hand,
17 * and strong deeds they wrought yet oft,
18 * and loved the woods, whose ways more soft
19 * them seemed than thralls of that black throne
20 * to live and languish in halls of stone.
21 * "The Lay of Leithian", Canto II, lines 135-40
7c884029 22 *
4ac71550 23 * [p.162 of _The Lays of Beleriand_]
7c884029
CB
24 */
25
a0d0e21e
LW
26#include <acedef.h>
27#include <acldef.h>
28#include <armdef.h>
748a9306 29#include <atrdef.h>
a0d0e21e 30#include <chpdef.h>
8fde5078 31#include <clidef.h>
a3e9d8c9 32#include <climsgdef.h>
cd1191f1 33#include <dcdef.h>
a0d0e21e 34#include <descrip.h>
22d4bb9c 35#include <devdef.h>
a0d0e21e 36#include <dvidef.h>
748a9306 37#include <fibdef.h>
a0d0e21e
LW
38#include <float.h>
39#include <fscndef.h>
40#include <iodef.h>
41#include <jpidef.h>
61bb5906 42#include <kgbdef.h>
f675dbe5 43#include <libclidef.h>
a0d0e21e
LW
44#include <libdef.h>
45#include <lib$routines.h>
46#include <lnmdef.h>
aeb5cf3c 47#include <msgdef.h>
4fdf8f88 48#include <ossdef.h>
f7ddb74a
JM
49#if __CRTL_VER >= 70301000 && !defined(__VAX)
50#include <ppropdef.h>
51#endif
748a9306 52#include <prvdef.h>
a0d0e21e
LW
53#include <psldef.h>
54#include <rms.h>
55#include <shrdef.h>
56#include <ssdef.h>
57#include <starlet.h>
f86702cc 58#include <strdef.h>
59#include <str$routines.h>
a0d0e21e 60#include <syidef.h>
748a9306
LW
61#include <uaidef.h>
62#include <uicdef.h>
2fbb330f
JM
63#include <stsdef.h>
64#include <rmsdef.h>
cfcfe586
JM
65#if __CRTL_VER >= 70000000 /* FIXME to earliest version */
66#include <efndef.h>
67#define NO_EFN EFN$C_ENF
68#else
69#define NO_EFN 0;
70#endif
a0d0e21e 71
f7ddb74a
JM
72#if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
73int decc$feature_get_index(const char *name);
74char* decc$feature_get_name(int index);
75int decc$feature_get_value(int index, int mode);
76int decc$feature_set_value(int index, int mode, int value);
77#else
78#include <unixlib.h>
79#endif
80
cfcfe586
JM
81#pragma member_alignment save
82#pragma nomember_alignment longword
83struct item_list_3 {
84 unsigned short len;
85 unsigned short code;
86 void * bufadr;
87 unsigned short * retadr;
88};
89#pragma member_alignment restore
90
7a7fd8e0 91#if __CRTL_VER >= 70300000 && !defined(__VAX)
f7ddb74a
JM
92
93static int set_feature_default(const char *name, int value)
94{
95 int status;
96 int index;
97
98 index = decc$feature_get_index(name);
99
100 status = decc$feature_set_value(index, 1, value);
101 if (index == -1 || (status == -1)) {
102 return -1;
103 }
104
105 status = decc$feature_get_value(index, 1);
106 if (status != value) {
107 return -1;
108 }
109
110return 0;
111}
112#endif
f7ddb74a 113
740ce14c 114/* Older versions of ssdef.h don't have these */
115#ifndef SS$_INVFILFOROP
116# define SS$_INVFILFOROP 3930
117#endif
118#ifndef SS$_NOSUCHOBJECT
b7ae7a0d 119# define SS$_NOSUCHOBJECT 2696
120#endif
121
a15cef0c
CB
122/* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
123#define PERLIO_NOT_STDIO 0
124
2497a41f 125/* Don't replace system definitions of vfork, getenv, lstat, and stat,
aa689395 126 * code below needs to get to the underlying CRTL routines. */
127#define DONT_MASK_RTL_CALLS
a0d0e21e
LW
128#include "EXTERN.h"
129#include "perl.h"
748a9306 130#include "XSUB.h"
3eeba6fb
CB
131/* Anticipating future expansion in lexical warnings . . . */
132#ifndef WARN_INTERNAL
133# define WARN_INTERNAL WARN_MISC
134#endif
a0d0e21e 135
988c775c
JM
136#ifdef VMS_LONGNAME_SUPPORT
137#include <libfildef.h>
138#endif
139
22d4bb9c
CB
140#if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
141# define RTL_USES_UTC 1
142#endif
143
58472d87
CB
144#if !defined(__VAX) && __CRTL_VER >= 80200000
145#ifdef lstat
146#undef lstat
147#endif
148#else
149#ifdef lstat
150#undef lstat
151#endif
152#define lstat(_x, _y) stat(_x, _y)
153#endif
154
5f1992ed
CB
155/* Routine to create a decterm for use with the Perl debugger */
156/* No headers, this information was found in the Programming Concepts Manual */
157
8cb5d3d5 158static int (*decw_term_port)
5f1992ed
CB
159 (const struct dsc$descriptor_s * display,
160 const struct dsc$descriptor_s * setup_file,
161 const struct dsc$descriptor_s * customization,
162 struct dsc$descriptor_s * result_device_name,
163 unsigned short * result_device_name_length,
164 void * controller,
165 void * char_buffer,
8cb5d3d5 166 void * char_change_buffer) = 0;
22d4bb9c 167
c07a80fd 168/* gcc's header files don't #define direct access macros
169 * corresponding to VAXC's variant structs */
170#ifdef __GNUC__
482b294c 171# define uic$v_format uic$r_uic_form.uic$v_format
172# define uic$v_group uic$r_uic_form.uic$v_group
173# define uic$v_member uic$r_uic_form.uic$v_member
c07a80fd 174# define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
175# define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
176# define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
177# define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
178#endif
179
c645ec3f
GS
180#if defined(NEED_AN_H_ERRNO)
181dEXT int h_errno;
182#endif
c07a80fd 183
f7ddb74a
JM
184#ifdef __DECC
185#pragma message disable pragma
186#pragma member_alignment save
187#pragma nomember_alignment longword
188#pragma message save
189#pragma message disable misalgndmem
190#endif
a0d0e21e
LW
191struct itmlst_3 {
192 unsigned short int buflen;
193 unsigned short int itmcode;
194 void *bufadr;
748a9306 195 unsigned short int *retlen;
a0d0e21e 196};
657054d4
JM
197
198struct filescan_itmlst_2 {
199 unsigned short length;
200 unsigned short itmcode;
201 char * component;
202};
203
dca5a913
JM
204struct vs_str_st {
205 unsigned short length;
206 char str[65536];
207};
208
f7ddb74a
JM
209#ifdef __DECC
210#pragma message restore
211#pragma member_alignment restore
212#endif
a0d0e21e 213
360732b5
JM
214#define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
215#define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
216#define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
217#define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
218#define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
219#define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
b1a8dcd7 220#define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
360732b5
JM
221#define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
222#define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
f7ddb74a 223#define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
4b19af01
CB
224#define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
225#define getredirection(a,b) mp_getredirection(aTHX_ a,b)
226
360732b5
JM
227static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
228static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
229static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
230static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
f7ddb74a 231
6fb6c614
JM
232static char * int_rmsexpand_vms(
233 const char * filespec, char * outbuf, unsigned opts);
234static char * int_rmsexpand_tovms(
235 const char * filespec, char * outbuf, unsigned opts);
df278665
JM
236static char *int_tovmsspec
237 (const char *path, char *buf, int dir_flag, int * utf8_flag);
a979ce91 238static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
0e5ce2c7 239static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
4846f1d7 240static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
df278665 241
0e06870b
CB
242/* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
243#define PERL_LNM_MAX_ALLOWED_INDEX 127
244
2d9f3838
CB
245/* OpenVMS User's Guide says at least 9 iterative translations will be performed,
246 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
247 * the Perl facility.
248 */
249#define PERL_LNM_MAX_ITER 10
250
2497a41f
JM
251 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
252#if __CRTL_VER >= 70302000 && !defined(__VAX)
253#define MAX_DCL_SYMBOL (8192)
254#define MAX_DCL_LINE_LENGTH (4096 - 4)
255#else
256#define MAX_DCL_SYMBOL (1024)
257#define MAX_DCL_LINE_LENGTH (1024 - 4)
258#endif
ff7adb52 259
01b8edb6 260static char *__mystrtolower(char *str)
261{
262 if (str) for (; *str; ++str) *str= tolower(*str);
263 return str;
264}
265
f675dbe5
CB
266static struct dsc$descriptor_s fildevdsc =
267 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
268static struct dsc$descriptor_s crtlenvdsc =
269 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
270static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
271static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
272static struct dsc$descriptor_s **env_tables = defenv;
273static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
274
93948341
CB
275/* True if we shouldn't treat barewords as logicals during directory */
276/* munching */
277static int no_translate_barewords;
278
22d4bb9c
CB
279#ifndef RTL_USES_UTC
280static int tz_updated = 1;
281#endif
282
f7ddb74a
JM
283/* DECC Features that may need to affect how Perl interprets
284 * displays filename information
285 */
286static int decc_disable_to_vms_logname_translation = 1;
287static int decc_disable_posix_root = 1;
288int decc_efs_case_preserve = 0;
289static int decc_efs_charset = 0;
b53f3677 290static int decc_efs_charset_index = -1;
f7ddb74a
JM
291static int decc_filename_unix_no_version = 0;
292static int decc_filename_unix_only = 0;
293int decc_filename_unix_report = 0;
294int decc_posix_compliant_pathnames = 0;
295int decc_readdir_dropdotnotype = 0;
296static int vms_process_case_tolerant = 1;
360732b5
JM
297int vms_vtf7_filenames = 0;
298int gnv_unix_shell = 0;
e0e5e8d6 299static int vms_unlink_all_versions = 0;
1a3aec58 300static int vms_posix_exit = 0;
f7ddb74a 301
2497a41f 302/* bug workarounds if needed */
682e4b71 303int decc_bug_devnull = 1;
2497a41f 304int decc_dir_barename = 0;
b53f3677 305int vms_bug_stat_filename = 0;
2497a41f 306
9c1171d1 307static int vms_debug_on_exception = 0;
b53f3677
JM
308static int vms_debug_fileify = 0;
309
310/* Simple logical name translation */
311static int simple_trnlnm
312 (const char * logname,
313 char * value,
314 int value_len)
315{
316 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
317 const unsigned long attr = LNM$M_CASE_BLIND;
318 struct dsc$descriptor_s name_dsc;
319 int status;
320 unsigned short result;
321 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
322 {0, 0, 0, 0}};
323
324 name_dsc.dsc$w_length = strlen(logname);
325 name_dsc.dsc$a_pointer = (char *)logname;
326 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
327 name_dsc.dsc$b_class = DSC$K_CLASS_S;
328
329 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
330
331 if ($VMS_STATUS_SUCCESS(status)) {
332
333 /* Null terminate and return the string */
334 /*--------------------------------------*/
335 value[result] = 0;
336 return result;
337 }
338
339 return 0;
340}
341
9c1171d1 342
f7ddb74a
JM
343/* Is this a UNIX file specification?
344 * No longer a simple check with EFS file specs
345 * For now, not a full check, but need to
346 * handle POSIX ^UP^ specifications
347 * Fixing to handle ^/ cases would require
348 * changes to many other conversion routines.
349 */
350
657054d4 351static int is_unix_filespec(const char *path)
f7ddb74a
JM
352{
353int ret_val;
354const char * pch1;
355
356 ret_val = 0;
357 if (strncmp(path,"\"^UP^",5) != 0) {
358 pch1 = strchr(path, '/');
359 if (pch1 != NULL)
360 ret_val = 1;
361 else {
362
363 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
364 if (decc_filename_unix_report || decc_filename_unix_only) {
365 if (strcmp(path,".") == 0)
366 ret_val = 1;
367 }
368 }
369 }
370 return ret_val;
371}
372
360732b5
JM
373/* This routine converts a UCS-2 character to be VTF-7 encoded.
374 */
375
376static void ucs2_to_vtf7
377 (char *outspec,
378 unsigned long ucs2_char,
379 int * output_cnt)
380{
381unsigned char * ucs_ptr;
382int hex;
383
384 ucs_ptr = (unsigned char *)&ucs2_char;
385
386 outspec[0] = '^';
387 outspec[1] = 'U';
388 hex = (ucs_ptr[1] >> 4) & 0xf;
389 if (hex < 0xA)
390 outspec[2] = hex + '0';
391 else
392 outspec[2] = (hex - 9) + 'A';
393 hex = ucs_ptr[1] & 0xF;
394 if (hex < 0xA)
395 outspec[3] = hex + '0';
396 else {
397 outspec[3] = (hex - 9) + 'A';
398 }
399 hex = (ucs_ptr[0] >> 4) & 0xf;
400 if (hex < 0xA)
401 outspec[4] = hex + '0';
402 else
403 outspec[4] = (hex - 9) + 'A';
404 hex = ucs_ptr[1] & 0xF;
405 if (hex < 0xA)
406 outspec[5] = hex + '0';
407 else {
408 outspec[5] = (hex - 9) + 'A';
409 }
410 *output_cnt = 6;
411}
412
413
414/* This handles the conversion of a UNIX extended character set to a ^
415 * escaped VMS character.
416 * in a UNIX file specification.
417 *
418 * The output count variable contains the number of characters added
419 * to the output string.
420 *
421 * The return value is the number of characters read from the input string
422 */
423static int copy_expand_unix_filename_escape
424 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
425{
426int count;
360732b5
JM
427int utf8_flag;
428
429 utf8_flag = 0;
430 if (utf8_fl)
431 utf8_flag = *utf8_fl;
432
433 count = 0;
434 *output_cnt = 0;
435 if (*inspec >= 0x80) {
436 if (utf8_fl && vms_vtf7_filenames) {
437 unsigned long ucs_char;
438
439 ucs_char = 0;
440
441 if ((*inspec & 0xE0) == 0xC0) {
442 /* 2 byte Unicode */
443 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
444 if (ucs_char >= 0x80) {
445 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
446 return 2;
447 }
448 } else if ((*inspec & 0xF0) == 0xE0) {
449 /* 3 byte Unicode */
450 ucs_char = ((inspec[0] & 0xF) << 12) +
451 ((inspec[1] & 0x3f) << 6) +
452 (inspec[2] & 0x3f);
453 if (ucs_char >= 0x800) {
454 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
455 return 3;
456 }
457
458#if 0 /* I do not see longer sequences supported by OpenVMS */
459 /* Maybe some one can fix this later */
460 } else if ((*inspec & 0xF8) == 0xF0) {
461 /* 4 byte Unicode */
462 /* UCS-4 to UCS-2 */
463 } else if ((*inspec & 0xFC) == 0xF8) {
464 /* 5 byte Unicode */
465 /* UCS-4 to UCS-2 */
466 } else if ((*inspec & 0xFE) == 0xFC) {
467 /* 6 byte Unicode */
468 /* UCS-4 to UCS-2 */
469#endif
470 }
471 }
472
38a44b82 473 /* High bit set, but not a Unicode character! */
360732b5
JM
474
475 /* Non printing DECMCS or ISO Latin-1 character? */
476 if (*inspec <= 0x9F) {
477 int hex;
478 outspec[0] = '^';
479 outspec++;
480 hex = (*inspec >> 4) & 0xF;
481 if (hex < 0xA)
482 outspec[1] = hex + '0';
483 else {
484 outspec[1] = (hex - 9) + 'A';
485 }
486 hex = *inspec & 0xF;
487 if (hex < 0xA)
488 outspec[2] = hex + '0';
489 else {
490 outspec[2] = (hex - 9) + 'A';
491 }
492 *output_cnt = 3;
493 return 1;
494 } else if (*inspec == 0xA0) {
495 outspec[0] = '^';
496 outspec[1] = 'A';
497 outspec[2] = '0';
498 *output_cnt = 3;
499 return 1;
500 } else if (*inspec == 0xFF) {
501 outspec[0] = '^';
502 outspec[1] = 'F';
503 outspec[2] = 'F';
504 *output_cnt = 3;
505 return 1;
506 }
507 *outspec = *inspec;
508 *output_cnt = 1;
509 return 1;
510 }
511
512 /* Is this a macro that needs to be passed through?
513 * Macros start with $( and an alpha character, followed
514 * by a string of alpha numeric characters ending with a )
515 * If this does not match, then encode it as ODS-5.
516 */
517 if ((inspec[0] == '$') && (inspec[1] == '(')) {
518 int tcnt;
519
520 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
521 tcnt = 3;
522 outspec[0] = inspec[0];
523 outspec[1] = inspec[1];
524 outspec[2] = inspec[2];
525
526 while(isalnum(inspec[tcnt]) ||
527 (inspec[2] == '.') || (inspec[2] == '_')) {
528 outspec[tcnt] = inspec[tcnt];
529 tcnt++;
530 }
531 if (inspec[tcnt] == ')') {
532 outspec[tcnt] = inspec[tcnt];
533 tcnt++;
534 *output_cnt = tcnt;
535 return tcnt;
536 }
537 }
538 }
539
540 switch (*inspec) {
541 case 0x7f:
542 outspec[0] = '^';
543 outspec[1] = '7';
544 outspec[2] = 'F';
545 *output_cnt = 3;
546 return 1;
547 break;
548 case '?':
549 if (decc_efs_charset == 0)
550 outspec[0] = '%';
551 else
552 outspec[0] = '?';
553 *output_cnt = 1;
554 return 1;
555 break;
556 case '.':
557 case '~':
558 case '!':
559 case '#':
560 case '&':
561 case '\'':
562 case '`':
563 case '(':
564 case ')':
565 case '+':
566 case '@':
567 case '{':
568 case '}':
569 case ',':
570 case ';':
571 case '[':
572 case ']':
573 case '%':
574 case '^':
449de3c2 575 case '\\':
adc11f0b
CB
576 /* Don't escape again if following character is
577 * already something we escape.
578 */
449de3c2 579 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
adc11f0b
CB
580 *outspec = *inspec;
581 *output_cnt = 1;
582 return 1;
583 break;
584 }
585 /* But otherwise fall through and escape it. */
360732b5
JM
586 case '=':
587 /* Assume that this is to be escaped */
588 outspec[0] = '^';
589 outspec[1] = *inspec;
590 *output_cnt = 2;
591 return 1;
592 break;
593 case ' ': /* space */
594 /* Assume that this is to be escaped */
595 outspec[0] = '^';
596 outspec[1] = '_';
597 *output_cnt = 2;
598 return 1;
599 break;
600 default:
601 *outspec = *inspec;
602 *output_cnt = 1;
603 return 1;
604 break;
605 }
606}
607
608
657054d4
JM
609/* This handles the expansion of a '^' prefix to the proper character
610 * in a UNIX file specification.
611 *
612 * The output count variable contains the number of characters added
613 * to the output string.
614 *
615 * The return value is the number of characters read from the input
616 * string
617 */
618static int copy_expand_vms_filename_escape
619 (char *outspec, const char *inspec, int *output_cnt)
620{
621int count;
622int scnt;
623
624 count = 0;
625 *output_cnt = 0;
626 if (*inspec == '^') {
627 inspec++;
628 switch (*inspec) {
adc11f0b
CB
629 /* Spaces and non-trailing dots should just be passed through,
630 * but eat the escape character.
631 */
657054d4 632 case '.':
657054d4 633 *outspec = *inspec;
adc11f0b
CB
634 count += 2;
635 (*output_cnt)++;
657054d4
JM
636 break;
637 case '_': /* space */
638 *outspec = ' ';
adc11f0b 639 count += 2;
657054d4
JM
640 (*output_cnt)++;
641 break;
adc11f0b
CB
642 case '^':
643 /* Hmm. Better leave the escape escaped. */
644 outspec[0] = '^';
645 outspec[1] = '^';
646 count += 2;
647 (*output_cnt) += 2;
648 break;
360732b5 649 case 'U': /* Unicode - FIX-ME this is wrong. */
657054d4
JM
650 inspec++;
651 count++;
652 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
653 if (scnt == 4) {
2f4077ca
JM
654 unsigned int c1, c2;
655 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
9960802c
NK
656 outspec[0] = c1 & 0xff;
657 outspec[1] = c2 & 0xff;
657054d4
JM
658 if (scnt > 1) {
659 (*output_cnt) += 2;
660 count += 4;
661 }
662 }
663 else {
664 /* Error - do best we can to continue */
665 *outspec = 'U';
666 outspec++;
667 (*output_cnt++);
668 *outspec = *inspec;
669 count++;
670 (*output_cnt++);
671 }
672 break;
673 default:
674 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
675 if (scnt == 2) {
676 /* Hex encoded */
2f4077ca
JM
677 unsigned int c1;
678 scnt = sscanf(inspec, "%2x", &c1);
679 outspec[0] = c1 & 0xff;
657054d4
JM
680 if (scnt > 0) {
681 (*output_cnt++);
682 count += 2;
683 }
684 }
685 else {
686 *outspec = *inspec;
687 count++;
688 (*output_cnt++);
689 }
690 }
691 }
692 else {
693 *outspec = *inspec;
694 count++;
695 (*output_cnt)++;
696 }
697 return count;
698}
699
657054d4
JM
700/* vms_split_path - Verify that the input file specification is a
701 * VMS format file specification, and provide pointers to the components of
702 * it. With EFS format filenames, this is virtually the only way to
703 * parse a VMS path specification into components.
704 *
705 * If the sum of the components do not add up to the length of the
706 * string, then the passed file specification is probably a UNIX style
707 * path.
708 */
709static int vms_split_path
360732b5 710 (const char * path,
dca5a913 711 char * * volume,
657054d4 712 int * vol_len,
dca5a913 713 char * * root,
657054d4 714 int * root_len,
dca5a913 715 char * * dir,
657054d4 716 int * dir_len,
dca5a913 717 char * * name,
657054d4 718 int * name_len,
dca5a913 719 char * * ext,
657054d4 720 int * ext_len,
dca5a913 721 char * * version,
657054d4
JM
722 int * ver_len)
723{
724struct dsc$descriptor path_desc;
725int status;
726unsigned long flags;
727int ret_stat;
728struct filescan_itmlst_2 item_list[9];
729const int filespec = 0;
730const int nodespec = 1;
731const int devspec = 2;
732const int rootspec = 3;
733const int dirspec = 4;
734const int namespec = 5;
735const int typespec = 6;
736const int verspec = 7;
737
738 /* Assume the worst for an easy exit */
739 ret_stat = -1;
740 *volume = NULL;
741 *vol_len = 0;
742 *root = NULL;
743 *root_len = 0;
744 *dir = NULL;
657054d4
JM
745 *name = NULL;
746 *name_len = 0;
747 *ext = NULL;
748 *ext_len = 0;
749 *version = NULL;
750 *ver_len = 0;
751
752 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
753 path_desc.dsc$w_length = strlen(path);
754 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
755 path_desc.dsc$b_class = DSC$K_CLASS_S;
756
757 /* Get the total length, if it is shorter than the string passed
758 * then this was probably not a VMS formatted file specification
759 */
760 item_list[filespec].itmcode = FSCN$_FILESPEC;
761 item_list[filespec].length = 0;
762 item_list[filespec].component = NULL;
763
764 /* If the node is present, then it gets considered as part of the
765 * volume name to hopefully make things simple.
766 */
767 item_list[nodespec].itmcode = FSCN$_NODE;
768 item_list[nodespec].length = 0;
769 item_list[nodespec].component = NULL;
770
771 item_list[devspec].itmcode = FSCN$_DEVICE;
772 item_list[devspec].length = 0;
773 item_list[devspec].component = NULL;
774
775 /* root is a special case, adding it to either the directory or
94ae10c0 776 * the device components will probably complicate things for the
657054d4
JM
777 * callers of this routine, so leave it separate.
778 */
779 item_list[rootspec].itmcode = FSCN$_ROOT;
780 item_list[rootspec].length = 0;
781 item_list[rootspec].component = NULL;
782
783 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
784 item_list[dirspec].length = 0;
785 item_list[dirspec].component = NULL;
786
787 item_list[namespec].itmcode = FSCN$_NAME;
788 item_list[namespec].length = 0;
789 item_list[namespec].component = NULL;
790
791 item_list[typespec].itmcode = FSCN$_TYPE;
792 item_list[typespec].length = 0;
793 item_list[typespec].component = NULL;
794
795 item_list[verspec].itmcode = FSCN$_VERSION;
796 item_list[verspec].length = 0;
797 item_list[verspec].component = NULL;
798
799 item_list[8].itmcode = 0;
800 item_list[8].length = 0;
801 item_list[8].component = NULL;
802
7566800d 803 status = sys$filescan
657054d4
JM
804 ((const struct dsc$descriptor_s *)&path_desc, item_list,
805 &flags, NULL, NULL);
360732b5 806 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
657054d4
JM
807
808 /* If we parsed it successfully these two lengths should be the same */
809 if (path_desc.dsc$w_length != item_list[filespec].length)
810 return ret_stat;
811
812 /* If we got here, then it is a VMS file specification */
813 ret_stat = 0;
814
815 /* set the volume name */
816 if (item_list[nodespec].length > 0) {
817 *volume = item_list[nodespec].component;
818 *vol_len = item_list[nodespec].length + item_list[devspec].length;
819 }
820 else {
821 *volume = item_list[devspec].component;
822 *vol_len = item_list[devspec].length;
823 }
824
825 *root = item_list[rootspec].component;
826 *root_len = item_list[rootspec].length;
827
828 *dir = item_list[dirspec].component;
829 *dir_len = item_list[dirspec].length;
830
831 /* Now fun with versions and EFS file specifications
832 * The parser can not tell the difference when a "." is a version
833 * delimiter or a part of the file specification.
834 */
835 if ((decc_efs_charset) &&
836 (item_list[verspec].length > 0) &&
837 (item_list[verspec].component[0] == '.')) {
838 *name = item_list[namespec].component;
839 *name_len = item_list[namespec].length + item_list[typespec].length;
840 *ext = item_list[verspec].component;
841 *ext_len = item_list[verspec].length;
842 *version = NULL;
843 *ver_len = 0;
844 }
845 else {
846 *name = item_list[namespec].component;
847 *name_len = item_list[namespec].length;
848 *ext = item_list[typespec].component;
849 *ext_len = item_list[typespec].length;
850 *version = item_list[verspec].component;
851 *ver_len = item_list[verspec].length;
852 }
853 return ret_stat;
854}
855
df278665
JM
856/* Routine to determine if the file specification ends with .dir */
857static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
858
859 /* e_len must be 4, and version must be <= 2 characters */
860 if (e_len != 4 || vs_len > 2)
861 return 0;
862
863 /* If a version number is present, it needs to be one */
864 if ((vs_len == 2) && (vs_spec[1] != '1'))
865 return 0;
866
867 /* Look for the DIR on the extension */
868 if (vms_process_case_tolerant) {
869 if ((toupper(e_spec[1]) == 'D') &&
870 (toupper(e_spec[2]) == 'I') &&
871 (toupper(e_spec[3]) == 'R')) {
872 return 1;
873 }
874 } else {
875 /* Directory extensions are supposed to be in upper case only */
876 /* I would not be surprised if this rule can not be enforced */
877 /* if and when someone fully debugs the case sensitive mode */
878 if ((e_spec[1] == 'D') &&
879 (e_spec[2] == 'I') &&
880 (e_spec[3] == 'R')) {
881 return 1;
882 }
883 }
884 return 0;
885}
886
f7ddb74a 887
fa537f88
CB
888/* my_maxidx
889 * Routine to retrieve the maximum equivalence index for an input
890 * logical name. Some calls to this routine have no knowledge if
891 * the variable is a logical or not. So on error we return a max
892 * index of zero.
893 */
f7ddb74a 894/*{{{int my_maxidx(const char *lnm) */
fa537f88 895static int
f7ddb74a 896my_maxidx(const char *lnm)
fa537f88
CB
897{
898 int status;
899 int midx;
900 int attr = LNM$M_CASE_BLIND;
901 struct dsc$descriptor lnmdsc;
902 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
903 {0, 0, 0, 0}};
904
905 lnmdsc.dsc$w_length = strlen(lnm);
906 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
907 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
f7ddb74a 908 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
fa537f88
CB
909
910 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
911 if ((status & 1) == 0)
912 midx = 0;
913
914 return (midx);
915}
916/*}}}*/
917
f675dbe5 918/*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
c07a80fd 919int
fd8cd3a3 920Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
f675dbe5 921 struct dsc$descriptor_s **tabvec, unsigned long int flags)
748a9306 922{
f7ddb74a
JM
923 const char *cp1;
924 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
f675dbe5 925 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
748a9306 926 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
fa537f88 927 int midx;
f675dbe5
CB
928 unsigned char acmode;
929 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
930 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
931 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
932 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
748a9306 933 {0, 0, 0, 0}};
f675dbe5 934 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
fd8cd3a3
DS
935#if defined(PERL_IMPLICIT_CONTEXT)
936 pTHX = NULL;
fd8cd3a3
DS
937 if (PL_curinterp) {
938 aTHX = PERL_GET_INTERP;
cc077a9f 939 } else {
fd8cd3a3 940 aTHX = NULL;
cc077a9f
HM
941 }
942#endif
748a9306 943
fa537f88 944 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
b7ae7a0d 945 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
946 }
f7ddb74a 947 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
948 *cp2 = _toupper(*cp1);
949 if (cp1 - lnm > LNM$C_NAMLENGTH) {
950 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
951 return 0;
952 }
953 }
954 lnmdsc.dsc$w_length = cp1 - lnm;
955 lnmdsc.dsc$a_pointer = uplnm;
fd7385b9 956 uplnm[lnmdsc.dsc$w_length] = '\0';
f675dbe5
CB
957 secure = flags & PERL__TRNENV_SECURE;
958 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
959 if (!tabvec || !*tabvec) tabvec = env_tables;
960
961 for (curtab = 0; tabvec[curtab]; curtab++) {
962 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
963 if (!ivenv && !secure) {
4e0c9737 964 char *eq;
f675dbe5
CB
965 int i;
966 if (!environ) {
967 ivenv = 1;
ebd4d70b
JM
968#if defined(PERL_IMPLICIT_CONTEXT)
969 if (aTHX == NULL) {
970 fprintf(stderr,
873f5ddf 971 "Can't read CRTL environ\n");
ebd4d70b
JM
972 } else
973#endif
974 Perl_warn(aTHX_ "Can't read CRTL environ\n");
f675dbe5
CB
975 continue;
976 }
977 retsts = SS$_NOLOGNAM;
978 for (i = 0; environ[i]; i++) {
979 if ((eq = strchr(environ[i],'=')) &&
299d126a 980 lnmdsc.dsc$w_length == (eq - environ[i]) &&
f675dbe5
CB
981 !strncmp(environ[i],uplnm,eq - environ[i])) {
982 eq++;
983 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
984 if (!eqvlen) continue;
985 retsts = SS$_NORMAL;
986 break;
987 }
988 }
989 if (retsts != SS$_NOLOGNAM) break;
990 }
991 }
992 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
993 !str$case_blind_compare(&tmpdsc,&clisym)) {
994 if (!ivsym && !secure) {
995 unsigned short int deflen = LNM$C_NAMLENGTH;
996 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
94ae10c0 997 /* dynamic dsc to accommodate possible long value */
ebd4d70b 998 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
f675dbe5
CB
999 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1000 if (retsts & 1) {
2497a41f 1001 if (eqvlen > MAX_DCL_SYMBOL) {
f675dbe5 1002 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
2497a41f 1003 eqvlen = MAX_DCL_SYMBOL;
cc077a9f
HM
1004 /* Special hack--we might be called before the interpreter's */
1005 /* fully initialized, in which case either thr or PL_curcop */
1006 /* might be bogus. We have to check, since ckWARN needs them */
1007 /* both to be valid if running threaded */
8a646e0b
JM
1008#if defined(PERL_IMPLICIT_CONTEXT)
1009 if (aTHX == NULL) {
1010 fprintf(stderr,
873f5ddf 1011 "Value of CLI symbol \"%s\" too long",lnm);
8a646e0b
JM
1012 } else
1013#endif
cc077a9f 1014 if (ckWARN(WARN_MISC)) {
f98bc0c6 1015 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
cc077a9f 1016 }
f675dbe5
CB
1017 }
1018 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1019 }
ebd4d70b 1020 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
f675dbe5
CB
1021 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1022 if (retsts == LIB$_NOSUCHSYM) continue;
1023 break;
1024 }
1025 }
1026 else if (!ivlnm) {
843027b0 1027 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
f7ddb74a
JM
1028 midx = my_maxidx(lnm);
1029 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1030 lnmlst[1].bufadr = cp2;
fa537f88
CB
1031 eqvlen = 0;
1032 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1033 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1034 if (retsts == SS$_NOLOGNAM) break;
1035 /* PPFs have a prefix */
1036 if (
fd7385b9 1037#if INTSIZE == 4
fa537f88 1038 *((int *)uplnm) == *((int *)"SYS$") &&
fd7385b9 1039#endif
fa537f88
CB
1040 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1041 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1042 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1043 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1044 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
18a3d61e 1045 memmove(eqv,eqv+4,eqvlen-4);
fa537f88
CB
1046 eqvlen -= 4;
1047 }
f7ddb74a
JM
1048 cp2 += eqvlen;
1049 *cp2 = '\0';
fa537f88
CB
1050 }
1051 if ((retsts == SS$_IVLOGNAM) ||
1052 (retsts == SS$_NOLOGNAM)) { continue; }
fd7385b9 1053 }
fa537f88 1054 else {
fa537f88
CB
1055 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1056 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1057 if (retsts == SS$_NOLOGNAM) continue;
1058 eqv[eqvlen] = '\0';
1059 }
1060 eqvlen = strlen(eqv);
f675dbe5
CB
1061 break;
1062 }
c07a80fd 1063 }
f675dbe5
CB
1064 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1065 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1066 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1067 retsts == SS$_NOLOGNAM) {
1068 set_errno(EINVAL); set_vaxc_errno(retsts);
748a9306 1069 }
ebd4d70b 1070 else _ckvmssts_noperl(retsts);
f675dbe5
CB
1071 return 0;
1072} /* end of vmstrnenv */
1073/*}}}*/
c07a80fd 1074
f675dbe5
CB
1075/*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1076/* Define as a function so we can access statics. */
4b19af01 1077int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
f675dbe5 1078{
8a646e0b
JM
1079 int flags = 0;
1080
1081#if defined(PERL_IMPLICIT_CONTEXT)
1082 if (aTHX != NULL)
1083#endif
f675dbe5 1084#ifdef SECURE_INTERNAL_GETENV
8a646e0b
JM
1085 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1086 PERL__TRNENV_SECURE : 0;
f675dbe5 1087#endif
8a646e0b
JM
1088
1089 return vmstrnenv(lnm, eqv, idx, fildev, flags);
f675dbe5
CB
1090}
1091/*}}}*/
a0d0e21e
LW
1092
1093/* my_getenv
61bb5906
CB
1094 * Note: Uses Perl temp to store result so char * can be returned to
1095 * caller; this pointer will be invalidated at next Perl statement
1096 * transition.
a6c40364 1097 * We define this as a function rather than a macro in terms of my_getenv_len()
f675dbe5
CB
1098 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1099 * allocate SVs).
a0d0e21e 1100 */
f675dbe5 1101/*{{{ char *my_getenv(const char *lnm, bool sys)*/
a0d0e21e 1102char *
5c84aa53 1103Perl_my_getenv(pTHX_ const char *lnm, bool sys)
a0d0e21e 1104{
f7ddb74a 1105 const char *cp1;
fa537f88 1106 static char *__my_getenv_eqv = NULL;
f7ddb74a 1107 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
c07a80fd 1108 unsigned long int idx = 0;
4e0c9737 1109 int success, secure, saverr, savvmserr;
843027b0 1110 int midx, flags;
61bb5906 1111 SV *tmpsv;
a0d0e21e 1112
f7ddb74a 1113 midx = my_maxidx(lnm) + 1;
fa537f88 1114
6b88bc9c 1115 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
61bb5906
CB
1116 /* Set up a temporary buffer for the return value; Perl will
1117 * clean it up at the next statement transition */
fa537f88 1118 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
61bb5906
CB
1119 if (!tmpsv) return NULL;
1120 eqv = SvPVX(tmpsv);
1121 }
fa537f88
CB
1122 else {
1123 /* Assume no interpreter ==> single thread */
1124 if (__my_getenv_eqv != NULL) {
1125 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1126 }
1127 else {
a02a5408 1128 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1129 }
1130 eqv = __my_getenv_eqv;
1131 }
1132
f7ddb74a 1133 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1134 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
2497a41f 1135 int len;
61bb5906 1136 getcwd(eqv,LNM$C_NAMLENGTH);
2497a41f
JM
1137
1138 len = strlen(eqv);
1139
1140 /* Get rid of "000000/ in rooted filespecs */
1141 if (len > 7) {
1142 char * zeros;
1143 zeros = strstr(eqv, "/000000/");
1144 if (zeros != NULL) {
1145 int mlen;
1146 mlen = len - (zeros - eqv) - 7;
1147 memmove(zeros, &zeros[7], mlen);
1148 len = len - 7;
1149 eqv[len] = '\0';
1150 }
1151 }
61bb5906 1152 return eqv;
748a9306 1153 }
a0d0e21e 1154 else {
2512681b 1155 /* Impose security constraints only if tainting */
bc10a425
CB
1156 if (sys) {
1157 /* Impose security constraints only if tainting */
1158 secure = PL_curinterp ? PL_tainting : will_taint;
1159 saverr = errno; savvmserr = vaxc$errno;
1160 }
843027b0
CB
1161 else {
1162 secure = 0;
1163 }
1164
1165 flags =
f675dbe5 1166#ifdef SECURE_INTERNAL_GETENV
843027b0 1167 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1168#else
843027b0 1169 0
f675dbe5 1170#endif
843027b0
CB
1171 ;
1172
1173 /* For the getenv interface we combine all the equivalence names
1174 * of a search list logical into one value to acquire a maximum
1175 * value length of 255*128 (assuming %ENV is using logicals).
1176 */
1177 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1178
1179 /* If the name contains a semicolon-delimited index, parse it
1180 * off and make sure we only retrieve the equivalence name for
1181 * that index. */
1182 if ((cp2 = strchr(lnm,';')) != NULL) {
1183 strcpy(uplnm,lnm);
1184 uplnm[cp2-lnm] = '\0';
1185 idx = strtoul(cp2+1,NULL,0);
1186 lnm = uplnm;
1187 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1188 }
1189
1190 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1191
bc10a425
CB
1192 /* Discard NOLOGNAM on internal calls since we're often looking
1193 * for an optional name, and this "error" often shows up as the
1194 * (bogus) exit status for a die() call later on. */
1195 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
4e205ed6 1196 return success ? eqv : NULL;
a0d0e21e 1197 }
a0d0e21e
LW
1198
1199} /* end of my_getenv() */
1200/*}}}*/
1201
f675dbe5 1202
a6c40364
GS
1203/*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1204char *
fd8cd3a3 1205Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
f675dbe5 1206{
f7ddb74a
JM
1207 const char *cp1;
1208 char *buf, *cp2;
a6c40364 1209 unsigned long idx = 0;
843027b0 1210 int midx, flags;
fa537f88 1211 static char *__my_getenv_len_eqv = NULL;
bc10a425 1212 int secure, saverr, savvmserr;
cc077a9f
HM
1213 SV *tmpsv;
1214
f7ddb74a 1215 midx = my_maxidx(lnm) + 1;
fa537f88 1216
cc077a9f
HM
1217 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1218 /* Set up a temporary buffer for the return value; Perl will
1219 * clean it up at the next statement transition */
fa537f88 1220 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
cc077a9f
HM
1221 if (!tmpsv) return NULL;
1222 buf = SvPVX(tmpsv);
1223 }
fa537f88
CB
1224 else {
1225 /* Assume no interpreter ==> single thread */
1226 if (__my_getenv_len_eqv != NULL) {
1227 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1228 }
1229 else {
a02a5408 1230 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1231 }
1232 buf = __my_getenv_len_eqv;
1233 }
1234
f7ddb74a 1235 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1236 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
f7ddb74a
JM
1237 char * zeros;
1238
f675dbe5 1239 getcwd(buf,LNM$C_NAMLENGTH);
a6c40364 1240 *len = strlen(buf);
f7ddb74a
JM
1241
1242 /* Get rid of "000000/ in rooted filespecs */
1243 if (*len > 7) {
1244 zeros = strstr(buf, "/000000/");
1245 if (zeros != NULL) {
1246 int mlen;
1247 mlen = *len - (zeros - buf) - 7;
1248 memmove(zeros, &zeros[7], mlen);
1249 *len = *len - 7;
1250 buf[*len] = '\0';
1251 }
1252 }
a6c40364 1253 return buf;
f675dbe5
CB
1254 }
1255 else {
bc10a425
CB
1256 if (sys) {
1257 /* Impose security constraints only if tainting */
1258 secure = PL_curinterp ? PL_tainting : will_taint;
1259 saverr = errno; savvmserr = vaxc$errno;
1260 }
843027b0
CB
1261 else {
1262 secure = 0;
1263 }
1264
1265 flags =
f675dbe5 1266#ifdef SECURE_INTERNAL_GETENV
843027b0 1267 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1268#else
843027b0 1269 0
f675dbe5 1270#endif
843027b0
CB
1271 ;
1272
1273 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1274
1275 if ((cp2 = strchr(lnm,';')) != NULL) {
1276 strcpy(buf,lnm);
1277 buf[cp2-lnm] = '\0';
1278 idx = strtoul(cp2+1,NULL,0);
1279 lnm = buf;
1280 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1281 }
1282
1283 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1284
f7ddb74a
JM
1285 /* Get rid of "000000/ in rooted filespecs */
1286 if (*len > 7) {
1287 char * zeros;
1288 zeros = strstr(buf, "/000000/");
1289 if (zeros != NULL) {
1290 int mlen;
1291 mlen = *len - (zeros - buf) - 7;
1292 memmove(zeros, &zeros[7], mlen);
1293 *len = *len - 7;
1294 buf[*len] = '\0';
1295 }
1296 }
1297
bc10a425
CB
1298 /* Discard NOLOGNAM on internal calls since we're often looking
1299 * for an optional name, and this "error" often shows up as the
1300 * (bogus) exit status for a die() call later on. */
1301 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
4e205ed6 1302 return *len ? buf : NULL;
f675dbe5
CB
1303 }
1304
a6c40364 1305} /* end of my_getenv_len() */
f675dbe5
CB
1306/*}}}*/
1307
8a646e0b 1308static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
8fde5078
CB
1309
1310static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1e422769 1311
740ce14c 1312/*{{{ void prime_env_iter() */
1313void
1314prime_env_iter(void)
1315/* Fill the %ENV associative array with all logical names we can
1316 * find, in preparation for iterating over it.
1317 */
1318{
17f28c40 1319 static int primed = 0;
3eeba6fb 1320 HV *seenhv = NULL, *envhv;
22be8b3c 1321 SV *sv = NULL;
4e205ed6 1322 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
8fde5078
CB
1323 unsigned short int chan;
1324#ifndef CLI$M_TRUSTED
1325# define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1326#endif
f675dbe5 1327 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
4e0c9737 1328 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
f675dbe5
CB
1329 long int i;
1330 bool have_sym = FALSE, have_lnm = FALSE;
1331 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1332 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1333 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1334 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1335 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
fd8cd3a3
DS
1336#if defined(PERL_IMPLICIT_CONTEXT)
1337 pTHX;
1338#endif
3db8f154 1339#if defined(USE_ITHREADS)
b2b3adea
HM
1340 static perl_mutex primenv_mutex;
1341 MUTEX_INIT(&primenv_mutex);
61bb5906 1342#endif
740ce14c 1343
fd8cd3a3
DS
1344#if defined(PERL_IMPLICIT_CONTEXT)
1345 /* We jump through these hoops because we can be called at */
1346 /* platform-specific initialization time, which is before anything is */
1347 /* set up--we can't even do a plain dTHX since that relies on the */
1348 /* interpreter structure to be initialized */
fd8cd3a3
DS
1349 if (PL_curinterp) {
1350 aTHX = PERL_GET_INTERP;
1351 } else {
ebd4d70b
JM
1352 /* we never get here because the NULL pointer will cause the */
1353 /* several of the routines called by this routine to access violate */
1354
1355 /* This routine is only called by hv.c/hv_iterinit which has a */
1356 /* context, so the real fix may be to pass it through instead of */
1357 /* the hoops above */
fd8cd3a3
DS
1358 aTHX = NULL;
1359 }
1360#endif
fd8cd3a3 1361
3eeba6fb 1362 if (primed || !PL_envgv) return;
61bb5906
CB
1363 MUTEX_LOCK(&primenv_mutex);
1364 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
3eeba6fb 1365 envhv = GvHVn(PL_envgv);
740ce14c 1366 /* Perform a dummy fetch as an lval to insure that the hash table is
8fde5078 1367 * set up. Otherwise, the hv_store() will turn into a nullop. */
740ce14c 1368 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
740ce14c 1369
f675dbe5
CB
1370 for (i = 0; env_tables[i]; i++) {
1371 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1372 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
f02a1854 1373 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
8fde5078 1374 }
f675dbe5
CB
1375 if (have_sym || have_lnm) {
1376 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1377 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1378 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1379 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
61bb5906 1380 }
f675dbe5
CB
1381
1382 for (i--; i >= 0; i--) {
1383 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1384 char *start;
1385 int j;
1386 for (j = 0; environ[j]; j++) {
1387 if (!(start = strchr(environ[j],'='))) {
3eeba6fb 1388 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1389 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
f675dbe5
CB
1390 }
1391 else {
1392 start++;
22be8b3c
CB
1393 sv = newSVpv(start,0);
1394 SvTAINTED_on(sv);
1395 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
f675dbe5
CB
1396 }
1397 }
1398 continue;
740ce14c 1399 }
f675dbe5
CB
1400 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1401 !str$case_blind_compare(&tmpdsc,&clisym)) {
1402 strcpy(cmd,"Show Symbol/Global *");
1403 cmddsc.dsc$w_length = 20;
1404 if (env_tables[i]->dsc$w_length == 12 &&
1405 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1406 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1407 flags = defflags | CLI$M_NOLOGNAM;
1408 }
1409 else {
1410 strcpy(cmd,"Show Logical *");
1411 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1412 strcat(cmd," /Table=");
1413 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1414 cmddsc.dsc$w_length = strlen(cmd);
1415 }
1416 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1417 flags = defflags | CLI$M_NOCLISYM;
1418 }
1419
1420 /* Create a new subprocess to execute each command, to exclude the
1421 * remote possibility that someone could subvert a mbx or file used
1422 * to write multiple commands to a single subprocess.
1423 */
1424 do {
1425 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1426 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1427 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1428 defflags &= ~CLI$M_TRUSTED;
1429 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1430 _ckvmssts(retsts);
a02a5408 1431 if (!buf) Newx(buf,mbxbufsiz + 1,char);
f675dbe5
CB
1432 if (seenhv) SvREFCNT_dec(seenhv);
1433 seenhv = newHV();
1434 while (1) {
1435 char *cp1, *cp2, *key;
1436 unsigned long int sts, iosb[2], retlen, keylen;
1437 register U32 hash;
1438
1439 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1440 if (sts & 1) sts = iosb[0] & 0xffff;
1441 if (sts == SS$_ENDOFFILE) {
1442 int wakect = 0;
1443 while (substs == 0) { sys$hiber(); wakect++;}
1444 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1445 _ckvmssts(substs);
1446 break;
1447 }
1448 _ckvmssts(sts);
1449 retlen = iosb[0] >> 16;
1450 if (!retlen) continue; /* blank line */
1451 buf[retlen] = '\0';
1452 if (iosb[1] != subpid) {
1453 if (iosb[1]) {
5c84aa53 1454 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
f675dbe5
CB
1455 }
1456 continue;
1457 }
3eeba6fb 1458 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
f98bc0c6 1459 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
f675dbe5
CB
1460
1461 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1462 if (*cp1 == '(' || /* Logical name table name */
1463 *cp1 == '=' /* Next eqv of searchlist */) continue;
1464 if (*cp1 == '"') cp1++;
1465 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1466 key = cp1; keylen = cp2 - cp1;
1467 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1468 while (*cp2 && *cp2 != '=') cp2++;
1f47e8e2
CB
1469 while (*cp2 && *cp2 == '=') cp2++;
1470 while (*cp2 && *cp2 == ' ') cp2++;
1471 if (*cp2 == '"') { /* String translation; may embed "" */
1472 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1473 cp2++; cp1--; /* Skip "" surrounding translation */
1474 }
1475 else { /* Numeric translation */
1476 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1477 cp1--; /* stop on last non-space char */
1478 }
1479 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
f98bc0c6 1480 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
edc7bc49
CB
1481 continue;
1482 }
5afd6d42 1483 PERL_HASH(hash,key,keylen);
ff79d39d
CB
1484
1485 if (cp1 == cp2 && *cp2 == '.') {
1486 /* A single dot usually means an unprintable character, such as a null
1487 * to indicate a zero-length value. Get the actual value to make sure.
1488 */
1489 char lnm[LNM$C_NAMLENGTH+1];
2497a41f 1490 char eqv[MAX_DCL_SYMBOL+1];
0faef845 1491 int trnlen;
ff79d39d 1492 strncpy(lnm, key, keylen);
0faef845 1493 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
ff79d39d
CB
1494 sv = newSVpvn(eqv, strlen(eqv));
1495 }
1496 else {
1497 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1498 }
1499
22be8b3c
CB
1500 SvTAINTED_on(sv);
1501 hv_store(envhv,key,keylen,sv,hash);
f675dbe5 1502 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
edc7bc49 1503 }
f675dbe5
CB
1504 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1505 /* get the PPFs for this process, not the subprocess */
f7ddb74a 1506 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
f675dbe5
CB
1507 char eqv[LNM$C_NAMLENGTH+1];
1508 int trnlen, i;
1509 for (i = 0; ppfs[i]; i++) {
1510 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
22be8b3c
CB
1511 sv = newSVpv(eqv,trnlen);
1512 SvTAINTED_on(sv);
1513 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
f675dbe5 1514 }
740ce14c 1515 }
1516 }
f675dbe5
CB
1517 primed = 1;
1518 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1519 if (buf) Safefree(buf);
1520 if (seenhv) SvREFCNT_dec(seenhv);
1521 MUTEX_UNLOCK(&primenv_mutex);
1522 return;
1523
740ce14c 1524} /* end of prime_env_iter */
1525/*}}}*/
740ce14c 1526
f675dbe5 1527
2c590a56 1528/*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
1529/* Define or delete an element in the same "environment" as
1530 * vmstrnenv(). If an element is to be deleted, it's removed from
1531 * the first place it's found. If it's to be set, it's set in the
1532 * place designated by the first element of the table vector.
3eeba6fb 1533 * Like setenv() returns 0 for success, non-zero on error.
a0d0e21e 1534 */
f675dbe5 1535int
2c590a56 1536Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
a0d0e21e 1537{
f7ddb74a
JM
1538 const char *cp1;
1539 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
f675dbe5 1540 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
fa537f88 1541 int nseg = 0, j;
a0d0e21e 1542 unsigned long int retsts, usermode = PSL$C_USER;
fa537f88 1543 struct itmlst_3 *ile, *ilist;
a0d0e21e 1544 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
f675dbe5
CB
1545 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1546 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1547 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1548 $DESCRIPTOR(local,"_LOCAL");
1549
ed253963
CB
1550 if (!lnm) {
1551 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1552 return SS$_IVLOGNAM;
1553 }
1554
f7ddb74a 1555 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
1556 *cp2 = _toupper(*cp1);
1557 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1558 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1559 return SS$_IVLOGNAM;
1560 }
1561 }
a0d0e21e 1562 lnmdsc.dsc$w_length = cp1 - lnm;
f675dbe5
CB
1563 if (!tabvec || !*tabvec) tabvec = env_tables;
1564
3eeba6fb 1565 if (!eqv) { /* we're deleting n element */
f675dbe5
CB
1566 for (curtab = 0; tabvec[curtab]; curtab++) {
1567 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1568 int i;
299d126a 1569 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
f675dbe5 1570 if ((cp1 = strchr(environ[i],'=')) &&
299d126a 1571 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
f675dbe5 1572 !strncmp(environ[i],lnm,cp1 - environ[i])) {
3eeba6fb 1573#ifdef HAS_SETENV
0e06870b 1574 return setenv(lnm,"",1) ? vaxc$errno : 0;
f675dbe5
CB
1575 }
1576 }
1577 ivenv = 1; retsts = SS$_NOLOGNAM;
1578#else
3eeba6fb 1579 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1580 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
3eeba6fb
CB
1581 ivenv = 1; retsts = SS$_NOSUCHPGM;
1582 break;
1583 }
1584 }
f675dbe5
CB
1585#endif
1586 }
1587 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1588 !str$case_blind_compare(&tmpdsc,&clisym)) {
1589 unsigned int symtype;
1590 if (tabvec[curtab]->dsc$w_length == 12 &&
1591 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1592 !str$case_blind_compare(&tmpdsc,&local))
1593 symtype = LIB$K_CLI_LOCAL_SYM;
1594 else symtype = LIB$K_CLI_GLOBAL_SYM;
1595 retsts = lib$delete_symbol(&lnmdsc,&symtype);
3eeba6fb
CB
1596 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1597 if (retsts == LIB$_NOSUCHSYM) continue;
f675dbe5
CB
1598 break;
1599 }
1600 else if (!ivlnm) {
1601 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1602 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1603 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1604 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1605 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1606 }
a0d0e21e
LW
1607 }
1608 }
f675dbe5
CB
1609 else { /* we're defining a value */
1610 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1611#ifdef HAS_SETENV
3eeba6fb 1612 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
f675dbe5 1613#else
3eeba6fb 1614 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1615 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
f675dbe5
CB
1616 retsts = SS$_NOSUCHPGM;
1617#endif
1618 }
1619 else {
f7ddb74a 1620 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
f675dbe5
CB
1621 eqvdsc.dsc$w_length = strlen(eqv);
1622 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1623 !str$case_blind_compare(&tmpdsc,&clisym)) {
1624 unsigned int symtype;
1625 if (tabvec[0]->dsc$w_length == 12 &&
1626 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1627 !str$case_blind_compare(&tmpdsc,&local))
1628 symtype = LIB$K_CLI_LOCAL_SYM;
1629 else symtype = LIB$K_CLI_GLOBAL_SYM;
1630 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1631 }
3eeba6fb
CB
1632 else {
1633 if (!*eqv) eqvdsc.dsc$w_length = 1;
a1dfe751 1634 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
fa537f88
CB
1635
1636 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1637 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1638 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1639 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1640 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1641 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1642 }
1643
a02a5408 1644 Newx(ilist,nseg+1,struct itmlst_3);
fa537f88
CB
1645 ile = ilist;
1646 if (!ile) {
1647 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1648 return SS$_INSFMEM;
a1dfe751 1649 }
fa537f88
CB
1650 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1651
1652 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1653 ile->itmcode = LNM$_STRING;
1654 ile->bufadr = c;
1655 if ((j+1) == nseg) {
1656 ile->buflen = strlen(c);
1657 /* in case we are truncating one that's too long */
1658 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1659 }
1660 else {
1661 ile->buflen = LNM$C_NAMLENGTH;
1662 }
1663 }
1664
1665 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1666 Safefree (ilist);
1667 }
1668 else {
1669 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
a1dfe751 1670 }
3eeba6fb 1671 }
f675dbe5
CB
1672 }
1673 }
1674 if (!(retsts & 1)) {
1675 switch (retsts) {
1676 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1677 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1678 set_errno(EVMSERR); break;
1679 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1680 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1681 set_errno(EINVAL); break;
1682 case SS$_NOPRIV:
7d2497bf 1683 set_errno(EACCES); break;
f675dbe5
CB
1684 default:
1685 _ckvmssts(retsts);
1686 set_errno(EVMSERR);
1687 }
1688 set_vaxc_errno(retsts);
1689 return (int) retsts || 44; /* retsts should never be 0, but just in case */
a0d0e21e 1690 }
3eeba6fb
CB
1691 else {
1692 /* We reset error values on success because Perl does an hv_fetch()
1693 * before each hv_store(), and if the thing we're setting didn't
1694 * previously exist, we've got a leftover error message. (Of course,
1695 * this fails in the face of
1696 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1697 * in that the error reported in $! isn't spurious,
1698 * but it's right more often than not.)
1699 */
f675dbe5
CB
1700 set_errno(0); set_vaxc_errno(retsts);
1701 return 0;
1702 }
1703
1704} /* end of vmssetenv() */
1705/*}}}*/
a0d0e21e 1706
2c590a56 1707/*{{{ void my_setenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
1708/* This has to be a function since there's a prototype for it in proto.h */
1709void
2c590a56 1710Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
f675dbe5 1711{
bc10a425
CB
1712 if (lnm && *lnm) {
1713 int len = strlen(lnm);
1714 if (len == 7) {
1715 char uplnm[8];
22d4bb9c
CB
1716 int i;
1717 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
bc10a425 1718 if (!strcmp(uplnm,"DEFAULT")) {
7ded3206 1719 if (eqv && *eqv) my_chdir(eqv);
bc10a425
CB
1720 return;
1721 }
1722 }
1723#ifndef RTL_USES_UTC
1724 if (len == 6 || len == 2) {
1725 char uplnm[7];
1726 int i;
1727 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1728 uplnm[len] = '\0';
1729 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1730 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
22d4bb9c
CB
1731 }
1732#endif
1733 }
f675dbe5
CB
1734 (void) vmssetenv(lnm,eqv,NULL);
1735}
a0d0e21e
LW
1736/*}}}*/
1737
27c67b75 1738/*{{{static void vmssetuserlnm(char *name, char *eqv); */
0e06870b
CB
1739/* vmssetuserlnm
1740 * sets a user-mode logical in the process logical name table
1741 * used for redirection of sys$error
4d9538c1
JM
1742 *
1743 * Fix-me: The pTHX is not needed for this routine, however doio.c
1744 * is calling it with one instead of using a macro.
1745 * A macro needs to be added to vmsish.h and doio.c updated to use it.
1746 *
0e06870b
CB
1747 */
1748void
2fbb330f 1749Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
0e06870b
CB
1750{
1751 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1752 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
2d5e9e5d 1753 unsigned long int iss, attr = LNM$M_CONFINE;
0e06870b
CB
1754 unsigned char acmode = PSL$C_USER;
1755 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1756 {0, 0, 0, 0}};
2fbb330f 1757 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
0e06870b
CB
1758 d_name.dsc$w_length = strlen(name);
1759
1760 lnmlst[0].buflen = strlen(eqv);
2fbb330f 1761 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
0e06870b
CB
1762
1763 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1764 if (!(iss&1)) lib$signal(iss);
1765}
1766/*}}}*/
c07a80fd 1767
f675dbe5 1768
c07a80fd 1769/*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1770/* my_crypt - VMS password hashing
1771 * my_crypt() provides an interface compatible with the Unix crypt()
1772 * C library function, and uses sys$hash_password() to perform VMS
1773 * password hashing. The quadword hashed password value is returned
1774 * as a NUL-terminated 8 character string. my_crypt() does not change
1775 * the case of its string arguments; in order to match the behavior
1776 * of LOGINOUT et al., alphabetic characters in both arguments must
1777 * be upcased by the caller.
2497a41f
JM
1778 *
1779 * - fix me to call ACM services when available
c07a80fd 1780 */
1781char *
fd8cd3a3 1782Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
c07a80fd 1783{
1784# ifndef UAI$C_PREFERRED_ALGORITHM
1785# define UAI$C_PREFERRED_ALGORITHM 127
1786# endif
1787 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1788 unsigned short int salt = 0;
1789 unsigned long int sts;
1790 struct const_dsc {
1791 unsigned short int dsc$w_length;
1792 unsigned char dsc$b_type;
1793 unsigned char dsc$b_class;
1794 const char * dsc$a_pointer;
1795 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1796 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1797 struct itmlst_3 uailst[3] = {
1798 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1799 { sizeof salt, UAI$_SALT, &salt, 0},
1800 { 0, 0, NULL, NULL}};
1801 static char hash[9];
1802
1803 usrdsc.dsc$w_length = strlen(usrname);
1804 usrdsc.dsc$a_pointer = usrname;
1805 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1806 switch (sts) {
f282b18d 1807 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
c07a80fd 1808 set_errno(EACCES);
1809 break;
1810 case RMS$_RNF:
1811 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1812 break;
1813 default:
1814 set_errno(EVMSERR);
1815 }
1816 set_vaxc_errno(sts);
1817 if (sts != RMS$_RNF) return NULL;
1818 }
1819
1820 txtdsc.dsc$w_length = strlen(textpasswd);
1821 txtdsc.dsc$a_pointer = textpasswd;
1822 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1823 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1824 }
1825
1826 return (char *) hash;
1827
1828} /* end of my_crypt() */
1829/*}}}*/
1830
1831
360732b5
JM
1832static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1833static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1834static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
a0d0e21e 1835
2497a41f
JM
1836/* fixup barenames that are directories for internal use.
1837 * There have been problems with the consistent handling of UNIX
1838 * style directory names when routines are presented with a name that
94ae10c0 1839 * has no directory delimiters at all. So this routine will eventually
2497a41f
JM
1840 * fix the issue.
1841 */
1842static char * fixup_bare_dirnames(const char * name)
1843{
1844 if (decc_disable_to_vms_logname_translation) {
1845/* fix me */
1846 }
1847 return NULL;
1848}
1849
e0e5e8d6
JM
1850/* 8.3, remove() is now broken on symbolic links */
1851static int rms_erase(const char * vmsname);
1852
1853
2497a41f 1854/* mp_do_kill_file
94ae10c0 1855 * A little hack to get around a bug in some implementation of remove()
2497a41f
JM
1856 * that do not know how to delete a directory
1857 *
1858 * Delete any file to which user has control access, regardless of whether
1859 * delete access is explicitly allowed.
1860 * Limitations: User must have write access to parent directory.
1861 * Does not block signals or ASTs; if interrupted in midstream
1862 * may leave file with an altered ACL.
1863 * HANDLE WITH CARE!
1864 */
1865/*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1866static int
1867mp_do_kill_file(pTHX_ const char *name, int dirflag)
1868{
e0e5e8d6
JM
1869 char *vmsname;
1870 char *rslt;
2497a41f
JM
1871 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1872 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1873 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1874 struct myacedef {
1875 unsigned char myace$b_length;
1876 unsigned char myace$b_type;
1877 unsigned short int myace$w_flags;
1878 unsigned long int myace$l_access;
1879 unsigned long int myace$l_ident;
1880 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1881 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1882 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1883 struct itmlst_3
1884 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1885 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1886 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1887 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1888 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1889 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1890
1891 /* Expand the input spec using RMS, since the CRTL remove() and
1892 * system services won't do this by themselves, so we may miss
1893 * a file "hiding" behind a logical name or search list. */
c5375c28 1894 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
ebd4d70b 1895 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 1896
6fb6c614 1897 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
e0e5e8d6 1898 if (rslt == NULL) {
c5375c28 1899 PerlMem_free(vmsname);
2497a41f
JM
1900 return -1;
1901 }
c5375c28 1902
e0e5e8d6
JM
1903 /* Erase the file */
1904 rmsts = rms_erase(vmsname);
2497a41f 1905
e0e5e8d6
JM
1906 /* Did it succeed */
1907 if ($VMS_STATUS_SUCCESS(rmsts)) {
1908 PerlMem_free(vmsname);
1909 return 0;
2497a41f
JM
1910 }
1911
1912 /* If not, can changing protections help? */
e0e5e8d6
JM
1913 if (rmsts != RMS$_PRV) {
1914 set_vaxc_errno(rmsts);
1915 PerlMem_free(vmsname);
2497a41f
JM
1916 return -1;
1917 }
1918
1919 /* No, so we get our own UIC to use as a rights identifier,
1920 * and the insert an ACE at the head of the ACL which allows us
1921 * to delete the file.
1922 */
ebd4d70b 1923 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
e0e5e8d6
JM
1924 fildsc.dsc$w_length = strlen(vmsname);
1925 fildsc.dsc$a_pointer = vmsname;
2497a41f
JM
1926 cxt = 0;
1927 newace.myace$l_ident = oldace.myace$l_ident;
e0e5e8d6 1928 rmsts = -1;
2497a41f
JM
1929 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1930 switch (aclsts) {
1931 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1932 set_errno(ENOENT); break;
1933 case RMS$_DIR:
1934 set_errno(ENOTDIR); break;
1935 case RMS$_DEV:
1936 set_errno(ENODEV); break;
1937 case RMS$_SYN: case SS$_INVFILFOROP:
1938 set_errno(EINVAL); break;
1939 case RMS$_PRV:
1940 set_errno(EACCES); break;
1941 default:
ebd4d70b 1942 _ckvmssts_noperl(aclsts);
2497a41f
JM
1943 }
1944 set_vaxc_errno(aclsts);
e0e5e8d6 1945 PerlMem_free(vmsname);
2497a41f
JM
1946 return -1;
1947 }
1948 /* Grab any existing ACEs with this identifier in case we fail */
1949 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1950 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1951 || fndsts == SS$_NOMOREACE ) {
1952 /* Add the new ACE . . . */
1953 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1954 goto yourroom;
1955
e0e5e8d6
JM
1956 rmsts = rms_erase(vmsname);
1957 if ($VMS_STATUS_SUCCESS(rmsts)) {
1958 rmsts = 0;
2497a41f
JM
1959 }
1960 else {
e0e5e8d6 1961 rmsts = -1;
2497a41f
JM
1962 /* We blew it - dir with files in it, no write priv for
1963 * parent directory, etc. Put things back the way they were. */
1964 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1965 goto yourroom;
1966 if (fndsts & 1) {
1967 addlst[0].bufadr = &oldace;
1968 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1969 goto yourroom;
1970 }
1971 }
1972 }
1973
1974 yourroom:
1975 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1976 /* We just deleted it, so of course it's not there. Some versions of
1977 * VMS seem to return success on the unlock operation anyhow (after all
1978 * the unlock is successful), but others don't.
1979 */
1980 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1981 if (aclsts & 1) aclsts = fndsts;
1982 if (!(aclsts & 1)) {
1983 set_errno(EVMSERR);
1984 set_vaxc_errno(aclsts);
2497a41f
JM
1985 }
1986
e0e5e8d6 1987 PerlMem_free(vmsname);
2497a41f
JM
1988 return rmsts;
1989
1990} /* end of kill_file() */
1991/*}}}*/
1992
1993
a0d0e21e
LW
1994/*{{{int do_rmdir(char *name)*/
1995int
b8ffc8df 1996Perl_do_rmdir(pTHX_ const char *name)
a0d0e21e 1997{
e0e5e8d6 1998 char * dirfile;
a0d0e21e 1999 int retval;
61bb5906 2000 Stat_t st;
a0d0e21e 2001
d94c5a78
JM
2002 /* lstat returns a VMS fileified specification of the name */
2003 /* that is looked up, and also lets verifies that this is a directory */
e0e5e8d6 2004
46c05374 2005 retval = flex_lstat(name, &st);
d94c5a78
JM
2006 if (retval != 0) {
2007 char * ret_spec;
2008
2009 /* Due to a historical feature, flex_stat/lstat can not see some */
2010 /* Unix format file names that the rest of the CRTL can see */
2011 /* Fixing that feature will cause some perl tests to fail */
2012 /* So try this one more time. */
2013
2014 retval = lstat(name, &st.crtl_stat);
2015 if (retval != 0)
2016 return -1;
2017
2018 /* force it to a file spec for the kill file to work. */
2019 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
2020 if (ret_spec == NULL) {
2021 errno = EIO;
2022 return -1;
2023 }
e0e5e8d6 2024 }
d94c5a78
JM
2025
2026 if (!S_ISDIR(st.st_mode)) {
e0e5e8d6
JM
2027 errno = ENOTDIR;
2028 retval = -1;
2029 }
d94c5a78
JM
2030 else {
2031 dirfile = st.st_devnam;
2032
2033 /* It may be possible for flex_stat to find a file and vmsify() to */
2034 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
2035 /* with that case, so fail it */
2036 if (dirfile[0] == 0) {
2037 errno = EIO;
2038 return -1;
2039 }
2040
e0e5e8d6 2041 retval = mp_do_kill_file(aTHX_ dirfile, 1);
d94c5a78 2042 }
e0e5e8d6 2043
a0d0e21e
LW
2044 return retval;
2045
2046} /* end of do_rmdir */
2047/*}}}*/
2048
2049/* kill_file
2050 * Delete any file to which user has control access, regardless of whether
2051 * delete access is explicitly allowed.
2052 * Limitations: User must have write access to parent directory.
2053 * Does not block signals or ASTs; if interrupted in midstream
2054 * may leave file with an altered ACL.
2055 * HANDLE WITH CARE!
2056 */
2057/*{{{int kill_file(char *name)*/
2058int
b8ffc8df 2059Perl_kill_file(pTHX_ const char *name)
a0d0e21e 2060{
d94c5a78 2061 char * vmsfile;
e0e5e8d6
JM
2062 Stat_t st;
2063 int rmsts;
a0d0e21e 2064
d94c5a78
JM
2065 /* Convert the filename to VMS format and see if it is a directory */
2066 /* flex_lstat returns a vmsified file specification */
46c05374 2067 rmsts = flex_lstat(name, &st);
d94c5a78
JM
2068 if (rmsts != 0) {
2069
2070 /* Due to a historical feature, flex_stat/lstat can not see some */
2071 /* Unix format file names that the rest of the CRTL can see when */
2072 /* ODS-2 file specifications are in use. */
2073 /* Fixing that feature will cause some perl tests to fail */
2074 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2075 st.st_mode = 0;
2076 vmsfile = (char *) name; /* cast ok */
2077
2078 } else {
2079 vmsfile = st.st_devnam;
2080 if (vmsfile[0] == 0) {
2081 /* It may be possible for flex_stat to find a file and vmsify() */
2082 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2083 /* deal with that case, so fail it */
2084 errno = EIO;
2085 return -1;
2086 }
2087 }
2088
2089 /* Remove() is allowed to delete directories, according to the X/Open
2090 * specifications.
2091 * This may need special handling to work with the ACL hacks.
a0d0e21e 2092 */
d94c5a78
JM
2093 if (S_ISDIR(st.st_mode)) {
2094 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2095 return rmsts;
a0d0e21e
LW
2096 }
2097
d94c5a78
JM
2098 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2099
2100 /* Need to delete all versions ? */
2101 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2102 int i = 0;
2103
2104 /* Just use lstat() here as do not need st_dev */
2105 /* and we know that the file is in VMS format or that */
2106 /* because of a historical bug, flex_stat can not see the file */
2107 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2108 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2109 if (rmsts != 0)
2110 break;
2111 i++;
2112
2113 /* Make sure that we do not loop forever */
2114 if (i > 32767) {
2115 errno = EIO;
2116 rmsts = -1;
2117 break;
2118 }
2119 }
2120 }
a0d0e21e
LW
2121
2122 return rmsts;
2123
2124} /* end of kill_file() */
2125/*}}}*/
2126
8cc95fdb 2127
84902520 2128/*{{{int my_mkdir(char *,Mode_t)*/
8cc95fdb 2129int
b8ffc8df 2130Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
8cc95fdb 2131{
2132 STRLEN dirlen = strlen(dir);
2133
a2a90019
CB
2134 /* zero length string sometimes gives ACCVIO */
2135 if (dirlen == 0) return -1;
2136
8cc95fdb 2137 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2138 * null file name/type. However, it's commonplace under Unix,
2139 * so we'll allow it for a gain in portability.
2140 */
2141 if (dir[dirlen-1] == '/') {
2142 char *newdir = savepvn(dir,dirlen-1);
2143 int ret = mkdir(newdir,mode);
2144 Safefree(newdir);
2145 return ret;
2146 }
2147 else return mkdir(dir,mode);
2148} /* end of my_mkdir */
2149/*}}}*/
2150
ee8c7f54
CB
2151/*{{{int my_chdir(char *)*/
2152int
b8ffc8df 2153Perl_my_chdir(pTHX_ const char *dir)
ee8c7f54
CB
2154{
2155 STRLEN dirlen = strlen(dir);
ee8c7f54
CB
2156
2157 /* zero length string sometimes gives ACCVIO */
2158 if (dirlen == 0) return -1;
f7ddb74a
JM
2159 const char *dir1;
2160
2161 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2162 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2163 * so that existing scripts do not need to be changed.
2164 */
2165 dir1 = dir;
2166 while ((dirlen > 0) && (*dir1 == ' ')) {
2167 dir1++;
2168 dirlen--;
2169 }
ee8c7f54
CB
2170
2171 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2172 * that implies
2173 * null file name/type. However, it's commonplace under Unix,
2174 * so we'll allow it for a gain in portability.
f7ddb74a 2175 *
4d9538c1 2176 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
ee8c7f54 2177 */
f7ddb74a 2178 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
4d9538c1
JM
2179 char *newdir;
2180 int ret;
2181 newdir = PerlMem_malloc(dirlen);
2182 if (newdir ==NULL)
2183 _ckvmssts_noperl(SS$_INSFMEM);
2184 strncpy(newdir, dir1, dirlen-1);
2185 newdir[dirlen-1] = '\0';
2186 ret = chdir(newdir);
2187 PerlMem_free(newdir);
2188 return ret;
ee8c7f54 2189 }
dca5a913 2190 else return chdir(dir1);
ee8c7f54
CB
2191} /* end of my_chdir */
2192/*}}}*/
8cc95fdb 2193
674d6c38 2194
f1db9cda
JM
2195/*{{{int my_chmod(char *, mode_t)*/
2196int
2197Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2198{
4d9538c1
JM
2199 Stat_t st;
2200 int ret = -1;
2201 char * changefile;
f1db9cda
JM
2202 STRLEN speclen = strlen(file_spec);
2203
2204 /* zero length string sometimes gives ACCVIO */
2205 if (speclen == 0) return -1;
2206
2207 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2208 * that implies null file name/type. However, it's commonplace under Unix,
2209 * so we'll allow it for a gain in portability.
2210 *
2211 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2212 * in VMS file.dir notation.
2213 */
4d9538c1
JM
2214 changefile = (char *) file_spec; /* cast ok */
2215 ret = flex_lstat(file_spec, &st);
2216 if (ret != 0) {
f1db9cda 2217
4d9538c1
JM
2218 /* Due to a historical feature, flex_stat/lstat can not see some */
2219 /* Unix format file names that the rest of the CRTL can see when */
2220 /* ODS-2 file specifications are in use. */
2221 /* Fixing that feature will cause some perl tests to fail */
2222 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2223 st.st_mode = 0;
f1db9cda 2224
4d9538c1
JM
2225 } else {
2226 /* It may be possible to get here with nothing in st_devname */
2227 /* chmod still may work though */
2228 if (st.st_devnam[0] != 0) {
2229 changefile = st.st_devnam;
2230 }
f1db9cda 2231 }
4d9538c1
JM
2232 ret = chmod(changefile, mode);
2233 return ret;
f1db9cda
JM
2234} /* end of my_chmod */
2235/*}}}*/
2236
2237
674d6c38
CB
2238/*{{{FILE *my_tmpfile()*/
2239FILE *
2240my_tmpfile(void)
2241{
2242 FILE *fp;
2243 char *cp;
674d6c38
CB
2244
2245 if ((fp = tmpfile())) return fp;
2246
c5375c28
JM
2247 cp = PerlMem_malloc(L_tmpnam+24);
2248 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2249
2497a41f
JM
2250 if (decc_filename_unix_only == 0)
2251 strcpy(cp,"Sys$Scratch:");
2252 else
2253 strcpy(cp,"/tmp/");
674d6c38
CB
2254 tmpnam(cp+strlen(cp));
2255 strcat(cp,".Perltmp");
2256 fp = fopen(cp,"w+","fop=dlt");
c5375c28 2257 PerlMem_free(cp);
674d6c38
CB
2258 return fp;
2259}
2260/*}}}*/
2261
5c2d7af2
CB
2262
2263#ifndef HOMEGROWN_POSIX_SIGNALS
2264/*
2265 * The C RTL's sigaction fails to check for invalid signal numbers so we
2266 * help it out a bit. The docs are correct, but the actual routine doesn't
2267 * do what the docs say it will.
2268 */
2269/*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2270int
2271Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2272 struct sigaction* oact)
2273{
2274 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2275 SETERRNO(EINVAL, SS$_INVARG);
2276 return -1;
2277 }
2278 return sigaction(sig, act, oact);
2279}
2280/*}}}*/
2281#endif
2282
f2610a60
CL
2283#ifdef KILL_BY_SIGPRC
2284#include <errnodef.h>
2285
05c058bc
CB
2286/* We implement our own kill() using the undocumented system service
2287 sys$sigprc for one of two reasons:
2288
2289 1.) If the kill() in an older CRTL uses sys$forcex, causing the
f2610a60
CL
2290 target process to do a sys$exit, which usually can't be handled
2291 gracefully...certainly not by Perl and the %SIG{} mechanism.
2292
05c058bc
CB
2293 2.) If the kill() in the CRTL can't be called from a signal
2294 handler without disappearing into the ether, i.e., the signal
2295 it purportedly sends is never trapped. Still true as of VMS 7.3.
2296
2297 sys$sigprc has the same parameters as sys$forcex, but throws an exception
f2610a60
CL
2298 in the target process rather than calling sys$exit.
2299
2300 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2301 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2302 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2303 with condition codes C$_SIG0+nsig*8, catching the exception on the
2304 target process and resignaling with appropriate arguments.
2305
2306 But we don't have that VMS 7.0+ exception handler, so if you
2307 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2308
2309 Also note that SIGTERM is listed in the docs as being "unimplemented",
2310 yet always seems to be signaled with a VMS condition code of 4 (and
2311 correctly handled for that code). So we hardwire it in.
2312
2313 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2314 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2315 than signalling with an unrecognized (and unhandled by CRTL) code.
2316*/
2317
fe1de8ce 2318#define _MY_SIG_MAX 28
f2610a60 2319
9c1171d1
JM
2320static unsigned int
2321Perl_sig_to_vmscondition_int(int sig)
f2610a60 2322{
2e34cc90 2323 static unsigned int sig_code[_MY_SIG_MAX+1] =
f2610a60
CL
2324 {
2325 0, /* 0 ZERO */
2326 SS$_HANGUP, /* 1 SIGHUP */
2327 SS$_CONTROLC, /* 2 SIGINT */
2328 SS$_CONTROLY, /* 3 SIGQUIT */
2329 SS$_RADRMOD, /* 4 SIGILL */
2330 SS$_BREAK, /* 5 SIGTRAP */
2331 SS$_OPCCUS, /* 6 SIGABRT */
2332 SS$_COMPAT, /* 7 SIGEMT */
2333#ifdef __VAX
2334 SS$_FLTOVF, /* 8 SIGFPE VAX */
2335#else
2336 SS$_HPARITH, /* 8 SIGFPE AXP */
2337#endif
2338 SS$_ABORT, /* 9 SIGKILL */
2339 SS$_ACCVIO, /* 10 SIGBUS */
2340 SS$_ACCVIO, /* 11 SIGSEGV */
2341 SS$_BADPARAM, /* 12 SIGSYS */
2342 SS$_NOMBX, /* 13 SIGPIPE */
2343 SS$_ASTFLT, /* 14 SIGALRM */
2344 4, /* 15 SIGTERM */
2345 0, /* 16 SIGUSR1 */
fe1de8ce
CB
2346 0, /* 17 SIGUSR2 */
2347 0, /* 18 */
2348 0, /* 19 */
2349 0, /* 20 SIGCHLD */
2350 0, /* 21 SIGCONT */
2351 0, /* 22 SIGSTOP */
2352 0, /* 23 SIGTSTP */
2353 0, /* 24 SIGTTIN */
2354 0, /* 25 SIGTTOU */
2355 0, /* 26 */
2356 0, /* 27 */
2357 0 /* 28 SIGWINCH */
f2610a60
CL
2358 };
2359
2360#if __VMS_VER >= 60200000
2361 static int initted = 0;
2362 if (!initted) {
2363 initted = 1;
2364 sig_code[16] = C$_SIGUSR1;
2365 sig_code[17] = C$_SIGUSR2;
fe1de8ce
CB
2366#if __CRTL_VER >= 70000000
2367 sig_code[20] = C$_SIGCHLD;
2368#endif
2369#if __CRTL_VER >= 70300000
2370 sig_code[28] = C$_SIGWINCH;
2371#endif
f2610a60
CL
2372 }
2373#endif
2374
2e34cc90
CL
2375 if (sig < _SIG_MIN) return 0;
2376 if (sig > _MY_SIG_MAX) return 0;
2377 return sig_code[sig];
2378}
2379
9c1171d1
JM
2380unsigned int
2381Perl_sig_to_vmscondition(int sig)
2382{
2383#ifdef SS$_DEBUG
2384 if (vms_debug_on_exception != 0)
2385 lib$signal(SS$_DEBUG);
2386#endif
2387 return Perl_sig_to_vmscondition_int(sig);
2388}
2389
2390
2e34cc90
CL
2391int
2392Perl_my_kill(int pid, int sig)
2393{
2394 int iss;
2395 unsigned int code;
17072196 2396#define sys$sigprc SYS$SIGPRC
2e34cc90
CL
2397 int sys$sigprc(unsigned int *pidadr,
2398 struct dsc$descriptor_s *prcname,
2399 unsigned int code);
2400
7a7fd8e0
JM
2401 /* sig 0 means validate the PID */
2402 /*------------------------------*/
2403 if (sig == 0) {
2404 const unsigned long int jpicode = JPI$_PID;
2405 pid_t ret_pid;
2406 int status;
2407 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2408 if ($VMS_STATUS_SUCCESS(status))
2409 return 0;
2410 switch (status) {
2411 case SS$_NOSUCHNODE:
2412 case SS$_UNREACHABLE:
2413 case SS$_NONEXPR:
2414 errno = ESRCH;
2415 break;
2416 case SS$_NOPRIV:
2417 errno = EPERM;
2418 break;
2419 default:
2420 errno = EVMSERR;
2421 }
2422 vaxc$errno=status;
2423 return -1;
2424 }
2425
9c1171d1 2426 code = Perl_sig_to_vmscondition_int(sig);
2e34cc90 2427
7a7fd8e0
JM
2428 if (!code) {
2429 SETERRNO(EINVAL, SS$_BADPARAM);
2430 return -1;
2431 }
2432
2433 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2434 * signals are to be sent to multiple processes.
2435 * pid = 0 - all processes in group except ones that the system exempts
2436 * pid = -1 - all processes except ones that the system exempts
2437 * pid = -n - all processes in group (abs(n)) except ...
2438 * For now, just report as not supported.
2439 */
2440
2441 if (pid <= 0) {
2442 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
f2610a60
CL
2443 return -1;
2444 }
2445
2e34cc90 2446 iss = sys$sigprc((unsigned int *)&pid,0,code);
f2610a60
CL
2447 if (iss&1) return 0;
2448
2449 switch (iss) {
2450 case SS$_NOPRIV:
2451 set_errno(EPERM); break;
2452 case SS$_NONEXPR:
2453 case SS$_NOSUCHNODE:
2454 case SS$_UNREACHABLE:
2455 set_errno(ESRCH); break;
2456 case SS$_INSFMEM:
2457 set_errno(ENOMEM); break;
2458 default:
ebd4d70b 2459 _ckvmssts_noperl(iss);
f2610a60
CL
2460 set_errno(EVMSERR);
2461 }
2462 set_vaxc_errno(iss);
2463
2464 return -1;
2465}
2466#endif
2467
2fbb330f
JM
2468/* Routine to convert a VMS status code to a UNIX status code.
2469** More tricky than it appears because of conflicting conventions with
2470** existing code.
2471**
2472** VMS status codes are a bit mask, with the least significant bit set for
2473** success.
2474**
2475** Special UNIX status of EVMSERR indicates that no translation is currently
2476** available, and programs should check the VMS status code.
2477**
2478** Programs compiled with _POSIX_EXIT have a special encoding that requires
2479** decoding.
2480*/
2481
2482#ifndef C_FACILITY_NO
2483#define C_FACILITY_NO 0x350000
2484#endif
2485#ifndef DCL_IVVERB
2486#define DCL_IVVERB 0x38090
2487#endif
2488
7a7fd8e0 2489int Perl_vms_status_to_unix(int vms_status, int child_flag)
2fbb330f
JM
2490{
2491int facility;
2492int fac_sp;
2493int msg_no;
2494int msg_status;
2495int unix_status;
2496
2497 /* Assume the best or the worst */
2498 if (vms_status & STS$M_SUCCESS)
2499 unix_status = 0;
2500 else
2501 unix_status = EVMSERR;
2502
2503 msg_status = vms_status & ~STS$M_CONTROL;
2504
2505 facility = vms_status & STS$M_FAC_NO;
2506 fac_sp = vms_status & STS$M_FAC_SP;
2507 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2508
0968cdad 2509 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2fbb330f
JM
2510 switch(msg_no) {
2511 case SS$_NORMAL:
2512 unix_status = 0;
2513 break;
2514 case SS$_ACCVIO:
2515 unix_status = EFAULT;
2516 break;
7a7fd8e0
JM
2517 case SS$_DEVOFFLINE:
2518 unix_status = EBUSY;
2519 break;
2520 case SS$_CLEARED:
2521 unix_status = ENOTCONN;
2522 break;
2523 case SS$_IVCHAN:
2fbb330f
JM
2524 case SS$_IVLOGNAM:
2525 case SS$_BADPARAM:
2526 case SS$_IVLOGTAB:
2527 case SS$_NOLOGNAM:
2528 case SS$_NOLOGTAB:
2529 case SS$_INVFILFOROP:
2530 case SS$_INVARG:
2531 case SS$_NOSUCHID:
2532 case SS$_IVIDENT:
2533 unix_status = EINVAL;
2534 break;
7a7fd8e0
JM
2535 case SS$_UNSUPPORTED:
2536 unix_status = ENOTSUP;
2537 break;
2fbb330f
JM
2538 case SS$_FILACCERR:
2539 case SS$_NOGRPPRV:
2540 case SS$_NOSYSPRV:
2541 unix_status = EACCES;
2542 break;
2543 case SS$_DEVICEFULL:
2544 unix_status = ENOSPC;
2545 break;
2546 case SS$_NOSUCHDEV:
2547 unix_status = ENODEV;
2548 break;
2549 case SS$_NOSUCHFILE:
2550 case SS$_NOSUCHOBJECT:
2551 unix_status = ENOENT;
2552 break;
fb38d079
JM
2553 case SS$_ABORT: /* Fatal case */
2554 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2555 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2fbb330f
JM
2556 unix_status = EINTR;
2557 break;
2558 case SS$_BUFFEROVF:
2559 unix_status = E2BIG;
2560 break;
2561 case SS$_INSFMEM:
2562 unix_status = ENOMEM;
2563 break;
2564 case SS$_NOPRIV:
2565 unix_status = EPERM;
2566 break;
2567 case SS$_NOSUCHNODE:
2568 case SS$_UNREACHABLE:
2569 unix_status = ESRCH;
2570 break;
2571 case SS$_NONEXPR:
2572 unix_status = ECHILD;
2573 break;
2574 default:
2575 if ((facility == 0) && (msg_no < 8)) {
2576 /* These are not real VMS status codes so assume that they are
2577 ** already UNIX status codes
2578 */
2579 unix_status = msg_no;
2580 break;
2581 }
2582 }
2583 }
2584 else {
2585 /* Translate a POSIX exit code to a UNIX exit code */
2586 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
7a7fd8e0 2587 unix_status = (msg_no & 0x07F8) >> 3;
2fbb330f
JM
2588 }
2589 else {
7a7fd8e0
JM
2590
2591 /* Documented traditional behavior for handling VMS child exits */
2592 /*--------------------------------------------------------------*/
2593 if (child_flag != 0) {
2594
2595 /* Success / Informational return 0 */
2596 /*----------------------------------*/
2597 if (msg_no & STS$K_SUCCESS)
2598 return 0;
2599
2600 /* Warning returns 1 */
2601 /*-------------------*/
2602 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2603 return 1;
2604
2605 /* Everything else pass through the severity bits */
2606 /*------------------------------------------------*/
2607 return (msg_no & STS$M_SEVERITY);
2608 }
2609
2610 /* Normal VMS status to ERRNO mapping attempt */
2611 /*--------------------------------------------*/
2fbb330f
JM
2612 switch(msg_status) {
2613 /* case RMS$_EOF: */ /* End of File */
2614 case RMS$_FNF: /* File Not Found */
2615 case RMS$_DNF: /* Dir Not Found */
2616 unix_status = ENOENT;
2617 break;
2618 case RMS$_RNF: /* Record Not Found */
2619 unix_status = ESRCH;
2620 break;
2621 case RMS$_DIR:
2622 unix_status = ENOTDIR;
2623 break;
2624 case RMS$_DEV:
2625 unix_status = ENODEV;
2626 break;
7a7fd8e0
JM
2627 case RMS$_IFI:
2628 case RMS$_FAC:
2629 case RMS$_ISI:
2630 unix_status = EBADF;
2631 break;
2632 case RMS$_FEX:
2633 unix_status = EEXIST;
2634 break;
2fbb330f
JM
2635 case RMS$_SYN:
2636 case RMS$_FNM:
2637 case LIB$_INVSTRDES:
2638 case LIB$_INVARG:
2639 case LIB$_NOSUCHSYM:
2640 case LIB$_INVSYMNAM:
2641 case DCL_IVVERB:
2642 unix_status = EINVAL;
2643 break;
2644 case CLI$_BUFOVF:
2645 case RMS$_RTB:
2646 case CLI$_TKNOVF:
2647 case CLI$_RSLOVF:
2648 unix_status = E2BIG;
2649 break;
2650 case RMS$_PRV: /* No privilege */
2651 case RMS$_ACC: /* ACP file access failed */
2652 case RMS$_WLK: /* Device write locked */
2653 unix_status = EACCES;
2654 break;
ed1b9de0
JM
2655 case RMS$_MKD: /* Failed to mark for delete */
2656 unix_status = EPERM;
2657 break;
2fbb330f
JM
2658 /* case RMS$_NMF: */ /* No more files */
2659 }
2660 }
2661 }
2662
2663 return unix_status;
2664}
2665
7a7fd8e0
JM
2666/* Try to guess at what VMS error status should go with a UNIX errno
2667 * value. This is hard to do as there could be many possible VMS
2668 * error statuses that caused the errno value to be set.
2669 */
2670
2671int Perl_unix_status_to_vms(int unix_status)
2672{
2673int test_unix_status;
2674
2675 /* Trivial cases first */
2676 /*---------------------*/
2677 if (unix_status == EVMSERR)
2678 return vaxc$errno;
2679
2680 /* Is vaxc$errno sane? */
2681 /*---------------------*/
2682 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2683 if (test_unix_status == unix_status)
2684 return vaxc$errno;
2685
2686 /* If way out of range, must be VMS code already */
2687 /*-----------------------------------------------*/
2688 if (unix_status > EVMSERR)
2689 return unix_status;
2690
2691 /* If out of range, punt */
2692 /*-----------------------*/
2693 if (unix_status > __ERRNO_MAX)
2694 return SS$_ABORT;
2695
2696
2697 /* Ok, now we have to do it the hard way. */
2698 /*----------------------------------------*/
2699 switch(unix_status) {
2700 case 0: return SS$_NORMAL;
2701 case EPERM: return SS$_NOPRIV;
2702 case ENOENT: return SS$_NOSUCHOBJECT;
2703 case ESRCH: return SS$_UNREACHABLE;
2704 case EINTR: return SS$_ABORT;
2705 /* case EIO: */
2706 /* case ENXIO: */
2707 case E2BIG: return SS$_BUFFEROVF;
2708 /* case ENOEXEC */
2709 case EBADF: return RMS$_IFI;
2710 case ECHILD: return SS$_NONEXPR;
2711 /* case EAGAIN */
2712 case ENOMEM: return SS$_INSFMEM;
2713 case EACCES: return SS$_FILACCERR;
2714 case EFAULT: return SS$_ACCVIO;
2715 /* case ENOTBLK */
0968cdad 2716 case EBUSY: return SS$_DEVOFFLINE;
7a7fd8e0
JM
2717 case EEXIST: return RMS$_FEX;
2718 /* case EXDEV */
2719 case ENODEV: return SS$_NOSUCHDEV;
2720 case ENOTDIR: return RMS$_DIR;
2721 /* case EISDIR */
2722 case EINVAL: return SS$_INVARG;
2723 /* case ENFILE */
2724 /* case EMFILE */
2725 /* case ENOTTY */
2726 /* case ETXTBSY */
2727 /* case EFBIG */
2728 case ENOSPC: return SS$_DEVICEFULL;
2729 case ESPIPE: return LIB$_INVARG;
2730 /* case EROFS: */
2731 /* case EMLINK: */
2732 /* case EPIPE: */
2733 /* case EDOM */
2734 case ERANGE: return LIB$_INVARG;
2735 /* case EWOULDBLOCK */
2736 /* case EINPROGRESS */
2737 /* case EALREADY */
2738 /* case ENOTSOCK */
2739 /* case EDESTADDRREQ */
2740 /* case EMSGSIZE */
2741 /* case EPROTOTYPE */
2742 /* case ENOPROTOOPT */
2743 /* case EPROTONOSUPPORT */
2744 /* case ESOCKTNOSUPPORT */
2745 /* case EOPNOTSUPP */
2746 /* case EPFNOSUPPORT */
2747 /* case EAFNOSUPPORT */
2748 /* case EADDRINUSE */
2749 /* case EADDRNOTAVAIL */
2750 /* case ENETDOWN */
2751 /* case ENETUNREACH */
2752 /* case ENETRESET */
2753 /* case ECONNABORTED */
2754 /* case ECONNRESET */
2755 /* case ENOBUFS */
2756 /* case EISCONN */
2757 case ENOTCONN: return SS$_CLEARED;
2758 /* case ESHUTDOWN */
2759 /* case ETOOMANYREFS */
2760 /* case ETIMEDOUT */
2761 /* case ECONNREFUSED */
2762 /* case ELOOP */
2763 /* case ENAMETOOLONG */
2764 /* case EHOSTDOWN */
2765 /* case EHOSTUNREACH */
2766 /* case ENOTEMPTY */
2767 /* case EPROCLIM */
2768 /* case EUSERS */
2769 /* case EDQUOT */
2770 /* case ENOMSG */
2771 /* case EIDRM */
2772 /* case EALIGN */
2773 /* case ESTALE */
2774 /* case EREMOTE */
2775 /* case ENOLCK */
2776 /* case ENOSYS */
2777 /* case EFTYPE */
2778 /* case ECANCELED */
2779 /* case EFAIL */
2780 /* case EINPROG */
2781 case ENOTSUP:
2782 return SS$_UNSUPPORTED;
2783 /* case EDEADLK */
2784 /* case ENWAIT */
2785 /* case EILSEQ */
2786 /* case EBADCAT */
2787 /* case EBADMSG */
2788 /* case EABANDONED */
2789 default:
2790 return SS$_ABORT; /* punt */
2791 }
7a7fd8e0 2792}
2fbb330f
JM
2793
2794
22d4bb9c 2795/* default piping mailbox size */
df17c887
CB
2796#ifdef __VAX
2797# define PERL_BUFSIZ 512
2798#else
2799# define PERL_BUFSIZ 8192
2800#endif
22d4bb9c 2801
674d6c38 2802
a0d0e21e 2803static void
8a646e0b 2804create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
a0d0e21e 2805{
22d4bb9c
CB
2806 unsigned long int mbxbufsiz;
2807 static unsigned long int syssize = 0;
2808 unsigned long int dviitm = DVI$_DEVNAM;
22d4bb9c 2809 char csize[LNM$C_NAMLENGTH+1];
f7ddb74a
JM
2810 int sts;
2811
22d4bb9c
CB
2812 if (!syssize) {
2813 unsigned long syiitm = SYI$_MAXBUF;
a0d0e21e 2814 /*
22d4bb9c
CB
2815 * Get the SYSGEN parameter MAXBUF
2816 *
2817 * If the logical 'PERL_MBX_SIZE' is defined
2818 * use the value of the logical instead of PERL_BUFSIZ, but
2819 * keep the size between 128 and MAXBUF.
2820 *
a0d0e21e 2821 */
ebd4d70b 2822 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
22d4bb9c
CB
2823 }
2824
2825 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2826 mbxbufsiz = atoi(csize);
2827 } else {
2828 mbxbufsiz = PERL_BUFSIZ;
a0d0e21e 2829 }
22d4bb9c
CB
2830 if (mbxbufsiz < 128) mbxbufsiz = 128;
2831 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2832
ebd4d70b 2833 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
a0d0e21e 2834
ebd4d70b
JM
2835 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2836 _ckvmssts_noperl(sts);
a0d0e21e
LW
2837 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2838
2839} /* end of create_mbx() */
2840
22d4bb9c 2841
a0d0e21e 2842/*{{{ my_popen and my_pclose*/
22d4bb9c
CB
2843
2844typedef struct _iosb IOSB;
2845typedef struct _iosb* pIOSB;
2846typedef struct _pipe Pipe;
2847typedef struct _pipe* pPipe;
2848typedef struct pipe_details Info;
2849typedef struct pipe_details* pInfo;
2850typedef struct _srqp RQE;
2851typedef struct _srqp* pRQE;
2852typedef struct _tochildbuf CBuf;
2853typedef struct _tochildbuf* pCBuf;
2854
2855struct _iosb {
2856 unsigned short status;
2857 unsigned short count;
2858 unsigned long dvispec;
2859};
2860
2861#pragma member_alignment save
2862#pragma nomember_alignment quadword
2863struct _srqp { /* VMS self-relative queue entry */
2864 unsigned long qptr[2];
2865};
2866#pragma member_alignment restore
2867static RQE RQE_ZERO = {0,0};
2868
2869struct _tochildbuf {
2870 RQE q;
2871 int eof;
2872 unsigned short size;
2873 char *buf;
2874};
2875
2876struct _pipe {
2877 RQE free;
2878 RQE wait;
2879 int fd_out;
2880 unsigned short chan_in;
2881 unsigned short chan_out;
2882 char *buf;
2883 unsigned int bufsize;
2884 IOSB iosb;
2885 IOSB iosb2;
2886 int *pipe_done;
2887 int retry;
2888 int type;
2889 int shut_on_empty;
2890 int need_wake;
2891 pPipe *home;
2892 pInfo info;
2893 pCBuf curr;
2894 pCBuf curr2;
fd8cd3a3
DS
2895#if defined(PERL_IMPLICIT_CONTEXT)
2896 void *thx; /* Either a thread or an interpreter */
2897 /* pointer, depending on how we're built */
2898#endif
22d4bb9c
CB
2899};
2900
2901
a0d0e21e
LW
2902struct pipe_details
2903{
22d4bb9c 2904 pInfo next;
ff7adb52
CL
2905 PerlIO *fp; /* file pointer to pipe mailbox */
2906 int useFILE; /* using stdio, not perlio */
748a9306
LW
2907 int pid; /* PID of subprocess */
2908 int mode; /* == 'r' if pipe open for reading */
2909 int done; /* subprocess has completed */
ff7adb52 2910 int waiting; /* waiting for completion/closure */
22d4bb9c
CB
2911 int closing; /* my_pclose is closing this pipe */
2912 unsigned long completion; /* termination status of subprocess */
2913 pPipe in; /* pipe in to sub */
2914 pPipe out; /* pipe out of sub */
2915 pPipe err; /* pipe of sub's sys$error */
2916 int in_done; /* true when in pipe finished */
2917 int out_done;
2918 int err_done;
cd1191f1
CB
2919 unsigned short xchan; /* channel to debug xterm */
2920 unsigned short xchan_valid; /* channel is assigned */
a0d0e21e
LW
2921};
2922
748a9306
LW
2923struct exit_control_block
2924{
2925 struct exit_control_block *flink;
2926 unsigned long int (*exit_routine)();
2927 unsigned long int arg_count;
2928 unsigned long int *status_address;
2929 unsigned long int exit_status;
2930};
2931
d85f548a
JH
2932typedef struct _closed_pipes Xpipe;
2933typedef struct _closed_pipes* pXpipe;
2934
2935struct _closed_pipes {
2936 int pid; /* PID of subprocess */
2937 unsigned long completion; /* termination status of subprocess */
2938};
2939#define NKEEPCLOSED 50
2940static Xpipe closed_list[NKEEPCLOSED];
2941static int closed_index = 0;
2942static int closed_num = 0;
2943
22d4bb9c
CB
2944#define RETRY_DELAY "0 ::0.20"
2945#define MAX_RETRY 50
a0d0e21e 2946
22d4bb9c
CB
2947static int pipe_ef = 0; /* first call to safe_popen inits these*/
2948static unsigned long mypid;
2949static unsigned long delaytime[2];
2950
2951static pInfo open_pipes = NULL;
2952static $DESCRIPTOR(nl_desc, "NL:");
3eeba6fb 2953
ff7adb52
CL
2954#define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2955
2956
3eeba6fb 2957
748a9306 2958static unsigned long int
ebd4d70b 2959pipe_exit_routine()
748a9306 2960{
22d4bb9c 2961 pInfo info;
1e422769 2962 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
4e0c9737 2963 int sts, did_stuff, j;
ff7adb52 2964
5ce486e0
CB
2965 /*
2966 * Flush any pending i/o, but since we are in process run-down, be
2967 * careful about referencing PerlIO structures that may already have
2968 * been deallocated. We may not even have an interpreter anymore.
ff7adb52
CL
2969 */
2970 info = open_pipes;
2971 while (info) {
2972 if (info->fp) {
ebd4d70b
JM
2973#if defined(PERL_IMPLICIT_CONTEXT)
2974 /* We need to use the Perl context of the thread that created */
2975 /* the pipe. */
2976 pTHX;
2977 if (info->err)
2978 aTHX = info->err->thx;
2979 else if (info->out)
2980 aTHX = info->out->thx;
2981 else if (info->in)
2982 aTHX = info->in->thx;
2983#endif
5ce486e0
CB
2984 if (!info->useFILE
2985#if defined(USE_ITHREADS)
2986 && my_perl
2987#endif
a24c654f
CB
2988#ifdef USE_PERLIO
2989 && PL_perlio_fd_refcnt
2990#endif
2991 )
5ce486e0 2992 PerlIO_flush(info->fp);
ff7adb52
CL
2993 else
2994 fflush((FILE *)info->fp);
2995 }
2996 info = info->next;
2997 }
3eeba6fb
CB
2998
2999 /*
ff7adb52 3000 next we try sending an EOF...ignore if doesn't work, make sure we
3eeba6fb
CB
3001 don't hang
3002 */
3003 did_stuff = 0;
3004 info = open_pipes;
748a9306 3005
3eeba6fb 3006 while (info) {
d4c83939 3007 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 3008 if (info->in && !info->in->shut_on_empty) {
d4c83939 3009 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
ebd4d70b 3010 0, 0, 0, 0, 0, 0));
ff7adb52 3011 info->waiting = 1;
22d4bb9c 3012 did_stuff = 1;
748a9306 3013 }
d4c83939 3014 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3015 info = info->next;
3016 }
ff7adb52
CL
3017
3018 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3019
3020 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3021 int nwait = 0;
3022
3023 info = open_pipes;
3024 while (info) {
d4c83939 3025 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3026 if (info->waiting && info->done)
3027 info->waiting = 0;
3028 nwait += info->waiting;
d4c83939 3029 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3030 info = info->next;
3031 }
3032 if (!nwait) break;
3033 sleep(1);
3034 }
3eeba6fb
CB
3035
3036 did_stuff = 0;
3037 info = open_pipes;
3038 while (info) {
d4c83939 3039 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3040 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3041 sts = sys$forcex(&info->pid,0,&abort);
d4c83939 3042 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3eeba6fb
CB
3043 did_stuff = 1;
3044 }
d4c83939 3045 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3046 info = info->next;
3047 }
ff7adb52
CL
3048
3049 /* again, wait for effect */
3050
3051 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3052 int nwait = 0;
3053
3054 info = open_pipes;
3055 while (info) {
d4c83939 3056 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3057 if (info->waiting && info->done)
3058 info->waiting = 0;
3059 nwait += info->waiting;
d4c83939 3060 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3061 info = info->next;
3062 }
3063 if (!nwait) break;
3064 sleep(1);
3065 }
3eeba6fb
CB
3066
3067 info = open_pipes;
3068 while (info) {
d4c83939 3069 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3070 if (!info->done) { /* We tried to be nice . . . */
3071 sts = sys$delprc(&info->pid,0);
d4c83939 3072 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2f1dcba4 3073 info->done = 1; /* sys$delprc is as done as we're going to get. */
3eeba6fb 3074 }
d4c83939 3075 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3076 info = info->next;
3077 }
3078
3079 while(open_pipes) {
ebd4d70b
JM
3080
3081#if defined(PERL_IMPLICIT_CONTEXT)
3082 /* We need to use the Perl context of the thread that created */
3083 /* the pipe. */
3084 pTHX;
36b6faa8
CB
3085 if (open_pipes->err)
3086 aTHX = open_pipes->err->thx;
3087 else if (open_pipes->out)
3088 aTHX = open_pipes->out->thx;
3089 else if (open_pipes->in)
3090 aTHX = open_pipes->in->thx;
ebd4d70b 3091#endif
1e422769 3092 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3093 else if (!(sts & 1)) retsts = sts;
748a9306
LW
3094 }
3095 return retsts;
3096}
3097
3098static struct exit_control_block pipe_exitblock =
3099 {(struct exit_control_block *) 0,
3100 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3101
22d4bb9c
CB
3102static void pipe_mbxtofd_ast(pPipe p);
3103static void pipe_tochild1_ast(pPipe p);
3104static void pipe_tochild2_ast(pPipe p);
748a9306 3105
a0d0e21e 3106static void
22d4bb9c 3107popen_completion_ast(pInfo info)
a0d0e21e 3108{
22d4bb9c
CB
3109 pInfo i = open_pipes;
3110 int iss;
d85f548a
JH
3111
3112 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3113 closed_list[closed_index].pid = info->pid;
3114 closed_list[closed_index].completion = info->completion;
3115 closed_index++;
3116 if (closed_index == NKEEPCLOSED)
3117 closed_index = 0;
3118 closed_num++;
22d4bb9c
CB
3119
3120 while (i) {
3121 if (i == info) break;
3122 i = i->next;
3123 }
3124 if (!i) return; /* unlinked, probably freed too */
3125
22d4bb9c
CB
3126 info->done = TRUE;
3127
3128/*
3129 Writing to subprocess ...
3130 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3131
3132 chan_out may be waiting for "done" flag, or hung waiting
3133 for i/o completion to child...cancel the i/o. This will
3134 put it into "snarf mode" (done but no EOF yet) that discards
3135 input.
3136
3137 Output from subprocess (stdout, stderr) needs to be flushed and
3138 shut down. We try sending an EOF, but if the mbx is full the pipe
3139 routine should still catch the "shut_on_empty" flag, telling it to
3140 use immediate-style reads so that "mbx empty" -> EOF.
3141
3142
3143*/
3144 if (info->in && !info->in_done) { /* only for mode=w */
3145 if (info->in->shut_on_empty && info->in->need_wake) {
3146 info->in->need_wake = FALSE;
fd8cd3a3 3147 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
22d4bb9c 3148 } else {
fd8cd3a3 3149 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
22d4bb9c
CB
3150 }
3151 }
3152
3153 if (info->out && !info->out_done) { /* were we also piping output? */
3154 info->out->shut_on_empty = TRUE;
3155 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3156 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
fd8cd3a3 3157 _ckvmssts_noperl(iss);
22d4bb9c
CB
3158 }
3159
3160 if (info->err && !info->err_done) { /* we were piping stderr */
3161 info->err->shut_on_empty = TRUE;
3162 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3163 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
fd8cd3a3 3164 _ckvmssts_noperl(iss);
a0d0e21e 3165 }
fd8cd3a3 3166 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3167
a0d0e21e
LW
3168}
3169
2fbb330f 3170static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
218fdd94 3171static void vms_execfree(struct dsc$descriptor_s *vmscmd);
aa779de1 3172
22d4bb9c
CB
3173/*
3174 we actually differ from vmstrnenv since we use this to
3175 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3176 are pointing to the same thing
3177*/
3178
3179static unsigned short
fd8cd3a3 3180popen_translate(pTHX_ char *logical, char *result)
22d4bb9c
CB
3181{
3182 int iss;
3183 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3184 $DESCRIPTOR(d_log,"");
3185 struct _il3 {
3186 unsigned short length;
3187 unsigned short code;
3188 char * buffer_addr;
3189 unsigned short *retlenaddr;
3190 } itmlst[2];
3191 unsigned short l, ifi;
3192
3193 d_log.dsc$a_pointer = logical;
3194 d_log.dsc$w_length = strlen(logical);
3195
3196 itmlst[0].code = LNM$_STRING;
3197 itmlst[0].length = 255;
3198 itmlst[0].buffer_addr = result;
3199 itmlst[0].retlenaddr = &l;
3200
3201 itmlst[1].code = 0;
3202 itmlst[1].length = 0;
3203 itmlst[1].buffer_addr = 0;
3204 itmlst[1].retlenaddr = 0;
3205
3206 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3207 if (iss == SS$_NOLOGNAM) {
3208 iss = SS$_NORMAL;
3209 l = 0;
3210 }
3211 if (!(iss&1)) lib$signal(iss);
3212 result[l] = '\0';
3213/*
3214 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3215 strip it off and return the ifi, if any
3216*/
3217 ifi = 0;
3218 if (result[0] == 0x1b && result[1] == 0x00) {
18a3d61e 3219 memmove(&ifi,result+2,2);
22d4bb9c
CB
3220 strcpy(result,result+4);
3221 }
3222 return ifi; /* this is the RMS internal file id */
3223}
3224
22d4bb9c
CB
3225static void pipe_infromchild_ast(pPipe p);
3226
3227/*
3228 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3229 inside an AST routine without worrying about reentrancy and which Perl
3230 memory allocator is being used.
3231
3232 We read data and queue up the buffers, then spit them out one at a
3233 time to the output mailbox when the output mailbox is ready for one.
3234
3235*/
3236#define INITIAL_TOCHILDQUEUE 2
3237
3238static pPipe
fd8cd3a3 3239pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3240{
22d4bb9c
CB
3241 pPipe p;
3242 pCBuf b;
3243 char mbx1[64], mbx2[64];
3244 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3245 DSC$K_CLASS_S, mbx1},
3246 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3247 DSC$K_CLASS_S, mbx2};
3248 unsigned int dviitm = DVI$_DEVBUFSIZ;
3249 int j, n;
3250
d4c83939 3251 n = sizeof(Pipe);
ebd4d70b 3252 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3253
8a646e0b
JM
3254 create_mbx(&p->chan_in , &d_mbx1);
3255 create_mbx(&p->chan_out, &d_mbx2);
ebd4d70b 3256 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
22d4bb9c
CB
3257
3258 p->buf = 0;
3259 p->shut_on_empty = FALSE;
3260 p->need_wake = FALSE;
3261 p->type = 0;
3262 p->retry = 0;
3263 p->iosb.status = SS$_NORMAL;
3264 p->iosb2.status = SS$_NORMAL;
3265 p->free = RQE_ZERO;
3266 p->wait = RQE_ZERO;
3267 p->curr = 0;
3268 p->curr2 = 0;
3269 p->info = 0;
fd8cd3a3
DS
3270#ifdef PERL_IMPLICIT_CONTEXT
3271 p->thx = aTHX;
3272#endif
22d4bb9c
CB
3273
3274 n = sizeof(CBuf) + p->bufsize;
3275
3276 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
ebd4d70b 3277 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c 3278 b->buf = (char *) b + sizeof(CBuf);
ebd4d70b 3279 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
3280 }
3281
3282 pipe_tochild2_ast(p);
3283 pipe_tochild1_ast(p);
3284 strcpy(wmbx, mbx1);
3285 strcpy(rmbx, mbx2);
3286 return p;
3287}
3288
3289/* reads the MBX Perl is writing, and queues */
3290
3291static void
3292pipe_tochild1_ast(pPipe p)
3293{
22d4bb9c
CB
3294 pCBuf b = p->curr;
3295 int iss = p->iosb.status;
3296 int eof = (iss == SS$_ENDOFFILE);
f7ddb74a 3297 int sts;
fd8cd3a3
DS
3298#ifdef PERL_IMPLICIT_CONTEXT
3299 pTHX = p->thx;
3300#endif
22d4bb9c
CB
3301
3302 if (p->retry) {
3303 if (eof) {
3304 p->shut_on_empty = TRUE;
3305 b->eof = TRUE;
ebd4d70b 3306 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c 3307 } else {
ebd4d70b 3308 _ckvmssts_noperl(iss);
22d4bb9c
CB
3309 }
3310
3311 b->eof = eof;
3312 b->size = p->iosb.count;
ebd4d70b 3313 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
22d4bb9c
CB
3314 if (p->need_wake) {
3315 p->need_wake = FALSE;
ebd4d70b 3316 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
22d4bb9c
CB
3317 }
3318 } else {
3319 p->retry = 1; /* initial call */
3320 }
3321
3322 if (eof) { /* flush the free queue, return when done */
3323 int n = sizeof(CBuf) + p->bufsize;
3324 while (1) {
3325 iss = lib$remqti(&p->free, &b);
3326 if (iss == LIB$_QUEWASEMP) return;
ebd4d70b
JM
3327 _ckvmssts_noperl(iss);
3328 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c
CB
3329 }
3330 }
3331
3332 iss = lib$remqti(&p->free, &b);
3333 if (iss == LIB$_QUEWASEMP) {
3334 int n = sizeof(CBuf) + p->bufsize;
ebd4d70b 3335 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c
CB
3336 b->buf = (char *) b + sizeof(CBuf);
3337 } else {
ebd4d70b 3338 _ckvmssts_noperl(iss);
22d4bb9c
CB
3339 }
3340
3341 p->curr = b;
3342 iss = sys$qio(0,p->chan_in,
3343 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3344 &p->iosb,
3345 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3346 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
ebd4d70b 3347 _ckvmssts_noperl(iss);
22d4bb9c
CB
3348}
3349
3350
3351/* writes queued buffers to output, waits for each to complete before
3352 doing the next */
3353
3354static void
3355pipe_tochild2_ast(pPipe p)
3356{
22d4bb9c
CB
3357 pCBuf b = p->curr2;
3358 int iss = p->iosb2.status;
3359 int n = sizeof(CBuf) + p->bufsize;
3360 int done = (p->info && p->info->done) ||
3361 iss == SS$_CANCEL || iss == SS$_ABORT;
fd8cd3a3
DS
3362#if defined(PERL_IMPLICIT_CONTEXT)
3363 pTHX = p->thx;
3364#endif
22d4bb9c
CB
3365
3366 do {
3367 if (p->type) { /* type=1 has old buffer, dispose */
3368 if (p->shut_on_empty) {
ebd4d70b 3369 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c 3370 } else {
ebd4d70b 3371 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
3372 }
3373 p->type = 0;
3374 }
3375
3376 iss = lib$remqti(&p->wait, &b);
3377 if (iss == LIB$_QUEWASEMP) {
3378 if (p->shut_on_empty) {
3379 if (done) {
ebd4d70b 3380 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c 3381 *p->pipe_done = TRUE;
ebd4d70b 3382 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3383 } else {
ebd4d70b 3384 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
3385 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3386 }
3387 return;
3388 }
3389 p->need_wake = TRUE;
3390 return;
3391 }
ebd4d70b 3392 _ckvmssts_noperl(iss);
22d4bb9c
CB
3393 p->type = 1;
3394 } while (done);
3395
3396
3397 p->curr2 = b;
3398 if (b->eof) {
ebd4d70b 3399 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
3400 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3401 } else {
ebd4d70b 3402 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
22d4bb9c
CB
3403 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3404 }
3405
3406 return;
3407
3408}
3409
3410
3411static pPipe
fd8cd3a3 3412pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3413{
22d4bb9c
CB
3414 pPipe p;
3415 char mbx1[64], mbx2[64];
3416 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3417 DSC$K_CLASS_S, mbx1},
3418 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3419 DSC$K_CLASS_S, mbx2};
3420 unsigned int dviitm = DVI$_DEVBUFSIZ;
3421
d4c83939 3422 int n = sizeof(Pipe);
ebd4d70b 3423 _ckvmssts_noperl(lib$get_vm(&n, &p));
8a646e0b
JM
3424 create_mbx(&p->chan_in , &d_mbx1);
3425 create_mbx(&p->chan_out, &d_mbx2);
22d4bb9c 3426
ebd4d70b 3427 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3428 n = p->bufsize * sizeof(char);
ebd4d70b 3429 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3430 p->shut_on_empty = FALSE;
3431 p->info = 0;
3432 p->type = 0;
3433 p->iosb.status = SS$_NORMAL;
fd8cd3a3
DS
3434#if defined(PERL_IMPLICIT_CONTEXT)
3435 p->thx = aTHX;
3436#endif
22d4bb9c
CB
3437 pipe_infromchild_ast(p);
3438
3439 strcpy(wmbx, mbx1);
3440 strcpy(rmbx, mbx2);
3441 return p;
3442}
3443
3444static void
3445pipe_infromchild_ast(pPipe p)
3446{
22d4bb9c
CB
3447 int iss = p->iosb.status;
3448 int eof = (iss == SS$_ENDOFFILE);
3449 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3450 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
fd8cd3a3
DS
3451#if defined(PERL_IMPLICIT_CONTEXT)
3452 pTHX = p->thx;
3453#endif
22d4bb9c
CB
3454
3455 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
ebd4d70b 3456 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c
CB
3457 p->chan_out = 0;
3458 }
3459
3460 /* read completed:
3461 input shutdown if EOF from self (done or shut_on_empty)
3462 output shutdown if closing flag set (my_pclose)
3463 send data/eof from child or eof from self
3464 otherwise, re-read (snarf of data from child)
3465 */
3466
3467 if (p->type == 1) {
3468 p->type = 0;
3469 if (myeof && p->chan_in) { /* input shutdown */
ebd4d70b 3470 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c
CB
3471 p->chan_in = 0;
3472 }
3473
3474 if (p->chan_out) {
3475 if (myeof || kideof) { /* pass EOF to parent */
ebd4d70b
JM
3476 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3477 pipe_infromchild_ast, p,
3478 0, 0, 0, 0, 0, 0));
22d4bb9c
CB
3479 return;
3480 } else if (eof) { /* eat EOF --- fall through to read*/
3481
3482 } else { /* transmit data */
ebd4d70b
JM
3483 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3484 pipe_infromchild_ast,p,
3485 p->buf, p->iosb.count, 0, 0, 0, 0));
22d4bb9c
CB
3486 return;
3487 }
3488 }
3489 }
3490
3491 /* everything shut? flag as done */
3492
3493 if (!p->chan_in && !p->chan_out) {
3494 *p->pipe_done = TRUE;
ebd4d70b 3495 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
3496 return;
3497 }
3498
3499 /* write completed (or read, if snarfing from child)
3500 if still have input active,
3501 queue read...immediate mode if shut_on_empty so we get EOF if empty
3502 otherwise,
3503 check if Perl reading, generate EOFs as needed
3504 */
3505
3506 if (p->type == 0) {
3507 p->type = 1;
3508 if (p->chan_in) {
3509 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3510 pipe_infromchild_ast,p,
3511 p->buf, p->bufsize, 0, 0, 0, 0);
3512 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
ebd4d70b 3513 _ckvmssts_noperl(iss);
22d4bb9c
CB
3514 } else { /* send EOFs for extra reads */
3515 p->iosb.status = SS$_ENDOFFILE;
3516 p->iosb.dvispec = 0;
ebd4d70b
JM
3517 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3518 0, 0, 0,
3519 pipe_infromchild_ast, p, 0, 0, 0, 0));
22d4bb9c
CB
3520 }
3521 }
3522}
3523
3524static pPipe
fd8cd3a3 3525pipe_mbxtofd_setup(pTHX_ int fd, char *out)
22d4bb9c 3526{
22d4bb9c
CB
3527 pPipe p;
3528 char mbx[64];
3529 unsigned long dviitm = DVI$_DEVBUFSIZ;
3530 struct stat s;
3531 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3532 DSC$K_CLASS_S, mbx};
a480973c 3533 int n = sizeof(Pipe);
22d4bb9c
CB
3534
3535 /* things like terminals and mbx's don't need this filter */
3536 if (fd && fstat(fd,&s) == 0) {
4e0c9737 3537 unsigned long devchar;
cfcfe586
JM
3538 char device[65];
3539 unsigned short dev_len;
3540 struct dsc$descriptor_s d_dev;
3541 char * cptr;
3542 struct item_list_3 items[3];
3543 int status;
3544 unsigned short dvi_iosb[4];
3545
3546 cptr = getname(fd, out, 1);
ebd4d70b 3547 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
cfcfe586
JM
3548 d_dev.dsc$a_pointer = out;
3549 d_dev.dsc$w_length = strlen(out);
3550 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3551 d_dev.dsc$b_class = DSC$K_CLASS_S;
3552
3553 items[0].len = 4;
3554 items[0].code = DVI$_DEVCHAR;
3555 items[0].bufadr = &devchar;
3556 items[0].retadr = NULL;
3557 items[1].len = 64;
3558 items[1].code = DVI$_FULLDEVNAM;
3559 items[1].bufadr = device;
3560 items[1].retadr = &dev_len;
3561 items[2].len = 0;
3562 items[2].code = 0;
3563
3564 status = sys$getdviw
3565 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
ebd4d70b 3566 _ckvmssts_noperl(status);
cfcfe586
JM
3567 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3568 device[dev_len] = 0;
3569
3570 if (!(devchar & DEV$M_DIR)) {
3571 strcpy(out, device);
3572 return 0;
3573 }
3574 }
22d4bb9c
CB
3575 }
3576
ebd4d70b 3577 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3578 p->fd_out = dup(fd);
8a646e0b 3579 create_mbx(&p->chan_in, &d_mbx);
ebd4d70b 3580 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3581 n = (p->bufsize+1) * sizeof(char);
ebd4d70b 3582 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3583 p->shut_on_empty = FALSE;
3584 p->retry = 0;
3585 p->info = 0;
3586 strcpy(out, mbx);
3587
ebd4d70b
JM
3588 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3589 pipe_mbxtofd_ast, p,
3590 p->buf, p->bufsize, 0, 0, 0, 0));
22d4bb9c
CB
3591
3592 return p;
3593}
3594
3595static void
3596pipe_mbxtofd_ast(pPipe p)
3597{
22d4bb9c
CB
3598 int iss = p->iosb.status;
3599 int done = p->info->done;
3600 int iss2;
3601 int eof = (iss == SS$_ENDOFFILE);
3602 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3603 int err = !(iss&1) && !eof;
fd8cd3a3
DS
3604#if defined(PERL_IMPLICIT_CONTEXT)
3605 pTHX = p->thx;
3606#endif
22d4bb9c
CB
3607
3608 if (done && myeof) { /* end piping */
3609 close(p->fd_out);
3610 sys$dassgn(p->chan_in);
3611 *p->pipe_done = TRUE;
ebd4d70b 3612 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
3613 return;
3614 }
3615
3616 if (!err && !eof) { /* good data to send to file */
3617 p->buf[p->iosb.count] = '\n';
3618 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3619 if (iss2 < 0) {
3620 p->retry++;
3621 if (p->retry < MAX_RETRY) {
ebd4d70b 3622 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
22d4bb9c
CB
3623 return;
3624 }
3625 }
3626 p->retry = 0;
3627 } else if (err) {
ebd4d70b 3628 _ckvmssts_noperl(iss);
22d4bb9c
CB
3629 }
3630
3631
3632 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3633 pipe_mbxtofd_ast, p,
3634 p->buf, p->bufsize, 0, 0, 0, 0);
3635 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
ebd4d70b 3636 _ckvmssts_noperl(iss);
22d4bb9c
CB
3637}
3638
3639
3640typedef struct _pipeloc PLOC;
3641typedef struct _pipeloc* pPLOC;
3642
3643struct _pipeloc {
3644 pPLOC next;
3645 char dir[NAM$C_MAXRSS+1];
3646};
3647static pPLOC head_PLOC = 0;
3648
5c0ae288 3649void
fd8cd3a3 3650free_pipelocs(pTHX_ void *head)
5c0ae288
CL
3651{
3652 pPLOC p, pnext;
ff7adb52 3653 pPLOC *pHead = (pPLOC *)head;
5c0ae288 3654
ff7adb52 3655 p = *pHead;
5c0ae288
CL
3656 while (p) {
3657 pnext = p->next;
e0ef6b43 3658 PerlMem_free(p);
5c0ae288
CL
3659 p = pnext;
3660 }
ff7adb52 3661 *pHead = 0;
5c0ae288 3662}
22d4bb9c
CB
3663
3664static void
fd8cd3a3 3665store_pipelocs(pTHX)
22d4bb9c
CB
3666{
3667 int i;
3668 pPLOC p;
ff7adb52 3669 AV *av = 0;
22d4bb9c 3670 SV *dirsv;
22d4bb9c
CB
3671 char *dir, *x;
3672 char *unixdir;
3673 char temp[NAM$C_MAXRSS+1];
3674 STRLEN n_a;
3675
ff7adb52 3676 if (head_PLOC)
218fdd94 3677 free_pipelocs(aTHX_ &head_PLOC);
ff7adb52 3678
22d4bb9c
CB
3679/* the . directory from @INC comes last */
3680
e0ef6b43 3681 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3682 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3683 p->next = head_PLOC;
3684 head_PLOC = p;
3685 strcpy(p->dir,"./");
3686
3687/* get the directory from $^X */
3688
c5375c28 3689 unixdir = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 3690 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 3691
218fdd94
CL
3692#ifdef PERL_IMPLICIT_CONTEXT
3693 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3694#else
22d4bb9c 3695 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
218fdd94 3696#endif
22d4bb9c
CB
3697 strcpy(temp, PL_origargv[0]);
3698 x = strrchr(temp,']');
2497a41f
JM
3699 if (x == NULL) {
3700 x = strrchr(temp,'>');
3701 if (x == NULL) {
3702 /* It could be a UNIX path */
3703 x = strrchr(temp,'/');
3704 }
3705 }
3706 if (x)
3707 x[1] = '\0';
3708 else {
3709 /* Got a bare name, so use default directory */
3710 temp[0] = '.';
3711 temp[1] = '\0';
3712 }
22d4bb9c 3713
4e205ed6 3714 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
e0ef6b43 3715 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3716 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3717 p->next = head_PLOC;
3718 head_PLOC = p;
3719 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3720 p->dir[NAM$C_MAXRSS] = '\0';
c5375c28 3721 }
22d4bb9c
CB
3722 }
3723
3724/* reverse order of @INC entries, skip "." since entered above */
3725
218fdd94
CL
3726#ifdef PERL_IMPLICIT_CONTEXT
3727 if (aTHX)
3728#endif
ff7adb52
CL
3729 if (PL_incgv) av = GvAVn(PL_incgv);
3730
3731 for (i = 0; av && i <= AvFILL(av); i++) {
22d4bb9c
CB
3732 dirsv = *av_fetch(av,i,TRUE);
3733
3734 if (SvROK(dirsv)) continue;
3735 dir = SvPVx(dirsv,n_a);
3736 if (strcmp(dir,".") == 0) continue;
4e205ed6 3737 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
22d4bb9c
CB
3738 continue;
3739
e0ef6b43 3740 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
22d4bb9c
CB
3741 p->next = head_PLOC;
3742 head_PLOC = p;
3743 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3744 p->dir[NAM$C_MAXRSS] = '\0';
3745 }
3746
3747/* most likely spot (ARCHLIB) put first in the list */
3748
3749#ifdef ARCHLIB_EXP
4e205ed6 3750 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
e0ef6b43 3751 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3752 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3753 p->next = head_PLOC;
3754 head_PLOC = p;
3755 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3756 p->dir[NAM$C_MAXRSS] = '\0';
3757 }
3758#endif
c5375c28 3759 PerlMem_free(unixdir);
22d4bb9c
CB
3760}
3761
a1887106
JM
3762static I32
3763Perl_cando_by_name_int
3764 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3765#if !defined(PERL_IMPLICIT_CONTEXT)
3766#define cando_by_name_int Perl_cando_by_name_int
3767#else
3768#define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3769#endif
22d4bb9c
CB
3770
3771static char *
fd8cd3a3 3772find_vmspipe(pTHX)
22d4bb9c
CB
3773{
3774 static int vmspipe_file_status = 0;
3775 static char vmspipe_file[NAM$C_MAXRSS+1];
3776
3777 /* already found? Check and use ... need read+execute permission */
3778
3779 if (vmspipe_file_status == 1) {
a1887106
JM
3780 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3781 && cando_by_name_int
3782 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
22d4bb9c
CB
3783 return vmspipe_file;
3784 }
3785 vmspipe_file_status = 0;
3786 }
3787
3788 /* scan through stored @INC, $^X */
3789
3790 if (vmspipe_file_status == 0) {
3791 char file[NAM$C_MAXRSS+1];
3792 pPLOC p = head_PLOC;
3793
3794 while (p) {
2f4077ca 3795 char * exp_res;
4d743a9b 3796 int dirlen;
22d4bb9c 3797 strcpy(file, p->dir);
4d743a9b
JM
3798 dirlen = strlen(file);
3799 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
22d4bb9c
CB
3800 file[NAM$C_MAXRSS] = '\0';
3801 p = p->next;
3802
6fb6c614 3803 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
2f4077ca 3804 if (!exp_res) continue;
22d4bb9c 3805
a1887106
JM
3806 if (cando_by_name_int
3807 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3808 && cando_by_name_int
3809 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
22d4bb9c
CB
3810 vmspipe_file_status = 1;
3811 return vmspipe_file;
3812 }
3813 }
3814 vmspipe_file_status = -1; /* failed, use tempfiles */
3815 }
3816
3817 return 0;
3818}
3819
3820static FILE *
fd8cd3a3 3821vmspipe_tempfile(pTHX)
22d4bb9c
CB
3822{
3823 char file[NAM$C_MAXRSS+1];
3824 FILE *fp;
3825 static int index = 0;
2497a41f
JM
3826 Stat_t s0, s1;
3827 int cmp_result;
22d4bb9c
CB
3828
3829 /* create a tempfile */
3830
3831 /* we can't go from W, shr=get to R, shr=get without
3832 an intermediate vulnerable state, so don't bother trying...
3833
3834 and lib$spawn doesn't shr=put, so have to close the write
3835
3836 So... match up the creation date/time and the FID to
3837 make sure we're dealing with the same file
3838
3839 */
3840
3841 index++;
2497a41f
JM
3842 if (!decc_filename_unix_only) {
3843 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3844 fp = fopen(file,"w");
3845 if (!fp) {
22d4bb9c
CB
3846 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3847 fp = fopen(file,"w");
3848 if (!fp) {
3849 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3850 fp = fopen(file,"w");
2497a41f
JM
3851 }
3852 }
3853 }
3854 else {
3855 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3856 fp = fopen(file,"w");
3857 if (!fp) {
3858 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3859 fp = fopen(file,"w");
3860 if (!fp) {
3861 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3862 fp = fopen(file,"w");
3863 }
3864 }
22d4bb9c
CB
3865 }
3866 if (!fp) return 0; /* we're hosed */
3867
f9ecfa39 3868 fprintf(fp,"$! 'f$verify(0)'\n");
22d4bb9c
CB
3869 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3870 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3871 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3872 fprintf(fp,"$ perl_on = \"set noon\"\n");
3873 fprintf(fp,"$ perl_exit = \"exit\"\n");
3874 fprintf(fp,"$ perl_del = \"delete\"\n");
3875 fprintf(fp,"$ pif = \"if\"\n");
3876 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
2d5e9e5d
JH
3877 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3878 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
0e06870b 3879 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
48b5a746
CL
3880 fprintf(fp,"$! --- build command line to get max possible length\n");
3881 fprintf(fp,"$c=perl_popen_cmd0\n");
3882 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3883 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3884 fprintf(fp,"$x=perl_popen_cmd3\n");
3885 fprintf(fp,"$c=c+x\n");
22d4bb9c 3886 fprintf(fp,"$ perl_on\n");
f9ecfa39 3887 fprintf(fp,"$ 'c'\n");
22d4bb9c 3888 fprintf(fp,"$ perl_status = $STATUS\n");
0e06870b 3889 fprintf(fp,"$ perl_del 'perl_cfile'\n");
22d4bb9c
CB
3890 fprintf(fp,"$ perl_exit 'perl_status'\n");
3891 fsync(fileno(fp));
3892
3893 fgetname(fp, file, 1);
312ac60b 3894 fstat(fileno(fp), &s0.crtl_stat);
22d4bb9c
CB
3895 fclose(fp);
3896
2497a41f 3897 if (decc_filename_unix_only)
0e5ce2c7 3898 int_tounixspec(file, file, NULL);
22d4bb9c
CB
3899 fp = fopen(file,"r","shr=get");
3900 if (!fp) return 0;
312ac60b 3901 fstat(fileno(fp), &s1.crtl_stat);
2497a41f 3902
682e4b71 3903 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
2497a41f 3904 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
22d4bb9c
CB
3905 fclose(fp);
3906 return 0;
3907 }
3908
3909 return fp;
3910}
3911
3912
cd1191f1
CB
3913static int vms_is_syscommand_xterm(void)
3914{
3915 const static struct dsc$descriptor_s syscommand_dsc =
3916 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3917
3918 const static struct dsc$descriptor_s decwdisplay_dsc =
3919 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3920
3921 struct item_list_3 items[2];
3922 unsigned short dvi_iosb[4];
3923 unsigned long devchar;
3924 unsigned long devclass;
3925 int status;
3926
3927 /* Very simple check to guess if sys$command is a decterm? */
3928 /* First see if the DECW$DISPLAY: device exists */
3929 items[0].len = 4;
3930 items[0].code = DVI$_DEVCHAR;
3931 items[0].bufadr = &devchar;
3932 items[0].retadr = NULL;
3933 items[1].len = 0;
3934 items[1].code = 0;
3935
3936 status = sys$getdviw
3937 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3938
3939 if ($VMS_STATUS_SUCCESS(status)) {
3940 status = dvi_iosb[0];
3941 }
3942
3943 if (!$VMS_STATUS_SUCCESS(status)) {
3944 SETERRNO(EVMSERR, status);
3945 return -1;
3946 }
3947
3948 /* If it does, then for now assume that we are on a workstation */
3949 /* Now verify that SYS$COMMAND is a terminal */
3950 /* for creating the debugger DECTerm */
3951
3952 items[0].len = 4;
3953 items[0].code = DVI$_DEVCLASS;
3954 items[0].bufadr = &devclass;
3955 items[0].retadr = NULL;
3956 items[1].len = 0;
3957 items[1].code = 0;
3958
3959 status = sys$getdviw
3960 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3961
3962 if ($VMS_STATUS_SUCCESS(status)) {
3963 status = dvi_iosb[0];
3964 }
3965
3966 if (!$VMS_STATUS_SUCCESS(status)) {
3967 SETERRNO(EVMSERR, status);
3968 return -1;
3969 }
3970 else {
3971 if (devclass == DC$_TERM) {
3972 return 0;
3973 }
3974 }
3975 return -1;
3976}
3977
3978/* If we are on a DECTerm, we can pretend to fork xterms when requested */
3979static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3980{
3981 int status;
3982 int ret_stat;
3983 char * ret_char;
3984 char device_name[65];
3985 unsigned short device_name_len;
3986 struct dsc$descriptor_s customization_dsc;
3987 struct dsc$descriptor_s device_name_dsc;
3988 const char * cptr;
cd1191f1
CB
3989 char customization[200];
3990 char title[40];
3991 pInfo info = NULL;
3992 char mbx1[64];
3993 unsigned short p_chan;
3994 int n;
3995 unsigned short iosb[4];
cd1191f1
CB
3996 const char * cust_str =
3997 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3998 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3999 DSC$K_CLASS_S, mbx1};
4000
8cb5d3d5
JM
4001 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4002 /*---------------------------------------*/
d30c1055 4003 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
8cb5d3d5
JM
4004
4005
4006 /* Make sure that this is from the Perl debugger */
cd1191f1
CB
4007 ret_char = strstr(cmd," xterm ");
4008 if (ret_char == NULL)
4009 return NULL;
4010 cptr = ret_char + 7;
4011 ret_char = strstr(cmd,"tty");
4012 if (ret_char == NULL)
4013 return NULL;
4014 ret_char = strstr(cmd,"sleep");
4015 if (ret_char == NULL)
4016 return NULL;
4017
8cb5d3d5
JM
4018 if (decw_term_port == 0) {
4019 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4020 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4021 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4022
d30c1055 4023 status = lib$find_image_symbol
8cb5d3d5
JM
4024 (&filename1_dsc,
4025 &decw_term_port_dsc,
4026 (void *)&decw_term_port,
4027 NULL,
4028 0);
4029
4030 /* Try again with the other image name */
4031 if (!$VMS_STATUS_SUCCESS(status)) {
4032
d30c1055 4033 status = lib$find_image_symbol
8cb5d3d5
JM
4034 (&filename2_dsc,
4035 &decw_term_port_dsc,
4036 (void *)&decw_term_port,
4037 NULL,
4038 0);
4039
4040 }
4041
4042 }
4043
4044
4045 /* No decw$term_port, give it up */
4046 if (!$VMS_STATUS_SUCCESS(status))
4047 return NULL;
4048
cd1191f1
CB
4049 /* Are we on a workstation? */
4050 /* to do: capture the rows / columns and pass their properties */
4051 ret_stat = vms_is_syscommand_xterm();
4052 if (ret_stat < 0)
4053 return NULL;
4054
4055 /* Make the title: */
4056 ret_char = strstr(cptr,"-title");
4057 if (ret_char != NULL) {
4058 while ((*cptr != 0) && (*cptr != '\"')) {
4059 cptr++;
4060 }
4061 if (*cptr == '\"')
4062 cptr++;
4063 n = 0;
4064 while ((*cptr != 0) && (*cptr != '\"')) {
4065 title[n] = *cptr;
4066 n++;
4067 if (n == 39) {
07bee079 4068 title[39] = 0;
cd1191f1
CB
4069 break;
4070 }
4071 cptr++;
4072 }
4073 title[n] = 0;
4074 }
4075 else {
4076 /* Default title */
4077 strcpy(title,"Perl Debug DECTerm");
4078 }
4079 sprintf(customization, cust_str, title);
4080
4081 customization_dsc.dsc$a_pointer = customization;
4082 customization_dsc.dsc$w_length = strlen(customization);
4083 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4084 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4085
4086 device_name_dsc.dsc$a_pointer = device_name;
4087 device_name_dsc.dsc$w_length = sizeof device_name -1;
4088 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4089 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4090
4091 device_name_len = 0;
4092
4093 /* Try to create the window */
8cb5d3d5 4094 status = (*decw_term_port)
cd1191f1
CB
4095 (NULL,
4096 NULL,
4097 &customization_dsc,
4098 &device_name_dsc,
4099 &device_name_len,
4100 NULL,
4101 NULL,
4102 NULL);
4103 if (!$VMS_STATUS_SUCCESS(status)) {
4104 SETERRNO(EVMSERR, status);
4105 return NULL;
4106 }
4107
4108 device_name[device_name_len] = '\0';
4109
4110 /* Need to set this up to look like a pipe for cleanup */
4111 n = sizeof(Info);
4112 status = lib$get_vm(&n, &info);
4113 if (!$VMS_STATUS_SUCCESS(status)) {
4114 SETERRNO(ENOMEM, status);
4115 return NULL;
4116 }
4117
4118 info->mode = *mode;
4119 info->done = FALSE;
4120 info->completion = 0;
4121 info->closing = FALSE;
4122 info->in = 0;
4123 info->out = 0;
4124 info->err = 0;
4e205ed6 4125 info->fp = NULL;
cd1191f1
CB
4126 info->useFILE = 0;
4127 info->waiting = 0;
4128 info->in_done = TRUE;
4129 info->out_done = TRUE;
4130 info->err_done = TRUE;
4131
4132 /* Assign a channel on this so that it will persist, and not login */
4133 /* We stash this channel in the info structure for reference. */
4134 /* The created xterm self destructs when the last channel is removed */
4135 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4136 /* So leave this assigned. */
4137 device_name_dsc.dsc$w_length = device_name_len;
4138 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4139 if (!$VMS_STATUS_SUCCESS(status)) {
4140 SETERRNO(EVMSERR, status);
4141 return NULL;
4142 }
4143 info->xchan_valid = 1;
4144
4145 /* Now create a mailbox to be read by the application */
4146
8a646e0b 4147 create_mbx(&p_chan, &d_mbx1);
cd1191f1
CB
4148
4149 /* write the name of the created terminal to the mailbox */
4150 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4151 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4152
4153 if (!$VMS_STATUS_SUCCESS(status)) {
4154 SETERRNO(EVMSERR, status);
4155 return NULL;
4156 }
4157
4158 info->fp = PerlIO_open(mbx1, mode);
4159
4160 /* Done with this channel */
4161 sys$dassgn(p_chan);
4162
4163 /* If any errors, then clean up */
4164 if (!info->fp) {
4165 n = sizeof(Info);
ebd4d70b 4166 _ckvmssts_noperl(lib$free_vm(&n, &info));
cd1191f1
CB
4167 return NULL;
4168 }
4169
4170 /* All done */
4171 return info->fp;
4172}
22d4bb9c 4173
ebd4d70b
JM
4174static I32 my_pclose_pinfo(pTHX_ pInfo info);
4175
8fde5078 4176static PerlIO *
2fbb330f 4177safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
a0d0e21e 4178{
748a9306 4179 static int handler_set_up = FALSE;
ebd4d70b 4180 PerlIO * ret_fp;
55f2b99c 4181 unsigned long int sts, flags = CLI$M_NOWAIT;
f9ecfa39
PP
4182 /* The use of a GLOBAL table (as was done previously) rendered
4183 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4184 * environment. Hence we've switched to LOCAL symbol table.
4185 */
4186 unsigned int table = LIB$K_CLI_LOCAL_SYM;
d4c83939 4187 int j, wait = 0, n;
ff7adb52 4188 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
cfcfe586 4189 char *in, *out, *err, mbx[512];
22d4bb9c
CB
4190 FILE *tpipe = 0;
4191 char tfilebuf[NAM$C_MAXRSS+1];
d4c83939 4192 pInfo info = NULL;
48b5a746 4193 char cmd_sym_name[20];
22d4bb9c
CB
4194 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4195 DSC$K_CLASS_S, symbol};
22d4bb9c 4196 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
a0d0e21e 4197 DSC$K_CLASS_S, 0};
48b5a746
CL
4198 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4199 DSC$K_CLASS_S, cmd_sym_name};
218fdd94 4200 struct dsc$descriptor_s *vmscmd;
22d4bb9c 4201 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
0e06870b 4202 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
22d4bb9c 4203 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
cd1191f1 4204
cd1191f1
CB
4205 /* Check here for Xterm create request. This means looking for
4206 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4207 * is possible to create an xterm.
4208 */
4209 if (*in_mode == 'r') {
4210 PerlIO * xterm_fd;
4211
4d9538c1
JM
4212#if defined(PERL_IMPLICIT_CONTEXT)
4213 /* Can not fork an xterm with a NULL context */
4214 /* This probably could never happen */
4215 xterm_fd = NULL;
4216 if (aTHX != NULL)
4217#endif
cd1191f1 4218 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4e205ed6 4219 if (xterm_fd != NULL)
cd1191f1
CB
4220 return xterm_fd;
4221 }
cd1191f1 4222
afd8f436
JH
4223 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4224
22d4bb9c
CB
4225 /* once-per-program initialization...
4226 note that the SETAST calls and the dual test of pipe_ef
4227 makes sure that only the FIRST thread through here does
4228 the initialization...all other threads wait until it's
4229 done.
4230
4231 Yeah, uglier than a pthread call, it's got all the stuff inline
4232 rather than in a separate routine.
4233 */
4234
4235 if (!pipe_ef) {
ebd4d70b 4236 _ckvmssts_noperl(sys$setast(0));
22d4bb9c
CB
4237 if (!pipe_ef) {
4238 unsigned long int pidcode = JPI$_PID;
4239 $DESCRIPTOR(d_delay, RETRY_DELAY);
ebd4d70b
JM
4240 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4241 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4242 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
22d4bb9c
CB
4243 }
4244 if (!handler_set_up) {
ebd4d70b 4245 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
22d4bb9c
CB
4246 handler_set_up = TRUE;
4247 }
ebd4d70b 4248 _ckvmssts_noperl(sys$setast(1));
22d4bb9c
CB
4249 }
4250
4251 /* see if we can find a VMSPIPE.COM */
4252
4253 tfilebuf[0] = '@';
fd8cd3a3 4254 vmspipe = find_vmspipe(aTHX);
22d4bb9c
CB
4255 if (vmspipe) {
4256 strcpy(tfilebuf+1,vmspipe);
4257 } else { /* uh, oh...we're in tempfile hell */
fd8cd3a3 4258 tpipe = vmspipe_tempfile(aTHX);
22d4bb9c
CB
4259 if (!tpipe) { /* a fish popular in Boston */
4260 if (ckWARN(WARN_PIPE)) {
f98bc0c6 4261 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
22d4bb9c 4262 }
4e205ed6 4263 return NULL;
22d4bb9c
CB
4264 }
4265 fgetname(tpipe,tfilebuf+1,1);
4266 }
4267 vmspipedsc.dsc$a_pointer = tfilebuf;
4268 vmspipedsc.dsc$w_length = strlen(tfilebuf);
a0d0e21e 4269
218fdd94 4270 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
a2669cfc
JH
4271 if (!(sts & 1)) {
4272 switch (sts) {
4273 case RMS$_FNF: case RMS$_DNF:
4274 set_errno(ENOENT); break;
4275 case RMS$_DIR:
4276 set_errno(ENOTDIR); break;
4277 case RMS$_DEV:
4278 set_errno(ENODEV); break;
4279 case RMS$_PRV:
4280 set_errno(EACCES); break;
4281 case RMS$_SYN:
4282 set_errno(EINVAL); break;
4283 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4284 set_errno(E2BIG); break;
4285 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 4286 _ckvmssts_noperl(sts); /* fall through */
a2669cfc
JH
4287 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4288 set_errno(EVMSERR);
4289 }
4290 set_vaxc_errno(sts);
cd1191f1 4291 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
f98bc0c6 4292 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
a2669cfc 4293 }
ff7adb52 4294 *psts = sts;
4e205ed6 4295 return NULL;
a2669cfc 4296 }
d4c83939 4297 n = sizeof(Info);
ebd4d70b 4298 _ckvmssts_noperl(lib$get_vm(&n, &info));
22d4bb9c 4299
ff7adb52 4300 strcpy(mode,in_mode);
22d4bb9c
CB
4301 info->mode = *mode;
4302 info->done = FALSE;
4303 info->completion = 0;
4304 info->closing = FALSE;
4305 info->in = 0;
4306 info->out = 0;
4307 info->err = 0;
4e205ed6 4308 info->fp = NULL;
ff7adb52
CL
4309 info->useFILE = 0;
4310 info->waiting = 0;
22d4bb9c
CB
4311 info->in_done = TRUE;
4312 info->out_done = TRUE;
4313 info->err_done = TRUE;
cd1191f1
CB
4314 info->xchan = 0;
4315 info->xchan_valid = 0;
cfcfe586
JM
4316
4317 in = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4318 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4319 out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4320 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4321 err = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4322 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4323
0e06870b 4324 in[0] = out[0] = err[0] = '\0';
22d4bb9c 4325
ff7adb52
CL
4326 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4327 info->useFILE = 1;
4328 strcpy(p,p+1);
4329 }
4330 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4331 wait = 1;
4332 strcpy(p,p+1);
4333 }
4334
22d4bb9c 4335 if (*mode == 'r') { /* piping from subroutine */
22d4bb9c 4336
fd8cd3a3 4337 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
22d4bb9c
CB
4338 if (info->out) {
4339 info->out->pipe_done = &info->out_done;
4340 info->out_done = FALSE;
4341 info->out->info = info;
4342 }
ff7adb52 4343 if (!info->useFILE) {
cd1191f1 4344 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4345 } else {
4346 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4347 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4348 }
4349
22d4bb9c
CB
4350 if (!info->fp && info->out) {
4351 sys$cancel(info->out->chan_out);
4352
4353 while (!info->out_done) {
4354 int done;
ebd4d70b 4355 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4356 done = info->out_done;
ebd4d70b
JM
4357 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4358 _ckvmssts_noperl(sys$setast(1));
4359 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
0e06870b 4360 }
22d4bb9c 4361
d4c83939
CB
4362 if (info->out->buf) {
4363 n = info->out->bufsize * sizeof(char);
ebd4d70b 4364 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
d4c83939
CB
4365 }
4366 n = sizeof(Pipe);
ebd4d70b 4367 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
d4c83939 4368 n = sizeof(Info);
ebd4d70b 4369 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4370 *psts = RMS$_FNF;
4e205ed6 4371 return NULL;
0e06870b 4372 }
22d4bb9c 4373
fd8cd3a3 4374 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
22d4bb9c
CB
4375 if (info->err) {
4376 info->err->pipe_done = &info->err_done;
4377 info->err_done = FALSE;
4378 info->err->info = info;
4379 }
a0d0e21e 4380
ff7adb52
CL
4381 } else if (*mode == 'w') { /* piping to subroutine */
4382
4383 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4384 if (info->out) {
4385 info->out->pipe_done = &info->out_done;
4386 info->out_done = FALSE;
4387 info->out->info = info;
4388 }
4389
4390 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4391 if (info->err) {
4392 info->err->pipe_done = &info->err_done;
4393 info->err_done = FALSE;
4394 info->err->info = info;
4395 }
a0d0e21e 4396
fd8cd3a3 4397 info->in = pipe_tochild_setup(aTHX_ in,mbx);
ff7adb52 4398 if (!info->useFILE) {
a480973c 4399 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4400 } else {
4401 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4402 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4403 }
4404
22d4bb9c
CB
4405 if (info->in) {
4406 info->in->pipe_done = &info->in_done;
4407 info->in_done = FALSE;
4408 info->in->info = info;
4409 }
a0d0e21e 4410
22d4bb9c
CB
4411 /* error cleanup */
4412 if (!info->fp && info->in) {
4413 info->done = TRUE;
ebd4d70b
JM
4414 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4415 0, 0, 0, 0, 0, 0, 0, 0));
22d4bb9c
CB
4416
4417 while (!info->in_done) {
4418 int done;
ebd4d70b 4419 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4420 done = info->in_done;
ebd4d70b
JM
4421 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4422 _ckvmssts_noperl(sys$setast(1));
4423 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
22d4bb9c 4424 }
a0d0e21e 4425
d4c83939
CB
4426 if (info->in->buf) {
4427 n = info->in->bufsize * sizeof(char);
ebd4d70b 4428 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
d4c83939
CB
4429 }
4430 n = sizeof(Pipe);
ebd4d70b 4431 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
d4c83939 4432 n = sizeof(Info);
ebd4d70b 4433 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4434 *psts = RMS$_FNF;
4e205ed6 4435 return NULL;
22d4bb9c 4436 }
a0d0e21e 4437
22d4bb9c 4438
ff7adb52 4439 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
fd8cd3a3 4440 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
22d4bb9c
CB
4441 if (info->out) {
4442 info->out->pipe_done = &info->out_done;
4443 info->out_done = FALSE;
4444 info->out->info = info;
4445 }
0e06870b 4446
fd8cd3a3 4447 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
0e06870b
CB
4448 if (info->err) {
4449 info->err->pipe_done = &info->err_done;
4450 info->err_done = FALSE;
4451 info->err->info = info;
4452 }
748a9306 4453 }
22d4bb9c
CB
4454
4455 symbol[MAX_DCL_SYMBOL] = '\0';
4456
4457 strncpy(symbol, in, MAX_DCL_SYMBOL);
4458 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4459 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
22d4bb9c
CB
4460
4461 strncpy(symbol, err, MAX_DCL_SYMBOL);
4462 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4463 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
22d4bb9c 4464
0e06870b
CB
4465 strncpy(symbol, out, MAX_DCL_SYMBOL);
4466 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4467 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
22d4bb9c 4468
cfcfe586
JM
4469 /* Done with the names for the pipes */
4470 PerlMem_free(err);
4471 PerlMem_free(out);
4472 PerlMem_free(in);
4473
218fdd94 4474 p = vmscmd->dsc$a_pointer;
22d4bb9c
CB
4475 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4476 if (*p == '$') p++; /* remove leading $ */
4477 while (*p == ' ' || *p == '\t') p++;
48b5a746
CL
4478
4479 for (j = 0; j < 4; j++) {
4480 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4481 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4482
22d4bb9c
CB
4483 strncpy(symbol, p, MAX_DCL_SYMBOL);
4484 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4485 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
22d4bb9c 4486
48b5a746
CL
4487 if (strlen(p) > MAX_DCL_SYMBOL) {
4488 p += MAX_DCL_SYMBOL;
4489 } else {
4490 p += strlen(p);
4491 }
4492 }
ebd4d70b 4493 _ckvmssts_noperl(sys$setast(0));
a0d0e21e
LW
4494 info->next=open_pipes; /* prepend to list */
4495 open_pipes=info;
ebd4d70b 4496 _ckvmssts_noperl(sys$setast(1));
55f2b99c
CB
4497 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4498 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4499 * have SYS$COMMAND if we need it.
4500 */
ebd4d70b 4501 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
22d4bb9c
CB
4502 0, &info->pid, &info->completion,
4503 0, popen_completion_ast,info,0,0,0));
4504
4505 /* if we were using a tempfile, close it now */
4506
4507 if (tpipe) fclose(tpipe);
4508
ff7adb52 4509 /* once the subprocess is spawned, it has copied the symbols and
22d4bb9c
CB
4510 we can get rid of ours */
4511
48b5a746
CL
4512 for (j = 0; j < 4; j++) {
4513 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4514 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
ebd4d70b 4515 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
48b5a746 4516 }
ebd4d70b
JM
4517 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4518 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4519 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
218fdd94 4520 vms_execfree(vmscmd);
a0d0e21e 4521
218fdd94
CL
4522#ifdef PERL_IMPLICIT_CONTEXT
4523 if (aTHX)
4524#endif
6b88bc9c 4525 PL_forkprocess = info->pid;
218fdd94 4526
ebd4d70b 4527 ret_fp = info->fp;
ff7adb52 4528 if (wait) {
ebd4d70b 4529 dSAVEDERRNO;
ff7adb52
CL
4530 int done = 0;
4531 while (!done) {
ebd4d70b 4532 _ckvmssts_noperl(sys$setast(0));
ff7adb52 4533 done = info->done;
ebd4d70b
JM
4534 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4535 _ckvmssts_noperl(sys$setast(1));
4536 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
ff7adb52
CL
4537 }
4538 *psts = info->completion;
2fbb330f
JM
4539/* Caller thinks it is open and tries to close it. */
4540/* This causes some problems, as it changes the error status */
4541/* my_pclose(info->fp); */
ebd4d70b
JM
4542
4543 /* If we did not have a file pointer open, then we have to */
4544 /* clean up here or eventually we will run out of something */
4545 SAVE_ERRNO;
4546 if (info->fp == NULL) {
4547 my_pclose_pinfo(aTHX_ info);
4548 }
4549 RESTORE_ERRNO;
4550
ff7adb52 4551 } else {
eed5d6a1 4552 *psts = info->pid;
ff7adb52 4553 }
ebd4d70b 4554 return ret_fp;
1e422769 4555} /* end of safe_popen */
4556
4557
a15cef0c
CB
4558/*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4559PerlIO *
2fbb330f 4560Perl_my_popen(pTHX_ const char *cmd, const char *mode)
1e422769 4561{
ff7adb52 4562 int sts;
1e422769 4563 TAINT_ENV();
4564 TAINT_PROPER("popen");
45bc9206 4565 PERL_FLUSHALL_FOR_CHILD;
ff7adb52 4566 return safe_popen(aTHX_ cmd,mode,&sts);
a0d0e21e 4567}
1e422769 4568
a0d0e21e
LW
4569/*}}}*/
4570
ebd4d70b
JM
4571
4572/* Routine to close and cleanup a pipe info structure */
4573
4574static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4575
748a9306 4576 unsigned long int retsts;
4e0c9737 4577 int done, n;
ebd4d70b 4578 pInfo next, last;
748a9306 4579
bbce6d69 4580 /* If we were writing to a subprocess, insure that someone reading from
4581 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
22d4bb9c
CB
4582 * produce an EOF record in the mailbox.
4583 *
4584 * well, at least sometimes it *does*, so we have to watch out for
4585 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4586 */
ff7adb52 4587 if (info->fp) {
5ce486e0
CB
4588 if (!info->useFILE
4589#if defined(USE_ITHREADS)
4590 && my_perl
4591#endif
a24c654f
CB
4592#ifdef USE_PERLIO
4593 && PL_perlio_fd_refcnt
4594#endif
4595 )
5ce486e0 4596 PerlIO_flush(info->fp);
ff7adb52
CL
4597 else
4598 fflush((FILE *)info->fp);
4599 }
22d4bb9c 4600
b08af3f0 4601 _ckvmssts(sys$setast(0));
22d4bb9c
CB
4602 info->closing = TRUE;
4603 done = info->done && info->in_done && info->out_done && info->err_done;
4604 /* hanging on write to Perl's input? cancel it */
4605 if (info->mode == 'r' && info->out && !info->out_done) {
4606 if (info->out->chan_out) {
4607 _ckvmssts(sys$cancel(info->out->chan_out));
4608 if (!info->out->chan_in) { /* EOF generation, need AST */
4609 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4610 }
4611 }
4612 }
4613 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4614 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4615 0, 0, 0, 0, 0, 0));
b08af3f0 4616 _ckvmssts(sys$setast(1));
ff7adb52 4617 if (info->fp) {
5ce486e0
CB
4618 if (!info->useFILE
4619#if defined(USE_ITHREADS)
4620 && my_perl
4621#endif
a24c654f
CB
4622#ifdef USE_PERLIO
4623 && PL_perlio_fd_refcnt
4624#endif
4625 )
d4c83939 4626 PerlIO_close(info->fp);
ff7adb52
CL
4627 else
4628 fclose((FILE *)info->fp);
4629 }
22d4bb9c
CB
4630 /*
4631 we have to wait until subprocess completes, but ALSO wait until all
4632 the i/o completes...otherwise we'll be freeing the "info" structure
4633 that the i/o ASTs could still be using...
4634 */
4635
4636 while (!done) {
4637 _ckvmssts(sys$setast(0));
4638 done = info->done && info->in_done && info->out_done && info->err_done;
4639 if (!done) _ckvmssts(sys$clref(pipe_ef));
4640 _ckvmssts(sys$setast(1));
4641 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4642 }
4643 retsts = info->completion;
a0d0e21e 4644
a0d0e21e 4645 /* remove from list of open pipes */
b08af3f0 4646 _ckvmssts(sys$setast(0));
ebd4d70b
JM
4647 last = NULL;
4648 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4649 if (next == info)
4650 break;
4651 }
4652
4653 if (last)
4654 last->next = info->next;
4655 else
4656 open_pipes = info->next;
b08af3f0 4657 _ckvmssts(sys$setast(1));
22d4bb9c
CB
4658
4659 /* free buffers and structures */
4660
4661 if (info->in) {
d4c83939
CB
4662 if (info->in->buf) {
4663 n = info->in->bufsize * sizeof(char);
4664 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4665 }
4666 n = sizeof(Pipe);
4667 _ckvmssts(lib$free_vm(&n, &info->in));
22d4bb9c
CB
4668 }
4669 if (info->out) {
d4c83939
CB
4670 if (info->out->buf) {
4671 n = info->out->bufsize * sizeof(char);
4672 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4673 }
4674 n = sizeof(Pipe);
4675 _ckvmssts(lib$free_vm(&n, &info->out));
22d4bb9c
CB
4676 }
4677 if (info->err) {
d4c83939
CB
4678 if (info->err->buf) {
4679 n = info->err->bufsize * sizeof(char);
4680 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4681 }
4682 n = sizeof(Pipe);
4683 _ckvmssts(lib$free_vm(&n, &info->err));
22d4bb9c 4684 }
d4c83939
CB
4685 n = sizeof(Info);
4686 _ckvmssts(lib$free_vm(&n, &info));
a0d0e21e
LW
4687
4688 return retsts;
ebd4d70b
JM
4689}
4690
4691
4692/*{{{ I32 my_pclose(PerlIO *fp)*/
4693I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4694{
4695 pInfo info, last = NULL;
4696 I32 ret_status;
4697
4698 /* Fixme - need ast and mutex protection here */
4699 for (info = open_pipes; info != NULL; last = info, info = info->next)
4700 if (info->fp == fp) break;
4701
4702 if (info == NULL) { /* no such pipe open */
4703 set_errno(ECHILD); /* quoth POSIX */
4704 set_vaxc_errno(SS$_NONEXPR);
4705 return -1;
4706 }
4707
4708 ret_status = my_pclose_pinfo(aTHX_ info);
4709
4710 return ret_status;
748a9306 4711
a0d0e21e
LW
4712} /* end of my_pclose() */
4713
119586db 4714#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
4715 /* Roll our own prototype because we want this regardless of whether
4716 * _VMS_WAIT is defined.
4717 */
4718 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4719#endif
4720/* sort-of waitpid; special handling of pipe clean-up for subprocesses
4721 created with popen(); otherwise partially emulate waitpid() unless
4722 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4723 Also check processes not considered by the CRTL waitpid().
4724 */
4fdae800 4725/*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4726Pid_t
fd8cd3a3 4727Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
a0d0e21e 4728{
22d4bb9c
CB
4729 pInfo info;
4730 int done;
aeb5cf3c 4731 int sts;
d85f548a 4732 int j;
aeb5cf3c
CB
4733
4734 if (statusp) *statusp = 0;
a0d0e21e
LW
4735
4736 for (info = open_pipes; info != NULL; info = info->next)
4737 if (info->pid == pid) break;
4738
4739 if (info != NULL) { /* we know about this child */
748a9306 4740 while (!info->done) {
22d4bb9c
CB
4741 _ckvmssts(sys$setast(0));
4742 done = info->done;
4743 if (!done) _ckvmssts(sys$clref(pipe_ef));
4744 _ckvmssts(sys$setast(1));
4745 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
a0d0e21e
LW
4746 }
4747
aeb5cf3c 4748 if (statusp) *statusp = info->completion;
a0d0e21e 4749 return pid;
d85f548a
JH
4750 }
4751
4752 /* child that already terminated? */
aeb5cf3c 4753
d85f548a
JH
4754 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4755 if (closed_list[j].pid == pid) {
4756 if (statusp) *statusp = closed_list[j].completion;
4757 return pid;
4758 }
a0d0e21e 4759 }
d85f548a
JH
4760
4761 /* fall through if this child is not one of our own pipe children */
aeb5cf3c 4762
119586db 4763#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
4764
4765 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4766 * in 7.2 did we get a version that fills in the VMS completion
4767 * status as Perl has always tried to do.
4768 */
4769
4770 sts = __vms_waitpid( pid, statusp, flags );
4771
4772 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4773 return sts;
4774
4775 /* If the real waitpid tells us the child does not exist, we
4776 * fall through here to implement waiting for a child that
4777 * was created by some means other than exec() (say, spawned
4778 * from DCL) or to wait for a process that is not a subprocess
4779 * of the current process.
4780 */
4781
119586db 4782#endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
aeb5cf3c 4783
21bc9d50 4784 {
a0d0e21e 4785 $DESCRIPTOR(intdsc,"0 00:00:01");
aeb5cf3c
CB
4786 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4787 unsigned long int pidcode = JPI$_PID, mypid;
4788 unsigned long int interval[2];
aeb5cf3c 4789 unsigned int jpi_iosb[2];
d85f548a 4790 struct itmlst_3 jpilist[2] = {
aeb5cf3c 4791 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
aeb5cf3c
CB
4792 { 0, 0, 0, 0}
4793 };
aeb5cf3c
CB
4794
4795 if (pid <= 0) {
4796 /* Sorry folks, we don't presently implement rooting around for
4797 the first child we can find, and we definitely don't want to
4798 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4799 */
4800 set_errno(ENOTSUP);
4801 return -1;
4802 }
4803
d85f548a
JH
4804 /* Get the owner of the child so I can warn if it's not mine. If the
4805 * process doesn't exist or I don't have the privs to look at it,
4806 * I can go home early.
aeb5cf3c
CB
4807 */
4808 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4809 if (sts & 1) sts = jpi_iosb[0];
4810 if (!(sts & 1)) {
4811 switch (sts) {
4812 case SS$_NONEXPR:
4813 set_errno(ECHILD);
4814 break;
4815 case SS$_NOPRIV:
4816 set_errno(EACCES);
4817 break;
4818 default:
4819 _ckvmssts(sts);
4820 }
4821 set_vaxc_errno(sts);
4822 return -1;
4823 }
a0d0e21e 4824
3eeba6fb 4825 if (ckWARN(WARN_EXEC)) {
aeb5cf3c
CB
4826 /* remind folks they are asking for non-standard waitpid behavior */
4827 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
748a9306 4828 if (ownerpid != mypid)
f98bc0c6 4829 Perl_warner(aTHX_ packWARN(WARN_EXEC),
aeb5cf3c
CB
4830 "waitpid: process %x is not a child of process %x",
4831 pid,mypid);
748a9306 4832 }
a0d0e21e 4833
d85f548a
JH
4834 /* simply check on it once a second until it's not there anymore. */
4835
4836 _ckvmssts(sys$bintim(&intdsc,interval));
4837 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
aeb5cf3c
CB
4838 _ckvmssts(sys$schdwk(0,0,interval,0));
4839 _ckvmssts(sys$hiber());
d85f548a
JH
4840 }
4841 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
aeb5cf3c
CB
4842
4843 _ckvmssts(sts);
a0d0e21e 4844 return pid;
21bc9d50 4845 }
a0d0e21e 4846} /* end of waitpid() */
a0d0e21e
LW
4847/*}}}*/
4848/*}}}*/
4849/*}}}*/
4850
4851/*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4852char *
4853my_gconvert(double val, int ndig, int trail, char *buf)
4854{
4855 static char __gcvtbuf[DBL_DIG+1];
4856 char *loc;
4857
4858 loc = buf ? buf : __gcvtbuf;
71be2cbc 4859
4860#ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4861 if (val < 1) {
4862 sprintf(loc,"%.*g",ndig,val);
4863 return loc;
4864 }
4865#endif
4866
a0d0e21e
LW
4867 if (val) {
4868 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4869 return gcvt(val,ndig,loc);
4870 }
4871 else {
4872 loc[0] = '0'; loc[1] = '\0';
4873 return loc;
4874 }
4875
4876}
4877/*}}}*/
4878
988c775c 4879#if defined(__VAX) || !defined(NAML$C_MAXRSS)
a480973c
JM
4880static int rms_free_search_context(struct FAB * fab)
4881{
4882struct NAM * nam;
4883
4884 nam = fab->fab$l_nam;
4885 nam->nam$b_nop |= NAM$M_SYNCHK;
4886 nam->nam$l_rlf = NULL;
4887 fab->fab$b_dns = 0;
4888 return sys$parse(fab, NULL, NULL);
4889}
4890
4891#define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4d743a9b 4892#define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
a480973c
JM
4893#define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4894#define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4895#define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4896#define rms_nam_esll(nam) nam.nam$b_esl
4897#define rms_nam_esl(nam) nam.nam$b_esl
4898#define rms_nam_name(nam) nam.nam$l_name
4899#define rms_nam_namel(nam) nam.nam$l_name
4900#define rms_nam_type(nam) nam.nam$l_type
4901#define rms_nam_typel(nam) nam.nam$l_type
4902#define rms_nam_ver(nam) nam.nam$l_ver
4903#define rms_nam_verl(nam) nam.nam$l_ver
4904#define rms_nam_rsll(nam) nam.nam$b_rsl
4905#define rms_nam_rsl(nam) nam.nam$b_rsl
4906#define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4907#define rms_set_fna(fab, nam, name, size) \
a1887106 4908 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
a480973c
JM
4909#define rms_get_fna(fab, nam) fab.fab$l_fna
4910#define rms_set_dna(fab, nam, name, size) \
a1887106
JM
4911 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4912#define rms_nam_dns(fab, nam) fab.fab$b_dns
d584a1c6 4913#define rms_set_esa(nam, name, size) \
a1887106 4914 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
a480973c 4915#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 4916 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
a480973c 4917#define rms_set_rsa(nam, name, size) \
a1887106 4918 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
a480973c 4919#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106
JM
4920 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4921#define rms_nam_name_type_l_size(nam) \
4922 (nam.nam$b_name + nam.nam$b_type)
a480973c
JM
4923#else
4924static int rms_free_search_context(struct FAB * fab)
4925{
4926struct NAML * nam;
4927
4928 nam = fab->fab$l_naml;
4929 nam->naml$b_nop |= NAM$M_SYNCHK;
4930 nam->naml$l_rlf = NULL;
4931 nam->naml$l_long_defname_size = 0;
988c775c 4932
a480973c
JM
4933 fab->fab$b_dns = 0;
4934 return sys$parse(fab, NULL, NULL);
4935}
4936
4937#define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4d743a9b 4938#define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
a480973c
JM
4939#define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4940#define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4941#define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4942#define rms_nam_esll(nam) nam.naml$l_long_expand_size
4943#define rms_nam_esl(nam) nam.naml$b_esl
4944#define rms_nam_name(nam) nam.naml$l_name
4945#define rms_nam_namel(nam) nam.naml$l_long_name
4946#define rms_nam_type(nam) nam.naml$l_type
4947#define rms_nam_typel(nam) nam.naml$l_long_type
4948#define rms_nam_ver(nam) nam.naml$l_ver
4949#define rms_nam_verl(nam) nam.naml$l_long_ver
4950#define rms_nam_rsll(nam) nam.naml$l_long_result_size
4951#define rms_nam_rsl(nam) nam.naml$b_rsl
4952#define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4953#define rms_set_fna(fab, nam, name, size) \
a1887106 4954 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
a480973c 4955 nam.naml$l_long_filename_size = size; \
a1887106 4956 nam.naml$l_long_filename = name;}
a480973c
JM
4957#define rms_get_fna(fab, nam) nam.naml$l_long_filename
4958#define rms_set_dna(fab, nam, name, size) \
a1887106 4959 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
a480973c 4960 nam.naml$l_long_defname_size = size; \
a1887106 4961 nam.naml$l_long_defname = name; }
a480973c 4962#define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
d584a1c6 4963#define rms_set_esa(nam, name, size) \
a1887106 4964 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
a480973c 4965 nam.naml$l_long_expand_alloc = size; \
a1887106 4966 nam.naml$l_long_expand = name; }
a480973c 4967#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 4968 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
a480973c 4969 nam.naml$l_long_expand = l_name; \
a1887106 4970 nam.naml$l_long_expand_alloc = l_size; }
a480973c 4971#define rms_set_rsa(nam, name, size) \
a1887106 4972 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
a480973c 4973 nam.naml$l_long_result = name; \
a1887106 4974 nam.naml$l_long_result_alloc = size; }
a480973c 4975#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106 4976 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
a480973c 4977 nam.naml$l_long_result = l_name; \
a1887106
JM
4978 nam.naml$l_long_result_alloc = l_size; }
4979#define rms_nam_name_type_l_size(nam) \
4980 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
a480973c
JM
4981#endif
4982
4fdf8f88 4983
e0e5e8d6
JM
4984/* rms_erase
4985 * The CRTL for 8.3 and later can create symbolic links in any mode,
4fdf8f88 4986 * however in 8.3 the unlink/remove/delete routines will only properly handle
e0e5e8d6 4987 * them if one of the PCP modes is active.
e0e5e8d6
JM
4988 */
4989static int rms_erase(const char * vmsname)
4990{
4991 int status;
4992 struct FAB myfab = cc$rms_fab;
4993 rms_setup_nam(mynam);
4994
4995 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4996 rms_bind_fab_nam(myfab, mynam);
4fdf8f88 4997
e0e5e8d6
JM
4998#ifdef NAML$M_OPEN_SPECIAL
4999 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5000#endif
5001
d30c1055 5002 status = sys$erase(&myfab, 0, 0);
e0e5e8d6
JM
5003
5004 return status;
5005}
5006
bbce6d69 5007
4fdf8f88
JM
5008static int
5009vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5010 const struct dsc$descriptor_s * vms_dst_dsc,
5011 unsigned long flags)
5012{
5013 /* VMS and UNIX handle file permissions differently and the
5014 * the same ACL trick may be needed for renaming files,
5015 * especially if they are directories.
5016 */
5017
5018 /* todo: get kill_file and rename to share common code */
5019 /* I can not find online documentation for $change_acl
5020 * it appears to be replaced by $set_security some time ago */
5021
5022const unsigned int access_mode = 0;
5023$DESCRIPTOR(obj_file_dsc,"FILE");
5024char *vmsname;
5025char *rslt;
4e0c9737 5026unsigned long int jpicode = JPI$_UIC;
4fdf8f88
JM
5027int aclsts, fndsts, rnsts = -1;
5028unsigned int ctx = 0;
5029struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5030struct dsc$descriptor_s * clean_dsc;
5031
5032struct myacedef {
5033 unsigned char myace$b_length;
5034 unsigned char myace$b_type;
5035 unsigned short int myace$w_flags;
5036 unsigned long int myace$l_access;
5037 unsigned long int myace$l_ident;
5038} newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5039 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5040 0},
5041 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5042
5043struct item_list_3
5044 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5045 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5046 {0,0,0,0}},
5047 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5048 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5049 {0,0,0,0}};
5050
5051
5052 /* Expand the input spec using RMS, since we do not want to put
5053 * ACLs on the target of a symbolic link */
5054 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5055 if (vmsname == NULL)
5056 return SS$_INSFMEM;
5057
6fb6c614 5058 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4fdf8f88 5059 vmsname,
6fb6c614 5060 PERL_RMSEXPAND_M_SYMLINK);
4fdf8f88
JM
5061 if (rslt == NULL) {
5062 PerlMem_free(vmsname);
5063 return SS$_INSFMEM;
5064 }
5065
5066 /* So we get our own UIC to use as a rights identifier,
5067 * and the insert an ACE at the head of the ACL which allows us
5068 * to delete the file.
5069 */
ebd4d70b 5070 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4fdf8f88
JM
5071
5072 fildsc.dsc$w_length = strlen(vmsname);
5073 fildsc.dsc$a_pointer = vmsname;
5074 ctx = 0;
5075 newace.myace$l_ident = oldace.myace$l_ident;
5076 rnsts = SS$_ABORT;
5077
5078 /* Grab any existing ACEs with this identifier in case we fail */
5079 clean_dsc = &fildsc;
5080 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5081 &fildsc,
5082 NULL,
5083 OSS$M_WLOCK,
5084 findlst,
5085 &ctx,
5086 &access_mode);
5087
5088 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5089 /* Add the new ACE . . . */
5090
5091 /* if the sys$get_security succeeded, then ctx is valid, and the
5092 * object/file descriptors will be ignored. But otherwise they
5093 * are needed
5094 */
5095 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5096 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5097 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5098 set_errno(EVMSERR);
5099 set_vaxc_errno(aclsts);
5100 PerlMem_free(vmsname);
5101 return aclsts;
5102 }
5103
5104 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5105 NULL, NULL,
5106 &flags,
5107 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5108
5109 if ($VMS_STATUS_SUCCESS(rnsts)) {
5110 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5111 }
5112
5113 /* Put things back the way they were. */
5114 ctx = 0;
5115 aclsts = sys$get_security(&obj_file_dsc,
5116 clean_dsc,
5117 NULL,
5118 OSS$M_WLOCK,
5119 findlst,
5120 &ctx,
5121 &access_mode);
5122
5123 if ($VMS_STATUS_SUCCESS(aclsts)) {
5124 int sec_flags;
5125
5126 sec_flags = 0;
5127 if (!$VMS_STATUS_SUCCESS(fndsts))
5128 sec_flags = OSS$M_RELCTX;
5129
5130 /* Get rid of the new ACE */
5131 aclsts = sys$set_security(NULL, NULL, NULL,
5132 sec_flags, dellst, &ctx, &access_mode);
5133
5134 /* If there was an old ACE, put it back */
5135 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5136 addlst[0].bufadr = &oldace;
5137 aclsts = sys$set_security(NULL, NULL, NULL,
5138 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5139 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5140 set_errno(EVMSERR);
5141 set_vaxc_errno(aclsts);
5142 rnsts = aclsts;
5143 }
5144 } else {
5145 int aclsts2;
5146
5147 /* Try to clear the lock on the ACL list */
5148 aclsts2 = sys$set_security(NULL, NULL, NULL,
5149 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5150
5151 /* Rename errors are most important */
5152 if (!$VMS_STATUS_SUCCESS(rnsts))
5153 aclsts = rnsts;
5154 set_errno(EVMSERR);
5155 set_vaxc_errno(aclsts);
5156 rnsts = aclsts;
5157 }
5158 }
5159 else {
5160 if (aclsts != SS$_ACLEMPTY)
5161 rnsts = aclsts;
5162 }
5163 }
5164 else
5165 rnsts = fndsts;
5166
5167 PerlMem_free(vmsname);
5168 return rnsts;
5169}
5170
5171
5172/*{{{int rename(const char *, const char * */
5173/* Not exactly what X/Open says to do, but doing it absolutely right
5174 * and efficiently would require a lot more work. This should be close
5175 * enough to pass all but the most strict X/Open compliance test.
5176 */
5177int
5178Perl_rename(pTHX_ const char *src, const char * dst)
5179{
5180int retval;
5181int pre_delete = 0;
5182int src_sts;
5183int dst_sts;
5184Stat_t src_st;
5185Stat_t dst_st;
5186
5187 /* Validate the source file */
46c05374 5188 src_sts = flex_lstat(src, &src_st);
4fdf8f88
JM
5189 if (src_sts != 0) {
5190
5191 /* No source file or other problem */
5192 return src_sts;
5193 }
b94a8c49
JM
5194 if (src_st.st_devnam[0] == 0) {
5195 /* This may be possible so fail if it is seen. */
5196 errno = EIO;
5197 return -1;
5198 }
4fdf8f88 5199
46c05374 5200 dst_sts = flex_lstat(dst, &dst_st);
4fdf8f88
JM
5201 if (dst_sts == 0) {
5202
5203 if (dst_st.st_dev != src_st.st_dev) {
5204 /* Must be on the same device */
5205 errno = EXDEV;
5206 return -1;
5207 }
5208
5209 /* VMS_INO_T_COMPARE is true if the inodes are different
5210 * to match the output of memcmp
5211 */
5212
5213 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5214 /* That was easy, the files are the same! */
5215 return 0;
5216 }
5217
5218 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5219 /* If source is a directory, so must be dest */
5220 errno = EISDIR;
5221 return -1;
5222 }
5223
5224 }
5225
5226
5227 if ((dst_sts == 0) &&
5228 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5229
5230 /* We have issues here if vms_unlink_all_versions is set
5231 * If the destination exists, and is not a directory, then
5232 * we must delete in advance.
5233 *
5234 * If the src is a directory, then we must always pre-delete
5235 * the destination.
5236 *
5237 * If we successfully delete the dst in advance, and the rename fails
5238 * X/Open requires that errno be EIO.
5239 *
5240 */
5241
5242 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5243 int d_sts;
46c05374 5244 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
b94a8c49
JM
5245 S_ISDIR(dst_st.st_mode));
5246
5247 /* Need to delete all versions ? */
5248 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5249 int i = 0;
5250
5251 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
46c05374 5252 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
b94a8c49
JM
5253 if (d_sts != 0)
5254 break;
5255 i++;
5256
5257 /* Make sure that we do not loop forever */
5258 if (i > 32767) {
5259 errno = EIO;
5260 d_sts = -1;
5261 break;
5262 }
5263 }
5264 }
5265
4fdf8f88
JM
5266 if (d_sts != 0)
5267 return d_sts;
5268
5269 /* We killed the destination, so only errno now is EIO */
5270 pre_delete = 1;
5271 }
5272 }
5273
5274 /* Originally the idea was to call the CRTL rename() and only
5275 * try the lib$rename_file if it failed.
5276 * It turns out that there are too many variants in what the
5277 * the CRTL rename might do, so only use lib$rename_file
5278 */
5279 retval = -1;
5280
5281 {
5282 /* Is the source and dest both in VMS format */
5283 /* if the source is a directory, then need to fileify */
94ae10c0 5284 /* and dest must be a directory or non-existent. */
4fdf8f88 5285
4fdf8f88
JM
5286 char * vms_dst;
5287 int sts;
5288 char * ret_str;
5289 unsigned long flags;
5290 struct dsc$descriptor_s old_file_dsc;
5291 struct dsc$descriptor_s new_file_dsc;
5292
5293 /* We need to modify the src and dst depending
5294 * on if one or more of them are directories.
5295 */
5296
4fdf8f88
JM
5297 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5298 if (vms_dst == NULL)
ebd4d70b 5299 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88
JM
5300
5301 if (S_ISDIR(src_st.st_mode)) {
5302 char * ret_str;
5303 char * vms_dir_file;
5304
5305 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5306 if (vms_dir_file == NULL)
ebd4d70b 5307 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88 5308
4fdf8f88
JM
5309 /* If the dest is a directory, we must remove it
5310 if (dst_sts == 0) {
5311 int d_sts;
46c05374 5312 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
4fdf8f88 5313 if (d_sts != 0) {
4fdf8f88
JM
5314 PerlMem_free(vms_dst);
5315 errno = EIO;
5316 return sts;
5317 }
5318
5319 pre_delete = 1;
5320 }
5321
5322 /* The dest must be a VMS file specification */
df278665 5323 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5324 if (ret_str == NULL) {
4fdf8f88
JM
5325 PerlMem_free(vms_dst);
5326 errno = EIO;
5327 return -1;
5328 }
5329
5330 /* The source must be a file specification */
4fdf8f88
JM
5331 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5332 if (ret_str == NULL) {
4fdf8f88
JM
5333 PerlMem_free(vms_dst);
5334 PerlMem_free(vms_dir_file);
5335 errno = EIO;
5336 return -1;
5337 }
5338 PerlMem_free(vms_dst);
5339 vms_dst = vms_dir_file;
5340
5341 } else {
5342 /* File to file or file to new dir */
5343
5344 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5345 /* VMS pathify a dir target */
4846f1d7 5346 ret_str = int_tovmspath(dst, vms_dst, NULL);
4fdf8f88 5347 if (ret_str == NULL) {
4fdf8f88
JM
5348 PerlMem_free(vms_dst);
5349 errno = EIO;
5350 return -1;
5351 }
5352 } else {
b94a8c49
JM
5353 char * v_spec, * r_spec, * d_spec, * n_spec;
5354 char * e_spec, * vs_spec;
5355 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
4fdf8f88
JM
5356
5357 /* fileify a target VMS file specification */
df278665 5358 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5359 if (ret_str == NULL) {
4fdf8f88
JM
5360 PerlMem_free(vms_dst);
5361 errno = EIO;
5362 return -1;
5363 }
b94a8c49
JM
5364
5365 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5366 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5367 &e_len, &vs_spec, &vs_len);
5368 if (sts == 0) {
5369 if (e_len == 0) {
5370 /* Get rid of the version */
5371 if (vs_len != 0) {
5372 *vs_spec = '\0';
5373 }
5374 /* Need to specify a '.' so that the extension */
5375 /* is not inherited */
5376 strcat(vms_dst,".");
5377 }
5378 }
4fdf8f88
JM
5379 }
5380 }
5381
b94a8c49
JM
5382 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5383 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
4fdf8f88
JM
5384 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5385 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5386
5387 new_file_dsc.dsc$a_pointer = vms_dst;
5388 new_file_dsc.dsc$w_length = strlen(vms_dst);
5389 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5390 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5391
5392 flags = 0;
5393#if !defined(__VAX) && defined(NAML$C_MAXRSS)
449de3c2 5394 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
4fdf8f88
JM
5395#endif
5396
5397 sts = lib$rename_file(&old_file_dsc,
5398 &new_file_dsc,
5399 NULL, NULL,
5400 &flags,
5401 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5402 if (!$VMS_STATUS_SUCCESS(sts)) {
5403
5404 /* We could have failed because VMS style permissions do not
5405 * permit renames that UNIX will allow. Just like the hack
5406 * in for kill_file.
5407 */
5408 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5409 }
5410
4fdf8f88
JM
5411 PerlMem_free(vms_dst);
5412 if (!$VMS_STATUS_SUCCESS(sts)) {
5413 errno = EIO;
5414 return -1;
5415 }
5416 retval = 0;
5417 }
5418
5419 if (vms_unlink_all_versions) {
5420 /* Now get rid of any previous versions of the source file that
5421 * might still exist
5422 */
b94a8c49
JM
5423 int i = 0;
5424 dSAVEDERRNO;
5425 SAVE_ERRNO;
46c05374 5426 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
5427 S_ISDIR(src_st.st_mode));
5428 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
46c05374 5429 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
5430 S_ISDIR(src_st.st_mode));
5431 if (src_sts != 0)
5432 break;
5433 i++;
5434
5435 /* Make sure that we do not loop forever */
5436 if (i > 32767) {
5437 src_sts = -1;
5438 break;
5439 }
5440 }
5441 RESTORE_ERRNO;
4fdf8f88
JM
5442 }
5443
5444 /* We deleted the destination, so must force the error to be EIO */
5445 if ((retval != 0) && (pre_delete != 0))
5446 errno = EIO;
5447
5448 return retval;
5449}
5450/*}}}*/
5451
5452
bbce6d69 5453/*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5454/* Shortcut for common case of simple calls to $PARSE and $SEARCH
5455 * to expand file specification. Allows for a single default file
5456 * specification and a simple mask of options. If outbuf is non-NULL,
5457 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5458 * the resultant file specification is placed. If outbuf is NULL, the
5459 * resultant file specification is placed into a static buffer.
5460 * The third argument, if non-NULL, is taken to be a default file
5461 * specification string. The fourth argument is unused at present.
5462 * rmesexpand() returns the address of the resultant string if
5463 * successful, and NULL on error.
e886094b
JM
5464 *
5465 * New functionality for previously unused opts value:
5466 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
a1887106
JM
5467 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5468 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
e0e5e8d6 5469 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
bbce6d69 5470 */
360732b5 5471static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
96e4d5b1 5472
bbce6d69 5473static char *
6fb6c614
JM
5474int_rmsexpand
5475 (const char *filespec,
360732b5 5476 char *outbuf,
360732b5
JM
5477 const char *defspec,
5478 unsigned opts,
5479 int * fs_utf8,
5480 int * dfs_utf8)
bbce6d69 5481{
6fb6c614
JM
5482 char * ret_spec;
5483 const char * in_spec;
5484 char * spec_buf;
5485 const char * def_spec;
5486 char * vmsfspec, *vmsdefspec;
5487 char * esa;
7566800d 5488 char * esal = NULL;
18a3d61e
JM
5489 char * outbufl;
5490 struct FAB myfab = cc$rms_fab;
a480973c 5491 rms_setup_nam(mynam);
18a3d61e
JM
5492 STRLEN speclen;
5493 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5494 int sts;
5495
360732b5
JM
5496 /* temp hack until UTF8 is actually implemented */
5497 if (fs_utf8 != NULL)
5498 *fs_utf8 = 0;
5499
18a3d61e
JM
5500 if (!filespec || !*filespec) {
5501 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5502 return NULL;
5503 }
18a3d61e
JM
5504
5505 vmsfspec = NULL;
6fb6c614 5506 vmsdefspec = NULL;
18a3d61e 5507 outbufl = NULL;
a1887106 5508
6fb6c614 5509 in_spec = filespec;
a1887106
JM
5510 isunix = 0;
5511 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
6fb6c614
JM
5512 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5513 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5514
5515 /* If this is a UNIX file spec, convert it to VMS */
5516 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5517 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5518 &e_len, &vs_spec, &vs_len);
5519 if (sts != 0) {
5520 isunix = 1;
5521 char * ret_spec;
5522
5523 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5524 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5525 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5526 if (ret_spec == NULL) {
5527 PerlMem_free(vmsfspec);
5528 return NULL;
5529 }
5530 in_spec = (const char *)vmsfspec;
18a3d61e 5531
6fb6c614
JM
5532 /* Unless we are forcing to VMS format, a UNIX input means
5533 * UNIX output, and that requires long names to be used
5534 */
5535 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
b1a8dcd7 5536#if !defined(__VAX) && defined(NAML$C_MAXRSS)
6fb6c614 5537 opts |= PERL_RMSEXPAND_M_LONG;
778e045f
CB
5538#else
5539 NOOP;
b1a8dcd7 5540#endif
6fb6c614
JM
5541 else
5542 isunix = 0;
a1887106 5543 }
18a3d61e 5544
6fb6c614
JM
5545 }
5546
5547 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
a480973c 5548 rms_bind_fab_nam(myfab, mynam);
18a3d61e 5549
6fb6c614
JM
5550 /* Process the default file specification if present */
5551 def_spec = defspec;
18a3d61e
JM
5552 if (defspec && *defspec) {
5553 int t_isunix;
5554 t_isunix = is_unix_filespec(defspec);
5555 if (t_isunix) {
6fb6c614
JM
5556 vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5557 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5558 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5559
5560 if (ret_spec == NULL) {
5561 /* Clean up and bail */
5562 PerlMem_free(vmsdefspec);
5563 if (vmsfspec != NULL)
5564 PerlMem_free(vmsfspec);
5565 return NULL;
5566 }
5567 def_spec = (const char *)vmsdefspec;
18a3d61e 5568 }
6fb6c614
JM
5569 rms_set_dna(myfab, mynam,
5570 (char *)def_spec, strlen(def_spec)); /* cast ok */
18a3d61e
JM
5571 }
5572
6fb6c614 5573 /* Now we need the expansion buffers */
c5375c28 5574 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 5575 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5576#if !defined(__VAX) && defined(NAML$C_MAXRSS)
a1887106 5577 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5578 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5579#endif
a1887106 5580 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
18a3d61e 5581
d584a1c6
JM
5582 /* If a NAML block is used RMS always writes to the long and short
5583 * addresses unless you suppress the short name.
5584 */
a480973c 5585#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6 5586 outbufl = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5587 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5588#endif
d584a1c6 5589 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
18a3d61e 5590
f7ddb74a
JM
5591#ifdef NAM$M_NO_SHORT_UPCASE
5592 if (decc_efs_case_preserve)
a480973c 5593 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 5594#endif
18a3d61e 5595
e0e5e8d6
JM
5596 /* We may not want to follow symbolic links */
5597#ifdef NAML$M_OPEN_SPECIAL
5598 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5599 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5600#endif
5601
18a3d61e
JM
5602 /* First attempt to parse as an existing file */
5603 retsts = sys$parse(&myfab,0,0);
5604 if (!(retsts & STS$K_SUCCESS)) {
5605
5606 /* Could not find the file, try as syntax only if error is not fatal */
a480973c 5607 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
6fb6c614
JM
5608 if (retsts == RMS$_DNF ||
5609 retsts == RMS$_DIR ||
5610 retsts == RMS$_DEV ||
5611 retsts == RMS$_PRV) {
18a3d61e 5612 retsts = sys$parse(&myfab,0,0);
6fb6c614 5613 if (retsts & STS$K_SUCCESS) goto int_expanded;
18a3d61e
JM
5614 }
5615
5616 /* Still could not parse the file specification */
5617 /*----------------------------------------------*/
a480973c 5618 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5619 if (vmsdefspec != NULL)
5620 PerlMem_free(vmsdefspec);
18a3d61e 5621 if (vmsfspec != NULL)
c5375c28
JM
5622 PerlMem_free(vmsfspec);
5623 if (outbufl != NULL)
5624 PerlMem_free(outbufl);
5625 PerlMem_free(esa);
7566800d
CB
5626 if (esal != NULL)
5627 PerlMem_free(esal);
18a3d61e
JM
5628 set_vaxc_errno(retsts);
5629 if (retsts == RMS$_PRV) set_errno(EACCES);
5630 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5631 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5632 else set_errno(EVMSERR);
5633 return NULL;
5634 }
5635 retsts = sys$search(&myfab,0,0);
5636 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
a480973c 5637 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5638 if (vmsdefspec != NULL)
5639 PerlMem_free(vmsdefspec);
18a3d61e 5640 if (vmsfspec != NULL)
c5375c28
JM
5641 PerlMem_free(vmsfspec);
5642 if (outbufl != NULL)
5643 PerlMem_free(outbufl);
5644 PerlMem_free(esa);
7566800d
CB
5645 if (esal != NULL)
5646 PerlMem_free(esal);
18a3d61e
JM
5647 set_vaxc_errno(retsts);
5648 if (retsts == RMS$_PRV) set_errno(EACCES);
5649 else set_errno(EVMSERR);
5650 return NULL;
5651 }
5652
5653 /* If the input filespec contained any lowercase characters,
5654 * downcase the result for compatibility with Unix-minded code. */
6fb6c614 5655int_expanded:
18a3d61e 5656 if (!decc_efs_case_preserve) {
6fb6c614 5657 char * tbuf;
c5375c28
JM
5658 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5659 if (islower(*tbuf)) { haslower = 1; break; }
18a3d61e
JM
5660 }
5661
5662 /* Is a long or a short name expected */
5663 /*------------------------------------*/
6fb6c614 5664 spec_buf = NULL;
778e045f 5665#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5666 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5667 if (rms_nam_rsll(mynam)) {
6fb6c614 5668 spec_buf = outbufl;
a480973c 5669 speclen = rms_nam_rsll(mynam);
18a3d61e
JM
5670 }
5671 else {
6fb6c614 5672 spec_buf = esal; /* Not esa */
a480973c 5673 speclen = rms_nam_esll(mynam);
18a3d61e
JM
5674 }
5675 }
5676 else {
778e045f 5677#endif
a480973c 5678 if (rms_nam_rsl(mynam)) {
6fb6c614 5679 spec_buf = outbuf;
a480973c 5680 speclen = rms_nam_rsl(mynam);
18a3d61e
JM
5681 }
5682 else {
6fb6c614 5683 spec_buf = esa; /* Not esal */
a480973c 5684 speclen = rms_nam_esl(mynam);
18a3d61e 5685 }
778e045f 5686#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5687 }
778e045f 5688#endif
6fb6c614 5689 spec_buf[speclen] = '\0';
4d743a9b 5690
18a3d61e
JM
5691 /* Trim off null fields added by $PARSE
5692 * If type > 1 char, must have been specified in original or default spec
5693 * (not true for version; $SEARCH may have added version of existing file).
5694 */
a480973c 5695 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
18a3d61e 5696 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
5697 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5698 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
18a3d61e
JM
5699 }
5700 else {
a480973c
JM
5701 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5702 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
18a3d61e
JM
5703 }
5704 if (trimver || trimtype) {
5705 if (defspec && *defspec) {
5706 char *defesal = NULL;
d584a1c6
JM
5707 char *defesa = NULL;
5708 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5709 if (defesa != NULL) {
6fb6c614 5710 struct FAB deffab = cc$rms_fab;
d584a1c6
JM
5711#if !defined(__VAX) && defined(NAML$C_MAXRSS)
5712 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 5713 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 5714#endif
a480973c 5715 rms_setup_nam(defnam);
18a3d61e 5716
a480973c
JM
5717 rms_bind_fab_nam(deffab, defnam);
5718
5719 /* Cast ok */
5720 rms_set_fna
5721 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5722
d584a1c6
JM
5723 /* RMS needs the esa/esal as a work area if wildcards are involved */
5724 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
a480973c 5725
4d743a9b 5726 rms_clear_nam_nop(defnam);
a480973c 5727 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
18a3d61e
JM
5728#ifdef NAM$M_NO_SHORT_UPCASE
5729 if (decc_efs_case_preserve)
a480973c 5730 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
18a3d61e 5731#endif
e0e5e8d6
JM
5732#ifdef NAML$M_OPEN_SPECIAL
5733 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5734 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5735#endif
18a3d61e
JM
5736 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5737 if (trimver) {
a480973c 5738 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
18a3d61e
JM
5739 }
5740 if (trimtype) {
a480973c 5741 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
18a3d61e
JM
5742 }
5743 }
d584a1c6
JM
5744 if (defesal != NULL)
5745 PerlMem_free(defesal);
5746 PerlMem_free(defesa);
6fb6c614
JM
5747 } else {
5748 _ckvmssts_noperl(SS$_INSFMEM);
18a3d61e
JM
5749 }
5750 }
5751 if (trimver) {
5752 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5753 if (*(rms_nam_verl(mynam)) != '\"')
6fb6c614 5754 speclen = rms_nam_verl(mynam) - spec_buf;
18a3d61e
JM
5755 }
5756 else {
a480973c 5757 if (*(rms_nam_ver(mynam)) != '\"')
6fb6c614 5758 speclen = rms_nam_ver(mynam) - spec_buf;
18a3d61e
JM
5759 }
5760 }
5761 if (trimtype) {
5762 /* If we didn't already trim version, copy down */
5763 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
6fb6c614 5764 if (speclen > rms_nam_verl(mynam) - spec_buf)
18a3d61e 5765 memmove
a480973c
JM
5766 (rms_nam_typel(mynam),
5767 rms_nam_verl(mynam),
6fb6c614 5768 speclen - (rms_nam_verl(mynam) - spec_buf));
a480973c 5769 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
18a3d61e
JM
5770 }
5771 else {
6fb6c614 5772 if (speclen > rms_nam_ver(mynam) - spec_buf)
18a3d61e 5773 memmove
a480973c
JM
5774 (rms_nam_type(mynam),
5775 rms_nam_ver(mynam),
6fb6c614 5776 speclen - (rms_nam_ver(mynam) - spec_buf));
a480973c 5777 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
18a3d61e
JM
5778 }
5779 }
5780 }
5781
5782 /* Done with these copies of the input files */
5783 /*-------------------------------------------*/
5784 if (vmsfspec != NULL)
c5375c28 5785 PerlMem_free(vmsfspec);
6fb6c614
JM
5786 if (vmsdefspec != NULL)
5787 PerlMem_free(vmsdefspec);
18a3d61e
JM
5788
5789 /* If we just had a directory spec on input, $PARSE "helpfully"
5790 * adds an empty name and type for us */
d584a1c6 5791#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5792 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
5793 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5794 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5795 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
6fb6c614 5796 speclen = rms_nam_namel(mynam) - spec_buf;
18a3d61e 5797 }
d584a1c6
JM
5798 else
5799#endif
5800 {
a480973c
JM
5801 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5802 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5803 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
6fb6c614 5804 speclen = rms_nam_name(mynam) - spec_buf;
18a3d61e
JM
5805 }
5806
5807 /* Posix format specifications must have matching quotes */
4d743a9b 5808 if (speclen < (VMS_MAXRSS - 1)) {
6fb6c614
JM
5809 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5810 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5811 spec_buf[speclen] = '\"';
4d743a9b
JM
5812 speclen++;
5813 }
18a3d61e
JM
5814 }
5815 }
6fb6c614
JM
5816 spec_buf[speclen] = '\0';
5817 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
18a3d61e
JM
5818
5819 /* Have we been working with an expanded, but not resultant, spec? */
5820 /* Also, convert back to Unix syntax if necessary. */
d584a1c6
JM
5821 {
5822 int rsl;
18a3d61e 5823
d584a1c6
JM
5824#if !defined(__VAX) && defined(NAML$C_MAXRSS)
5825 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5826 rsl = rms_nam_rsll(mynam);
5827 } else
5828#endif
5829 {
5830 rsl = rms_nam_rsl(mynam);
5831 }
5832 if (!rsl) {
6fb6c614
JM
5833 /* rsl is not present, it means that spec_buf is either */
5834 /* esa or esal, and needs to be copied to outbuf */
5835 /* convert to Unix if desired */
d584a1c6 5836 if (isunix) {
6fb6c614
JM
5837 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5838 } else {
5839 /* VMS file specs are not in UTF-8 */
5840 if (fs_utf8 != NULL)
5841 *fs_utf8 = 0;
5842 strcpy(outbuf, spec_buf);
5843 ret_spec = outbuf;
18a3d61e
JM
5844 }
5845 }
6fb6c614
JM
5846 else {
5847 /* Now spec_buf is either outbuf or outbufl */
5848 /* We need the result into outbuf */
5849 if (isunix) {
5850 /* If we need this in UNIX, then we need another buffer */
5851 /* to keep things in order */
5852 char * src;
5853 char * new_src = NULL;
5854 if (spec_buf == outbuf) {
5855 new_src = PerlMem_malloc(VMS_MAXRSS);
5856 strcpy(new_src, spec_buf);
5857 } else {
5858 src = spec_buf;
5859 }
5860 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5861 if (new_src) {
5862 PerlMem_free(new_src);
5863 }
5864 } else {
5865 /* VMS file specs are not in UTF-8 */
5866 if (fs_utf8 != NULL)
5867 *fs_utf8 = 0;
5868
5869 /* Copy the buffer if needed */
5870 if (outbuf != spec_buf)
5871 strcpy(outbuf, spec_buf);
5872 ret_spec = outbuf;
d584a1c6 5873 }
18a3d61e 5874 }
18a3d61e 5875 }
6fb6c614
JM
5876
5877 /* Need to clean up the search context */
a480973c
JM
5878 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5879 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5880
5881 /* Clean up the extra buffers */
7566800d 5882 if (esal != NULL)
6fb6c614
JM
5883 PerlMem_free(esal);
5884 PerlMem_free(esa);
c5375c28
JM
5885 if (outbufl != NULL)
5886 PerlMem_free(outbufl);
6fb6c614
JM
5887
5888 /* Return the result */
5889 return ret_spec;
5890}
5891
5892/* Common simple case - Expand an already VMS spec */
5893static char *
5894int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5895 opts |= PERL_RMSEXPAND_M_VMS_IN;
5896 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5897}
5898
5899/* Common simple case - Expand to a VMS spec */
5900static char *
5901int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5902 opts |= PERL_RMSEXPAND_M_VMS;
5903 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5904}
5905
5906
5907/* Entry point used by perl routines */
5908static char *
5909mp_do_rmsexpand
5910 (pTHX_ const char *filespec,
5911 char *outbuf,
5912 int ts,
5913 const char *defspec,
5914 unsigned opts,
5915 int * fs_utf8,
5916 int * dfs_utf8)
5917{
5918 static char __rmsexpand_retbuf[VMS_MAXRSS];
5919 char * expanded, *ret_spec, *ret_buf;
5920
5921 expanded = NULL;
5922 ret_buf = outbuf;
5923 if (ret_buf == NULL) {
5924 if (ts) {
5925 Newx(expanded, VMS_MAXRSS, char);
5926 if (expanded == NULL)
5927 _ckvmssts(SS$_INSFMEM);
5928 ret_buf = expanded;
5929 } else {
5930 ret_buf = __rmsexpand_retbuf;
5931 }
5932 }
5933
5934
5935 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5936 opts, fs_utf8, dfs_utf8);
5937
5938 if (ret_spec == NULL) {
5939 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5940 if (expanded)
5941 Safefree(expanded);
5942 }
5943
5944 return ret_spec;
bbce6d69 5945}
5946/*}}}*/
5947/* External entry points */
2fbb330f 5948char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5 5949{ return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
2fbb330f 5950char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5
JM
5951{ return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5952char *Perl_rmsexpand_utf8
5953 (pTHX_ const char *spec, char *buf, const char *def,
5954 unsigned opt, int * fs_utf8, int * dfs_utf8)
5955{ return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5956char *Perl_rmsexpand_utf8_ts
5957 (pTHX_ const char *spec, char *buf, const char *def,
5958 unsigned opt, int * fs_utf8, int * dfs_utf8)
5959{ return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
bbce6d69 5960
5961
a0d0e21e
LW
5962/*
5963** The following routines are provided to make life easier when
5964** converting among VMS-style and Unix-style directory specifications.
5965** All will take input specifications in either VMS or Unix syntax. On
5966** failure, all return NULL. If successful, the routines listed below
748a9306 5967** return a pointer to a buffer containing the appropriately
a0d0e21e
LW
5968** reformatted spec (and, therefore, subsequent calls to that routine
5969** will clobber the result), while the routines of the same names with
5970** a _ts suffix appended will return a pointer to a mallocd string
5971** containing the appropriately reformatted spec.
5972** In all cases, only explicit syntax is altered; no check is made that
5973** the resulting string is valid or that the directory in question
5974** actually exists.
5975**
5976** fileify_dirspec() - convert a directory spec into the name of the
5977** directory file (i.e. what you can stat() to see if it's a dir).
5978** The style (VMS or Unix) of the result is the same as the style
5979** of the parameter passed in.
5980** pathify_dirspec() - convert a directory spec into a path (i.e.
5981** what you prepend to a filename to indicate what directory it's in).
5982** The style (VMS or Unix) of the result is the same as the style
5983** of the parameter passed in.
5984** tounixpath() - convert a directory spec into a Unix-style path.
5985** tovmspath() - convert a directory spec into a VMS-style path.
5986** tounixspec() - convert any file spec into a Unix-style file spec.
5987** tovmsspec() - convert any file spec into a VMS-style spec.
360732b5 5988** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
e518068a 5989**
bd3fa61c 5990** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
01b8edb6 5991** Permission is given to distribute this code as part of the Perl
5992** standard distribution under the terms of the GNU General Public
5993** License or the Perl Artistic License. Copies of each may be
5994** found in the Perl standard distribution.
a0d0e21e
LW
5995 */
5996
a979ce91
JM
5997/*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5998static char *
5999int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
a0d0e21e 6000{
4e0c9737 6001 unsigned long int dirlen, retlen, hasfilename = 0;
a979ce91 6002 char *cp1, *cp2, *lastdir;
a480973c 6003 char *trndir, *vmsdir;
2d9f3838 6004 unsigned short int trnlnm_iter_count;
df278665
JM
6005 int is_vms = 0;
6006 int is_unix = 0;
f7ddb74a 6007 int sts;
360732b5
JM
6008 if (utf8_fl != NULL)
6009 *utf8_fl = 0;
a0d0e21e 6010
c07a80fd 6011 if (!dir || !*dir) {
6012 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6013 }
a0d0e21e 6014 dirlen = strlen(dir);
a2a90019 6015 while (dirlen && dir[dirlen-1] == '/') --dirlen;
61bb5906 6016 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
f7ddb74a
JM
6017 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6018 dir = "/sys$disk";
6019 dirlen = 9;
6020 }
6021 else
6022 dirlen = 1;
61bb5906 6023 }
a480973c
JM
6024 if (dirlen > (VMS_MAXRSS - 1)) {
6025 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6026 return NULL;
c07a80fd 6027 }
c5375c28 6028 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 6029 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
6030 if (!strpbrk(dir+1,"/]>:") &&
6031 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
e518068a 6032 strcpy(trndir,*dir == '/' ? dir + 1: dir);
2d9f3838 6033 trnlnm_iter_count = 0;
b8486b9d 6034 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
2d9f3838
CB
6035 trnlnm_iter_count++;
6036 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6037 }
b8ffc8df 6038 dirlen = strlen(trndir);
e518068a 6039 }
01b8edb6 6040 else {
6041 strncpy(trndir,dir,dirlen);
6042 trndir[dirlen] = '\0';
01b8edb6 6043 }
b8ffc8df
RGS
6044
6045 /* At this point we are done with *dir and use *trndir which is a
6046 * copy that can be modified. *dir must not be modified.
6047 */
6048
c07a80fd 6049 /* If we were handed a rooted logical name or spec, treat it like a
6050 * simple directory, so that
6051 * $ Define myroot dev:[dir.]
6052 * ... do_fileify_dirspec("myroot",buf,1) ...
6053 * does something useful.
6054 */
b8ffc8df
RGS
6055 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6056 trndir[--dirlen] = '\0';
6057 trndir[dirlen-1] = ']';
c07a80fd 6058 }
b8ffc8df
RGS
6059 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6060 trndir[--dirlen] = '\0';
6061 trndir[dirlen-1] = '>';
46112e17 6062 }
e518068a 6063
b8ffc8df 6064 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
b7ae7a0d 6065 /* If we've got an explicit filename, we can just shuffle the string. */
6066 if (*(cp1+1)) hasfilename = 1;
6067 /* Similarly, we can just back up a level if we've got multiple levels
6068 of explicit directories in a VMS spec which ends with directories. */
6069 else {
b8ffc8df 6070 for (cp2 = cp1; cp2 > trndir; cp2--) {
f7ddb74a
JM
6071 if (*cp2 == '.') {
6072 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
657054d4 6073/* fix-me, can not scan EFS file specs backward like this */
f7ddb74a
JM
6074 *cp2 = *cp1; *cp1 = '\0';
6075 hasfilename = 1;
6076 break;
6077 }
b7ae7a0d 6078 }
6079 if (*cp2 == '[' || *cp2 == '<') break;
6080 }
6081 }
6082 }
6083
c5375c28 6084 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 6085 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 6086 cp1 = strpbrk(trndir,"]:>");
a979ce91
JM
6087 if (hasfilename || !cp1) { /* filename present or not VMS */
6088
6089 if (decc_efs_charset && !cp1) {
6090
6091 /* EFS handling for UNIX mode */
6092
6093 /* Just remove the trailing '/' and we should be done */
6094 STRLEN trndir_len;
6095 trndir_len = strlen(trndir);
6096
6097 if (trndir_len > 1) {
6098 trndir_len--;
6099 if (trndir[trndir_len] == '/') {
6100 trndir[trndir_len] = '\0';
6101 }
6102 }
6103 strcpy(buf, trndir);
6104 PerlMem_free(trndir);
6105 PerlMem_free(vmsdir);
6106 return buf;
6107 }
6108
6109 /* For non-EFS mode, this is left for backwards compatibility */
6110 /* For EFS mode, this is only done for VMS format filespecs as */
6111 /* Perl programs generally have problems when a UNIX format spec */
6112 /* returns a VMS format spec */
b8ffc8df 6113 if (trndir[0] == '.') {
a480973c 6114 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
c5375c28
JM
6115 PerlMem_free(trndir);
6116 PerlMem_free(vmsdir);
a979ce91 6117 return int_fileify_dirspec("[]", buf, NULL);
a480973c 6118 }
b8ffc8df 6119 else if (trndir[1] == '.' &&
a480973c 6120 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
c5375c28
JM
6121 PerlMem_free(trndir);
6122 PerlMem_free(vmsdir);
a979ce91 6123 return int_fileify_dirspec("[-]", buf, NULL);
a480973c 6124 }
748a9306 6125 }
b8ffc8df 6126 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
a0d0e21e 6127 dirlen -= 1; /* to last element */
b8ffc8df 6128 lastdir = strrchr(trndir,'/');
a0d0e21e 6129 }
b8ffc8df 6130 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
01b8edb6 6131 /* If we have "/." or "/..", VMSify it and let the VMS code
6132 * below expand it, rather than repeating the code to handle
6133 * relative components of a filespec here */
4633a7c4
LW
6134 do {
6135 if (*(cp1+2) == '.') cp1++;
6136 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
a480973c 6137 char * ret_chr;
df278665 6138 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
c5375c28
JM
6139 PerlMem_free(trndir);
6140 PerlMem_free(vmsdir);
a480973c
JM
6141 return NULL;
6142 }
fc1ce8cc 6143 if (strchr(vmsdir,'/') != NULL) {
df278665 6144 /* If int_tovmsspec() returned it, it must have VMS syntax
fc1ce8cc
CB
6145 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6146 * the time to check this here only so we avoid a recursion
6147 * loop; otherwise, gigo.
6148 */
c5375c28
JM
6149 PerlMem_free(trndir);
6150 PerlMem_free(vmsdir);
a480973c
JM
6151 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6152 return NULL;
fc1ce8cc 6153 }
a979ce91 6154 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6155 PerlMem_free(trndir);
6156 PerlMem_free(vmsdir);
a480973c
JM
6157 return NULL;
6158 }
0e5ce2c7 6159 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6160 PerlMem_free(trndir);
6161 PerlMem_free(vmsdir);
a480973c 6162 return ret_chr;
4633a7c4
LW
6163 }
6164 cp1++;
6165 } while ((cp1 = strstr(cp1,"/.")) != NULL);
b8ffc8df 6166 lastdir = strrchr(trndir,'/');
748a9306 6167 }
b8ffc8df 6168 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
a480973c 6169 char * ret_chr;
61bb5906
CB
6170 /* Ditto for specs that end in an MFD -- let the VMS code
6171 * figure out whether it's a real device or a rooted logical. */
f7ddb74a
JM
6172
6173 /* This should not happen any more. Allowing the fake /000000
6174 * in a UNIX pathname causes all sorts of problems when trying
6175 * to run in UNIX emulation. So the VMS to UNIX conversions
6176 * now remove the fake /000000 directories.
6177 */
6178
b8ffc8df 6179 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
df278665 6180 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
c5375c28
JM
6181 PerlMem_free(trndir);
6182 PerlMem_free(vmsdir);
a480973c
JM
6183 return NULL;
6184 }
a979ce91 6185 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6186 PerlMem_free(trndir);
6187 PerlMem_free(vmsdir);
a480973c
JM
6188 return NULL;
6189 }
0e5ce2c7 6190 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6191 PerlMem_free(trndir);
6192 PerlMem_free(vmsdir);
a480973c 6193 return ret_chr;
61bb5906 6194 }
a0d0e21e 6195 else {
f7ddb74a 6196
b8ffc8df
RGS
6197 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6198 !(lastdir = cp1 = strrchr(trndir,']')) &&
6199 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
f7ddb74a 6200
a979ce91
JM
6201 cp2 = strrchr(cp1,'.');
6202 if (cp2) {
6203 int e_len, vs_len = 0;
6204 int is_dir = 0;
6205 char * cp3;
6206 cp3 = strchr(cp2,';');
6207 e_len = strlen(cp2);
6208 if (cp3) {
6209 vs_len = strlen(cp3);
6210 e_len = e_len - vs_len;
6211 }
6212 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6213 if (!is_dir) {
6214 if (!decc_efs_charset) {
6215 /* If this is not EFS, then not a directory */
6216 PerlMem_free(trndir);
6217 PerlMem_free(vmsdir);
6218 set_errno(ENOTDIR);
6219 set_vaxc_errno(RMS$_DIR);
6220 return NULL;
6221 }
6222 } else {
6223 /* Ok, here we have an issue, technically if a .dir shows */
6224 /* from inside a directory, then we should treat it as */
6225 /* xxx^.dir.dir. But we do not have that context at this */
6226 /* point unless this is totally restructured, so we remove */
6227 /* The .dir for now, and fix this better later */
6228 dirlen = cp2 - trndir;
6229 }
a0d0e21e 6230 }
a979ce91 6231
748a9306 6232 }
f7ddb74a
JM
6233
6234 retlen = dirlen + 6;
a979ce91
JM
6235 memcpy(buf, trndir, dirlen);
6236 buf[dirlen] = '\0';
f7ddb74a 6237
a0d0e21e
LW
6238 /* We've picked up everything up to the directory file name.
6239 Now just add the type and version, and we're set. */
df278665
JM
6240
6241 /* We should only add type for VMS syntax, but historically Perl
6242 has added it for UNIX style also */
6243
6244 /* Fix me - we should not be using the same routine for VMS and
6245 UNIX format files. Things are too tangled so we need to lookup
6246 what syntax the output is */
6247
6248 is_unix = 0;
6249 is_vms = 0;
6250 lastdir = strrchr(trndir,'/');
6251 if (lastdir) {
6252 is_unix = 1;
6253 } else {
6254 lastdir = strpbrk(trndir,"]:>");
6255 if (lastdir) {
6256 is_vms = 1;
6257 }
6258 }
6259
6260 if ((is_vms == 0) && (is_unix == 0)) {
6261 /* We still do not know? */
6262 is_unix = decc_filename_unix_report;
6263 if (is_unix == 0)
6264 is_vms = 1;
6265 }
6266
6267 if ((is_unix && !decc_efs_charset) || is_vms) {
6268
6269 /* It is a bug to add a .dir to a UNIX format directory spec */
6270 /* However Perl on VMS may have programs that expect this so */
6271 /* If not using EFS character specifications allow it. */
6272
6273 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6274 /* Traditionally Perl expects filenames in lower case */
a979ce91 6275 strcat(buf, ".dir");
df278665
JM
6276 } else {
6277 /* VMS expects the .DIR to be in upper case */
a979ce91 6278 strcat(buf, ".DIR");
df278665
JM
6279 }
6280
6281 /* It is also a bug to put a VMS format version on a UNIX file */
6282 /* specification. Perl self tests are looking for this */
6283 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
a979ce91 6284 strcat(buf, ";1");
df278665 6285 }
c5375c28
JM
6286 PerlMem_free(trndir);
6287 PerlMem_free(vmsdir);
a979ce91 6288 return buf;
a0d0e21e
LW
6289 }
6290 else { /* VMS-style directory spec */
a480973c 6291
d584a1c6
JM
6292 char *esa, *esal, term, *cp;
6293 char *my_esa;
6294 int my_esa_len;
4e0c9737 6295 unsigned long int cmplen, haslower = 0;
a0d0e21e 6296 struct FAB dirfab = cc$rms_fab;
a480973c
JM
6297 rms_setup_nam(savnam);
6298 rms_setup_nam(dirnam);
6299
d584a1c6 6300 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 6301 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
6302 esal = NULL;
6303#if !defined(__VAX) && defined(NAML$C_MAXRSS)
6304 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 6305 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 6306#endif
a480973c
JM
6307 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6308 rms_bind_fab_nam(dirfab, dirnam);
6309 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
d584a1c6 6310 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
f7ddb74a
JM
6311#ifdef NAM$M_NO_SHORT_UPCASE
6312 if (decc_efs_case_preserve)
a480973c 6313 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6314#endif
01b8edb6 6315
b8ffc8df 6316 for (cp = trndir; *cp; cp++)
01b8edb6 6317 if (islower(*cp)) { haslower = 1; break; }
a480973c 6318 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
a979ce91
JM
6319 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6320 (dirfab.fab$l_sts == RMS$_DNF) ||
6321 (dirfab.fab$l_sts == RMS$_PRV)) {
6322 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6323 sts = sys$parse(&dirfab);
e518068a 6324 }
6325 if (!sts) {
c5375c28 6326 PerlMem_free(esa);
d584a1c6
JM
6327 if (esal != NULL)
6328 PerlMem_free(esal);
c5375c28
JM
6329 PerlMem_free(trndir);
6330 PerlMem_free(vmsdir);
748a9306
LW
6331 set_errno(EVMSERR);
6332 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
6333 return NULL;
6334 }
e518068a 6335 }
6336 else {
6337 savnam = dirnam;
a480973c
JM
6338 /* Does the file really exist? */
6339 if (sys$search(&dirfab)& STS$K_SUCCESS) {
e518068a 6340 /* Yes; fake the fnb bits so we'll check type below */
a979ce91 6341 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
e518068a 6342 }
752635ea
CB
6343 else { /* No; just work with potential name */
6344 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6345 else {
2623a4a6
JM
6346 int fab_sts;
6347 fab_sts = dirfab.fab$l_sts;
6348 sts = rms_free_search_context(&dirfab);
c5375c28 6349 PerlMem_free(esa);
d584a1c6
JM
6350 if (esal != NULL)
6351 PerlMem_free(esal);
c5375c28
JM
6352 PerlMem_free(trndir);
6353 PerlMem_free(vmsdir);
2623a4a6 6354 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
e518068a 6355 return NULL;
6356 }
e518068a 6357 }
a0d0e21e 6358 }
d584a1c6
JM
6359
6360 /* Make sure we are using the right buffer */
778e045f 6361#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6
JM
6362 if (esal != NULL) {
6363 my_esa = esal;
6364 my_esa_len = rms_nam_esll(dirnam);
6365 } else {
778e045f 6366#endif
d584a1c6
JM
6367 my_esa = esa;
6368 my_esa_len = rms_nam_esl(dirnam);
778e045f 6369#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6 6370 }
778e045f 6371#endif
d584a1c6 6372 my_esa[my_esa_len] = '\0';
a480973c 6373 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
d584a1c6
JM
6374 cp1 = strchr(my_esa,']');
6375 if (!cp1) cp1 = strchr(my_esa,'>');
748a9306 6376 if (cp1) { /* Should always be true */
d584a1c6
JM
6377 my_esa_len -= cp1 - my_esa - 1;
6378 memmove(my_esa, cp1 + 1, my_esa_len);
748a9306
LW
6379 }
6380 }
a480973c 6381 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
a0d0e21e 6382 /* Yep; check version while we're at it, if it's there. */
a480973c
JM
6383 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6384 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
a0d0e21e 6385 /* Something other than .DIR[;1]. Bzzt. */
a480973c 6386 sts = rms_free_search_context(&dirfab);
c5375c28 6387 PerlMem_free(esa);
d584a1c6
JM
6388 if (esal != NULL)
6389 PerlMem_free(esal);
c5375c28
JM
6390 PerlMem_free(trndir);
6391 PerlMem_free(vmsdir);
748a9306
LW
6392 set_errno(ENOTDIR);
6393 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
6394 return NULL;
6395 }
748a9306 6396 }
ae6d78fe 6397
a480973c 6398 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
748a9306 6399 /* They provided at least the name; we added the type, if necessary, */
a979ce91 6400 strcpy(buf, my_esa);
a480973c 6401 sts = rms_free_search_context(&dirfab);
c5375c28
JM
6402 PerlMem_free(trndir);
6403 PerlMem_free(esa);
d584a1c6
JM
6404 if (esal != NULL)
6405 PerlMem_free(esal);
c5375c28 6406 PerlMem_free(vmsdir);
a979ce91 6407 return buf;
748a9306 6408 }
c07a80fd 6409 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6410 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6411 *cp1 = '\0';
d584a1c6 6412 my_esa_len -= 9;
c07a80fd 6413 }
d584a1c6 6414 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
752635ea 6415 if (cp1 == NULL) { /* should never happen */
a480973c 6416 sts = rms_free_search_context(&dirfab);
c5375c28
JM
6417 PerlMem_free(trndir);
6418 PerlMem_free(esa);
d584a1c6
JM
6419 if (esal != NULL)
6420 PerlMem_free(esal);
c5375c28 6421 PerlMem_free(vmsdir);
752635ea
CB
6422 return NULL;
6423 }
748a9306
LW
6424 term = *cp1;
6425 *cp1 = '\0';
d584a1c6
JM
6426 retlen = strlen(my_esa);
6427 cp1 = strrchr(my_esa,'.');
f7ddb74a 6428 /* ODS-5 directory specifications can have extra "." in them. */
657054d4 6429 /* Fix-me, can not scan EFS file specifications backwards */
f7ddb74a 6430 while (cp1 != NULL) {
d584a1c6 6431 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
f7ddb74a
JM
6432 break;
6433 else {
6434 cp1--;
d584a1c6 6435 while ((cp1 > my_esa) && (*cp1 != '.'))
f7ddb74a
JM
6436 cp1--;
6437 }
d584a1c6 6438 if (cp1 == my_esa)
f7ddb74a
JM
6439 cp1 = NULL;
6440 }
6441
6442 if ((cp1) != NULL) {
748a9306
LW
6443 /* There's more than one directory in the path. Just roll back. */
6444 *cp1 = term;
a979ce91 6445 strcpy(buf, my_esa);
a0d0e21e
LW
6446 }
6447 else {
a480973c 6448 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
748a9306 6449 /* Go back and expand rooted logical name */
a480973c 6450 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
f7ddb74a
JM
6451#ifdef NAM$M_NO_SHORT_UPCASE
6452 if (decc_efs_case_preserve)
a480973c 6453 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6454#endif
a480973c
JM
6455 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6456 sts = rms_free_search_context(&dirfab);
c5375c28 6457 PerlMem_free(esa);
d584a1c6
JM
6458 if (esal != NULL)
6459 PerlMem_free(esal);
c5375c28
JM
6460 PerlMem_free(trndir);
6461 PerlMem_free(vmsdir);
748a9306
LW
6462 set_errno(EVMSERR);
6463 set_vaxc_errno(dirfab.fab$l_sts);
6464 return NULL;
6465 }
d584a1c6
JM
6466
6467 /* This changes the length of the string of course */
6468 if (esal != NULL) {
6469 my_esa_len = rms_nam_esll(dirnam);
6470 } else {
6471 my_esa_len = rms_nam_esl(dirnam);
6472 }
6473
6474 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
d584a1c6
JM
6475 cp1 = strstr(my_esa,"][");
6476 if (!cp1) cp1 = strstr(my_esa,"]<");
6477 dirlen = cp1 - my_esa;
a979ce91 6478 memcpy(buf, my_esa, dirlen);
748a9306 6479 if (!strncmp(cp1+2,"000000]",7)) {
a979ce91 6480 buf[dirlen-1] = '\0';
657054d4 6481 /* fix-me Not full ODS-5, just extra dots in directories for now */
a979ce91
JM
6482 cp1 = buf + dirlen - 1;
6483 while (cp1 > buf)
f7ddb74a
JM
6484 {
6485 if (*cp1 == '[')
6486 break;
6487 if (*cp1 == '.') {
6488 if (*(cp1-1) != '^')
6489 break;
6490 }
6491 cp1--;
6492 }
4633a7c4
LW
6493 if (*cp1 == '.') *cp1 = ']';
6494 else {
a979ce91 6495 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6496 memmove(cp1+1,"000000]",7);
4633a7c4 6497 }
748a9306
LW
6498 }
6499 else {
a979ce91
JM
6500 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6501 buf[retlen] = '\0';
748a9306 6502 /* Convert last '.' to ']' */
a979ce91 6503 cp1 = buf+retlen-1;
f7ddb74a
JM
6504 while (*cp != '[') {
6505 cp1--;
6506 if (*cp1 == '.') {
6507 /* Do not trip on extra dots in ODS-5 directories */
a979ce91 6508 if ((cp1 == buf) || (*(cp1-1) != '^'))
f7ddb74a
JM
6509 break;
6510 }
6511 }
4633a7c4
LW
6512 if (*cp1 == '.') *cp1 = ']';
6513 else {
a979ce91 6514 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6515 memmove(cp1+1,"000000]",7);
4633a7c4 6516 }
748a9306 6517 }
a0d0e21e 6518 }
748a9306 6519 else { /* This is a top-level dir. Add the MFD to the path. */
d584a1c6 6520 cp1 = my_esa;
a979ce91 6521 cp2 = buf;
bbdb6c9a 6522 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
a0d0e21e
LW
6523 strcpy(cp2,":[000000]");
6524 cp1 += 2;
6525 strcpy(cp2+9,cp1);
6526 }
748a9306 6527 }
a480973c 6528 sts = rms_free_search_context(&dirfab);
748a9306 6529 /* We've set up the string up through the filename. Add the
a0d0e21e 6530 type and version, and we're done. */
a979ce91 6531 strcat(buf,".DIR;1");
01b8edb6 6532
6533 /* $PARSE may have upcased filespec, so convert output to lower
6534 * case if input contained any lowercase characters. */
a979ce91 6535 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
c5375c28
JM
6536 PerlMem_free(trndir);
6537 PerlMem_free(esa);
d584a1c6
JM
6538 if (esal != NULL)
6539 PerlMem_free(esal);
c5375c28 6540 PerlMem_free(vmsdir);
a979ce91 6541 return buf;
a0d0e21e 6542 }
a979ce91
JM
6543} /* end of int_fileify_dirspec() */
6544
6545
6546/*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6547static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6548{
6549 static char __fileify_retbuf[VMS_MAXRSS];
6550 char * fileified, *ret_spec, *ret_buf;
6551
6552 fileified = NULL;
6553 ret_buf = buf;
6554 if (ret_buf == NULL) {
6555 if (ts) {
6556 Newx(fileified, VMS_MAXRSS, char);
6557 if (fileified == NULL)
6558 _ckvmssts(SS$_INSFMEM);
6559 ret_buf = fileified;
6560 } else {
6561 ret_buf = __fileify_retbuf;
6562 }
6563 }
6564
6565 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6566
6567 if (ret_spec == NULL) {
6568 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6569 if (fileified)
6570 Safefree(fileified);
6571 }
6572
6573 return ret_spec;
a0d0e21e
LW
6574} /* end of do_fileify_dirspec() */
6575/*}}}*/
a979ce91 6576
a0d0e21e 6577/* External entry points */
b8ffc8df 6578char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 6579{ return do_fileify_dirspec(dir,buf,0,NULL); }
b8ffc8df 6580char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
6581{ return do_fileify_dirspec(dir,buf,1,NULL); }
6582char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6583{ return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6584char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6585{ return do_fileify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 6586
1fe570cc
JM
6587static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6588 char * v_spec, int v_len, char * r_spec, int r_len,
6589 char * d_spec, int d_len, char * n_spec, int n_len,
6590 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6591
6592 /* VMS specification - Try to do this the simple way */
6593 if ((v_len + r_len > 0) || (d_len > 0)) {
6594 int is_dir;
6595
6596 /* No name or extension component, already a directory */
6597 if ((n_len + e_len + vs_len) == 0) {
6598 strcpy(buf, dir);
6599 return buf;
6600 }
6601
6602 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6603 /* This results from catfile() being used instead of catdir() */
6604 /* So even though it should not work, we need to allow it */
6605
6606 /* If this is .DIR;1 then do a simple conversion */
6607 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6608 if (is_dir || (e_len == 0) && (d_len > 0)) {
6609 int len;
6610 len = v_len + r_len + d_len - 1;
6611 char dclose = d_spec[d_len - 1];
6612 strncpy(buf, dir, len);
6613 buf[len] = '.';
6614 len++;
6615 strncpy(&buf[len], n_spec, n_len);
6616 len += n_len;
6617 buf[len] = dclose;
6618 buf[len + 1] = '\0';
6619 return buf;
6620 }
6621
6622#ifdef HAS_SYMLINK
6623 else if (d_len > 0) {
6624 /* In the olden days, a directory needed to have a .DIR */
6625 /* extension to be a valid directory, but now it could */
6626 /* be a symbolic link */
6627 int len;
6628 len = v_len + r_len + d_len - 1;
6629 char dclose = d_spec[d_len - 1];
6630 strncpy(buf, dir, len);
6631 buf[len] = '.';
6632 len++;
6633 strncpy(&buf[len], n_spec, n_len);
6634 len += n_len;
6635 if (e_len > 0) {
6636 if (decc_efs_charset) {
6637 buf[len] = '^';
6638 len++;
6639 strncpy(&buf[len], e_spec, e_len);
6640 len += e_len;
6641 } else {
6642 set_vaxc_errno(RMS$_DIR);
6643 set_errno(ENOTDIR);
6644 return NULL;
6645 }
6646 }
6647 buf[len] = dclose;
6648 buf[len + 1] = '\0';
6649 return buf;
6650 }
6651#else
6652 else {
6653 set_vaxc_errno(RMS$_DIR);
6654 set_errno(ENOTDIR);
6655 return NULL;
6656 }
6657#endif
6658 }
6659 set_vaxc_errno(RMS$_DIR);
6660 set_errno(ENOTDIR);
6661 return NULL;
6662}
6663
6664
6665/* Internal routine to make sure or convert a directory to be in a */
6666/* path specification. No utf8 flag because it is not changed or used */
6667static char *int_pathify_dirspec(const char *dir, char *buf)
a0d0e21e 6668{
1fe570cc
JM
6669 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6670 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6671 char * exp_spec, *ret_spec;
6672 char * trndir;
2d9f3838 6673 unsigned short int trnlnm_iter_count;
baf3cf9c 6674 STRLEN trnlen;
1fe570cc
JM
6675 int need_to_lower;
6676
6677 if (vms_debug_fileify) {
6678 if (dir == NULL)
6679 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6680 else
6681 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6682 }
6683
6684 /* We may need to lower case the result if we translated */
6685 /* a logical name or got the current working directory */
6686 need_to_lower = 0;
a0d0e21e 6687
c07a80fd 6688 if (!dir || !*dir) {
1fe570cc
JM
6689 set_errno(EINVAL);
6690 set_vaxc_errno(SS$_BADPARAM);
6691 return NULL;
c07a80fd 6692 }
6693
c5375c28 6694 trndir = PerlMem_malloc(VMS_MAXRSS);
1fe570cc
JM
6695 if (trndir == NULL)
6696 _ckvmssts_noperl(SS$_INSFMEM);
c07a80fd 6697
1fe570cc
JM
6698 /* If no directory specified use the current default */
6699 if (*dir)
6700 strcpy(trndir, dir);
6701 else {
6702 getcwd(trndir, VMS_MAXRSS - 1);
6703 need_to_lower = 1;
6704 }
6705
6706 /* now deal with bare names that could be logical names */
2d9f3838 6707 trnlnm_iter_count = 0;
93948341 6708 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
1fe570cc
JM
6709 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6710 trnlnm_iter_count++;
6711 need_to_lower = 1;
6712 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6713 break;
6714 trnlen = strlen(trndir);
6715
6716 /* Trap simple rooted lnms, and return lnm:[000000] */
6717 if (!strcmp(trndir+trnlen-2,".]")) {
6718 strcpy(buf, dir);
6719 strcat(buf, ":[000000]");
6720 PerlMem_free(trndir);
6721
6722 if (vms_debug_fileify) {
6723 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6724 }
6725 return buf;
6726 }
c07a80fd 6727 }
748a9306 6728
1fe570cc 6729 /* At this point we do not work with *dir, but the copy in *trndir */
b8ffc8df 6730
1fe570cc
JM
6731 if (need_to_lower && !decc_efs_case_preserve) {
6732 /* Legacy mode, lower case the returned value */
6733 __mystrtolower(trndir);
6734 }
f7ddb74a 6735
1fe570cc
JM
6736
6737 /* Some special cases, '..', '.' */
6738 sts = 0;
6739 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6740 /* Force UNIX filespec */
6741 sts = 1;
6742
6743 } else {
6744 /* Is this Unix or VMS format? */
6745 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6746 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6747 &e_len, &vs_spec, &vs_len);
6748 if (sts == 0) {
6749
6750 /* Just a filename? */
6751 if ((v_len + r_len + d_len) == 0) {
6752
6753 /* Now we have a problem, this could be Unix or VMS */
6754 /* We have to guess. .DIR usually means VMS */
6755
6756 /* In UNIX report mode, the .DIR extension is removed */
6757 /* if one shows up, it is for a non-directory or a directory */
6758 /* in EFS charset mode */
6759
6760 /* So if we are in Unix report mode, assume that this */
6761 /* is a relative Unix directory specification */
6762
6763 sts = 1;
6764 if (!decc_filename_unix_report && decc_efs_charset) {
6765 int is_dir;
6766 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6767
6768 if (is_dir) {
6769 /* Traditional mode, assume .DIR is directory */
6770 buf[0] = '[';
6771 buf[1] = '.';
6772 strncpy(&buf[2], n_spec, n_len);
6773 buf[n_len + 2] = ']';
6774 buf[n_len + 3] = '\0';
6775 PerlMem_free(trndir);
6776 if (vms_debug_fileify) {
6777 fprintf(stderr,
6778 "int_pathify_dirspec: buf = %s\n",
6779 buf);
6780 }
6781 return buf;
6782 }
6783 }
6784 }
a0d0e21e 6785 }
a0d0e21e 6786 }
1fe570cc
JM
6787 if (sts == 0) {
6788 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6789 v_spec, v_len, r_spec, r_len,
6790 d_spec, d_len, n_spec, n_len,
6791 e_spec, e_len, vs_spec, vs_len);
a0d0e21e 6792
1fe570cc
JM
6793 if (ret_spec != NULL) {
6794 PerlMem_free(trndir);
6795 if (vms_debug_fileify) {
6796 fprintf(stderr,
6797 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6798 }
6799 return ret_spec;
b7ae7a0d 6800 }
1fe570cc
JM
6801
6802 /* Simple way did not work, which means that a logical name */
6803 /* was present for the directory specification. */
6804 /* Need to use an rmsexpand variant to decode it completely */
6805 exp_spec = PerlMem_malloc(VMS_MAXRSS);
6806 if (exp_spec == NULL)
6807 _ckvmssts_noperl(SS$_INSFMEM);
6808
6809 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6810 if (ret_spec != NULL) {
6811 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6812 &r_spec, &r_len, &d_spec, &d_len,
6813 &n_spec, &n_len, &e_spec,
6814 &e_len, &vs_spec, &vs_len);
6815 if (sts == 0) {
6816 ret_spec = int_pathify_dirspec_simple(
6817 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6818 d_spec, d_len, n_spec, n_len,
6819 e_spec, e_len, vs_spec, vs_len);
6820
6821 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6822 /* Legacy mode, lower case the returned value */
6823 __mystrtolower(ret_spec);
6824 }
6825 } else {
6826 set_vaxc_errno(RMS$_DIR);
6827 set_errno(ENOTDIR);
6828 ret_spec = NULL;
6829 }
b7ae7a0d 6830 }
1fe570cc
JM
6831 PerlMem_free(exp_spec);
6832 PerlMem_free(trndir);
6833 if (vms_debug_fileify) {
6834 if (ret_spec == NULL)
6835 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6836 else
6837 fprintf(stderr,
6838 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6839 }
6840 return ret_spec;
a480973c 6841
1fe570cc
JM
6842 } else {
6843 /* Unix specification, Could be trivial conversion */
6844 STRLEN dir_len;
6845 dir_len = strlen(trndir);
6846
6847 /* If the extended file character set is in effect */
6848 /* then pathify is simple */
6849
6850 if (!decc_efs_charset) {
94ae10c0 6851 /* Have to deal with trailing '.dir' or extra '.' */
1fe570cc
JM
6852 /* that should not be there in legacy mode, but is */
6853
6854 char * lastdot;
6855 char * lastslash;
6856 int is_dir;
6857
6858 lastslash = strrchr(trndir, '/');
6859 if (lastslash == NULL)
6860 lastslash = trndir;
6861 else
6862 lastslash++;
6863
6864 lastdot = NULL;
6865
6866 /* '..' or '.' are valid directory components */
6867 is_dir = 0;
6868 if (lastslash[0] == '.') {
6869 if (lastslash[1] == '\0') {
6870 is_dir = 1;
6871 } else if (lastslash[1] == '.') {
6872 if (lastslash[2] == '\0') {
6873 is_dir = 1;
6874 } else {
6875 /* And finally allow '...' */
6876 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6877 is_dir = 1;
6878 }
6879 }
6880 }
6881 }
01b8edb6 6882
1fe570cc
JM
6883 if (!is_dir) {
6884 lastdot = strrchr(lastslash, '.');
6885 }
6886 if (lastdot != NULL) {
6887 STRLEN e_len;
01b8edb6 6888
1fe570cc
JM
6889 /* '.dir' is discarded, and any other '.' is invalid */
6890 e_len = strlen(lastdot);
6891
6892 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6893
6894 if (is_dir) {
6895 dir_len = dir_len - 4;
6896
6897 }
6898 }
e518068a 6899 }
1fe570cc
JM
6900
6901 strcpy(buf, trndir);
6902 if (buf[dir_len - 1] != '/') {
6903 buf[dir_len] = '/';
6904 buf[dir_len + 1] = '\0';
a0d0e21e 6905 }
1fe570cc
JM
6906
6907 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6908 if (!decc_efs_charset) {
6909 int dir_start = 0;
6910 char * str = buf;
6911 if (str[0] == '.') {
6912 char * dots = str;
6913 int cnt = 1;
6914 while ((dots[cnt] == '.') && (cnt < 3))
6915 cnt++;
6916 if (cnt <= 3) {
6917 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6918 dir_start = 1;
6919 str += cnt;
6920 }
6921 }
6922 }
6923 for (; *str; ++str) {
6924 while (*str == '/') {
6925 dir_start = 1;
6926 *str++;
6927 }
6928 if (dir_start) {
6929
6930 /* Have to skip up to three dots which could be */
6931 /* directories, 3 dots being a VMS extension for Perl */
6932 char * dots = str;
6933 int cnt = 0;
6934 while ((dots[cnt] == '.') && (cnt < 3)) {
6935 cnt++;
6936 }
6937 if (dots[cnt] == '\0')
6938 break;
6939 if ((cnt > 1) && (dots[cnt] != '/')) {
6940 dir_start = 0;
6941 } else {
6942 str += cnt;
6943 }
6944
6945 /* too many dots? */
6946 if ((cnt == 0) || (cnt > 3)) {
6947 dir_start = 0;
6948 }
6949 }
6950 if (!dir_start && (*str == '.')) {
6951 *str = '_';
6952 }
6953 }
e518068a 6954 }
1fe570cc
JM
6955 PerlMem_free(trndir);
6956 ret_spec = buf;
6957 if (vms_debug_fileify) {
6958 if (ret_spec == NULL)
6959 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6960 else
6961 fprintf(stderr,
6962 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
a0d0e21e 6963 }
1fe570cc
JM
6964 return ret_spec;
6965 }
6966}
d584a1c6 6967
1fe570cc
JM
6968/*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6969static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6970{
6971 static char __pathify_retbuf[VMS_MAXRSS];
6972 char * pathified, *ret_spec, *ret_buf;
6973
6974 pathified = NULL;
6975 ret_buf = buf;
6976 if (ret_buf == NULL) {
6977 if (ts) {
6978 Newx(pathified, VMS_MAXRSS, char);
6979 if (pathified == NULL)
6980 _ckvmssts(SS$_INSFMEM);
6981 ret_buf = pathified;
6982 } else {
6983 ret_buf = __pathify_retbuf;
6984 }
6985 }
d584a1c6 6986
1fe570cc
JM
6987 ret_spec = int_pathify_dirspec(dir, ret_buf);
6988
6989 if (ret_spec == NULL) {
6990 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6991 if (pathified)
6992 Safefree(pathified);
a0d0e21e
LW
6993 }
6994
1fe570cc
JM
6995 return ret_spec;
6996
a0d0e21e 6997} /* end of do_pathify_dirspec() */
1fe570cc
JM
6998
6999
a0d0e21e 7000/* External entry points */
b8ffc8df 7001char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 7002{ return do_pathify_dirspec(dir,buf,0,NULL); }
b8ffc8df 7003char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
7004{ return do_pathify_dirspec(dir,buf,1,NULL); }
7005char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7006{ return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7007char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7008{ return do_pathify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 7009
0e5ce2c7
JM
7010/* Internal tounixspec routine that does not use a thread context */
7011/*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7012static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
a0d0e21e 7013{
0e5ce2c7 7014 char *dirend, *cp1, *cp3, *tmp;
b8ffc8df 7015 const char *cp2;
4e0c9737 7016 int dirlen;
2d9f3838 7017 unsigned short int trnlnm_iter_count;
f7ddb74a 7018 int cmp_rslt;
360732b5
JM
7019 if (utf8_fl != NULL)
7020 *utf8_fl = 0;
a0d0e21e 7021
0e5ce2c7
JM
7022 if (vms_debug_fileify) {
7023 if (spec == NULL)
7024 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7025 else
7026 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7027 }
7028
7029
7030 if (spec == NULL) {
7031 set_errno(EINVAL);
7032 set_vaxc_errno(SS$_BADPARAM);
7033 return NULL;
7034 }
7035 if (strlen(spec) > (VMS_MAXRSS-1)) {
7036 set_errno(E2BIG);
7037 set_vaxc_errno(SS$_BUFFEROVF);
7038 return NULL;
e518068a 7039 }
f7ddb74a 7040
2497a41f
JM
7041 /* New VMS specific format needs translation
7042 * glob passes filenames with trailing '\n' and expects this preserved.
7043 */
7044 if (decc_posix_compliant_pathnames) {
7045 if (strncmp(spec, "\"^UP^", 5) == 0) {
7046 char * uspec;
7047 char *tunix;
7048 int tunix_len;
7049 int nl_flag;
7050
c5375c28 7051 tunix = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 7052 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2497a41f
JM
7053 strcpy(tunix, spec);
7054 tunix_len = strlen(tunix);
7055 nl_flag = 0;
7056 if (tunix[tunix_len - 1] == '\n') {
7057 tunix[tunix_len - 1] = '\"';
7058 tunix[tunix_len] = '\0';
7059 tunix_len--;
7060 nl_flag = 1;
7061 }
7062 uspec = decc$translate_vms(tunix);
367e4b85 7063 PerlMem_free(tunix);
2497a41f
JM
7064 if ((int)uspec > 0) {
7065 strcpy(rslt,uspec);
7066 if (nl_flag) {
7067 strcat(rslt,"\n");
7068 }
7069 else {
7070 /* If we can not translate it, makemaker wants as-is */
7071 strcpy(rslt, spec);
7072 }
7073 return rslt;
7074 }
7075 }
7076 }
7077
f7ddb74a
JM
7078 cmp_rslt = 0; /* Presume VMS */
7079 cp1 = strchr(spec, '/');
7080 if (cp1 == NULL)
7081 cmp_rslt = 0;
7082
7083 /* Look for EFS ^/ */
7084 if (decc_efs_charset) {
7085 while (cp1 != NULL) {
7086 cp2 = cp1 - 1;
7087 if (*cp2 != '^') {
7088 /* Found illegal VMS, assume UNIX */
7089 cmp_rslt = 1;
7090 break;
7091 }
7092 cp1++;
7093 cp1 = strchr(cp1, '/');
7094 }
7095 }
7096
7097 /* Look for "." and ".." */
7098 if (decc_filename_unix_report) {
7099 if (spec[0] == '.') {
7100 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7101 cmp_rslt = 1;
7102 }
7103 else {
7104 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7105 cmp_rslt = 1;
7106 }
7107 }
7108 }
7109 }
7110 /* This is already UNIX or at least nothing VMS understands */
7111 if (cmp_rslt) {
a0d0e21e 7112 strcpy(rslt,spec);
0e5ce2c7
JM
7113 if (vms_debug_fileify) {
7114 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7115 }
a0d0e21e
LW
7116 return rslt;
7117 }
7118
7119 cp1 = rslt;
7120 cp2 = spec;
7121 dirend = strrchr(spec,']');
7122 if (dirend == NULL) dirend = strrchr(spec,'>');
7123 if (dirend == NULL) dirend = strchr(spec,':');
7124 if (dirend == NULL) {
7125 strcpy(rslt,spec);
0e5ce2c7
JM
7126 if (vms_debug_fileify) {
7127 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7128 }
a0d0e21e
LW
7129 return rslt;
7130 }
f7ddb74a
JM
7131
7132 /* Special case 1 - sys$posix_root = / */
7133#if __CRTL_VER >= 70000000
7134 if (!decc_disable_posix_root) {
7135 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7136 *cp1 = '/';
7137 cp1++;
7138 cp2 = cp2 + 15;
7139 }
7140 }
7141#endif
7142
7143 /* Special case 2 - Convert NLA0: to /dev/null */
7144#if __CRTL_VER < 70000000
7145 cmp_rslt = strncmp(spec,"NLA0:", 5);
7146 if (cmp_rslt != 0)
7147 cmp_rslt = strncmp(spec,"nla0:", 5);
7148#else
7149 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7150#endif
7151 if (cmp_rslt == 0) {
7152 strcpy(rslt, "/dev/null");
7153 cp1 = cp1 + 9;
7154 cp2 = cp2 + 5;
7155 if (spec[6] != '\0') {
07bee079 7156 cp1[9] = '/';
f7ddb74a
JM
7157 cp1++;
7158 cp2++;
7159 }
7160 }
7161
7162 /* Also handle special case "SYS$SCRATCH:" */
7163#if __CRTL_VER < 70000000
7164 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7165 if (cmp_rslt != 0)
7166 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7167#else
7168 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7169#endif
c5375c28 7170 tmp = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 7171 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
7172 if (cmp_rslt == 0) {
7173 int islnm;
7174
b8486b9d 7175 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
f7ddb74a
JM
7176 if (!islnm) {
7177 strcpy(rslt, "/tmp");
7178 cp1 = cp1 + 4;
7179 cp2 = cp2 + 12;
7180 if (spec[12] != '\0') {
07bee079 7181 cp1[4] = '/';
f7ddb74a
JM
7182 cp1++;
7183 cp2++;
7184 }
7185 }
7186 }
7187
a5f75d66 7188 if (*cp2 != '[' && *cp2 != '<') {
a0d0e21e
LW
7189 *(cp1++) = '/';
7190 }
7191 else { /* the VMS spec begins with directories */
7192 cp2++;
a5f75d66 7193 if (*cp2 == ']' || *cp2 == '>') {
f86702cc 7194 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
367e4b85 7195 PerlMem_free(tmp);
a5f75d66
AD
7196 return rslt;
7197 }
f7ddb74a 7198 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
2f4077ca 7199 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
367e4b85 7200 PerlMem_free(tmp);
0e5ce2c7
JM
7201 if (vms_debug_fileify) {
7202 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7203 }
a0d0e21e
LW
7204 return NULL;
7205 }
2d9f3838 7206 trnlnm_iter_count = 0;
a0d0e21e
LW
7207 do {
7208 cp3 = tmp;
7209 while (*cp3 != ':' && *cp3) cp3++;
7210 *(cp3++) = '\0';
7211 if (strchr(cp3,']') != NULL) break;
2d9f3838
CB
7212 trnlnm_iter_count++;
7213 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
f675dbe5 7214 } while (vmstrnenv(tmp,tmp,0,fildev,0));
0e5ce2c7 7215 cp1 = rslt;
f86702cc 7216 cp3 = tmp;
7217 *(cp1++) = '/';
7218 while (*cp3) {
7219 *(cp1++) = *(cp3++);
0e5ce2c7 7220 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
367e4b85 7221 PerlMem_free(tmp);
0e5ce2c7
JM
7222 set_errno(ENAMETOOLONG);
7223 set_vaxc_errno(SS$_BUFFEROVF);
7224 if (vms_debug_fileify) {
7225 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7226 }
2f4077ca
JM
7227 return NULL; /* No room */
7228 }
a0d0e21e 7229 }
f86702cc 7230 *(cp1++) = '/';
7231 }
f7ddb74a
JM
7232 if ((*cp2 == '^')) {
7233 /* EFS file escape, pass the next character as is */
38a44b82 7234 /* Fix me: HEX encoding for Unicode not implemented */
f7ddb74a
JM
7235 cp2++;
7236 }
f86702cc 7237 else if ( *cp2 == '.') {
7238 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7239 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7240 cp2 += 3;
7241 }
7242 else cp2++;
a0d0e21e 7243 }
a0d0e21e 7244 }
367e4b85 7245 PerlMem_free(tmp);
a0d0e21e 7246 for (; cp2 <= dirend; cp2++) {
f7ddb74a
JM
7247 if ((*cp2 == '^')) {
7248 /* EFS file escape, pass the next character as is */
38a44b82 7249 /* Fix me: HEX encoding for Unicode not implemented */
42cd432e
CB
7250 *(cp1++) = *(++cp2);
7251 /* An escaped dot stays as is -- don't convert to slash */
7252 if (*cp2 == '.') cp2++;
f7ddb74a 7253 }
a0d0e21e
LW
7254 if (*cp2 == ':') {
7255 *(cp1++) = '/';
5ad5b34c 7256 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
a0d0e21e 7257 }
f86702cc 7258 else if (*cp2 == ']' || *cp2 == '>') {
7259 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7260 }
f7ddb74a 7261 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
a0d0e21e 7262 *(cp1++) = '/';
e518068a 7263 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7264 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7265 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7266 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7267 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7268 }
f86702cc 7269 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7270 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7271 cp2 += 2;
7272 }
a0d0e21e
LW
7273 }
7274 else if (*cp2 == '-') {
7275 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7276 while (*cp2 == '-') {
7277 cp2++;
7278 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7279 }
7280 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
0e5ce2c7 7281 /* filespecs like */
01b8edb6 7282 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
0e5ce2c7
JM
7283 if (vms_debug_fileify) {
7284 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7285 }
a0d0e21e
LW
7286 return NULL;
7287 }
a0d0e21e
LW
7288 }
7289 else *(cp1++) = *cp2;
7290 }
7291 else *(cp1++) = *cp2;
7292 }
0e5ce2c7 7293 /* Translate the rest of the filename. */
42cd432e 7294 while (*cp2) {
0e5ce2c7
JM
7295 int dot_seen;
7296 dot_seen = 0;
7297 switch(*cp2) {
7298 /* Fixme - for compatibility with the CRTL we should be removing */
7299 /* spaces from the file specifications, but this may show that */
7300 /* some tests that were appearing to pass are not really passing */
7301 case '%':
7302 cp2++;
7303 *(cp1++) = '?';
7304 break;
7305 case '^':
7306 /* Fix me hex expansions not implemented */
7307 cp2++; /* '^.' --> '.' and other. */
7308 if (*cp2) {
7309 if (*cp2 == '_') {
7310 cp2++;
7311 *(cp1++) = ' ';
7312 } else {
7313 *(cp1++) = *(cp2++);
7314 }
7315 }
7316 break;
7317 case ';':
7318 if (decc_filename_unix_no_version) {
7319 /* Easy, drop the version */
7320 while (*cp2)
7321 cp2++;
7322 break;
7323 } else {
7324 /* Punt - passing the version as a dot will probably */
7325 /* break perl in weird ways, but so did passing */
7326 /* through the ; as a version. Follow the CRTL and */
7327 /* hope for the best. */
7328 cp2++;
7329 *(cp1++) = '.';
7330 }
7331 break;
7332 case '.':
7333 if (dot_seen) {
7334 /* We will need to fix this properly later */
7335 /* As Perl may be installed on an ODS-5 volume, but not */
7336 /* have the EFS_CHARSET enabled, it still may encounter */
7337 /* filenames with extra dots in them, and a precedent got */
7338 /* set which allowed them to work, that we will uphold here */
7339 /* If extra dots are present in a name and no ^ is on them */
7340 /* VMS assumes that the first one is the extension delimiter */
7341 /* the rest have an implied ^. */
7342
7343 /* this is also a conflict as the . is also a version */
7344 /* delimiter in VMS, */
7345
7346 *(cp1++) = *(cp2++);
7347 break;
7348 }
7349 dot_seen = 1;
7350 /* This is an extension */
7351 if (decc_readdir_dropdotnotype) {
7352 cp2++;
7353 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7354 /* Drop the dot for the extension */
7355 break;
7356 } else {
7357 *(cp1++) = '.';
7358 }
7359 break;
7360 }
7361 default:
7362 *(cp1++) = *(cp2++);
7363 }
42cd432e 7364 }
a0d0e21e
LW
7365 *cp1 = '\0';
7366
f7ddb74a
JM
7367 /* This still leaves /000000/ when working with a
7368 * VMS device root or concealed root.
7369 */
7370 {
7371 int ulen;
7372 char * zeros;
7373
7374 ulen = strlen(rslt);
7375
7376 /* Get rid of "000000/ in rooted filespecs */
7377 if (ulen > 7) {
7378 zeros = strstr(rslt, "/000000/");
7379 if (zeros != NULL) {
7380 int mlen;
7381 mlen = ulen - (zeros - rslt) - 7;
7382 memmove(zeros, &zeros[7], mlen);
7383 ulen = ulen - 7;
7384 rslt[ulen] = '\0';
7385 }
7386 }
7387 }
7388
0e5ce2c7
JM
7389 if (vms_debug_fileify) {
7390 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7391 }
a0d0e21e
LW
7392 return rslt;
7393
0e5ce2c7
JM
7394} /* end of int_tounixspec() */
7395
7396
7397/*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7398static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7399{
7400 static char __tounixspec_retbuf[VMS_MAXRSS];
7401 char * unixspec, *ret_spec, *ret_buf;
7402
7403 unixspec = NULL;
7404 ret_buf = buf;
7405 if (ret_buf == NULL) {
7406 if (ts) {
7407 Newx(unixspec, VMS_MAXRSS, char);
7408 if (unixspec == NULL)
7409 _ckvmssts(SS$_INSFMEM);
7410 ret_buf = unixspec;
7411 } else {
7412 ret_buf = __tounixspec_retbuf;
7413 }
7414 }
7415
7416 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7417
7418 if (ret_spec == NULL) {
7419 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7420 if (unixspec)
7421 Safefree(unixspec);
7422 }
7423
7424 return ret_spec;
7425
a0d0e21e
LW
7426} /* end of do_tounixspec() */
7427/*}}}*/
7428/* External entry points */
360732b5
JM
7429char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7430 { return do_tounixspec(spec,buf,0, NULL); }
7431char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7432 { return do_tounixspec(spec,buf,1, NULL); }
7433char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7434 { return do_tounixspec(spec,buf,0, utf8_fl); }
7435char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7436 { return do_tounixspec(spec,buf,1, utf8_fl); }
a0d0e21e 7437
360732b5 7438#if __CRTL_VER >= 70200000 && !defined(__VAX)
2497a41f 7439
360732b5
JM
7440/*
7441 This procedure is used to identify if a path is based in either
7442 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7443 it returns the OpenVMS format directory for it.
7444
7445 It is expecting specifications of only '/' or '/xxxx/'
7446
7447 If a posix root does not exist, or 'xxxx' is not a directory
7448 in the posix root, it returns a failure.
7449
7450 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7451
7452 It is used only internally by posix_to_vmsspec_hardway().
7453 */
7454
7455static int posix_root_to_vms
7456 (char *vmspath, int vmspath_len,
7457 const char *unixpath,
d584a1c6
JM
7458 const int * utf8_fl)
7459{
2497a41f
JM
7460int sts;
7461struct FAB myfab = cc$rms_fab;
d584a1c6 7462rms_setup_nam(mynam);
2497a41f 7463struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
d584a1c6
JM
7464struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7465char * esa, * esal, * rsa, * rsal;
2497a41f
JM
7466int dir_flag;
7467int unixlen;
7468
360732b5 7469 dir_flag = 0;
d584a1c6 7470 vmspath[0] = '\0';
360732b5
JM
7471 unixlen = strlen(unixpath);
7472 if (unixlen == 0) {
360732b5
JM
7473 return RMS$_FNF;
7474 }
7475
7476#if __CRTL_VER >= 80200000
2497a41f 7477 /* If not a posix spec already, convert it */
360732b5
JM
7478 if (decc_posix_compliant_pathnames) {
7479 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7480 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7481 }
7482 else {
7483 /* This is already a VMS specification, no conversion */
7484 unixlen--;
7485 strncpy(vmspath,unixpath, vmspath_len);
7486 }
2497a41f 7487 }
360732b5
JM
7488 else
7489#endif
7490 {
7491 int path_len;
7492 int i,j;
7493
7494 /* Check to see if this is under the POSIX root */
7495 if (decc_disable_posix_root) {
7496 return RMS$_FNF;
7497 }
7498
7499 /* Skip leading / */
7500 if (unixpath[0] == '/') {
7501 unixpath++;
7502 unixlen--;
7503 }
7504
7505
7506 strcpy(vmspath,"SYS$POSIX_ROOT:");
7507
7508 /* If this is only the / , or blank, then... */
7509 if (unixpath[0] == '\0') {
7510 /* by definition, this is the answer */
7511 return SS$_NORMAL;
7512 }
7513
7514 /* Need to look up a directory */
7515 vmspath[15] = '[';
7516 vmspath[16] = '\0';
7517
7518 /* Copy and add '^' escape characters as needed */
7519 j = 16;
7520 i = 0;
7521 while (unixpath[i] != 0) {
7522 int k;
7523
7524 j += copy_expand_unix_filename_escape
7525 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7526 i += k;
7527 }
7528
7529 path_len = strlen(vmspath);
7530 if (vmspath[path_len - 1] == '/')
7531 path_len--;
7532 vmspath[path_len] = ']';
7533 path_len++;
7534 vmspath[path_len] = '\0';
7535
2497a41f
JM
7536 }
7537 vmspath[vmspath_len] = 0;
7538 if (unixpath[unixlen - 1] == '/')
7539 dir_flag = 1;
d584a1c6
JM
7540 esal = PerlMem_malloc(VMS_MAXRSS);
7541 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7542 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
c5375c28 7543 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
7544 rsal = PerlMem_malloc(VMS_MAXRSS);
7545 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7546 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7547 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7548 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7549 rms_bind_fab_nam(myfab, mynam);
7550 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7551 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
2497a41f
JM
7552 if (decc_efs_case_preserve)
7553 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
ea0c9945 7554#ifdef NAML$M_OPEN_SPECIAL
2497a41f 7555 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
ea0c9945 7556#endif
2497a41f
JM
7557
7558 /* Set up the remaining naml fields */
7559 sts = sys$parse(&myfab);
7560
7561 /* It failed! Try again as a UNIX filespec */
7562 if (!(sts & 1)) {
d584a1c6 7563 PerlMem_free(esal);
367e4b85 7564 PerlMem_free(esa);
d584a1c6
JM
7565 PerlMem_free(rsal);
7566 PerlMem_free(rsa);
2497a41f
JM
7567 return sts;
7568 }
7569
7570 /* get the Device ID and the FID */
7571 sts = sys$search(&myfab);
d584a1c6
JM
7572
7573 /* These are no longer needed */
7574 PerlMem_free(esa);
7575 PerlMem_free(rsal);
7576 PerlMem_free(rsa);
7577
2497a41f
JM
7578 /* on any failure, returned the POSIX ^UP^ filespec */
7579 if (!(sts & 1)) {
d584a1c6 7580 PerlMem_free(esal);
2497a41f
JM
7581 return sts;
7582 }
7583 specdsc.dsc$a_pointer = vmspath;
7584 specdsc.dsc$w_length = vmspath_len;
7585
7586 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7587 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7588 sts = lib$fid_to_name
7589 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7590
7591 /* on any failure, returned the POSIX ^UP^ filespec */
7592 if (!(sts & 1)) {
7593 /* This can happen if user does not have permission to read directories */
7594 if (strncmp(unixpath,"\"^UP^",5) != 0)
7595 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7596 else
7597 strcpy(vmspath, unixpath);
7598 }
7599 else {
7600 vmspath[specdsc.dsc$w_length] = 0;
7601
7602 /* Are we expecting a directory? */
7603 if (dir_flag != 0) {
7604 int i;
7605 char *eptr;
7606
7607 eptr = NULL;
7608
7609 i = specdsc.dsc$w_length - 1;
7610 while (i > 0) {
7611 int zercnt;
7612 zercnt = 0;
7613 /* Version must be '1' */
7614 if (vmspath[i--] != '1')
7615 break;
7616 /* Version delimiter is one of ".;" */
7617 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7618 break;
7619 i--;
7620 if (vmspath[i--] != 'R')
7621 break;
7622 if (vmspath[i--] != 'I')
7623 break;
7624 if (vmspath[i--] != 'D')
7625 break;
7626 if (vmspath[i--] != '.')
7627 break;
7628 eptr = &vmspath[i+1];
7629 while (i > 0) {
7630 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7631 if (vmspath[i-1] != '^') {
7632 if (zercnt != 6) {
7633 *eptr = vmspath[i];
7634 eptr[1] = '\0';
7635 vmspath[i] = '.';
7636 break;
7637 }
7638 else {
7639 /* Get rid of 6 imaginary zero directory filename */
7640 vmspath[i+1] = '\0';
7641 }
7642 }
7643 }
7644 if (vmspath[i] == '0')
7645 zercnt++;
7646 else
7647 zercnt = 10;
7648 i--;
7649 }
7650 break;
7651 }
7652 }
7653 }
d584a1c6 7654 PerlMem_free(esal);
2497a41f
JM
7655 return sts;
7656}
7657
360732b5
JM
7658/* /dev/mumble needs to be handled special.
7659 /dev/null becomes NLA0:, And there is the potential for other stuff
7660 like /dev/tty which may need to be mapped to something.
7661*/
7662
7663static int
7664slash_dev_special_to_vms
7665 (const char * unixptr,
7666 char * vmspath,
7667 int vmspath_len)
7668{
7669char * nextslash;
7670int len;
7671int cmp;
360732b5
JM
7672
7673 unixptr += 4;
7674 nextslash = strchr(unixptr, '/');
7675 len = strlen(unixptr);
7676 if (nextslash != NULL)
7677 len = nextslash - unixptr;
7678 cmp = strncmp("null", unixptr, 5);
7679 if (cmp == 0) {
7680 if (vmspath_len >= 6) {
7681 strcpy(vmspath, "_NLA0:");
7682 return SS$_NORMAL;
7683 }
7684 }
c5193628 7685 return 0;
360732b5
JM
7686}
7687
7688
7689/* The built in routines do not understand perl's special needs, so
7690 doing a manual conversion from UNIX to VMS
7691
7692 If the utf8_fl is not null and points to a non-zero value, then
7693 treat 8 bit characters as UTF-8.
7694
7695 The sequence starting with '$(' and ending with ')' will be passed
7696 through with out interpretation instead of being escaped.
7697
7698 */
2497a41f 7699static int posix_to_vmsspec_hardway
360732b5
JM
7700 (char *vmspath, int vmspath_len,
7701 const char *unixpath,
7702 int dir_flag,
7703 int * utf8_fl) {
2497a41f
JM
7704
7705char *esa;
7706const char *unixptr;
360732b5 7707const char *unixend;
2497a41f
JM
7708char *vmsptr;
7709const char *lastslash;
7710const char *lastdot;
7711int unixlen;
7712int vmslen;
7713int dir_start;
7714int dir_dot;
7715int quoted;
360732b5
JM
7716char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7717int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
2497a41f 7718
360732b5
JM
7719 if (utf8_fl != NULL)
7720 *utf8_fl = 0;
2497a41f
JM
7721
7722 unixptr = unixpath;
7723 dir_dot = 0;
7724
7725 /* Ignore leading "/" characters */
7726 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7727 unixptr++;
7728 }
7729 unixlen = strlen(unixptr);
7730
7731 /* Do nothing with blank paths */
7732 if (unixlen == 0) {
7733 vmspath[0] = '\0';
7734 return SS$_NORMAL;
7735 }
7736
360732b5
JM
7737 quoted = 0;
7738 /* This could have a "^UP^ on the front */
7739 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7740 quoted = 1;
7741 unixptr+= 5;
7742 unixlen-= 5;
7743 }
7744
2497a41f
JM
7745 lastslash = strrchr(unixptr,'/');
7746 lastdot = strrchr(unixptr,'.');
360732b5
JM
7747 unixend = strrchr(unixptr,'\"');
7748 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7749 unixend = unixptr + unixlen;
7750 }
2497a41f
JM
7751
7752 /* last dot is last dot or past end of string */
7753 if (lastdot == NULL)
7754 lastdot = unixptr + unixlen;
7755
7756 /* if no directories, set last slash to beginning of string */
7757 if (lastslash == NULL) {
7758 lastslash = unixptr;
7759 }
7760 else {
7761 /* Watch out for trailing "." after last slash, still a directory */
7762 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7763 lastslash = unixptr + unixlen;
7764 }
7765
94ae10c0 7766 /* Watch out for trailing ".." after last slash, still a directory */
2497a41f
JM
7767 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7768 lastslash = unixptr + unixlen;
7769 }
7770
7771 /* dots in directories are aways escaped */
7772 if (lastdot < lastslash)
7773 lastdot = unixptr + unixlen;
7774 }
7775
7776 /* if (unixptr < lastslash) then we are in a directory */
7777
7778 dir_start = 0;
2497a41f
JM
7779
7780 vmsptr = vmspath;
7781 vmslen = 0;
7782
2497a41f
JM
7783 /* Start with the UNIX path */
7784 if (*unixptr != '/') {
7785 /* relative paths */
360732b5
JM
7786
7787 /* If allowing logical names on relative pathnames, then handle here */
7788 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7789 !decc_posix_compliant_pathnames) {
7790 char * nextslash;
7791 int seg_len;
7792 char * trn;
7793 int islnm;
7794
7795 /* Find the next slash */
7796 nextslash = strchr(unixptr,'/');
7797
7798 esa = PerlMem_malloc(vmspath_len);
7799 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7800
7801 trn = PerlMem_malloc(VMS_MAXRSS);
7802 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7803
7804 if (nextslash != NULL) {
7805
7806 seg_len = nextslash - unixptr;
7807 strncpy(esa, unixptr, seg_len);
7808 esa[seg_len] = 0;
7809 }
7810 else {
7811 strcpy(esa, unixptr);
7812 seg_len = strlen(unixptr);
7813 }
7814 /* trnlnm(section) */
7815 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7816
7817 if (islnm) {
7818 /* Now fix up the directory */
7819
7820 /* Split up the path to find the components */
7821 sts = vms_split_path
7822 (trn,
7823 &v_spec,
7824 &v_len,
7825 &r_spec,
7826 &r_len,
7827 &d_spec,
7828 &d_len,
7829 &n_spec,
7830 &n_len,
7831 &e_spec,
7832 &e_len,
7833 &vs_spec,
7834 &vs_len);
7835
7836 while (sts == 0) {
360732b5
JM
7837 int cmp;
7838
7839 /* A logical name must be a directory or the full
7840 specification. It is only a full specification if
7841 it is the only component */
7842 if ((unixptr[seg_len] == '\0') ||
7843 (unixptr[seg_len+1] == '\0')) {
7844
7845 /* Is a directory being required? */
7846 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7847 /* Not a logical name */
7848 break;
7849 }
7850
7851
7852 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7853 /* This must be a directory */
7854 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7855 strcpy(vmsptr, esa);
7856 vmslen=strlen(vmsptr);
7857 vmsptr[vmslen] = ':';
7858 vmslen++;
7859 vmsptr[vmslen] = '\0';
7860 return SS$_NORMAL;
7861 }
7862 }
7863
7864 }
7865
7866
7867 /* must be dev/directory - ignore version */
7868 if ((n_len + e_len) != 0)
7869 break;
7870
7871 /* transfer the volume */
7872 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7873 strncpy(vmsptr, v_spec, v_len);
7874 vmsptr += v_len;
7875 vmsptr[0] = '\0';
7876 vmslen += v_len;
7877 }
7878
7879 /* unroot the rooted directory */
7880 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7881 r_spec[0] = '[';
7882 r_spec[r_len - 1] = ']';
7883
7884 /* This should not be there, but nothing is perfect */
7885 if (r_len > 9) {
7886 cmp = strcmp(&r_spec[1], "000000.");
7887 if (cmp == 0) {
7888 r_spec += 7;
7889 r_spec[7] = '[';
7890 r_len -= 7;
7891 if (r_len == 2)
7892 r_len = 0;
7893 }
7894 }
7895 if (r_len > 0) {
7896 strncpy(vmsptr, r_spec, r_len);
7897 vmsptr += r_len;
7898 vmslen += r_len;
7899 vmsptr[0] = '\0';
7900 }
7901 }
7902 /* Bring over the directory. */
7903 if ((d_len > 0) &&
7904 ((d_len + vmslen) < vmspath_len)) {
7905 d_spec[0] = '[';
7906 d_spec[d_len - 1] = ']';
7907 if (d_len > 9) {
7908 cmp = strcmp(&d_spec[1], "000000.");
7909 if (cmp == 0) {
7910 d_spec += 7;
7911 d_spec[7] = '[';
7912 d_len -= 7;
7913 if (d_len == 2)
7914 d_len = 0;
7915 }
7916 }
7917
7918 if (r_len > 0) {
7919 /* Remove the redundant root */
7920 if (r_len > 0) {
7921 /* remove the ][ */
7922 vmsptr--;
7923 vmslen--;
7924 d_spec++;
7925 d_len--;
7926 }
7927 strncpy(vmsptr, d_spec, d_len);
7928 vmsptr += d_len;
7929 vmslen += d_len;
7930 vmsptr[0] = '\0';
7931 }
7932 }
7933 break;
7934 }
7935 }
7936
7937 PerlMem_free(esa);
7938 PerlMem_free(trn);
7939 }
7940
2497a41f
JM
7941 if (lastslash > unixptr) {
7942 int dotdir_seen;
7943
7944 /* skip leading ./ */
7945 dotdir_seen = 0;
7946 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7947 dotdir_seen = 1;
7948 unixptr++;
7949 unixptr++;
7950 }
7951
7952 /* Are we still in a directory? */
7953 if (unixptr <= lastslash) {
7954 *vmsptr++ = '[';
7955 vmslen = 1;
7956 dir_start = 1;
7957
7958 /* if not backing up, then it is relative forward. */
7959 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
360732b5 7960 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
2497a41f
JM
7961 *vmsptr++ = '.';
7962 vmslen++;
7963 dir_dot = 1;
360732b5 7964 }
2497a41f
JM
7965 }
7966 else {
7967 if (dotdir_seen) {
7968 /* Perl wants an empty directory here to tell the difference
94ae10c0 7969 * between a DCL command and a filename
2497a41f
JM
7970 */
7971 *vmsptr++ = '[';
7972 *vmsptr++ = ']';
7973 vmslen = 2;
7974 }
7975 }
7976 }
7977 else {
7978 /* Handle two special files . and .. */
7979 if (unixptr[0] == '.') {
360732b5 7980 if (&unixptr[1] == unixend) {
2497a41f
JM
7981 *vmsptr++ = '[';
7982 *vmsptr++ = ']';
7983 vmslen += 2;
7984 *vmsptr++ = '\0';
7985 return SS$_NORMAL;
7986 }
360732b5 7987 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
2497a41f
JM
7988 *vmsptr++ = '[';
7989 *vmsptr++ = '-';
7990 *vmsptr++ = ']';
7991 vmslen += 3;
7992 *vmsptr++ = '\0';
7993 return SS$_NORMAL;
7994 }
7995 }
7996 }
7997 }
7998 else { /* Absolute PATH handling */
7999 int sts;
8000 char * nextslash;
8001 int seg_len;
8002 /* Need to find out where root is */
8003
8004 /* In theory, this procedure should never get an absolute POSIX pathname
8005 * that can not be found on the POSIX root.
8006 * In practice, that can not be relied on, and things will show up
8007 * here that are a VMS device name or concealed logical name instead.
8008 * So to make things work, this procedure must be tolerant.
8009 */
c5375c28
JM
8010 esa = PerlMem_malloc(vmspath_len);
8011 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2497a41f
JM
8012
8013 sts = SS$_NORMAL;
8014 nextslash = strchr(&unixptr[1],'/');
8015 seg_len = 0;
8016 if (nextslash != NULL) {
360732b5 8017 int cmp;
2497a41f
JM
8018 seg_len = nextslash - &unixptr[1];
8019 strncpy(vmspath, unixptr, seg_len + 1);
8020 vmspath[seg_len+1] = 0;
360732b5
JM
8021 cmp = 1;
8022 if (seg_len == 3) {
8023 cmp = strncmp(vmspath, "dev", 4);
8024 if (cmp == 0) {
8025 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
07bee079 8026 if (sts == SS$_NORMAL)
360732b5
JM
8027 return SS$_NORMAL;
8028 }
8029 }
8030 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
2497a41f
JM
8031 }
8032
360732b5 8033 if ($VMS_STATUS_SUCCESS(sts)) {
2497a41f
JM
8034 /* This is verified to be a real path */
8035
360732b5
JM
8036 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8037 if ($VMS_STATUS_SUCCESS(sts)) {
8038 strcpy(vmspath, esa);
8039 vmslen = strlen(vmspath);
8040 vmsptr = vmspath + vmslen;
8041 unixptr++;
8042 if (unixptr < lastslash) {
8043 char * rptr;
8044 vmsptr--;
8045 *vmsptr++ = '.';
8046 dir_start = 1;
8047 dir_dot = 1;
8048 if (vmslen > 7) {
8049 int cmp;
8050 rptr = vmsptr - 7;
8051 cmp = strcmp(rptr,"000000.");
8052 if (cmp == 0) {
8053 vmslen -= 7;
8054 vmsptr -= 7;
8055 vmsptr[1] = '\0';
8056 } /* removing 6 zeros */
8057 } /* vmslen < 7, no 6 zeros possible */
8058 } /* Not in a directory */
8059 } /* Posix root found */
8060 else {
8061 /* No posix root, fall back to default directory */
8062 strcpy(vmspath, "SYS$DISK:[");
8063 vmsptr = &vmspath[10];
8064 vmslen = 10;
8065 if (unixptr > lastslash) {
8066 *vmsptr = ']';
8067 vmsptr++;
8068 vmslen++;
8069 }
8070 else {
8071 dir_start = 1;
8072 }
8073 }
2497a41f
JM
8074 } /* end of verified real path handling */
8075 else {
8076 int add_6zero;
8077 int islnm;
8078
8079 /* Ok, we have a device or a concealed root that is not in POSIX
8080 * or we have garbage. Make the best of it.
8081 */
8082
8083 /* Posix to VMS destroyed this, so copy it again */
8084 strncpy(vmspath, &unixptr[1], seg_len);
8085 vmspath[seg_len] = 0;
8086 vmslen = seg_len;
8087 vmsptr = &vmsptr[vmslen];
8088 islnm = 0;
8089
8090 /* Now do we need to add the fake 6 zero directory to it? */
8091 add_6zero = 1;
8092 if ((*lastslash == '/') && (nextslash < lastslash)) {
8093 /* No there is another directory */
8094 add_6zero = 0;
8095 }
8096 else {
8097 int trnend;
360732b5 8098 int cmp;
2497a41f
JM
8099
8100 /* now we have foo:bar or foo:[000000]bar to decide from */
7ded3206 8101 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
360732b5
JM
8102
8103 if (!islnm && !decc_posix_compliant_pathnames) {
8104
8105 cmp = strncmp("bin", vmspath, 4);
8106 if (cmp == 0) {
8107 /* bin => SYS$SYSTEM: */
8108 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8109 }
8110 else {
8111 /* tmp => SYS$SCRATCH: */
8112 cmp = strncmp("tmp", vmspath, 4);
8113 if (cmp == 0) {
8114 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8115 }
8116 }
8117 }
8118
7ded3206 8119 trnend = islnm ? islnm - 1 : 0;
2497a41f
JM
8120
8121 /* if this was a logical name, ']' or '>' must be present */
8122 /* if not a logical name, then assume a device and hope. */
8123 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8124
8125 /* if log name and trailing '.' then rooted - treat as device */
8126 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8127
8128 /* Fix me, if not a logical name, a device lookup should be
8129 * done to see if the device is file structured. If the device
8130 * is not file structured, the 6 zeros should not be put on.
8131 *
8132 * As it is, perl is occasionally looking for dev:[000000]tty.
8133 * which looks a little strange.
360732b5
JM
8134 *
8135 * Not that easy to detect as "/dev" may be file structured with
8136 * special device files.
2497a41f
JM
8137 */
8138
30e68285 8139 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
360732b5 8140 (&nextslash[1] == unixend)) {
2497a41f
JM
8141 /* No real directory present */
8142 add_6zero = 1;
8143 }
8144 }
8145
8146 /* Put the device delimiter on */
8147 *vmsptr++ = ':';
8148 vmslen++;
8149 unixptr = nextslash;
8150 unixptr++;
8151
8152 /* Start directory if needed */
8153 if (!islnm || add_6zero) {
8154 *vmsptr++ = '[';
8155 vmslen++;
8156 dir_start = 1;
8157 }
8158
8159 /* add fake 000000] if needed */
8160 if (add_6zero) {
8161 *vmsptr++ = '0';
8162 *vmsptr++ = '0';
8163 *vmsptr++ = '0';
8164 *vmsptr++ = '0';
8165 *vmsptr++ = '0';
8166 *vmsptr++ = '0';
8167 *vmsptr++ = ']';
8168 vmslen += 7;
8169 dir_start = 0;
8170 }
8171
8172 } /* non-POSIX translation */
367e4b85 8173 PerlMem_free(esa);
2497a41f
JM
8174 } /* End of relative/absolute path handling */
8175
360732b5 8176 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
2497a41f 8177 int dash_flag;
360732b5
JM
8178 int in_cnt;
8179 int out_cnt;
2497a41f
JM
8180
8181 dash_flag = 0;
8182
8183 if (dir_start != 0) {
8184
8185 /* First characters in a directory are handled special */
8186 while ((*unixptr == '/') ||
8187 ((*unixptr == '.') &&
360732b5
JM
8188 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8189 (&unixptr[1]==unixend)))) {
2497a41f
JM
8190 int loop_flag;
8191
8192 loop_flag = 0;
8193
8194 /* Skip redundant / in specification */
8195 while ((*unixptr == '/') && (dir_start != 0)) {
8196 loop_flag = 1;
8197 unixptr++;
8198 if (unixptr == lastslash)
8199 break;
8200 }
8201 if (unixptr == lastslash)
8202 break;
8203
8204 /* Skip redundant ./ characters */
8205 while ((*unixptr == '.') &&
360732b5 8206 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
2497a41f
JM
8207 loop_flag = 1;
8208 unixptr++;
8209 if (unixptr == lastslash)
8210 break;
8211 if (*unixptr == '/')
8212 unixptr++;
8213 }
8214 if (unixptr == lastslash)
8215 break;
8216
8217 /* Skip redundant ../ characters */
8218 while ((*unixptr == '.') && (unixptr[1] == '.') &&
360732b5 8219 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
2497a41f
JM
8220 /* Set the backing up flag */
8221 loop_flag = 1;
8222 dir_dot = 0;
8223 dash_flag = 1;
8224 *vmsptr++ = '-';
8225 vmslen++;
8226 unixptr++; /* first . */
8227 unixptr++; /* second . */
8228 if (unixptr == lastslash)
8229 break;
8230 if (*unixptr == '/') /* The slash */
8231 unixptr++;
8232 }
8233 if (unixptr == lastslash)
8234 break;
8235
8236 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8237 /* Not needed when VMS is pretending to be UNIX. */
8238
8239 /* Is this loop stuck because of too many dots? */
8240 if (loop_flag == 0) {
8241 /* Exit the loop and pass the rest through */
8242 break;
8243 }
8244 }
8245
8246 /* Are we done with directories yet? */
8247 if (unixptr >= lastslash) {
8248
8249 /* Watch out for trailing dots */
8250 if (dir_dot != 0) {
8251 vmslen --;
8252 vmsptr--;
8253 }
8254 *vmsptr++ = ']';
8255 vmslen++;
8256 dash_flag = 0;
8257 dir_start = 0;
8258 if (*unixptr == '/')
8259 unixptr++;
8260 }
8261 else {
8262 /* Have we stopped backing up? */
8263 if (dash_flag) {
8264 *vmsptr++ = '.';
8265 vmslen++;
8266 dash_flag = 0;
8267 /* dir_start continues to be = 1 */
8268 }
8269 if (*unixptr == '-') {
8270 *vmsptr++ = '^';
8271 *vmsptr++ = *unixptr++;
8272 vmslen += 2;
8273 dir_start = 0;
8274
8275 /* Now are we done with directories yet? */
8276 if (unixptr >= lastslash) {
8277
8278 /* Watch out for trailing dots */
8279 if (dir_dot != 0) {
8280 vmslen --;
8281 vmsptr--;
8282 }
8283
8284 *vmsptr++ = ']';
8285 vmslen++;
8286 dash_flag = 0;
8287 dir_start = 0;
8288 }
8289 }
8290 }
8291 }
8292
8293 /* All done? */
360732b5 8294 if (unixptr >= unixend)
2497a41f
JM
8295 break;
8296
8297 /* Normal characters - More EFS work probably needed */
8298 dir_start = 0;
8299 dir_dot = 0;
8300
8301 switch(*unixptr) {
8302 case '/':
8303 /* remove multiple / */
8304 while (unixptr[1] == '/') {
8305 unixptr++;
8306 }
8307 if (unixptr == lastslash) {
8308 /* Watch out for trailing dots */
8309 if (dir_dot != 0) {
8310 vmslen --;
8311 vmsptr--;
8312 }
8313 *vmsptr++ = ']';
8314 }
8315 else {
8316 dir_start = 1;
8317 *vmsptr++ = '.';
8318 dir_dot = 1;
8319
8320 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8321 /* Not needed when VMS is pretending to be UNIX. */
8322
8323 }
8324 dash_flag = 0;
360732b5 8325 if (unixptr != unixend)
2497a41f
JM
8326 unixptr++;
8327 vmslen++;
8328 break;
2497a41f 8329 case '.':
360732b5
JM
8330 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8331 (&unixptr[1] == unixend)) {
2497a41f
JM
8332 *vmsptr++ = '^';
8333 *vmsptr++ = '.';
8334 vmslen += 2;
8335 unixptr++;
8336
8337 /* trailing dot ==> '^..' on VMS */
360732b5 8338 if (unixptr == unixend) {
2497a41f
JM
8339 *vmsptr++ = '.';
8340 vmslen++;
360732b5 8341 unixptr++;
2497a41f 8342 }
2497a41f
JM
8343 break;
8344 }
360732b5 8345
2497a41f 8346 *vmsptr++ = *unixptr++;
360732b5
JM
8347 vmslen ++;
8348 break;
8349 case '"':
8350 if (quoted && (&unixptr[1] == unixend)) {
8351 unixptr++;
8352 break;
8353 }
8354 in_cnt = copy_expand_unix_filename_escape
8355 (vmsptr, unixptr, &out_cnt, utf8_fl);
8356 vmsptr += out_cnt;
8357 unixptr += in_cnt;
2497a41f
JM
8358 break;
8359 case '~':
8360 case ';':
8361 case '\\':
360732b5
JM
8362 case '?':
8363 case ' ':
2497a41f 8364 default:
360732b5
JM
8365 in_cnt = copy_expand_unix_filename_escape
8366 (vmsptr, unixptr, &out_cnt, utf8_fl);
8367 vmsptr += out_cnt;
8368 unixptr += in_cnt;
2497a41f
JM
8369 break;
8370 }
8371 }
8372
8373 /* Make sure directory is closed */
8374 if (unixptr == lastslash) {
8375 char *vmsptr2;
8376 vmsptr2 = vmsptr - 1;
8377
8378 if (*vmsptr2 != ']') {
8379 *vmsptr2--;
8380
8381 /* directories do not end in a dot bracket */
8382 if (*vmsptr2 == '.') {
8383 vmsptr2--;
8384
8385 /* ^. is allowed */
8386 if (*vmsptr2 != '^') {
8387 vmsptr--; /* back up over the dot */
8388 }
8389 }
8390 *vmsptr++ = ']';
8391 }
8392 }
8393 else {
8394 char *vmsptr2;
8395 /* Add a trailing dot if a file with no extension */
8396 vmsptr2 = vmsptr - 1;
360732b5
JM
8397 if ((vmslen > 1) &&
8398 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
30e68285 8399 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
2497a41f
JM
8400 *vmsptr++ = '.';
8401 vmslen++;
8402 }
8403 }
8404
8405 *vmsptr = '\0';
8406 return SS$_NORMAL;
8407}
8408#endif
8409
360732b5
JM
8410 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8411static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8412{
8413char * result;
8414int utf8_flag;
8415
8416 /* If a UTF8 flag is being passed, honor it */
8417 utf8_flag = 0;
8418 if (utf8_fl != NULL) {
8419 utf8_flag = *utf8_fl;
8420 *utf8_fl = 0;
8421 }
8422
8423 if (utf8_flag) {
8424 /* If there is a possibility of UTF8, then if any UTF8 characters
8425 are present, then they must be converted to VTF-7
8426 */
8427 result = strcpy(rslt, path); /* FIX-ME */
8428 }
8429 else
8430 result = strcpy(rslt, path);
8431
8432 return result;
8433}
8434
8435
df278665 8436
360732b5 8437/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
df278665
JM
8438static char *int_tovmsspec
8439 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8440 char *dirend;
f7ddb74a 8441 char *lastdot;
b8ffc8df
RGS
8442 register char *cp1;
8443 const char *cp2;
e518068a 8444 unsigned long int infront = 0, hasdir = 1;
f7ddb74a
JM
8445 int rslt_len;
8446 int no_type_seen;
360732b5
JM
8447 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8448 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
a0d0e21e 8449
df278665
JM
8450 if (vms_debug_fileify) {
8451 if (path == NULL)
8452 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8453 else
8454 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8455 }
8456
8457 if (path == NULL) {
8458 /* If we fail, we should be setting errno */
8459 set_errno(EINVAL);
8460 set_vaxc_errno(SS$_BADPARAM);
8461 return NULL;
8462 }
4d743a9b 8463 rslt_len = VMS_MAXRSS-1;
360732b5
JM
8464
8465 /* '.' and '..' are "[]" and "[-]" for a quick check */
8466 if (path[0] == '.') {
8467 if (path[1] == '\0') {
8468 strcpy(rslt,"[]");
8469 if (utf8_flag != NULL)
8470 *utf8_flag = 0;
8471 return rslt;
8472 }
8473 else {
8474 if (path[1] == '.' && path[2] == '\0') {
8475 strcpy(rslt,"[-]");
8476 if (utf8_flag != NULL)
8477 *utf8_flag = 0;
8478 return rslt;
8479 }
8480 }
a0d0e21e 8481 }
f7ddb74a 8482
2497a41f
JM
8483 /* Posix specifications are now a native VMS format */
8484 /*--------------------------------------------------*/
8485#if __CRTL_VER >= 80200000 && !defined(__VAX)
8486 if (decc_posix_compliant_pathnames) {
8487 if (strncmp(path,"\"^UP^",5) == 0) {
360732b5 8488 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
2497a41f
JM
8489 return rslt;
8490 }
8491 }
8492#endif
8493
360732b5
JM
8494 /* This is really the only way to see if this is already in VMS format */
8495 sts = vms_split_path
8496 (path,
8497 &v_spec,
8498 &v_len,
8499 &r_spec,
8500 &r_len,
8501 &d_spec,
8502 &d_len,
8503 &n_spec,
8504 &n_len,
8505 &e_spec,
8506 &e_len,
8507 &vs_spec,
8508 &vs_len);
8509 if (sts == 0) {
8510 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8511 replacement, because the above parse just took care of most of
8512 what is needed to do vmspath when the specification is already
8513 in VMS format.
8514
8515 And if it is not already, it is easier to do the conversion as
8516 part of this routine than to call this routine and then work on
8517 the result.
8518 */
2497a41f 8519
360732b5
JM
8520 /* If VMS punctuation was found, it is already VMS format */
8521 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8522 if (utf8_flag != NULL)
8523 *utf8_flag = 0;
8524 strcpy(rslt, path);
df278665
JM
8525 if (vms_debug_fileify) {
8526 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8527 }
360732b5
JM
8528 return rslt;
8529 }
8530 /* Now, what to do with trailing "." cases where there is no
8531 extension? If this is a UNIX specification, and EFS characters
8532 are enabled, then the trailing "." should be converted to a "^.".
8533 But if this was already a VMS specification, then it should be
8534 left alone.
2497a41f 8535
360732b5
JM
8536 So in the case of ambiguity, leave the specification alone.
8537 */
2497a41f 8538
2497a41f 8539
360732b5
JM
8540 /* If there is a possibility of UTF8, then if any UTF8 characters
8541 are present, then they must be converted to VTF-7
8542 */
8543 if (utf8_flag != NULL)
8544 *utf8_flag = 0;
8545 strcpy(rslt, path);
df278665
JM
8546 if (vms_debug_fileify) {
8547 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8548 }
2497a41f
JM
8549 return rslt;
8550 }
8551
360732b5
JM
8552 dirend = strrchr(path,'/');
8553
8554 if (dirend == NULL) {
df278665
JM
8555 char *macro_start;
8556 int has_macro;
8557
360732b5
JM
8558 /* If we get here with no UNIX directory delimiters, then this is
8559 not a complete file specification, either garbage a UNIX glob
8560 specification that can not be converted to a VMS wildcard, or
df278665
JM
8561 it a UNIX shell macro. MakeMaker wants shell macros passed
8562 through AS-IS,
360732b5
JM
8563
8564 utf8 flag setting needs to be preserved.
8565 */
df278665
JM
8566 hasdir = 0;
8567
8568 has_macro = 0;
8569 macro_start = strchr(path,'$');
8570 if (macro_start != NULL) {
8571 if (macro_start[1] == '(') {
8572 has_macro = 1;
8573 }
8574 }
8575 if ((decc_efs_charset == 0) || (has_macro)) {
8576 strcpy(rslt, path);
8577 if (vms_debug_fileify) {
8578 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8579 }
8580 return rslt;
8581 }
360732b5
JM
8582 }
8583
30e68285 8584/* If EFS charset mode active, handle the conversion */
2497a41f 8585#if __CRTL_VER >= 80200000 && !defined(__VAX)
360732b5
JM
8586 if (decc_efs_charset) {
8587 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
df278665
JM
8588 if (vms_debug_fileify) {
8589 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8590 }
2497a41f
JM
8591 return rslt;
8592 }
8593#endif
f7ddb74a 8594
f86702cc 8595 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
748a9306
LW
8596 if (!*(dirend+2)) dirend +=2;
8597 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
360732b5
JM
8598 if (decc_efs_charset == 0) {
8599 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8600 }
748a9306 8601 }
f7ddb74a 8602
a0d0e21e
LW
8603 cp1 = rslt;
8604 cp2 = path;
f7ddb74a 8605 lastdot = strrchr(cp2,'.');
a0d0e21e 8606 if (*cp2 == '/') {
a480973c 8607 char *trndev;
e518068a 8608 int islnm, rooted;
8609 STRLEN trnend;
8610
b7ae7a0d 8611 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
61bb5906 8612 if (!*(cp2+1)) {
f7ddb74a
JM
8613 if (decc_disable_posix_root) {
8614 strcpy(rslt,"sys$disk:[000000]");
8615 }
8616 else {
8617 strcpy(rslt,"sys$posix_root:[000000]");
8618 }
360732b5
JM
8619 if (utf8_flag != NULL)
8620 *utf8_flag = 0;
df278665
JM
8621 if (vms_debug_fileify) {
8622 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8623 }
61bb5906
CB
8624 return rslt;
8625 }
a0d0e21e 8626 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
e518068a 8627 *cp1 = '\0';
c5375c28 8628 trndev = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 8629 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
b8486b9d 8630 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8631
8632 /* DECC special handling */
8633 if (!islnm) {
8634 if (strcmp(rslt,"bin") == 0) {
8635 strcpy(rslt,"sys$system");
8636 cp1 = rslt + 10;
8637 *cp1 = 0;
b8486b9d 8638 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8639 }
8640 else if (strcmp(rslt,"tmp") == 0) {
8641 strcpy(rslt,"sys$scratch");
8642 cp1 = rslt + 11;
8643 *cp1 = 0;
b8486b9d 8644 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8645 }
8646 else if (!decc_disable_posix_root) {
8647 strcpy(rslt, "sys$posix_root");
b8486b9d 8648 cp1 = rslt + 14;
f7ddb74a
JM
8649 *cp1 = 0;
8650 cp2 = path;
8651 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
b8486b9d 8652 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8653 }
8654 else if (strcmp(rslt,"dev") == 0) {
8655 if (strncmp(cp2,"/null", 5) == 0) {
8656 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8657 strcpy(rslt,"NLA0");
8658 cp1 = rslt + 4;
8659 *cp1 = 0;
8660 cp2 = cp2 + 5;
b8486b9d 8661 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8662 }
8663 }
8664 }
8665 }
8666
e518068a 8667 trnend = islnm ? strlen(trndev) - 1 : 0;
8668 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8669 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8670 /* If the first element of the path is a logical name, determine
8671 * whether it has to be translated so we can add more directories. */
8672 if (!islnm || rooted) {
8673 *(cp1++) = ':';
8674 *(cp1++) = '[';
8675 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8676 else cp2++;
8677 }
8678 else {
8679 if (cp2 != dirend) {
e518068a 8680 strcpy(rslt,trndev);
8681 cp1 = rslt + trnend;
755b3d5d
JM
8682 if (*cp2 != 0) {
8683 *(cp1++) = '.';
8684 cp2++;
8685 }
e518068a 8686 }
8687 else {
f7ddb74a
JM
8688 if (decc_disable_posix_root) {
8689 *(cp1++) = ':';
8690 hasdir = 0;
8691 }
e518068a 8692 }
8693 }
367e4b85 8694 PerlMem_free(trndev);
748a9306 8695 }
a0d0e21e
LW
8696 else {
8697 *(cp1++) = '[';
748a9306
LW
8698 if (*cp2 == '.') {
8699 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8700 cp2 += 2; /* skip over "./" - it's redundant */
8701 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8702 }
8703 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8704 *(cp1++) = '-'; /* "../" --> "-" */
8705 cp2 += 3;
8706 }
f86702cc 8707 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8708 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8709 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8710 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8711 cp2 += 4;
8712 }
f7ddb74a
JM
8713 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8714 /* Escape the extra dots in EFS file specifications */
8715 *(cp1++) = '^';
8716 }
748a9306
LW
8717 if (cp2 > dirend) cp2 = dirend;
8718 }
8719 else *(cp1++) = '.';
8720 }
8721 for (; cp2 < dirend; cp2++) {
8722 if (*cp2 == '/') {
01b8edb6 8723 if (*(cp2-1) == '/') continue;
748a9306
LW
8724 if (*(cp1-1) != '.') *(cp1++) = '.';
8725 infront = 0;
8726 }
8727 else if (!infront && *cp2 == '.') {
01b8edb6 8728 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8729 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
fd7385b9
CB
8730 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8731 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
748a9306 8732 else if (*(cp1-2) == '[') *(cp1-1) = '-';
fd7385b9
CB
8733 else { /* back up over previous directory name */
8734 cp1--;
8735 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8736 if (*(cp1-1) == '[') {
8737 memcpy(cp1,"000000.",7);
8738 cp1 += 7;
8739 }
748a9306
LW
8740 }
8741 cp2 += 2;
01b8edb6 8742 if (cp2 == dirend) break;
748a9306 8743 }
f86702cc 8744 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8745 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8746 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8747 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8748 if (!*(cp2+3)) {
8749 *(cp1++) = '.'; /* Simulate trailing '/' */
8750 cp2 += 2; /* for loop will incr this to == dirend */
8751 }
8752 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8753 }
f7ddb74a
JM
8754 else {
8755 if (decc_efs_charset == 0)
8756 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8757 else {
8758 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8759 *(cp1++) = '.';
8760 }
8761 }
748a9306
LW
8762 }
8763 else {
e518068a 8764 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
f7ddb74a
JM
8765 if (*cp2 == '.') {
8766 if (decc_efs_charset == 0)
8767 *(cp1++) = '_';
8768 else {
8769 *(cp1++) = '^';
8770 *(cp1++) = '.';
8771 }
8772 }
748a9306
LW
8773 else *(cp1++) = *cp2;
8774 infront = 1;
8775 }
a0d0e21e 8776 }
748a9306 8777 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
e518068a 8778 if (hasdir) *(cp1++) = ']';
748a9306 8779 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
f7ddb74a
JM
8780 /* fixme for ODS5 */
8781 no_type_seen = 0;
8782 if (cp2 > lastdot)
8783 no_type_seen = 1;
8784 while (*cp2) {
8785 switch(*cp2) {
8786 case '?':
360732b5
JM
8787 if (decc_efs_charset == 0)
8788 *(cp1++) = '%';
8789 else
8790 *(cp1++) = '?';
f7ddb74a
JM
8791 cp2++;
8792 case ' ':
8793 *(cp1)++ = '^';
8794 *(cp1)++ = '_';
8795 cp2++;
8796 break;
8797 case '.':
8798 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8799 decc_readdir_dropdotnotype) {
8800 *(cp1)++ = '^';
8801 *(cp1)++ = '.';
8802 cp2++;
8803
8804 /* trailing dot ==> '^..' on VMS */
8805 if (*cp2 == '\0') {
8806 *(cp1++) = '.';
8807 no_type_seen = 0;
8808 }
8809 }
8810 else {
8811 *(cp1++) = *(cp2++);
8812 no_type_seen = 0;
8813 }
8814 break;
360732b5
JM
8815 case '$':
8816 /* This could be a macro to be passed through */
8817 *(cp1++) = *(cp2++);
8818 if (*cp2 == '(') {
8819 const char * save_cp2;
8820 char * save_cp1;
8821 int is_macro;
8822
8823 /* paranoid check */
8824 save_cp2 = cp2;
8825 save_cp1 = cp1;
8826 is_macro = 0;
8827
8828 /* Test through */
8829 *(cp1++) = *(cp2++);
8830 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8831 *(cp1++) = *(cp2++);
8832 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8833 *(cp1++) = *(cp2++);
8834 }
8835 if (*cp2 == ')') {
8836 *(cp1++) = *(cp2++);
8837 is_macro = 1;
8838 }
8839 }
8840 if (is_macro == 0) {
8841 /* Not really a macro - never mind */
8842 cp2 = save_cp2;
8843 cp1 = save_cp1;
8844 }
8845 }
8846 break;
f7ddb74a
JM
8847 case '\"':
8848 case '~':
8849 case '`':
8850 case '!':
8851 case '#':
8852 case '%':
8853 case '^':
adc11f0b
CB
8854 /* Don't escape again if following character is
8855 * already something we escape.
8856 */
8857 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8858 *(cp1++) = *(cp2++);
8859 break;
8860 }
8861 /* But otherwise fall through and escape it. */
f7ddb74a
JM
8862 case '&':
8863 case '(':
8864 case ')':
8865 case '=':
8866 case '+':
8867 case '\'':
8868 case '@':
8869 case '[':
8870 case ']':
8871 case '{':
8872 case '}':
8873 case ':':
8874 case '\\':
8875 case '|':
8876 case '<':
8877 case '>':
8878 *(cp1++) = '^';
8879 *(cp1++) = *(cp2++);
8880 break;
8881 case ';':
8882 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
657054d4 8883 * which is wrong. UNIX notation should be ".dir." unless
f7ddb74a
JM
8884 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8885 * changing this behavior could break more things at this time.
2497a41f
JM
8886 * efs character set effectively does not allow "." to be a version
8887 * delimiter as a further complication about changing this.
f7ddb74a
JM
8888 */
8889 if (decc_filename_unix_report != 0) {
8890 *(cp1++) = '^';
8891 }
8892 *(cp1++) = *(cp2++);
8893 break;
8894 default:
8895 *(cp1++) = *(cp2++);
8896 }
8897 }
8898 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8899 char *lcp1;
8900 lcp1 = cp1;
8901 lcp1--;
8902 /* Fix me for "^]", but that requires making sure that you do
8903 * not back up past the start of the filename
8904 */
8905 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8906 *cp1++ = '.';
8907 }
a0d0e21e
LW
8908 *cp1 = '\0';
8909
360732b5
JM
8910 if (utf8_flag != NULL)
8911 *utf8_flag = 0;
df278665
JM
8912 if (vms_debug_fileify) {
8913 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8914 }
a0d0e21e
LW
8915 return rslt;
8916
df278665
JM
8917} /* end of int_tovmsspec() */
8918
8919
8920/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8921static char *mp_do_tovmsspec
8922 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8923 static char __tovmsspec_retbuf[VMS_MAXRSS];
8924 char * vmsspec, *ret_spec, *ret_buf;
8925
8926 vmsspec = NULL;
8927 ret_buf = buf;
8928 if (ret_buf == NULL) {
8929 if (ts) {
8930 Newx(vmsspec, VMS_MAXRSS, char);
8931 if (vmsspec == NULL)
8932 _ckvmssts(SS$_INSFMEM);
8933 ret_buf = vmsspec;
8934 } else {
8935 ret_buf = __tovmsspec_retbuf;
8936 }
8937 }
8938
8939 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8940
8941 if (ret_spec == NULL) {
8942 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8943 if (vmsspec)
8944 Safefree(vmsspec);
8945 }
8946
8947 return ret_spec;
8948
8949} /* end of mp_do_tovmsspec() */
a0d0e21e
LW
8950/*}}}*/
8951/* External entry points */
360732b5
JM
8952char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8953 { return do_tovmsspec(path,buf,0,NULL); }
8954char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8955 { return do_tovmsspec(path,buf,1,NULL); }
8956char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8957 { return do_tovmsspec(path,buf,0,utf8_fl); }
8958char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8959 { return do_tovmsspec(path,buf,1,utf8_fl); }
8960
4846f1d7 8961/*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
94ae10c0 8962/* Internal routine for use with out an explicit context present */
4846f1d7
JM
8963static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8964
8965 char * ret_spec, *pathified;
8966
8967 if (path == NULL)
8968 return NULL;
8969
8970 pathified = PerlMem_malloc(VMS_MAXRSS);
8971 if (pathified == NULL)
8972 _ckvmssts_noperl(SS$_INSFMEM);
8973
8974 ret_spec = int_pathify_dirspec(path, pathified);
8975
8976 if (ret_spec == NULL) {
8977 PerlMem_free(pathified);
8978 return NULL;
8979 }
8980
8981 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8982
8983 PerlMem_free(pathified);
8984 return ret_spec;
8985
8986}
8987
360732b5
JM
8988/*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8989static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 8990 static char __tovmspath_retbuf[VMS_MAXRSS];
a0d0e21e 8991 int vmslen;
a480973c 8992 char *pathified, *vmsified, *cp;
a0d0e21e 8993
748a9306 8994 if (path == NULL) return NULL;
c5375c28
JM
8995 pathified = PerlMem_malloc(VMS_MAXRSS);
8996 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 8997 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 8998 PerlMem_free(pathified);
a480973c
JM
8999 return NULL;
9000 }
c5375c28
JM
9001
9002 vmsified = NULL;
9003 if (buf == NULL)
9004 Newx(vmsified, VMS_MAXRSS, char);
360732b5 9005 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
c5375c28
JM
9006 PerlMem_free(pathified);
9007 if (vmsified) Safefree(vmsified);
a480973c
JM
9008 return NULL;
9009 }
c5375c28 9010 PerlMem_free(pathified);
a480973c 9011 if (buf) {
a480973c
JM
9012 return buf;
9013 }
a0d0e21e
LW
9014 else if (ts) {
9015 vmslen = strlen(vmsified);
a02a5408 9016 Newx(cp,vmslen+1,char);
a0d0e21e
LW
9017 memcpy(cp,vmsified,vmslen);
9018 cp[vmslen] = '\0';
a480973c 9019 Safefree(vmsified);
a0d0e21e
LW
9020 return cp;
9021 }
9022 else {
9023 strcpy(__tovmspath_retbuf,vmsified);
a480973c 9024 Safefree(vmsified);
a0d0e21e
LW
9025 return __tovmspath_retbuf;
9026 }
9027
9028} /* end of do_tovmspath() */
9029/*}}}*/
9030/* External entry points */
360732b5
JM
9031char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9032 { return do_tovmspath(path,buf,0, NULL); }
9033char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9034 { return do_tovmspath(path,buf,1, NULL); }
9035char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9036 { return do_tovmspath(path,buf,0,utf8_fl); }
9037char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9038 { return do_tovmspath(path,buf,1,utf8_fl); }
9039
9040
9041/*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9042static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 9043 static char __tounixpath_retbuf[VMS_MAXRSS];
a0d0e21e 9044 int unixlen;
a480973c 9045 char *pathified, *unixified, *cp;
a0d0e21e 9046
748a9306 9047 if (path == NULL) return NULL;
c5375c28
JM
9048 pathified = PerlMem_malloc(VMS_MAXRSS);
9049 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 9050 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 9051 PerlMem_free(pathified);
a480973c
JM
9052 return NULL;
9053 }
c5375c28
JM
9054
9055 unixified = NULL;
9056 if (buf == NULL) {
9057 Newx(unixified, VMS_MAXRSS, char);
9058 }
360732b5 9059 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
c5375c28
JM
9060 PerlMem_free(pathified);
9061 if (unixified) Safefree(unixified);
a480973c
JM
9062 return NULL;
9063 }
c5375c28 9064 PerlMem_free(pathified);
a480973c 9065 if (buf) {
a480973c
JM
9066 return buf;
9067 }
a0d0e21e
LW
9068 else if (ts) {
9069 unixlen = strlen(unixified);
a02a5408 9070 Newx(cp,unixlen+1,char);
a0d0e21e
LW
9071 memcpy(cp,unixified,unixlen);
9072 cp[unixlen] = '\0';
a480973c 9073 Safefree(unixified);
a0d0e21e
LW
9074 return cp;
9075 }
9076 else {
9077 strcpy(__tounixpath_retbuf,unixified);
a480973c 9078 Safefree(unixified);
a0d0e21e
LW
9079 return __tounixpath_retbuf;
9080 }
9081
9082} /* end of do_tounixpath() */
9083/*}}}*/
9084/* External entry points */
360732b5
JM
9085char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9086 { return do_tounixpath(path,buf,0,NULL); }
9087char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9088 { return do_tounixpath(path,buf,1,NULL); }
9089char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9090 { return do_tounixpath(path,buf,0,utf8_fl); }
9091char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9092 { return do_tounixpath(path,buf,1,utf8_fl); }
a0d0e21e
LW
9093
9094/*
cbb8049c 9095 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
9096 *
9097 *****************************************************************************
9098 * *
cbb8049c 9099 * Copyright (C) 1989-1994, 2007 by *
a0d0e21e
LW
9100 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9101 * *
cbb8049c
MP
9102 * Permission is hereby granted for the reproduction of this software *
9103 * on condition that this copyright notice is included in source *
9104 * distributions of the software. The code may be modified and *
9105 * distributed under the same terms as Perl itself. *
a0d0e21e
LW
9106 * *
9107 * 27-Aug-1994 Modified for inclusion in perl5 *
cbb8049c 9108 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
a0d0e21e
LW
9109 *****************************************************************************
9110 */
9111
9112/*
9113 * getredirection() is intended to aid in porting C programs
9114 * to VMS (Vax-11 C). The native VMS environment does not support
9115 * '>' and '<' I/O redirection, or command line wild card expansion,
9116 * or a command line pipe mechanism using the '|' AND background
9117 * command execution '&'. All of these capabilities are provided to any
9118 * C program which calls this procedure as the first thing in the
9119 * main program.
9120 * The piping mechanism will probably work with almost any 'filter' type
9121 * of program. With suitable modification, it may useful for other
9122 * portability problems as well.
9123 *
cbb8049c 9124 * Author: Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
9125 */
9126struct list_item
9127 {
9128 struct list_item *next;
9129 char *value;
9130 };
9131
9132static void add_item(struct list_item **head,
9133 struct list_item **tail,
9134 char *value,
9135 int *count);
9136
4b19af01
CB
9137static void mp_expand_wild_cards(pTHX_ char *item,
9138 struct list_item **head,
9139 struct list_item **tail,
9140 int *count);
a0d0e21e 9141
8df869cb 9142static int background_process(pTHX_ int argc, char **argv);
a0d0e21e 9143
fd8cd3a3 9144static void pipe_and_fork(pTHX_ char **cmargv);
a0d0e21e
LW
9145
9146/*{{{ void getredirection(int *ac, char ***av)*/
84902520 9147static void
4b19af01 9148mp_getredirection(pTHX_ int *ac, char ***av)
a0d0e21e
LW
9149/*
9150 * Process vms redirection arg's. Exit if any error is seen.
9151 * If getredirection() processes an argument, it is erased
9152 * from the vector. getredirection() returns a new argc and argv value.
9153 * In the event that a background command is requested (by a trailing "&"),
9154 * this routine creates a background subprocess, and simply exits the program.
9155 *
9156 * Warning: do not try to simplify the code for vms. The code
9157 * presupposes that getredirection() is called before any data is
9158 * read from stdin or written to stdout.
9159 *
9160 * Normal usage is as follows:
9161 *
9162 * main(argc, argv)
9163 * int argc;
9164 * char *argv[];
9165 * {
9166 * getredirection(&argc, &argv);
9167 * }
9168 */
9169{
9170 int argc = *ac; /* Argument Count */
9171 char **argv = *av; /* Argument Vector */
9172 char *ap; /* Argument pointer */
9173 int j; /* argv[] index */
9174 int item_count = 0; /* Count of Items in List */
9175 struct list_item *list_head = 0; /* First Item in List */
9176 struct list_item *list_tail; /* Last Item in List */
9177 char *in = NULL; /* Input File Name */
9178 char *out = NULL; /* Output File Name */
9179 char *outmode = "w"; /* Mode to Open Output File */
9180 char *err = NULL; /* Error File Name */
9181 char *errmode = "w"; /* Mode to Open Error File */
9182 int cmargc = 0; /* Piped Command Arg Count */
9183 char **cmargv = NULL;/* Piped Command Arg Vector */
a0d0e21e
LW
9184
9185 /*
9186 * First handle the case where the last thing on the line ends with
9187 * a '&'. This indicates the desire for the command to be run in a
9188 * subprocess, so we satisfy that desire.
9189 */
9190 ap = argv[argc-1];
9191 if (0 == strcmp("&", ap))
8c3eed29 9192 exit(background_process(aTHX_ --argc, argv));
e518068a 9193 if (*ap && '&' == ap[strlen(ap)-1])
a0d0e21e
LW
9194 {
9195 ap[strlen(ap)-1] = '\0';
8c3eed29 9196 exit(background_process(aTHX_ argc, argv));
a0d0e21e
LW
9197 }
9198 /*
9199 * Now we handle the general redirection cases that involve '>', '>>',
9200 * '<', and pipes '|'.
9201 */
9202 for (j = 0; j < argc; ++j)
9203 {
9204 if (0 == strcmp("<", argv[j]))
9205 {
9206 if (j+1 >= argc)
9207 {
fd71b04b 9208 fprintf(stderr,"No input file after < on command line");
748a9306 9209 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9210 }
9211 in = argv[++j];
9212 continue;
9213 }
9214 if ('<' == *(ap = argv[j]))
9215 {
9216 in = 1 + ap;
9217 continue;
9218 }
9219 if (0 == strcmp(">", ap))
9220 {
9221 if (j+1 >= argc)
9222 {
fd71b04b 9223 fprintf(stderr,"No output file after > on command line");
748a9306 9224 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9225 }
9226 out = argv[++j];
9227 continue;
9228 }
9229 if ('>' == *ap)
9230 {
9231 if ('>' == ap[1])
9232 {
9233 outmode = "a";
9234 if ('\0' == ap[2])
9235 out = argv[++j];
9236 else
9237 out = 2 + ap;
9238 }
9239 else
9240 out = 1 + ap;
9241 if (j >= argc)
9242 {
fd71b04b 9243 fprintf(stderr,"No output file after > or >> on command line");
748a9306 9244 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9245 }
9246 continue;
9247 }
9248 if (('2' == *ap) && ('>' == ap[1]))
9249 {
9250 if ('>' == ap[2])
9251 {
9252 errmode = "a";
9253 if ('\0' == ap[3])
9254 err = argv[++j];
9255 else
9256 err = 3 + ap;
9257 }
9258 else
9259 if ('\0' == ap[2])
9260 err = argv[++j];
9261 else
748a9306 9262 err = 2 + ap;
a0d0e21e
LW
9263 if (j >= argc)
9264 {
fd71b04b 9265 fprintf(stderr,"No output file after 2> or 2>> on command line");
748a9306 9266 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9267 }
9268 continue;
9269 }
9270 if (0 == strcmp("|", argv[j]))
9271 {
9272 if (j+1 >= argc)
9273 {
fd71b04b 9274 fprintf(stderr,"No command into which to pipe on command line");
748a9306 9275 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9276 }
9277 cmargc = argc-(j+1);
9278 cmargv = &argv[j+1];
9279 argc = j;
9280 continue;
9281 }
9282 if ('|' == *(ap = argv[j]))
9283 {
9284 ++argv[j];
9285 cmargc = argc-j;
9286 cmargv = &argv[j];
9287 argc = j;
9288 continue;
9289 }
9290 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9291 }
9292 /*
9293 * Allocate and fill in the new argument vector, Some Unix's terminate
9294 * the list with an extra null pointer.
9295 */
e0ef6b43 9296 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
c5375c28 9297 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9298 *av = argv;
9299 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9300 argv[j] = list_head->value;
9301 *ac = item_count;
9302 if (cmargv != NULL)
9303 {
9304 if (out != NULL)
9305 {
fd71b04b 9306 fprintf(stderr,"'|' and '>' may not both be specified on command line");
748a9306 9307 exit(LIB$_INVARGORD);
a0d0e21e 9308 }
fd8cd3a3 9309 pipe_and_fork(aTHX_ cmargv);
a0d0e21e
LW
9310 }
9311
9312 /* Check for input from a pipe (mailbox) */
9313
a5f75d66 9314 if (in == NULL && 1 == isapipe(0))
a0d0e21e
LW
9315 {
9316 char mbxname[L_tmpnam];
9317 long int bufsize;
9318 long int dvi_item = DVI$_DEVBUFSIZ;
9319 $DESCRIPTOR(mbxnam, "");
9320 $DESCRIPTOR(mbxdevnam, "");
9321
9322 /* Input from a pipe, reopen it in binary mode to disable */
9323 /* carriage control processing. */
9324
bf8d1304 9325 fgetname(stdin, mbxname, 1);
a0d0e21e
LW
9326 mbxnam.dsc$a_pointer = mbxname;
9327 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9328 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9329 mbxdevnam.dsc$a_pointer = mbxname;
9330 mbxdevnam.dsc$w_length = sizeof(mbxname);
9331 dvi_item = DVI$_DEVNAM;
9332 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9333 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
748a9306
LW
9334 set_errno(0);
9335 set_vaxc_errno(1);
a0d0e21e
LW
9336 freopen(mbxname, "rb", stdin);
9337 if (errno != 0)
9338 {
fd71b04b 9339 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
748a9306 9340 exit(vaxc$errno);
a0d0e21e
LW
9341 }
9342 }
9343 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9344 {
fd71b04b 9345 fprintf(stderr,"Can't open input file %s as stdin",in);
748a9306 9346 exit(vaxc$errno);
a0d0e21e
LW
9347 }
9348 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9349 {
fd71b04b 9350 fprintf(stderr,"Can't open output file %s as stdout",out);
748a9306 9351 exit(vaxc$errno);
a0d0e21e 9352 }
fd8cd3a3 9353 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
0e06870b 9354
748a9306 9355 if (err != NULL) {
71d7ec5d 9356 if (strcmp(err,"&1") == 0) {
a15cef0c 9357 dup2(fileno(stdout), fileno(stderr));
fd8cd3a3 9358 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
71d7ec5d 9359 } else {
748a9306
LW
9360 FILE *tmperr;
9361 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9362 {
fd71b04b 9363 fprintf(stderr,"Can't open error file %s as stderr",err);
748a9306
LW
9364 exit(vaxc$errno);
9365 }
9366 fclose(tmperr);
a15cef0c 9367 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
748a9306
LW
9368 {
9369 exit(vaxc$errno);
9370 }
fd8cd3a3 9371 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
a0d0e21e 9372 }
71d7ec5d 9373 }
a0d0e21e 9374#ifdef ARGPROC_DEBUG
740ce14c 9375 PerlIO_printf(Perl_debug_log, "Arglist:\n");
a0d0e21e 9376 for (j = 0; j < *ac; ++j)
740ce14c 9377 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
a0d0e21e 9378#endif
b7ae7a0d 9379 /* Clear errors we may have hit expanding wildcards, so they don't
9380 show up in Perl's $! later */
9381 set_errno(0); set_vaxc_errno(1);
a0d0e21e
LW
9382} /* end of getredirection() */
9383/*}}}*/
9384
9385static void add_item(struct list_item **head,
9386 struct list_item **tail,
9387 char *value,
9388 int *count)
9389{
9390 if (*head == 0)
9391 {
e0ef6b43 9392 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9393 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9394 *tail = *head;
9395 }
9396 else {
e0ef6b43 9397 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9398 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9399 *tail = (*tail)->next;
9400 }
9401 (*tail)->value = value;
9402 ++(*count);
9403}
9404
4b19af01 9405static void mp_expand_wild_cards(pTHX_ char *item,
a0d0e21e
LW
9406 struct list_item **head,
9407 struct list_item **tail,
9408 int *count)
9409{
9410int expcount = 0;
748a9306 9411unsigned long int context = 0;
a0d0e21e 9412int isunix = 0;
773da73d 9413int item_len = 0;
a0d0e21e
LW
9414char *had_version;
9415char *had_device;
9416int had_directory;
f675dbe5 9417char *devdir,*cp;
a480973c 9418char *vmsspec;
a0d0e21e 9419$DESCRIPTOR(filespec, "");
748a9306 9420$DESCRIPTOR(defaultspec, "SYS$DISK:[]");
a0d0e21e 9421$DESCRIPTOR(resultspec, "");
a480973c
JM
9422unsigned long int lff_flags = 0;
9423int sts;
dca5a913 9424int rms_sts;
a480973c
JM
9425
9426#ifdef VMS_LONGNAME_SUPPORT
9427 lff_flags = LIB$M_FIL_LONG_NAMES;
9428#endif
a0d0e21e 9429
f675dbe5
CB
9430 for (cp = item; *cp; cp++) {
9431 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9432 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9433 }
9434 if (!*cp || isspace(*cp))
a0d0e21e
LW
9435 {
9436 add_item(head, tail, item, count);
9437 return;
9438 }
773da73d
JH
9439 else
9440 {
9441 /* "double quoted" wild card expressions pass as is */
9442 /* From DCL that means using e.g.: */
9443 /* perl program """perl.*""" */
9444 item_len = strlen(item);
9445 if ( '"' == *item && '"' == item[item_len-1] )
9446 {
9447 item++;
9448 item[item_len-2] = '\0';
9449 add_item(head, tail, item, count);
9450 return;
9451 }
9452 }
a0d0e21e
LW
9453 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9454 resultspec.dsc$b_class = DSC$K_CLASS_D;
9455 resultspec.dsc$a_pointer = NULL;
c5375c28
JM
9456 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9457 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
748a9306 9458 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
df278665 9459 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
a0d0e21e
LW
9460 if (!isunix || !filespec.dsc$a_pointer)
9461 filespec.dsc$a_pointer = item;
9462 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9463 /*
9464 * Only return version specs, if the caller specified a version
9465 */
9466 had_version = strchr(item, ';');
9467 /*
94ae10c0 9468 * Only return device and directory specs, if the caller specified either.
a0d0e21e
LW
9469 */
9470 had_device = strchr(item, ':');
9471 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9472
a480973c
JM
9473 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9474 (&filespec, &resultspec, &context,
dca5a913 9475 &defaultspec, 0, &rms_sts, &lff_flags)))
a0d0e21e
LW
9476 {
9477 char *string;
9478 char *c;
9479
c5375c28
JM
9480 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9481 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9482 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9483 string[resultspec.dsc$w_length] = '\0';
9484 if (NULL == had_version)
f7ddb74a 9485 *(strrchr(string, ';')) = '\0';
a0d0e21e
LW
9486 if ((!had_directory) && (had_device == NULL))
9487 {
9488 if (NULL == (devdir = strrchr(string, ']')))
9489 devdir = strrchr(string, '>');
9490 strcpy(string, devdir + 1);
9491 }
9492 /*
9493 * Be consistent with what the C RTL has already done to the rest of
9494 * the argv items and lowercase all of these names.
9495 */
f7ddb74a
JM
9496 if (!decc_efs_case_preserve) {
9497 for (c = string; *c; ++c)
a0d0e21e
LW
9498 if (isupper(*c))
9499 *c = tolower(*c);
f7ddb74a 9500 }
f86702cc 9501 if (isunix) trim_unixpath(string,item,1);
a0d0e21e
LW
9502 add_item(head, tail, string, count);
9503 ++expcount;
a480973c 9504 }
367e4b85 9505 PerlMem_free(vmsspec);
c07a80fd 9506 if (sts != RMS$_NMF)
9507 {
9508 set_vaxc_errno(sts);
9509 switch (sts)
9510 {
f282b18d 9511 case RMS$_FNF: case RMS$_DNF:
c07a80fd 9512 set_errno(ENOENT); break;
f282b18d
CB
9513 case RMS$_DIR:
9514 set_errno(ENOTDIR); break;
c07a80fd 9515 case RMS$_DEV:
9516 set_errno(ENODEV); break;
f282b18d 9517 case RMS$_FNM: case RMS$_SYN:
c07a80fd 9518 set_errno(EINVAL); break;
9519 case RMS$_PRV:
9520 set_errno(EACCES); break;
9521 default:
b7ae7a0d 9522 _ckvmssts_noperl(sts);
c07a80fd 9523 }
9524 }
a0d0e21e
LW
9525 if (expcount == 0)
9526 add_item(head, tail, item, count);
b7ae7a0d 9527 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9528 _ckvmssts_noperl(lib$find_file_end(&context));
a0d0e21e
LW
9529}
9530
9531static int child_st[2];/* Event Flag set when child process completes */
9532
748a9306 9533static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
a0d0e21e 9534
748a9306 9535static unsigned long int exit_handler(int *status)
a0d0e21e
LW
9536{
9537short iosb[4];
9538
9539 if (0 == child_st[0])
9540 {
9541#ifdef ARGPROC_DEBUG
740ce14c 9542 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
a0d0e21e
LW
9543#endif
9544 fflush(stdout); /* Have to flush pipe for binary data to */
9545 /* terminate properly -- <tp@mccall.com> */
9546 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9547 sys$dassgn(child_chan);
9548 fclose(stdout);
9549 sys$synch(0, child_st);
9550 }
9551 return(1);
9552}
9553
9554static void sig_child(int chan)
9555{
9556#ifdef ARGPROC_DEBUG
740ce14c 9557 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
a0d0e21e
LW
9558#endif
9559 if (child_st[0] == 0)
9560 child_st[0] = 1;
9561}
9562
748a9306 9563static struct exit_control_block exit_block =
a0d0e21e
LW
9564 {
9565 0,
9566 exit_handler,
9567 1,
9568 &exit_block.exit_status,
9569 0
9570 };
9571
ff7adb52
CL
9572static void
9573pipe_and_fork(pTHX_ char **cmargv)
a0d0e21e 9574{
ff7adb52 9575 PerlIO *fp;
218fdd94 9576 struct dsc$descriptor_s *vmscmd;
ff7adb52
CL
9577 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9578 int sts, j, l, ismcr, quote, tquote = 0;
9579
218fdd94
CL
9580 sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9581 vms_execfree(vmscmd);
ff7adb52
CL
9582
9583 j = l = 0;
9584 p = subcmd;
9585 q = cmargv[0];
9586 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9587 && toupper(*(q+2)) == 'R' && !*(q+3);
9588
9589 while (q && l < MAX_DCL_LINE_LENGTH) {
9590 if (!*q) {
9591 if (j > 0 && quote) {
9592 *p++ = '"';
9593 l++;
9594 }
9595 q = cmargv[++j];
9596 if (q) {
9597 if (ismcr && j > 1) quote = 1;
9598 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9599 *p++ = ' ';
9600 l++;
9601 if (quote || tquote) {
9602 *p++ = '"';
9603 l++;
9604 }
988c775c 9605 }
ff7adb52
CL
9606 } else {
9607 if ((quote||tquote) && *q == '"') {
9608 *p++ = '"';
9609 l++;
988c775c 9610 }
ff7adb52
CL
9611 *p++ = *q++;
9612 l++;
9613 }
9614 }
9615 *p = '\0';
a0d0e21e 9616
218fdd94 9617 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
4e205ed6 9618 if (fp == NULL) {
ff7adb52 9619 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
988c775c 9620 }
a0d0e21e
LW
9621}
9622
8df869cb 9623static int background_process(pTHX_ int argc, char **argv)
a0d0e21e 9624{
a480973c 9625char command[MAX_DCL_SYMBOL + 1] = "$";
a0d0e21e
LW
9626$DESCRIPTOR(value, "");
9627static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9628static $DESCRIPTOR(null, "NLA0:");
9629static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9630char pidstring[80];
9631$DESCRIPTOR(pidstr, "");
9632int pid;
748a9306 9633unsigned long int flags = 17, one = 1, retsts;
a480973c 9634int len;
a0d0e21e
LW
9635
9636 strcat(command, argv[0]);
a480973c
JM
9637 len = strlen(command);
9638 while (--argc && (len < MAX_DCL_SYMBOL))
a0d0e21e
LW
9639 {
9640 strcat(command, " \"");
9641 strcat(command, *(++argv));
9642 strcat(command, "\"");
a480973c 9643 len = strlen(command);
a0d0e21e
LW
9644 }
9645 value.dsc$a_pointer = command;
9646 value.dsc$w_length = strlen(value.dsc$a_pointer);
b7ae7a0d 9647 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
748a9306
LW
9648 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9649 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
b7ae7a0d 9650 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
748a9306
LW
9651 }
9652 else {
b7ae7a0d 9653 _ckvmssts_noperl(retsts);
748a9306 9654 }
a0d0e21e 9655#ifdef ARGPROC_DEBUG
740ce14c 9656 PerlIO_printf(Perl_debug_log, "%s\n", command);
a0d0e21e
LW
9657#endif
9658 sprintf(pidstring, "%08X", pid);
740ce14c 9659 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
a0d0e21e
LW
9660 pidstr.dsc$a_pointer = pidstring;
9661 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9662 lib$set_symbol(&pidsymbol, &pidstr);
9663 return(SS$_NORMAL);
9664}
9665/*}}}*/
9666/***** End of code taken from Mark Pizzolato's argproc.c package *****/
9667
84902520
TB
9668
9669/* OS-specific initialization at image activation (not thread startup) */
61bb5906
CB
9670/* Older VAXC header files lack these constants */
9671#ifndef JPI$_RIGHTS_SIZE
9672# define JPI$_RIGHTS_SIZE 817
9673#endif
9674#ifndef KGB$M_SUBSYSTEM
9675# define KGB$M_SUBSYSTEM 0x8
9676#endif
a480973c 9677
e0ef6b43
CB
9678/* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9679
84902520
TB
9680/*{{{void vms_image_init(int *, char ***)*/
9681void
9682vms_image_init(int *argcp, char ***argvp)
9683{
b53f3677 9684 int status;
f675dbe5
CB
9685 char eqv[LNM$C_NAMLENGTH+1] = "";
9686 unsigned int len, tabct = 8, tabidx = 0;
9687 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
61bb5906
CB
9688 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9689 unsigned short int dummy, rlen;
f675dbe5 9690 struct dsc$descriptor_s **tabvec;
fd8cd3a3
DS
9691#if defined(PERL_IMPLICIT_CONTEXT)
9692 pTHX = NULL;
9693#endif
61bb5906
CB
9694 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9695 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9696 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9697 { 0, 0, 0, 0} };
84902520 9698
2e34cc90 9699#ifdef KILL_BY_SIGPRC
f7ddb74a 9700 Perl_csighandler_init();
2e34cc90
CL
9701#endif
9702
778e045f 9703#if __CRTL_VER >= 70300000 && !defined(__VAX)
b53f3677
JM
9704 /* This was moved from the pre-image init handler because on threaded */
9705 /* Perl it was always returning 0 for the default value. */
98c7875d 9706 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
b53f3677
JM
9707 if (status > 0) {
9708 int s;
9709 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9710 if (s > 0) {
9711 int initial;
9712 initial = decc$feature_get_value(s, 4);
98c7875d
CB
9713 if (initial > 0) {
9714 /* initial is: 0 if nothing has set the feature */
9715 /* -1 if initialized to default */
9716 /* 1 if set by logical name */
9717 /* 2 if set by decc$feature_set_value */
b53f3677
JM
9718 decc_disable_posix_root = decc$feature_get_value(s, 1);
9719
9720 /* If the value is not valid, force the feature off */
9721 if (decc_disable_posix_root < 0) {
9722 decc$feature_set_value(s, 1, 1);
9723 decc_disable_posix_root = 1;
9724 }
9725 }
9726 else {
98c7875d 9727 /* Nothing has asked for it explicitly, so use our own default. */
b53f3677
JM
9728 decc_disable_posix_root = 1;
9729 decc$feature_set_value(s, 1, 1);
9730 }
9731 }
9732 }
778e045f 9733#endif
b53f3677 9734
fd8cd3a3
DS
9735 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9736 _ckvmssts_noperl(iosb[0]);
61bb5906
CB
9737 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9738 if (iprv[i]) { /* Running image installed with privs? */
fd8cd3a3 9739 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
f675dbe5 9740 will_taint = TRUE;
84902520
TB
9741 break;
9742 }
9743 }
61bb5906 9744 /* Rights identifiers might trigger tainting as well. */
f675dbe5 9745 if (!will_taint && (rlen || rsz)) {
61bb5906
CB
9746 while (rlen < rsz) {
9747 /* We didn't get all the identifiers on the first pass. Allocate a
9748 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9749 * were needed to hold all identifiers at time of last call; we'll
9750 * allocate that many unsigned long ints), and go back and get 'em.
22d4bb9c
CB
9751 * If it gave us less than it wanted to despite ample buffer space,
9752 * something's broken. Is your system missing a system identifier?
61bb5906 9753 */
22d4bb9c
CB
9754 if (rsz <= jpilist[1].buflen) {
9755 /* Perl_croak accvios when used this early in startup. */
9756 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9757 rsz, (unsigned long) jpilist[1].buflen,
9758 "Check your rights database for corruption.\n");
9759 exit(SS$_ABORT);
9760 }
e0ef6b43
CB
9761 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9762 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
c5375c28 9763 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
61bb5906 9764 jpilist[1].buflen = rsz * sizeof(unsigned long int);
fd8cd3a3
DS
9765 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9766 _ckvmssts_noperl(iosb[0]);
61bb5906
CB
9767 }
9768 mask = jpilist[1].bufadr;
9769 /* Check attribute flags for each identifier (2nd longword); protected
9770 * subsystem identifiers trigger tainting.
9771 */
9772 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9773 if (mask[i] & KGB$M_SUBSYSTEM) {
f675dbe5 9774 will_taint = TRUE;
61bb5906
CB
9775 break;
9776 }
9777 }
367e4b85 9778 if (mask != rlst) PerlMem_free(mask);
61bb5906 9779 }
f7ddb74a
JM
9780
9781 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9782 * logical, some versions of the CRTL will add a phanthom /000000/
9783 * directory. This needs to be removed.
9784 */
9785 if (decc_filename_unix_report) {
9786 char * zeros;
9787 int ulen;
9788 ulen = strlen(argvp[0][0]);
9789 if (ulen > 7) {
9790 zeros = strstr(argvp[0][0], "/000000/");
9791 if (zeros != NULL) {
9792 int mlen;
9793 mlen = ulen - (zeros - argvp[0][0]) - 7;
9794 memmove(zeros, &zeros[7], mlen);
9795 ulen = ulen - 7;
9796 argvp[0][0][ulen] = '\0';
9797 }
9798 }
9799 /* It also may have a trailing dot that needs to be removed otherwise
9800 * it will be converted to VMS mode incorrectly.
9801 */
9802 ulen--;
9803 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9804 argvp[0][0][ulen] = '\0';
9805 }
9806
61bb5906 9807 /* We need to use this hack to tell Perl it should run with tainting,
6b88bc9c 9808 * since its tainting flag may be part of the PL_curinterp struct, which
61bb5906
CB
9809 * hasn't been allocated when vms_image_init() is called.
9810 */
f675dbe5 9811 if (will_taint) {
ec618cdf
CB
9812 char **newargv, **oldargv;
9813 oldargv = *argvp;
e0ef6b43 9814 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
c5375c28 9815 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf 9816 newargv[0] = oldargv[0];
c5375c28
JM
9817 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9818 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf
CB
9819 strcpy(newargv[1], "-T");
9820 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9821 (*argcp)++;
9822 newargv[*argcp] = NULL;
61bb5906
CB
9823 /* We orphan the old argv, since we don't know where it's come from,
9824 * so we don't know how to free it.
9825 */
ec618cdf 9826 *argvp = newargv;
61bb5906 9827 }
f675dbe5
CB
9828 else { /* Did user explicitly request tainting? */
9829 int i;
9830 char *cp, **av = *argvp;
9831 for (i = 1; i < *argcp; i++) {
9832 if (*av[i] != '-') break;
9833 for (cp = av[i]+1; *cp; cp++) {
9834 if (*cp == 'T') { will_taint = 1; break; }
9835 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9836 strchr("DFIiMmx",*cp)) break;
9837 }
9838 if (will_taint) break;
9839 }
9840 }
9841
9842 for (tabidx = 0;
9843 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9844 tabidx++) {
c5375c28
JM
9845 if (!tabidx) {
9846 tabvec = (struct dsc$descriptor_s **)
9847 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9848 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9849 }
f675dbe5
CB
9850 else if (tabidx >= tabct) {
9851 tabct += 8;
e0ef6b43 9852 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
c5375c28 9853 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5 9854 }
e0ef6b43 9855 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
c5375c28 9856 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5
CB
9857 tabvec[tabidx]->dsc$w_length = 0;
9858 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9859 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9860 tabvec[tabidx]->dsc$a_pointer = NULL;
fd8cd3a3 9861 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
f675dbe5
CB
9862 }
9863 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9864
84902520 9865 getredirection(argcp,argvp);
3bc25146
CB
9866#if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9867 {
9868# include <reentrancy.h>
f7ddb74a 9869 decc$set_reentrancy(C$C_MULTITHREAD);
3bc25146
CB
9870 }
9871#endif
84902520
TB
9872 return;
9873}
9874/*}}}*/
9875
9876
a0d0e21e
LW
9877/* trim_unixpath()
9878 * Trim Unix-style prefix off filespec, so it looks like what a shell
9879 * glob expansion would return (i.e. from specified prefix on, not
9880 * full path). Note that returned filespec is Unix-style, regardless
9881 * of whether input filespec was VMS-style or Unix-style.
9882 *
a3e9d8c9 9883 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
f86702cc 9884 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9885 * vector of options; at present, only bit 0 is used, and if set tells
9886 * trim unixpath to try the current default directory as a prefix when
9887 * presented with a possibly ambiguous ... wildcard.
a3e9d8c9 9888 *
9889 * Returns !=0 on success, with trimmed filespec replacing contents of
9890 * fspec, and 0 on failure, with contents of fpsec unchanged.
a0d0e21e 9891 */
f86702cc 9892/*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
a0d0e21e 9893int
2fbb330f 9894Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
a0d0e21e 9895{
a480973c 9896 char *unixified, *unixwild,
f86702cc 9897 *template, *base, *end, *cp1, *cp2;
9898 register int tmplen, reslen = 0, dirs = 0;
a0d0e21e 9899
a3e9d8c9 9900 if (!wildspec || !fspec) return 0;
ebd4d70b
JM
9901
9902 unixwild = PerlMem_malloc(VMS_MAXRSS);
9903 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f 9904 template = unixwild;
a3e9d8c9 9905 if (strpbrk(wildspec,"]>:") != NULL) {
0e5ce2c7 9906 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
367e4b85 9907 PerlMem_free(unixwild);
a480973c
JM
9908 return 0;
9909 }
a3e9d8c9 9910 }
2fbb330f 9911 else {
a480973c
JM
9912 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9913 unixwild[VMS_MAXRSS-1] = 0;
2fbb330f 9914 }
c5375c28 9915 unixified = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9916 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e 9917 if (strpbrk(fspec,"]>:") != NULL) {
0e5ce2c7 9918 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
367e4b85
JM
9919 PerlMem_free(unixwild);
9920 PerlMem_free(unixified);
a480973c
JM
9921 return 0;
9922 }
a0d0e21e 9923 else base = unixified;
a3e9d8c9 9924 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9925 * check to see that final result fits into (isn't longer than) fspec */
9926 reslen = strlen(fspec);
a0d0e21e
LW
9927 }
9928 else base = fspec;
a3e9d8c9 9929
9930 /* No prefix or absolute path on wildcard, so nothing to remove */
9931 if (!*template || *template == '/') {
367e4b85 9932 PerlMem_free(unixwild);
a480973c 9933 if (base == fspec) {
367e4b85 9934 PerlMem_free(unixified);
a480973c
JM
9935 return 1;
9936 }
a3e9d8c9 9937 tmplen = strlen(unixified);
a480973c 9938 if (tmplen > reslen) {
367e4b85 9939 PerlMem_free(unixified);
a480973c
JM
9940 return 0; /* not enough space */
9941 }
a3e9d8c9 9942 /* Copy unixified resultant, including trailing NUL */
9943 memmove(fspec,unixified,tmplen+1);
367e4b85 9944 PerlMem_free(unixified);
a3e9d8c9 9945 return 1;
9946 }
a0d0e21e 9947
f86702cc 9948 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9949 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9950 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9951 for (cp1 = end ;cp1 >= base; cp1--)
9952 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9953 { cp1++; break; }
9954 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
367e4b85
JM
9955 PerlMem_free(unixified);
9956 PerlMem_free(unixwild);
a3e9d8c9 9957 return 1;
9958 }
f86702cc 9959 else {
a480973c 9960 char *tpl, *lcres;
f86702cc 9961 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9962 int ells = 1, totells, segdirs, match;
a480973c 9963 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
f86702cc 9964 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9965
9966 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9967 totells = ells;
9968 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
367e4b85 9969 tpl = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9970 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f86702cc 9971 if (ellipsis == template && opts & 1) {
9972 /* Template begins with an ellipsis. Since we can't tell how many
9973 * directory names at the front of the resultant to keep for an
9974 * arbitrary starting point, we arbitrarily choose the current
9975 * default directory as a starting point. If it's there as a prefix,
9976 * clip it off. If not, fall through and act as if the leading
9977 * ellipsis weren't there (i.e. return shortest possible path that
9978 * could match template).
9979 */
a480973c 9980 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
367e4b85
JM
9981 PerlMem_free(tpl);
9982 PerlMem_free(unixified);
9983 PerlMem_free(unixwild);
a480973c
JM
9984 return 0;
9985 }
f7ddb74a
JM
9986 if (!decc_efs_case_preserve) {
9987 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9988 if (_tolower(*cp1) != _tolower(*cp2)) break;
9989 }
f86702cc 9990 segdirs = dirs - totells; /* Min # of dirs we must have left */
9991 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9992 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
18a3d61e 9993 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
9994 PerlMem_free(tpl);
9995 PerlMem_free(unixified);
9996 PerlMem_free(unixwild);
f86702cc 9997 return 1;
a3e9d8c9 9998 }
a3e9d8c9 9999 }
f86702cc 10000 /* First off, back up over constant elements at end of path */
10001 if (dirs) {
10002 for (front = end ; front >= base; front--)
10003 if (*front == '/' && !dirs--) { front++; break; }
a3e9d8c9 10004 }
c5375c28 10005 lcres = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 10006 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c
JM
10007 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10008 cp1++,cp2++) {
10009 if (!decc_efs_case_preserve) {
10010 *cp2 = _tolower(*cp1); /* Make lc copy for match */
10011 }
10012 else {
10013 *cp2 = *cp1;
10014 }
10015 }
10016 if (cp1 != '\0') {
367e4b85
JM
10017 PerlMem_free(tpl);
10018 PerlMem_free(unixified);
10019 PerlMem_free(unixwild);
c5375c28 10020 PerlMem_free(lcres);
a480973c 10021 return 0; /* Path too long. */
f7ddb74a 10022 }
f86702cc 10023 lcend = cp2;
10024 *cp2 = '\0'; /* Pick up with memcpy later */
10025 lcfront = lcres + (front - base);
10026 /* Now skip over each ellipsis and try to match the path in front of it. */
10027 while (ells--) {
10028 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10029 if (*(cp1) == '.' && *(cp1+1) == '.' &&
10030 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
10031 if (cp1 < template) break; /* template started with an ellipsis */
10032 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10033 ellipsis = cp1; continue;
10034 }
a480973c 10035 wilddsc.dsc$a_pointer = tpl;
f86702cc 10036 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10037 nextell = cp1;
10038 for (segdirs = 0, cp2 = tpl;
a480973c 10039 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
f86702cc 10040 cp1++, cp2++) {
10041 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
f7ddb74a
JM
10042 else {
10043 if (!decc_efs_case_preserve) {
10044 *cp2 = _tolower(*cp1); /* else lowercase for match */
10045 }
10046 else {
10047 *cp2 = *cp1; /* else preserve case for match */
10048 }
10049 }
f86702cc 10050 if (*cp2 == '/') segdirs++;
10051 }
a480973c 10052 if (cp1 != ellipsis - 1) {
367e4b85
JM
10053 PerlMem_free(tpl);
10054 PerlMem_free(unixified);
10055 PerlMem_free(unixwild);
10056 PerlMem_free(lcres);
a480973c
JM
10057 return 0; /* Path too long */
10058 }
f86702cc 10059 /* Back up at least as many dirs as in template before matching */
10060 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10061 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10062 for (match = 0; cp1 > lcres;) {
10063 resdsc.dsc$a_pointer = cp1;
10064 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10065 match++;
10066 if (match == 1) lcfront = cp1;
10067 }
10068 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10069 }
a480973c 10070 if (!match) {
367e4b85
JM
10071 PerlMem_free(tpl);
10072 PerlMem_free(unixified);
10073 PerlMem_free(unixwild);
10074 PerlMem_free(lcres);
a480973c
JM
10075 return 0; /* Can't find prefix ??? */
10076 }
f86702cc 10077 if (match > 1 && opts & 1) {
10078 /* This ... wildcard could cover more than one set of dirs (i.e.
10079 * a set of similar dir names is repeated). If the template
10080 * contains more than 1 ..., upstream elements could resolve the
10081 * ambiguity, but it's not worth a full backtracking setup here.
10082 * As a quick heuristic, clip off the current default directory
10083 * if it's present to find the trimmed spec, else use the
10084 * shortest string that this ... could cover.
10085 */
10086 char def[NAM$C_MAXRSS+1], *st;
10087
a480973c 10088 if (getcwd(def, sizeof def,0) == NULL) {
827f156d
JM
10089 PerlMem_free(unixified);
10090 PerlMem_free(unixwild);
10091 PerlMem_free(lcres);
10092 PerlMem_free(tpl);
a480973c
JM
10093 return 0;
10094 }
f7ddb74a
JM
10095 if (!decc_efs_case_preserve) {
10096 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10097 if (_tolower(*cp1) != _tolower(*cp2)) break;
10098 }
f86702cc 10099 segdirs = dirs - totells; /* Min # of dirs we must have left */
10100 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10101 if (*cp1 == '\0' && *cp2 == '/') {
18a3d61e 10102 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
10103 PerlMem_free(tpl);
10104 PerlMem_free(unixified);
10105 PerlMem_free(unixwild);
10106 PerlMem_free(lcres);
f86702cc 10107 return 1;
10108 }
10109 /* Nope -- stick with lcfront from above and keep going. */
10110 }
10111 }
18a3d61e 10112 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
367e4b85
JM
10113 PerlMem_free(tpl);
10114 PerlMem_free(unixified);
10115 PerlMem_free(unixwild);
10116 PerlMem_free(lcres);
a3e9d8c9 10117 return 1;
a0d0e21e 10118 }
a0d0e21e
LW
10119
10120} /* end of trim_unixpath() */
10121/*}}}*/
10122
a0d0e21e
LW
10123
10124/*
10125 * VMS readdir() routines.
10126 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
a0d0e21e 10127 *
bd3fa61c 10128 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
a0d0e21e
LW
10129 * Minor modifications to original routines.
10130 */
10131
a9852f7c
CB
10132/* readdir may have been redefined by reentr.h, so make sure we get
10133 * the local version for what we do here.
10134 */
10135#ifdef readdir
10136# undef readdir
10137#endif
10138#if !defined(PERL_IMPLICIT_CONTEXT)
10139# define readdir Perl_readdir
10140#else
10141# define readdir(a) Perl_readdir(aTHX_ a)
10142#endif
10143
a0d0e21e
LW
10144 /* Number of elements in vms_versions array */
10145#define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10146
10147/*
10148 * Open a directory, return a handle for later use.
10149 */
10150/*{{{ DIR *opendir(char*name) */
ddcbaa1c 10151DIR *
b8ffc8df 10152Perl_opendir(pTHX_ const char *name)
a0d0e21e 10153{
ddcbaa1c 10154 DIR *dd;
657054d4 10155 char *dir;
61bb5906 10156 Stat_t sb;
657054d4
JM
10157
10158 Newx(dir, VMS_MAXRSS, char);
4846f1d7 10159 if (int_tovmspath(name, dir, NULL) == NULL) {
657054d4 10160 Safefree(dir);
61bb5906 10161 return NULL;
a0d0e21e 10162 }
ada67d10
CB
10163 /* Check access before stat; otherwise stat does not
10164 * accurately report whether it's a directory.
10165 */
a1887106 10166 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
fac786e7 10167 /* cando_by_name has already set errno */
657054d4 10168 Safefree(dir);
ada67d10
CB
10169 return NULL;
10170 }
61bb5906
CB
10171 if (flex_stat(dir,&sb) == -1) return NULL;
10172 if (!S_ISDIR(sb.st_mode)) {
657054d4 10173 Safefree(dir);
61bb5906
CB
10174 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10175 return NULL;
10176 }
61bb5906 10177 /* Get memory for the handle, and the pattern. */
ddcbaa1c 10178 Newx(dd,1,DIR);
a02a5408 10179 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
a0d0e21e
LW
10180
10181 /* Fill in the fields; mainly playing with the descriptor. */
f7ddb74a 10182 sprintf(dd->pattern, "%s*.*",dir);
657054d4 10183 Safefree(dir);
a0d0e21e
LW
10184 dd->context = 0;
10185 dd->count = 0;
657054d4 10186 dd->flags = 0;
a096370a
CB
10187 /* By saying we always want the result of readdir() in unix format, we
10188 * are really saying we want all the escapes removed. Otherwise the caller,
10189 * having no way to know whether it's already in VMS format, might send it
10190 * through tovmsspec again, thus double escaping.
10191 */
10192 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
a0d0e21e
LW
10193 dd->pat.dsc$a_pointer = dd->pattern;
10194 dd->pat.dsc$w_length = strlen(dd->pattern);
10195 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10196 dd->pat.dsc$b_class = DSC$K_CLASS_S;
3bc25146 10197#if defined(USE_ITHREADS)
a02a5408 10198 Newx(dd->mutex,1,perl_mutex);
a9852f7c
CB
10199 MUTEX_INIT( (perl_mutex *) dd->mutex );
10200#else
10201 dd->mutex = NULL;
10202#endif
a0d0e21e
LW
10203
10204 return dd;
10205} /* end of opendir() */
10206/*}}}*/
10207
10208/*
10209 * Set the flag to indicate we want versions or not.
10210 */
10211/*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10212void
ddcbaa1c 10213vmsreaddirversions(DIR *dd, int flag)
a0d0e21e 10214{
657054d4
JM
10215 if (flag)
10216 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10217 else
10218 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
a0d0e21e
LW
10219}
10220/*}}}*/
10221
10222/*
10223 * Free up an opened directory.
10224 */
10225/*{{{ void closedir(DIR *dd)*/
10226void
ddcbaa1c 10227Perl_closedir(DIR *dd)
a0d0e21e 10228{
f7ddb74a
JM
10229 int sts;
10230
10231 sts = lib$find_file_end(&dd->context);
a0d0e21e 10232 Safefree(dd->pattern);
3bc25146 10233#if defined(USE_ITHREADS)
a9852f7c
CB
10234 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10235 Safefree(dd->mutex);
10236#endif
f7ddb74a 10237 Safefree(dd);
a0d0e21e
LW
10238}
10239/*}}}*/
10240
10241/*
10242 * Collect all the version numbers for the current file.
10243 */
10244static void
ddcbaa1c 10245collectversions(pTHX_ DIR *dd)
a0d0e21e
LW
10246{
10247 struct dsc$descriptor_s pat;
10248 struct dsc$descriptor_s res;
ddcbaa1c 10249 struct dirent *e;
657054d4 10250 char *p, *text, *buff;
a0d0e21e
LW
10251 int i;
10252 unsigned long context, tmpsts;
10253
10254 /* Convenient shorthand. */
10255 e = &dd->entry;
10256
10257 /* Add the version wildcard, ignoring the "*.*" put on before */
10258 i = strlen(dd->pattern);
a02a5408 10259 Newx(text,i + e->d_namlen + 3,char);
f7ddb74a
JM
10260 strcpy(text, dd->pattern);
10261 sprintf(&text[i - 3], "%s;*", e->d_name);
a0d0e21e
LW
10262
10263 /* Set up the pattern descriptor. */
10264 pat.dsc$a_pointer = text;
10265 pat.dsc$w_length = i + e->d_namlen - 1;
10266 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10267 pat.dsc$b_class = DSC$K_CLASS_S;
10268
10269 /* Set up result descriptor. */
657054d4 10270 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10271 res.dsc$a_pointer = buff;
657054d4 10272 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
10273 res.dsc$b_dtype = DSC$K_DTYPE_T;
10274 res.dsc$b_class = DSC$K_CLASS_S;
10275
10276 /* Read files, collecting versions. */
10277 for (context = 0, e->vms_verscount = 0;
10278 e->vms_verscount < VERSIZE(e);
10279 e->vms_verscount++) {
657054d4
JM
10280 unsigned long rsts;
10281 unsigned long flags = 0;
10282
10283#ifdef VMS_LONGNAME_SUPPORT
988c775c 10284 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10285#endif
10286 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
a0d0e21e 10287 if (tmpsts == RMS$_NMF || context == 0) break;
748a9306 10288 _ckvmssts(tmpsts);
657054d4 10289 buff[VMS_MAXRSS - 1] = '\0';
748a9306 10290 if ((p = strchr(buff, ';')))
a0d0e21e
LW
10291 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10292 else
10293 e->vms_versions[e->vms_verscount] = -1;
10294 }
10295
748a9306 10296 _ckvmssts(lib$find_file_end(&context));
a0d0e21e 10297 Safefree(text);
657054d4 10298 Safefree(buff);
a0d0e21e
LW
10299
10300} /* end of collectversions() */
10301
10302/*
10303 * Read the next entry from the directory.
10304 */
10305/*{{{ struct dirent *readdir(DIR *dd)*/
ddcbaa1c
CB
10306struct dirent *
10307Perl_readdir(pTHX_ DIR *dd)
a0d0e21e
LW
10308{
10309 struct dsc$descriptor_s res;
657054d4 10310 char *p, *buff;
a0d0e21e 10311 unsigned long int tmpsts;
657054d4
JM
10312 unsigned long rsts;
10313 unsigned long flags = 0;
dca5a913 10314 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
657054d4 10315 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
a0d0e21e
LW
10316
10317 /* Set up result descriptor, and get next file. */
657054d4 10318 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10319 res.dsc$a_pointer = buff;
657054d4 10320 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
10321 res.dsc$b_dtype = DSC$K_DTYPE_T;
10322 res.dsc$b_class = DSC$K_CLASS_S;
657054d4
JM
10323
10324#ifdef VMS_LONGNAME_SUPPORT
988c775c 10325 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10326#endif
10327
10328 tmpsts = lib$find_file
10329 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
4633a7c4
LW
10330 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10331 if (!(tmpsts & 1)) {
10332 set_vaxc_errno(tmpsts);
10333 switch (tmpsts) {
10334 case RMS$_PRV:
c07a80fd 10335 set_errno(EACCES); break;
4633a7c4 10336 case RMS$_DEV:
c07a80fd 10337 set_errno(ENODEV); break;
4633a7c4 10338 case RMS$_DIR:
f282b18d
CB
10339 set_errno(ENOTDIR); break;
10340 case RMS$_FNF: case RMS$_DNF:
c07a80fd 10341 set_errno(ENOENT); break;
4633a7c4
LW
10342 default:
10343 set_errno(EVMSERR);
10344 }
657054d4 10345 Safefree(buff);
4633a7c4
LW
10346 return NULL;
10347 }
10348 dd->count++;
a0d0e21e 10349 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
c43a0d1c
CB
10350 buff[res.dsc$w_length] = '\0';
10351 p = buff + res.dsc$w_length;
10352 while (--p >= buff) if (!isspace(*p)) break;
10353 *p = '\0';
f7ddb74a 10354 if (!decc_efs_case_preserve) {
f7ddb74a 10355 for (p = buff; *p; p++) *p = _tolower(*p);
f7ddb74a 10356 }
a0d0e21e
LW
10357
10358 /* Skip any directory component and just copy the name. */
657054d4 10359 sts = vms_split_path
360732b5 10360 (buff,
657054d4
JM
10361 &v_spec,
10362 &v_len,
10363 &r_spec,
10364 &r_len,
10365 &d_spec,
10366 &d_len,
10367 &n_spec,
10368 &n_len,
10369 &e_spec,
10370 &e_len,
10371 &vs_spec,
10372 &vs_len);
10373
0dddfaca
JM
10374 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10375
10376 /* In Unix report mode, remove the ".dir;1" from the name */
10377 /* if it is a real directory. */
10378 if (decc_filename_unix_report || decc_efs_charset) {
f785e3a1
JM
10379 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10380 Stat_t statbuf;
10381 int ret_sts;
10382
10383 ret_sts = flex_lstat(buff, &statbuf);
10384 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10385 e_len = 0;
10386 e_spec[0] = 0;
0dddfaca
JM
10387 }
10388 }
10389 }
10390
10391 /* Drop NULL extensions on UNIX file specification */
10392 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10393 e_len = 0;
10394 e_spec[0] = '\0';
10395 }
dca5a913
JM
10396 }
10397
657054d4
JM
10398 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10399 dd->entry.d_name[n_len + e_len] = '\0';
10400 dd->entry.d_namlen = strlen(dd->entry.d_name);
a0d0e21e 10401
657054d4
JM
10402 /* Convert the filename to UNIX format if needed */
10403 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10404
10405 /* Translate the encoded characters. */
38a44b82 10406 /* Fixme: Unicode handling could result in embedded 0 characters */
657054d4
JM
10407 if (strchr(dd->entry.d_name, '^') != NULL) {
10408 char new_name[256];
10409 char * q;
657054d4
JM
10410 p = dd->entry.d_name;
10411 q = new_name;
10412 while (*p != 0) {
f617045b
CB
10413 int inchars_read, outchars_added;
10414 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10415 p += inchars_read;
10416 q += outchars_added;
dca5a913 10417 /* fix-me */
f617045b 10418 /* if outchars_added > 1, then this is a wide file specification */
dca5a913 10419 /* Wide file specifications need to be passed in Perl */
38a44b82 10420 /* counted strings apparently with a Unicode flag */
657054d4
JM
10421 }
10422 *q = 0;
10423 strcpy(dd->entry.d_name, new_name);
f617045b 10424 dd->entry.d_namlen = strlen(dd->entry.d_name);
657054d4 10425 }
657054d4 10426 }
a0d0e21e 10427
a0d0e21e 10428 dd->entry.vms_verscount = 0;
657054d4
JM
10429 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10430 Safefree(buff);
a0d0e21e
LW
10431 return &dd->entry;
10432
10433} /* end of readdir() */
10434/*}}}*/
10435
10436/*
a9852f7c
CB
10437 * Read the next entry from the directory -- thread-safe version.
10438 */
10439/*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10440int
ddcbaa1c 10441Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
a9852f7c
CB
10442{
10443 int retval;
10444
10445 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10446
7ded3206 10447 entry = readdir(dd);
a9852f7c
CB
10448 *result = entry;
10449 retval = ( *result == NULL ? errno : 0 );
10450
10451 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10452
10453 return retval;
10454
10455} /* end of readdir_r() */
10456/*}}}*/
10457
10458/*
a0d0e21e
LW
10459 * Return something that can be used in a seekdir later.
10460 */
10461/*{{{ long telldir(DIR *dd)*/
10462long
ddcbaa1c 10463Perl_telldir(DIR *dd)
a0d0e21e
LW
10464{
10465 return dd->count;
10466}
10467/*}}}*/
10468
10469/*
10470 * Return to a spot where we used to be. Brute force.
10471 */
10472/*{{{ void seekdir(DIR *dd,long count)*/
10473void
ddcbaa1c 10474Perl_seekdir(pTHX_ DIR *dd, long count)
a0d0e21e 10475{
657054d4 10476 int old_flags;
a0d0e21e
LW
10477
10478 /* If we haven't done anything yet... */
10479 if (dd->count == 0)
10480 return;
10481
10482 /* Remember some state, and clear it. */
657054d4
JM
10483 old_flags = dd->flags;
10484 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
748a9306 10485 _ckvmssts(lib$find_file_end(&dd->context));
a0d0e21e
LW
10486 dd->context = 0;
10487
10488 /* The increment is in readdir(). */
10489 for (dd->count = 0; dd->count < count; )
f7ddb74a 10490 readdir(dd);
a0d0e21e 10491
657054d4 10492 dd->flags = old_flags;
a0d0e21e
LW
10493
10494} /* end of seekdir() */
10495/*}}}*/
10496
10497/* VMS subprocess management
10498 *
10499 * my_vfork() - just a vfork(), after setting a flag to record that
10500 * the current script is trying a Unix-style fork/exec.
10501 *
10502 * vms_do_aexec() and vms_do_exec() are called in response to the
10503 * perl 'exec' function. If this follows a vfork call, then they
a6d05634 10504 * call out the regular perl routines in doio.c which do an
a0d0e21e
LW
10505 * execvp (for those who really want to try this under VMS).
10506 * Otherwise, they do exactly what the perl docs say exec should
10507 * do - terminate the current script and invoke a new command
10508 * (See below for notes on command syntax.)
10509 *
10510 * do_aspawn() and do_spawn() implement the VMS side of the perl
10511 * 'system' function.
10512 *
10513 * Note on command arguments to perl 'exec' and 'system': When handled
10514 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
eed5d6a1
CB
10515 * are concatenated to form a DCL command string. If the first non-numeric
10516 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
a6d05634 10517 * the command string is handed off to DCL directly. Otherwise,
a0d0e21e
LW
10518 * the first token of the command is taken as the filespec of an image
10519 * to run. The filespec is expanded using a default type of '.EXE' and
3eeba6fb 10520 * the process defaults for device, directory, etc., and if found, the resultant
a0d0e21e 10521 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
3eeba6fb 10522 * the command string as parameters. This is perhaps a bit complicated,
a0d0e21e
LW
10523 * but I hope it will form a happy medium between what VMS folks expect
10524 * from lib$spawn and what Unix folks expect from exec.
10525 */
10526
10527static int vfork_called;
10528
10529/*{{{int my_vfork()*/
10530int
10531my_vfork()
10532{
748a9306 10533 vfork_called++;
a0d0e21e
LW
10534 return vfork();
10535}
10536/*}}}*/
10537
4633a7c4 10538
a0d0e21e 10539static void
218fdd94
CL
10540vms_execfree(struct dsc$descriptor_s *vmscmd)
10541{
10542 if (vmscmd) {
10543 if (vmscmd->dsc$a_pointer) {
c5375c28 10544 PerlMem_free(vmscmd->dsc$a_pointer);
218fdd94 10545 }
c5375c28 10546 PerlMem_free(vmscmd);
4633a7c4
LW
10547 }
10548}
10549
10550static char *
fd8cd3a3 10551setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
a0d0e21e 10552{
4e205ed6 10553 char *junk, *tmps = NULL;
a0d0e21e
LW
10554 register size_t cmdlen = 0;
10555 size_t rlen;
10556 register SV **idx;
2d8e6c8d 10557 STRLEN n_a;
a0d0e21e
LW
10558
10559 idx = mark;
4633a7c4
LW
10560 if (really) {
10561 tmps = SvPV(really,rlen);
10562 if (*tmps) {
10563 cmdlen += rlen + 1;
10564 idx++;
10565 }
a0d0e21e
LW
10566 }
10567
10568 for (idx++; idx <= sp; idx++) {
10569 if (*idx) {
10570 junk = SvPVx(*idx,rlen);
10571 cmdlen += rlen ? rlen + 1 : 0;
10572 }
10573 }
c5375c28 10574 Newx(PL_Cmd, cmdlen+1, char);
a0d0e21e 10575
4633a7c4 10576 if (tmps && *tmps) {
6b88bc9c 10577 strcpy(PL_Cmd,tmps);
a0d0e21e
LW
10578 mark++;
10579 }
6b88bc9c 10580 else *PL_Cmd = '\0';
a0d0e21e
LW
10581 while (++mark <= sp) {
10582 if (*mark) {
3eeba6fb
CB
10583 char *s = SvPVx(*mark,n_a);
10584 if (!*s) continue;
10585 if (*PL_Cmd) strcat(PL_Cmd," ");
10586 strcat(PL_Cmd,s);
a0d0e21e
LW
10587 }
10588 }
6b88bc9c 10589 return PL_Cmd;
a0d0e21e
LW
10590
10591} /* end of setup_argstr() */
10592
4633a7c4 10593
a0d0e21e 10594static unsigned long int
2fbb330f 10595setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
218fdd94 10596 struct dsc$descriptor_s **pvmscmd)
a0d0e21e 10597{
e919cd19
JM
10598 char * vmsspec;
10599 char * resspec;
e886094b
JM
10600 char image_name[NAM$C_MAXRSS+1];
10601 char image_argv[NAM$C_MAXRSS+1];
a0d0e21e 10602 $DESCRIPTOR(defdsc,".EXE");
8012a33e 10603 $DESCRIPTOR(defdsc2,".");
e919cd19 10604 struct dsc$descriptor_s resdsc;
218fdd94 10605 struct dsc$descriptor_s *vmscmd;
a0d0e21e 10606 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3eeba6fb 10607 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
aa779de1 10608 register char *s, *rest, *cp, *wordbreak;
2fbb330f
JM
10609 char * cmd;
10610 int cmdlen;
aa779de1 10611 register int isdcl;
a0d0e21e 10612
c5375c28 10613 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
ebd4d70b 10614 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f 10615
e919cd19
JM
10616 /* vmsspec is a DCL command buffer, not just a filename */
10617 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10618 if (vmsspec == NULL)
10619 _ckvmssts_noperl(SS$_INSFMEM);
10620
10621 resspec = PerlMem_malloc(VMS_MAXRSS);
10622 if (resspec == NULL)
10623 _ckvmssts_noperl(SS$_INSFMEM);
10624
2fbb330f
JM
10625 /* Make a copy for modification */
10626 cmdlen = strlen(incmd);
c5375c28 10627 cmd = PerlMem_malloc(cmdlen+1);
ebd4d70b 10628 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f
JM
10629 strncpy(cmd, incmd, cmdlen);
10630 cmd[cmdlen] = 0;
e886094b
JM
10631 image_name[0] = 0;
10632 image_argv[0] = 0;
2fbb330f 10633
e919cd19
JM
10634 resdsc.dsc$a_pointer = resspec;
10635 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10636 resdsc.dsc$b_class = DSC$K_CLASS_S;
10637 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10638
218fdd94
CL
10639 vmscmd->dsc$a_pointer = NULL;
10640 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10641 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10642 vmscmd->dsc$w_length = 0;
10643 if (pvmscmd) *pvmscmd = vmscmd;
10644
ff7adb52
CL
10645 if (suggest_quote) *suggest_quote = 0;
10646
2fbb330f 10647 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
c5375c28 10648 PerlMem_free(cmd);
e919cd19
JM
10649 PerlMem_free(vmsspec);
10650 PerlMem_free(resspec);
a2669cfc 10651 return CLI$_BUFOVF; /* continuation lines currently unsupported */
2fbb330f
JM
10652 }
10653
a0d0e21e 10654 s = cmd;
2fbb330f 10655
a0d0e21e 10656 while (*s && isspace(*s)) s++;
aa779de1
CB
10657
10658 if (*s == '@' || *s == '$') {
10659 vmsspec[0] = *s; rest = s + 1;
10660 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10661 }
10662 else { cp = vmsspec; rest = s; }
10663 if (*rest == '.' || *rest == '/') {
10664 char *cp2;
10665 for (cp2 = resspec;
e919cd19 10666 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
aa779de1
CB
10667 rest++, cp2++) *cp2 = *rest;
10668 *cp2 = '\0';
df278665 10669 if (int_tovmsspec(resspec, cp, 0, NULL)) {
aa779de1 10670 s = vmsspec;
cfbf46cd
JM
10671
10672 /* When a UNIX spec with no file type is translated to VMS, */
10673 /* A trailing '.' is appended under ODS-5 rules. */
10674 /* Here we do not want that trailing "." as it prevents */
10675 /* Looking for a implied ".exe" type. */
10676 if (decc_efs_charset) {
10677 int i;
10678 i = strlen(vmsspec);
10679 if (vmsspec[i-1] == '.') {
10680 vmsspec[i-1] = '\0';
10681 }
10682 }
10683
aa779de1
CB
10684 if (*rest) {
10685 for (cp2 = vmsspec + strlen(vmsspec);
e919cd19 10686 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
aa779de1
CB
10687 rest++, cp2++) *cp2 = *rest;
10688 *cp2 = '\0';
a0d0e21e
LW
10689 }
10690 }
10691 }
aa779de1
CB
10692 /* Intuit whether verb (first word of cmd) is a DCL command:
10693 * - if first nonspace char is '@', it's a DCL indirection
10694 * otherwise
10695 * - if verb contains a filespec separator, it's not a DCL command
10696 * - if it doesn't, caller tells us whether to default to a DCL
10697 * command, or to a local image unless told it's DCL (by leading '$')
10698 */
ff7adb52
CL
10699 if (*s == '@') {
10700 isdcl = 1;
10701 if (suggest_quote) *suggest_quote = 1;
10702 } else {
aa779de1
CB
10703 register char *filespec = strpbrk(s,":<[.;");
10704 rest = wordbreak = strpbrk(s," \"\t/");
10705 if (!wordbreak) wordbreak = s + strlen(s);
10706 if (*s == '$') check_img = 0;
10707 if (filespec && (filespec < wordbreak)) isdcl = 0;
10708 else isdcl = !check_img;
10709 }
10710
3eeba6fb 10711 if (!isdcl) {
dca5a913 10712 int rsts;
aa779de1
CB
10713 imgdsc.dsc$a_pointer = s;
10714 imgdsc.dsc$w_length = wordbreak - s;
dca5a913 10715 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8012a33e 10716 if (!(retsts&1)) {
ebd4d70b 10717 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10718 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f 10719 if (!(retsts & 1) && *s == '$') {
ebd4d70b 10720 _ckvmssts_noperl(lib$find_file_end(&cxt));
2497a41f 10721 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
dca5a913 10722 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
2497a41f 10723 if (!(retsts&1)) {
ebd4d70b 10724 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10725 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f
JM
10726 }
10727 }
aa779de1 10728 }
ebd4d70b 10729 _ckvmssts_noperl(lib$find_file_end(&cxt));
8012a33e 10730
aa779de1 10731 if (retsts & 1) {
8012a33e 10732 FILE *fp;
a0d0e21e
LW
10733 s = resspec;
10734 while (*s && !isspace(*s)) s++;
10735 *s = '\0';
8012a33e
CB
10736
10737 /* check that it's really not DCL with no file extension */
e886094b 10738 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8012a33e 10739 if (fp) {
2497a41f
JM
10740 char b[256] = {0,0,0,0};
10741 read(fileno(fp), b, 256);
8012a33e 10742 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
2497a41f 10743 if (isdcl) {
e886094b
JM
10744 int shebang_len;
10745
2497a41f 10746 /* Check for script */
e886094b
JM
10747 shebang_len = 0;
10748 if ((b[0] == '#') && (b[1] == '!'))
10749 shebang_len = 2;
10750#ifdef ALTERNATE_SHEBANG
10751 else {
10752 shebang_len = strlen(ALTERNATE_SHEBANG);
10753 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10754 char * perlstr;
10755 perlstr = strstr("perl",b);
10756 if (perlstr == NULL)
10757 shebang_len = 0;
10758 }
10759 else
10760 shebang_len = 0;
10761 }
10762#endif
10763
10764 if (shebang_len > 0) {
10765 int i;
10766 int j;
10767 char tmpspec[NAM$C_MAXRSS + 1];
10768
10769 i = shebang_len;
10770 /* Image is following after white space */
10771 /*--------------------------------------*/
10772 while (isprint(b[i]) && isspace(b[i]))
10773 i++;
10774
10775 j = 0;
10776 while (isprint(b[i]) && !isspace(b[i])) {
10777 tmpspec[j++] = b[i++];
10778 if (j >= NAM$C_MAXRSS)
10779 break;
10780 }
10781 tmpspec[j] = '\0';
10782
10783 /* There may be some default parameters to the image */
10784 /*---------------------------------------------------*/
10785 j = 0;
10786 while (isprint(b[i])) {
10787 image_argv[j++] = b[i++];
10788 if (j >= NAM$C_MAXRSS)
10789 break;
10790 }
10791 while ((j > 0) && !isprint(image_argv[j-1]))
10792 j--;
10793 image_argv[j] = 0;
10794
2497a41f 10795 /* It will need to be converted to VMS format and validated */
e886094b
JM
10796 if (tmpspec[0] != '\0') {
10797 char * iname;
10798
10799 /* Try to find the exact program requested to be run */
10800 /*---------------------------------------------------*/
6fb6c614
JM
10801 iname = int_rmsexpand
10802 (tmpspec, image_name, ".exe",
360732b5 10803 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10804 if (iname != NULL) {
a1887106
JM
10805 if (cando_by_name_int
10806 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
10807 /* MCR prefix needed */
10808 isdcl = 0;
10809 }
10810 else {
10811 /* Try again with a null type */
10812 /*----------------------------*/
6fb6c614
JM
10813 iname = int_rmsexpand
10814 (tmpspec, image_name, ".",
360732b5 10815 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10816 if (iname != NULL) {
a1887106
JM
10817 if (cando_by_name_int
10818 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
10819 /* MCR prefix needed */
10820 isdcl = 0;
10821 }
10822 }
10823 }
10824
10825 /* Did we find the image to run the script? */
10826 /*------------------------------------------*/
10827 if (isdcl) {
10828 char *tchr;
10829
10830 /* Assume DCL or foreign command exists */
10831 /*--------------------------------------*/
10832 tchr = strrchr(tmpspec, '/');
10833 if (tchr != NULL) {
10834 tchr++;
10835 }
10836 else {
10837 tchr = tmpspec;
10838 }
10839 strcpy(image_name, tchr);
10840 }
10841 }
10842 }
2497a41f
JM
10843 }
10844 }
8012a33e
CB
10845 fclose(fp);
10846 }
e919cd19
JM
10847 if (check_img && isdcl) {
10848 PerlMem_free(cmd);
10849 PerlMem_free(resspec);
10850 PerlMem_free(vmsspec);
10851 return RMS$_FNF;
10852 }
8012a33e 10853
3eeba6fb 10854 if (cando_by_name(S_IXUSR,0,resspec)) {
c5375c28 10855 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
ebd4d70b 10856 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8012a33e 10857 if (!isdcl) {
218fdd94 10858 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
e886094b
JM
10859 if (image_name[0] != 0) {
10860 strcat(vmscmd->dsc$a_pointer, image_name);
10861 strcat(vmscmd->dsc$a_pointer, " ");
10862 }
10863 } else if (image_name[0] != 0) {
10864 strcpy(vmscmd->dsc$a_pointer, image_name);
10865 strcat(vmscmd->dsc$a_pointer, " ");
8012a33e 10866 } else {
218fdd94 10867 strcpy(vmscmd->dsc$a_pointer,"@");
8012a33e 10868 }
e886094b
JM
10869 if (suggest_quote) *suggest_quote = 1;
10870
10871 /* If there is an image name, use original command */
10872 if (image_name[0] == 0)
10873 strcat(vmscmd->dsc$a_pointer,resspec);
10874 else {
10875 rest = cmd;
10876 while (*rest && isspace(*rest)) rest++;
10877 }
10878
10879 if (image_argv[0] != 0) {
10880 strcat(vmscmd->dsc$a_pointer,image_argv);
10881 strcat(vmscmd->dsc$a_pointer, " ");
10882 }
10883 if (rest) {
10884 int rest_len;
10885 int vmscmd_len;
10886
10887 rest_len = strlen(rest);
10888 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10889 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10890 strcat(vmscmd->dsc$a_pointer,rest);
10891 else
10892 retsts = CLI$_BUFOVF;
10893 }
218fdd94 10894 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
c5375c28 10895 PerlMem_free(cmd);
e919cd19
JM
10896 PerlMem_free(vmsspec);
10897 PerlMem_free(resspec);
218fdd94 10898 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
3eeba6fb 10899 }
c5375c28
JM
10900 else
10901 retsts = RMS$_PRV;
a0d0e21e
LW
10902 }
10903 }
3eeba6fb 10904 /* It's either a DCL command or we couldn't find a suitable image */
218fdd94 10905 vmscmd->dsc$w_length = strlen(cmd);
ff7adb52 10906
b011c7bd 10907 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
c5375c28 10908 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
b011c7bd 10909 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
c5375c28
JM
10910
10911 PerlMem_free(cmd);
e919cd19
JM
10912 PerlMem_free(resspec);
10913 PerlMem_free(vmsspec);
2fbb330f 10914
ff7adb52
CL
10915 /* check if it's a symbol (for quoting purposes) */
10916 if (suggest_quote && !*suggest_quote) {
10917 int iss;
10918 char equiv[LNM$C_NAMLENGTH];
10919 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10920 eqvdsc.dsc$a_pointer = equiv;
10921
218fdd94 10922 iss = lib$get_symbol(vmscmd,&eqvdsc);
ff7adb52
CL
10923 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10924 }
3eeba6fb
CB
10925 if (!(retsts & 1)) {
10926 /* just hand off status values likely to be due to user error */
10927 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10928 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10929 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
ebd4d70b 10930 else { _ckvmssts_noperl(retsts); }
3eeba6fb 10931 }
a0d0e21e 10932
218fdd94 10933 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
a3e9d8c9 10934
a0d0e21e
LW
10935} /* end of setup_cmddsc() */
10936
a3e9d8c9 10937
a0d0e21e
LW
10938/* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10939bool
fd8cd3a3 10940Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
a0d0e21e 10941{
c5375c28
JM
10942bool exec_sts;
10943char * cmd;
10944
a0d0e21e
LW
10945 if (sp > mark) {
10946 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
10947 vfork_called--;
10948 if (vfork_called < 0) {
5c84aa53 10949 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
10950 vfork_called = 0;
10951 }
10952 else return do_aexec(really,mark,sp);
a0d0e21e 10953 }
4633a7c4 10954 /* no vfork - act VMSish */
c5375c28
JM
10955 cmd = setup_argstr(aTHX_ really,mark,sp);
10956 exec_sts = vms_do_exec(cmd);
10957 Safefree(cmd); /* Clean up from setup_argstr() */
10958 return exec_sts;
a0d0e21e
LW
10959 }
10960
10961 return FALSE;
10962} /* end of vms_do_aexec() */
10963/*}}}*/
10964
10965/* {{{bool vms_do_exec(char *cmd) */
10966bool
2fbb330f 10967Perl_vms_do_exec(pTHX_ const char *cmd)
a0d0e21e 10968{
218fdd94 10969 struct dsc$descriptor_s *vmscmd;
a0d0e21e
LW
10970
10971 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
10972 vfork_called--;
10973 if (vfork_called < 0) {
5c84aa53 10974 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
10975 vfork_called = 0;
10976 }
10977 else return do_exec(cmd);
a0d0e21e 10978 }
748a9306
LW
10979
10980 { /* no vfork - act VMSish */
748a9306 10981 unsigned long int retsts;
a0d0e21e 10982
1e422769 10983 TAINT_ENV();
10984 TAINT_PROPER("exec");
218fdd94
CL
10985 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10986 retsts = lib$do_command(vmscmd);
a0d0e21e 10987
09b7f37c 10988 switch (retsts) {
f282b18d 10989 case RMS$_FNF: case RMS$_DNF:
09b7f37c 10990 set_errno(ENOENT); break;
f282b18d 10991 case RMS$_DIR:
09b7f37c 10992 set_errno(ENOTDIR); break;
f282b18d
CB
10993 case RMS$_DEV:
10994 set_errno(ENODEV); break;
09b7f37c
CB
10995 case RMS$_PRV:
10996 set_errno(EACCES); break;
10997 case RMS$_SYN:
10998 set_errno(EINVAL); break;
a2669cfc 10999 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
09b7f37c
CB
11000 set_errno(E2BIG); break;
11001 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 11002 _ckvmssts_noperl(retsts); /* fall through */
09b7f37c
CB
11003 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11004 set_errno(EVMSERR);
11005 }
748a9306 11006 set_vaxc_errno(retsts);
3eeba6fb 11007 if (ckWARN(WARN_EXEC)) {
f98bc0c6 11008 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
218fdd94 11009 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
3eeba6fb 11010 }
218fdd94 11011 vms_execfree(vmscmd);
a0d0e21e
LW
11012 }
11013
11014 return FALSE;
11015
11016} /* end of vms_do_exec() */
11017/*}}}*/
11018
9ec7171b 11019int do_spawn2(pTHX_ const char *, int);
a0d0e21e 11020
9ec7171b
CB
11021int
11022Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
a0d0e21e 11023{
c5375c28
JM
11024unsigned long int sts;
11025char * cmd;
eed5d6a1 11026int flags = 0;
a0d0e21e 11027
c5375c28 11028 if (sp > mark) {
eed5d6a1
CB
11029
11030 /* We'll copy the (undocumented?) Win32 behavior and allow a
11031 * numeric first argument. But the only value we'll support
11032 * through do_aspawn is a value of 1, which means spawn without
11033 * waiting for completion -- other values are ignored.
11034 */
9ec7171b 11035 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
eed5d6a1 11036 ++mark;
9ec7171b 11037 flags = SvIVx(*mark);
eed5d6a1
CB
11038 }
11039
11040 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11041 flags = CLI$M_NOWAIT;
11042 else
11043 flags = 0;
11044
9ec7171b 11045 cmd = setup_argstr(aTHX_ really, mark, sp);
eed5d6a1 11046 sts = do_spawn2(aTHX_ cmd, flags);
c5375c28
JM
11047 /* pp_sys will clean up cmd */
11048 return sts;
11049 }
a0d0e21e
LW
11050 return SS$_ABORT;
11051} /* end of do_aspawn() */
11052/*}}}*/
11053
eed5d6a1 11054
9ec7171b
CB
11055/* {{{int do_spawn(char* cmd) */
11056int
11057Perl_do_spawn(pTHX_ char* cmd)
a0d0e21e 11058{
7918f24d
NC
11059 PERL_ARGS_ASSERT_DO_SPAWN;
11060
eed5d6a1
CB
11061 return do_spawn2(aTHX_ cmd, 0);
11062}
11063/*}}}*/
11064
9ec7171b
CB
11065/* {{{int do_spawn_nowait(char* cmd) */
11066int
11067Perl_do_spawn_nowait(pTHX_ char* cmd)
11068{
11069 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11070
11071 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11072}
11073/*}}}*/
11074
11075/* {{{int do_spawn2(char *cmd) */
11076int
eed5d6a1
CB
11077do_spawn2(pTHX_ const char *cmd, int flags)
11078{
209030df 11079 unsigned long int sts, substs;
a0d0e21e 11080
c5375c28
JM
11081 /* The caller of this routine expects to Safefree(PL_Cmd) */
11082 Newx(PL_Cmd,10,char);
11083
1e422769 11084 TAINT_ENV();
11085 TAINT_PROPER("spawn");
748a9306 11086 if (!cmd || !*cmd) {
eed5d6a1 11087 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
c8795d8b
JH
11088 if (!(sts & 1)) {
11089 switch (sts) {
209030df
JH
11090 case RMS$_FNF: case RMS$_DNF:
11091 set_errno(ENOENT); break;
11092 case RMS$_DIR:
11093 set_errno(ENOTDIR); break;
11094 case RMS$_DEV:
11095 set_errno(ENODEV); break;
11096 case RMS$_PRV:
11097 set_errno(EACCES); break;
11098 case RMS$_SYN:
11099 set_errno(EINVAL); break;
11100 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11101 set_errno(E2BIG); break;
11102 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 11103 _ckvmssts_noperl(sts); /* fall through */
209030df
JH
11104 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11105 set_errno(EVMSERR);
c8795d8b
JH
11106 }
11107 set_vaxc_errno(sts);
11108 if (ckWARN(WARN_EXEC)) {
f98bc0c6 11109 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
c8795d8b
JH
11110 Strerror(errno));
11111 }
09b7f37c 11112 }
c8795d8b 11113 sts = substs;
48023aa8
CL
11114 }
11115 else {
eed5d6a1 11116 char mode[3];
2fbb330f 11117 PerlIO * fp;
eed5d6a1
CB
11118 if (flags & CLI$M_NOWAIT)
11119 strcpy(mode, "n");
11120 else
11121 strcpy(mode, "nW");
11122
11123 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
2fbb330f
JM
11124 if (fp != NULL)
11125 my_pclose(fp);
eed5d6a1 11126 /* sts will be the pid in the nowait case */
48023aa8 11127 }
48023aa8 11128 return sts;
eed5d6a1 11129} /* end of do_spawn2() */
a0d0e21e
LW
11130/*}}}*/
11131
bc10a425
CB
11132
11133static unsigned int *sockflags, sockflagsize;
11134
11135/*
11136 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11137 * routines found in some versions of the CRTL can't deal with sockets.
11138 * We don't shim the other file open routines since a socket isn't
11139 * likely to be opened by a name.
11140 */
275feba9
CB
11141/*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11142FILE *my_fdopen(int fd, const char *mode)
bc10a425 11143{
f7ddb74a 11144 FILE *fp = fdopen(fd, mode);
bc10a425
CB
11145
11146 if (fp) {
11147 unsigned int fdoff = fd / sizeof(unsigned int);
2497a41f 11148 Stat_t sbuf; /* native stat; we don't need flex_stat */
bc10a425
CB
11149 if (!sockflagsize || fdoff > sockflagsize) {
11150 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
a02a5408 11151 else Newx (sockflags,fdoff+2,unsigned int);
bc10a425
CB
11152 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11153 sockflagsize = fdoff + 2;
11154 }
312ac60b 11155 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
bc10a425
CB
11156 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11157 }
11158 return fp;
11159
11160}
11161/*}}}*/
11162
11163
11164/*
11165 * Clear the corresponding bit when the (possibly) socket stream is closed.
11166 * There still a small hole: we miss an implicit close which might occur
11167 * via freopen(). >> Todo
11168 */
11169/*{{{ int my_fclose(FILE *fp)*/
11170int my_fclose(FILE *fp) {
11171 if (fp) {
11172 unsigned int fd = fileno(fp);
11173 unsigned int fdoff = fd / sizeof(unsigned int);
11174
e0951028 11175 if (sockflagsize && fdoff < sockflagsize)
bc10a425
CB
11176 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11177 }
11178 return fclose(fp);
11179}
11180/*}}}*/
11181
11182
a0d0e21e
LW
11183/*
11184 * A simple fwrite replacement which outputs itmsz*nitm chars without
11185 * introducing record boundaries every itmsz chars.
22d4bb9c
CB
11186 * We are using fputs, which depends on a terminating null. We may
11187 * well be writing binary data, so we need to accommodate not only
11188 * data with nulls sprinkled in the middle but also data with no null
11189 * byte at the end.
a0d0e21e 11190 */
a15cef0c 11191/*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
a0d0e21e 11192int
a15cef0c 11193my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
a0d0e21e 11194{
2e05a54c
CB
11195 register char *cp, *end, *cpd;
11196 char *data;
bc10a425
CB
11197 register unsigned int fd = fileno(dest);
11198 register unsigned int fdoff = fd / sizeof(unsigned int);
22d4bb9c 11199 int retval;
bc10a425
CB
11200 int bufsize = itmsz * nitm + 1;
11201
11202 if (fdoff < sockflagsize &&
11203 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11204 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11205 return nitm;
11206 }
22d4bb9c 11207
bc10a425 11208 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
22d4bb9c
CB
11209 memcpy( data, src, itmsz*nitm );
11210 data[itmsz*nitm] = '\0';
a0d0e21e 11211
22d4bb9c
CB
11212 end = data + itmsz * nitm;
11213 retval = (int) nitm; /* on success return # items written */
a0d0e21e 11214
22d4bb9c
CB
11215 cpd = data;
11216 while (cpd <= end) {
11217 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11218 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
a0d0e21e 11219 if (cp < end)
22d4bb9c
CB
11220 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11221 cpd = cp + 1;
a0d0e21e
LW
11222 }
11223
bc10a425 11224 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
22d4bb9c 11225 return retval;
a0d0e21e
LW
11226
11227} /* end of my_fwrite() */
11228/*}}}*/
11229
d27fe803
JH
11230/*{{{ int my_flush(FILE *fp)*/
11231int
fd8cd3a3 11232Perl_my_flush(pTHX_ FILE *fp)
d27fe803
JH
11233{
11234 int res;
93948341 11235 if ((res = fflush(fp)) == 0 && fp) {
d27fe803 11236#ifdef VMS_DO_SOCKETS
61bb5906 11237 Stat_t s;
ed1b9de0 11238 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
d27fe803
JH
11239#endif
11240 res = fsync(fileno(fp));
11241 }
22d4bb9c
CB
11242/*
11243 * If the flush succeeded but set end-of-file, we need to clear
11244 * the error because our caller may check ferror(). BTW, this
11245 * probably means we just flushed an empty file.
11246 */
11247 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11248
d27fe803
JH
11249 return res;
11250}
11251/*}}}*/
11252
bf8d1304
JM
11253/* fgetname() is not returning the correct file specifications when
11254 * decc_filename_unix_report mode is active. So we have to have it
11255 * aways return filenames in VMS mode and convert it ourselves.
11256 */
11257
11258/*{{{ char * my_fgetname(FILE *fp, buf)*/
11259char *
11260Perl_my_fgetname(FILE *fp, char * buf) {
11261 char * retname;
11262 char * vms_name;
11263
11264 retname = fgetname(fp, buf, 1);
11265
11266 /* If we are in VMS mode, then we are done */
11267 if (!decc_filename_unix_report || (retname == NULL)) {
11268 return retname;
11269 }
11270
11271 /* Convert this to Unix format */
11272 vms_name = PerlMem_malloc(VMS_MAXRSS + 1);
11273 strcpy(vms_name, retname);
11274 retname = int_tounixspec(vms_name, buf, NULL);
11275 PerlMem_free(vms_name);
11276
11277 return retname;
11278}
11279/*}}}*/
11280
748a9306
LW
11281/*
11282 * Here are replacements for the following Unix routines in the VMS environment:
11283 * getpwuid Get information for a particular UIC or UID
11284 * getpwnam Get information for a named user
11285 * getpwent Get information for each user in the rights database
11286 * setpwent Reset search to the start of the rights database
11287 * endpwent Finish searching for users in the rights database
11288 *
11289 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11290 * (defined in pwd.h), which contains the following fields:-
11291 * struct passwd {
11292 * char *pw_name; Username (in lower case)
11293 * char *pw_passwd; Hashed password
11294 * unsigned int pw_uid; UIC
11295 * unsigned int pw_gid; UIC group number
11296 * char *pw_unixdir; Default device/directory (VMS-style)
11297 * char *pw_gecos; Owner name
11298 * char *pw_dir; Default device/directory (Unix-style)
11299 * char *pw_shell; Default CLI name (eg. DCL)
11300 * };
11301 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11302 *
11303 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11304 * not the UIC member number (eg. what's returned by getuid()),
11305 * getpwuid() can accept either as input (if uid is specified, the caller's
11306 * UIC group is used), though it won't recognise gid=0.
11307 *
11308 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11309 * information about other users in your group or in other groups, respectively.
11310 * If the required privilege is not available, then these routines fill only
11311 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11312 * string).
11313 *
11314 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11315 */
11316
11317/* sizes of various UAF record fields */
11318#define UAI$S_USERNAME 12
11319#define UAI$S_IDENT 31
11320#define UAI$S_OWNER 31
11321#define UAI$S_DEFDEV 31
11322#define UAI$S_DEFDIR 63
11323#define UAI$S_DEFCLI 31
11324#define UAI$S_PWD 8
11325
11326#define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11327 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11328 (uic).uic$v_group != UIC$K_WILD_GROUP)
11329
4633a7c4
LW
11330static char __empty[]= "";
11331static struct passwd __passwd_empty=
748a9306
LW
11332 {(char *) __empty, (char *) __empty, 0, 0,
11333 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11334static int contxt= 0;
11335static struct passwd __pwdcache;
11336static char __pw_namecache[UAI$S_IDENT+1];
11337
748a9306
LW
11338/*
11339 * This routine does most of the work extracting the user information.
11340 */
fd8cd3a3 11341static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
a0d0e21e 11342{
748a9306
LW
11343 static struct {
11344 unsigned char length;
11345 char pw_gecos[UAI$S_OWNER+1];
11346 } owner;
11347 static union uicdef uic;
11348 static struct {
11349 unsigned char length;
11350 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11351 } defdev;
11352 static struct {
11353 unsigned char length;
11354 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11355 } defdir;
11356 static struct {
11357 unsigned char length;
11358 char pw_shell[UAI$S_DEFCLI+1];
11359 } defcli;
11360 static char pw_passwd[UAI$S_PWD+1];
11361
11362 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11363 struct dsc$descriptor_s name_desc;
c07a80fd 11364 unsigned long int sts;
748a9306 11365
4633a7c4 11366 static struct itmlst_3 itmlst[]= {
748a9306
LW
11367 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11368 {sizeof(uic), UAI$_UIC, &uic, &luic},
11369 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11370 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11371 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11372 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11373 {0, 0, NULL, NULL}};
11374
11375 name_desc.dsc$w_length= strlen(name);
11376 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11377 name_desc.dsc$b_class= DSC$K_CLASS_S;
f7ddb74a 11378 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
748a9306
LW
11379
11380/* Note that sys$getuai returns many fields as counted strings. */
c07a80fd 11381 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11382 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11383 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11384 }
11385 else { _ckvmssts(sts); }
11386 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
748a9306
LW
11387
11388 if ((int) owner.length < lowner) lowner= (int) owner.length;
11389 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11390 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11391 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11392 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11393 owner.pw_gecos[lowner]= '\0';
11394 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11395 defcli.pw_shell[ldefcli]= '\0';
11396 if (valid_uic(uic)) {
11397 pwd->pw_uid= uic.uic$l_uic;
11398 pwd->pw_gid= uic.uic$v_group;
11399 }
11400 else
5c84aa53 11401 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
748a9306
LW
11402 pwd->pw_passwd= pw_passwd;
11403 pwd->pw_gecos= owner.pw_gecos;
11404 pwd->pw_dir= defdev.pw_dir;
360732b5 11405 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
748a9306
LW
11406 pwd->pw_shell= defcli.pw_shell;
11407 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11408 int ldir;
11409 ldir= strlen(pwd->pw_unixdir) - 1;
11410 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11411 }
11412 else
11413 strcpy(pwd->pw_unixdir, pwd->pw_dir);
f7ddb74a
JM
11414 if (!decc_efs_case_preserve)
11415 __mystrtolower(pwd->pw_unixdir);
c07a80fd 11416 return 1;
a0d0e21e 11417}
748a9306
LW
11418
11419/*
11420 * Get information for a named user.
11421*/
11422/*{{{struct passwd *getpwnam(char *name)*/
2fbb330f 11423struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
748a9306
LW
11424{
11425 struct dsc$descriptor_s name_desc;
11426 union uicdef uic;
4e0c9737 11427 unsigned long int sts;
748a9306
LW
11428
11429 __pwdcache = __passwd_empty;
fd8cd3a3 11430 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
748a9306
LW
11431 /* We still may be able to determine pw_uid and pw_gid */
11432 name_desc.dsc$w_length= strlen(name);
11433 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11434 name_desc.dsc$b_class= DSC$K_CLASS_S;
11435 name_desc.dsc$a_pointer= (char *) name;
aa689395 11436 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
748a9306
LW
11437 __pwdcache.pw_uid= uic.uic$l_uic;
11438 __pwdcache.pw_gid= uic.uic$v_group;
11439 }
c07a80fd 11440 else {
aa689395 11441 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11442 set_vaxc_errno(sts);
11443 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
c07a80fd 11444 return NULL;
11445 }
aa689395 11446 else { _ckvmssts(sts); }
c07a80fd 11447 }
748a9306 11448 }
748a9306
LW
11449 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11450 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11451 __pwdcache.pw_name= __pw_namecache;
11452 return &__pwdcache;
11453} /* end of my_getpwnam() */
a0d0e21e
LW
11454/*}}}*/
11455
748a9306
LW
11456/*
11457 * Get information for a particular UIC or UID.
11458 * Called by my_getpwent with uid=-1 to list all users.
11459*/
11460/*{{{struct passwd *my_getpwuid(Uid_t uid)*/
fd8cd3a3 11461struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
a0d0e21e 11462{
748a9306
LW
11463 const $DESCRIPTOR(name_desc,__pw_namecache);
11464 unsigned short lname;
11465 union uicdef uic;
11466 unsigned long int status;
11467
11468 if (uid == (unsigned int) -1) {
11469 do {
11470 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11471 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
c07a80fd 11472 set_vaxc_errno(status);
11473 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
748a9306
LW
11474 my_endpwent();
11475 return NULL;
11476 }
11477 else { _ckvmssts(status); }
11478 } while (!valid_uic (uic));
11479 }
11480 else {
11481 uic.uic$l_uic= uid;
c07a80fd 11482 if (!uic.uic$v_group)
76e3520e 11483 uic.uic$v_group= PerlProc_getgid();
748a9306
LW
11484 if (valid_uic(uic))
11485 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11486 else status = SS$_IVIDENT;
c07a80fd 11487 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11488 status == RMS$_PRV) {
11489 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11490 return NULL;
11491 }
11492 else { _ckvmssts(status); }
748a9306
LW
11493 }
11494 __pw_namecache[lname]= '\0';
01b8edb6 11495 __mystrtolower(__pw_namecache);
748a9306
LW
11496
11497 __pwdcache = __passwd_empty;
11498 __pwdcache.pw_name = __pw_namecache;
11499
11500/* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11501 The identifier's value is usually the UIC, but it doesn't have to be,
11502 so if we can, we let fillpasswd update this. */
11503 __pwdcache.pw_uid = uic.uic$l_uic;
11504 __pwdcache.pw_gid = uic.uic$v_group;
11505
fd8cd3a3 11506 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
748a9306 11507 return &__pwdcache;
a0d0e21e 11508
748a9306
LW
11509} /* end of my_getpwuid() */
11510/*}}}*/
11511
11512/*
11513 * Get information for next user.
11514*/
11515/*{{{struct passwd *my_getpwent()*/
fd8cd3a3 11516struct passwd *Perl_my_getpwent(pTHX)
748a9306
LW
11517{
11518 return (my_getpwuid((unsigned int) -1));
11519}
11520/*}}}*/
a0d0e21e 11521
748a9306
LW
11522/*
11523 * Finish searching rights database for users.
11524*/
11525/*{{{void my_endpwent()*/
fd8cd3a3 11526void Perl_my_endpwent(pTHX)
748a9306
LW
11527{
11528 if (contxt) {
11529 _ckvmssts(sys$finish_rdb(&contxt));
11530 contxt= 0;
11531 }
a0d0e21e
LW
11532}
11533/*}}}*/
748a9306 11534
61bb5906
CB
11535#ifdef HOMEGROWN_POSIX_SIGNALS
11536 /* Signal handling routines, pulled into the core from POSIX.xs.
11537 *
11538 * We need these for threads, so they've been rolled into the core,
11539 * rather than left in POSIX.xs.
11540 *
11541 * (DRS, Oct 23, 1997)
11542 */
5b411029 11543
61bb5906
CB
11544 /* sigset_t is atomic under VMS, so these routines are easy */
11545/*{{{int my_sigemptyset(sigset_t *) */
5b411029 11546int my_sigemptyset(sigset_t *set) {
61bb5906
CB
11547 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11548 *set = 0; return 0;
5b411029 11549}
61bb5906
CB
11550/*}}}*/
11551
11552
11553/*{{{int my_sigfillset(sigset_t *)*/
5b411029 11554int my_sigfillset(sigset_t *set) {
61bb5906
CB
11555 int i;
11556 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11557 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11558 return 0;
5b411029 11559}
61bb5906
CB
11560/*}}}*/
11561
11562
11563/*{{{int my_sigaddset(sigset_t *set, int sig)*/
5b411029 11564int my_sigaddset(sigset_t *set, int sig) {
61bb5906
CB
11565 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11566 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11567 *set |= (1 << (sig - 1));
11568 return 0;
5b411029 11569}
61bb5906
CB
11570/*}}}*/
11571
11572
11573/*{{{int my_sigdelset(sigset_t *set, int sig)*/
5b411029 11574int my_sigdelset(sigset_t *set, int sig) {
61bb5906
CB
11575 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11576 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11577 *set &= ~(1 << (sig - 1));
11578 return 0;
5b411029 11579}
61bb5906
CB
11580/*}}}*/
11581
11582
11583/*{{{int my_sigismember(sigset_t *set, int sig)*/
5b411029 11584int my_sigismember(sigset_t *set, int sig) {
61bb5906
CB
11585 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11586 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
73e350d9 11587 return *set & (1 << (sig - 1));
5b411029 11588}
61bb5906 11589/*}}}*/
5b411029 11590
5b411029 11591
61bb5906
CB
11592/*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11593int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11594 sigset_t tempmask;
11595
11596 /* If set and oset are both null, then things are badly wrong. Bail out. */
11597 if ((oset == NULL) && (set == NULL)) {
11598 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5b411029
MB
11599 return -1;
11600 }
5b411029 11601
61bb5906
CB
11602 /* If set's null, then we're just handling a fetch. */
11603 if (set == NULL) {
11604 tempmask = sigblock(0);
11605 }
11606 else {
11607 switch (how) {
11608 case SIG_SETMASK:
11609 tempmask = sigsetmask(*set);
11610 break;
11611 case SIG_BLOCK:
11612 tempmask = sigblock(*set);
11613 break;
11614 case SIG_UNBLOCK:
11615 tempmask = sigblock(0);
11616 sigsetmask(*oset & ~tempmask);
11617 break;
11618 default:
11619 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11620 return -1;
11621 }
11622 }
11623
11624 /* Did they pass us an oset? If so, stick our holding mask into it */
11625 if (oset)
11626 *oset = tempmask;
5b411029 11627
61bb5906 11628 return 0;
5b411029 11629}
61bb5906
CB
11630/*}}}*/
11631#endif /* HOMEGROWN_POSIX_SIGNALS */
11632
5b411029 11633
ff0cee69 11634/* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11635 * my_utime(), and flex_stat(), all of which operate on UTC unless
11636 * VMSISH_TIMES is true.
11637 */
11638/* method used to handle UTC conversions:
11639 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
e518068a 11640 */
ff0cee69 11641static int gmtime_emulation_type;
11642/* number of secs to add to UTC POSIX-style time to get local time */
11643static long int utc_offset_secs;
e518068a 11644
ff0cee69 11645/* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11646 * in vmsish.h. #undef them here so we can call the CRTL routines
11647 * directly.
e518068a 11648 */
11649#undef gmtime
ff0cee69 11650#undef localtime
11651#undef time
11652
61bb5906 11653
a44ceb8e
CB
11654/*
11655 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11656 * qualifier with the extern prefix pragma. This provisional
11657 * hack circumvents this prefix pragma problem in previous
11658 * precompilers.
11659 */
11660#if defined(__VMS_VER) && __VMS_VER >= 70000000
11661# if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11662# pragma __extern_prefix save
11663# pragma __extern_prefix "" /* set to empty to prevent prefixing */
11664# define gmtime decc$__utctz_gmtime
11665# define localtime decc$__utctz_localtime
11666# define time decc$__utc_time
11667# pragma __extern_prefix restore
11668
11669 struct tm *gmtime(), *localtime();
11670
11671# endif
11672#endif
11673
11674
61bb5906
CB
11675static time_t toutc_dst(time_t loc) {
11676 struct tm *rsltmp;
11677
11678 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11679 loc -= utc_offset_secs;
11680 if (rsltmp->tm_isdst) loc -= 3600;
11681 return loc;
11682}
32da55ab 11683#define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11684 ((gmtime_emulation_type || my_time(NULL)), \
11685 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11686 ((secs) - utc_offset_secs))))
11687
11688static time_t toloc_dst(time_t utc) {
11689 struct tm *rsltmp;
11690
11691 utc += utc_offset_secs;
11692 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11693 if (rsltmp->tm_isdst) utc += 3600;
11694 return utc;
11695}
32da55ab 11696#define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11697 ((gmtime_emulation_type || my_time(NULL)), \
11698 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11699 ((secs) + utc_offset_secs))))
11700
22d4bb9c
CB
11701#ifndef RTL_USES_UTC
11702/*
11703
11704 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11705 DST starts on 1st sun of april at 02:00 std time
11706 ends on last sun of october at 02:00 dst time
11707 see the UCX management command reference, SET CONFIG TIMEZONE
11708 for formatting info.
11709
11710 No, it's not as general as it should be, but then again, NOTHING
11711 will handle UK times in a sensible way.
11712*/
11713
11714
11715/*
11716 parse the DST start/end info:
11717 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11718*/
11719
11720static char *
11721tz_parse_startend(char *s, struct tm *w, int *past)
11722{
11723 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11724 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11725 time_t g;
11726
11727 if (!s) return 0;
11728 if (!w) return 0;
11729 if (!past) return 0;
11730
11731 ly = 0;
11732 if (w->tm_year % 4 == 0) ly = 1;
11733 if (w->tm_year % 100 == 0) ly = 0;
11734 if (w->tm_year+1900 % 400 == 0) ly = 1;
11735 if (ly) dinm[1]++;
11736
11737 dozjd = isdigit(*s);
11738 if (*s == 'J' || *s == 'j' || dozjd) {
11739 if (!dozjd && !isdigit(*++s)) return 0;
11740 d = *s++ - '0';
11741 if (isdigit(*s)) {
11742 d = d*10 + *s++ - '0';
11743 if (isdigit(*s)) {
11744 d = d*10 + *s++ - '0';
11745 }
11746 }
11747 if (d == 0) return 0;
11748 if (d > 366) return 0;
11749 d--;
11750 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11751 g = d * 86400;
11752 dozjd = 1;
11753 } else if (*s == 'M' || *s == 'm') {
11754 if (!isdigit(*++s)) return 0;
11755 m = *s++ - '0';
11756 if (isdigit(*s)) m = 10*m + *s++ - '0';
11757 if (*s != '.') return 0;
11758 if (!isdigit(*++s)) return 0;
11759 n = *s++ - '0';
11760 if (n < 1 || n > 5) return 0;
11761 if (*s != '.') return 0;
11762 if (!isdigit(*++s)) return 0;
11763 d = *s++ - '0';
11764 if (d > 6) return 0;
11765 }
11766
11767 if (*s == '/') {
11768 if (!isdigit(*++s)) return 0;
11769 hour = *s++ - '0';
11770 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11771 if (*s == ':') {
11772 if (!isdigit(*++s)) return 0;
11773 min = *s++ - '0';
11774 if (isdigit(*s)) min = 10*min + *s++ - '0';
11775 if (*s == ':') {
11776 if (!isdigit(*++s)) return 0;
11777 sec = *s++ - '0';
11778 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11779 }
11780 }
11781 } else {
11782 hour = 2;
11783 min = 0;
11784 sec = 0;
11785 }
11786
11787 if (dozjd) {
11788 if (w->tm_yday < d) goto before;
11789 if (w->tm_yday > d) goto after;
11790 } else {
11791 if (w->tm_mon+1 < m) goto before;
11792 if (w->tm_mon+1 > m) goto after;
11793
11794 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11795 k = d - j; /* mday of first d */
11796 if (k <= 0) k += 7;
11797 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11798 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11799 if (w->tm_mday < k) goto before;
11800 if (w->tm_mday > k) goto after;
11801 }
11802
11803 if (w->tm_hour < hour) goto before;
11804 if (w->tm_hour > hour) goto after;
11805 if (w->tm_min < min) goto before;
11806 if (w->tm_min > min) goto after;
11807 if (w->tm_sec < sec) goto before;
11808 goto after;
11809
11810before:
11811 *past = 0;
11812 return s;
11813after:
11814 *past = 1;
11815 return s;
11816}
11817
11818
11819
11820
11821/* parse the offset: (+|-)hh[:mm[:ss]] */
11822
11823static char *
11824tz_parse_offset(char *s, int *offset)
11825{
11826 int hour = 0, min = 0, sec = 0;
11827 int neg = 0;
11828 if (!s) return 0;
11829 if (!offset) return 0;
11830
11831 if (*s == '-') {neg++; s++;}
11832 if (*s == '+') s++;
11833 if (!isdigit(*s)) return 0;
11834 hour = *s++ - '0';
11835 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11836 if (hour > 24) return 0;
11837 if (*s == ':') {
11838 if (!isdigit(*++s)) return 0;
11839 min = *s++ - '0';
11840 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11841 if (min > 59) return 0;
11842 if (*s == ':') {
11843 if (!isdigit(*++s)) return 0;
11844 sec = *s++ - '0';
11845 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11846 if (sec > 59) return 0;
11847 }
11848 }
11849
11850 *offset = (hour*60+min)*60 + sec;
11851 if (neg) *offset = -*offset;
11852 return s;
11853}
11854
11855/*
11856 input time is w, whatever type of time the CRTL localtime() uses.
11857 sets dst, the zone, and the gmtoff (seconds)
11858
11859 caches the value of TZ and UCX$TZ env variables; note that
11860 my_setenv looks for these and sets a flag if they're changed
11861 for efficiency.
11862
11863 We have to watch out for the "australian" case (dst starts in
11864 october, ends in april)...flagged by "reverse" and checked by
11865 scanning through the months of the previous year.
11866
11867*/
11868
11869static int
fd8cd3a3 11870tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
22d4bb9c
CB
11871{
11872 time_t when;
11873 struct tm *w2;
11874 char *s,*s2;
11875 char *dstzone, *tz, *s_start, *s_end;
11876 int std_off, dst_off, isdst;
11877 int y, dststart, dstend;
11878 static char envtz[1025]; /* longer than any logical, symbol, ... */
11879 static char ucxtz[1025];
11880 static char reversed = 0;
11881
11882 if (!w) return 0;
11883
11884 if (tz_updated) {
11885 tz_updated = 0;
11886 reversed = -1; /* flag need to check */
11887 envtz[0] = ucxtz[0] = '\0';
11888 tz = my_getenv("TZ",0);
11889 if (tz) strcpy(envtz, tz);
11890 tz = my_getenv("UCX$TZ",0);
11891 if (tz) strcpy(ucxtz, tz);
11892 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11893 }
11894 tz = envtz;
11895 if (!*tz) tz = ucxtz;
11896
11897 s = tz;
11898 while (isalpha(*s)) s++;
11899 s = tz_parse_offset(s, &std_off);
11900 if (!s) return 0;
11901 if (!*s) { /* no DST, hurray we're done! */
11902 isdst = 0;
11903 goto done;
11904 }
11905
11906 dstzone = s;
11907 while (isalpha(*s)) s++;
11908 s2 = tz_parse_offset(s, &dst_off);
11909 if (s2) {
11910 s = s2;
11911 } else {
11912 dst_off = std_off - 3600;
11913 }
11914
11915 if (!*s) { /* default dst start/end?? */
11916 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11917 s = strchr(ucxtz,',');
11918 }
11919 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11920 }
11921 if (*s != ',') return 0;
11922
11923 when = *w;
11924 when = _toutc(when); /* convert to utc */
11925 when = when - std_off; /* convert to pseudolocal time*/
11926
11927 w2 = localtime(&when);
11928 y = w2->tm_year;
11929 s_start = s+1;
11930 s = tz_parse_startend(s_start,w2,&dststart);
11931 if (!s) return 0;
11932 if (*s != ',') return 0;
11933
11934 when = *w;
11935 when = _toutc(when); /* convert to utc */
11936 when = when - dst_off; /* convert to pseudolocal time*/
11937 w2 = localtime(&when);
11938 if (w2->tm_year != y) { /* spans a year, just check one time */
11939 when += dst_off - std_off;
11940 w2 = localtime(&when);
11941 }
11942 s_end = s+1;
11943 s = tz_parse_startend(s_end,w2,&dstend);
11944 if (!s) return 0;
11945
11946 if (reversed == -1) { /* need to check if start later than end */
11947 int j, ds, de;
11948
11949 when = *w;
11950 if (when < 2*365*86400) {
11951 when += 2*365*86400;
11952 } else {
11953 when -= 365*86400;
11954 }
11955 w2 =localtime(&when);
11956 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11957
11958 for (j = 0; j < 12; j++) {
11959 w2 =localtime(&when);
f7ddb74a
JM
11960 tz_parse_startend(s_start,w2,&ds);
11961 tz_parse_startend(s_end,w2,&de);
22d4bb9c
CB
11962 if (ds != de) break;
11963 when += 30*86400;
11964 }
11965 reversed = 0;
11966 if (de && !ds) reversed = 1;
11967 }
11968
11969 isdst = dststart && !dstend;
11970 if (reversed) isdst = dststart || !dstend;
11971
11972done:
11973 if (dst) *dst = isdst;
11974 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11975 if (isdst) tz = dstzone;
11976 if (zone) {
11977 while(isalpha(*tz)) *zone++ = *tz++;
11978 *zone = '\0';
11979 }
11980 return 1;
11981}
11982
11983#endif /* !RTL_USES_UTC */
61bb5906 11984
ff0cee69 11985/* my_time(), my_localtime(), my_gmtime()
61bb5906 11986 * By default traffic in UTC time values, using CRTL gmtime() or
ff0cee69 11987 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
61bb5906
CB
11988 * Note: We need to use these functions even when the CRTL has working
11989 * UTC support, since they also handle C<use vmsish qw(times);>
11990 *
ff0cee69 11991 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
bd3fa61c 11992 * Modified by Charles Bailey <bailey@newman.upenn.edu>
ff0cee69 11993 */
11994
11995/*{{{time_t my_time(time_t *timep)*/
fd8cd3a3 11996time_t Perl_my_time(pTHX_ time_t *timep)
e518068a 11997{
e518068a 11998 time_t when;
61bb5906 11999 struct tm *tm_p;
e518068a 12000
12001 if (gmtime_emulation_type == 0) {
61bb5906
CB
12002 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
12003 /* results of calls to gmtime() and localtime() */
12004 /* for same &base */
ff0cee69 12005
e518068a 12006 gmtime_emulation_type++;
ff0cee69 12007 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
f675dbe5 12008 char off[LNM$C_NAMLENGTH+1];;
ff0cee69 12009
e518068a 12010 gmtime_emulation_type++;
f675dbe5 12011 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
e518068a 12012 gmtime_emulation_type++;
22d4bb9c 12013 utc_offset_secs = 0;
5c84aa53 12014 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
ff0cee69 12015 }
12016 else { utc_offset_secs = atol(off); }
e518068a 12017 }
ff0cee69 12018 else { /* We've got a working gmtime() */
12019 struct tm gmt, local;
e518068a 12020
ff0cee69 12021 gmt = *tm_p;
12022 tm_p = localtime(&base);
12023 local = *tm_p;
12024 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
12025 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12026 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
12027 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
12028 }
e518068a 12029 }
ff0cee69 12030
12031 when = time(NULL);
61bb5906
CB
12032# ifdef VMSISH_TIME
12033# ifdef RTL_USES_UTC
12034 if (VMSISH_TIME) when = _toloc(when);
12035# else
12036 if (!VMSISH_TIME) when = _toutc(when);
12037# endif
12038# endif
ff0cee69 12039 if (timep != NULL) *timep = when;
12040 return when;
12041
12042} /* end of my_time() */
12043/*}}}*/
12044
12045
12046/*{{{struct tm *my_gmtime(const time_t *timep)*/
12047struct tm *
fd8cd3a3 12048Perl_my_gmtime(pTHX_ const time_t *timep)
ff0cee69 12049{
ff0cee69 12050 time_t when;
61bb5906 12051 struct tm *rsltmp;
ff0cee69 12052
68dc0745 12053 if (timep == NULL) {
12054 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12055 return NULL;
12056 }
12057 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
ff0cee69 12058
12059 when = *timep;
12060# ifdef VMSISH_TIME
61bb5906
CB
12061 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12062# endif
12063# ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
12064 return gmtime(&when);
12065# else
ff0cee69 12066 /* CRTL localtime() wants local time as input, so does no tz correction */
61bb5906
CB
12067 rsltmp = localtime(&when);
12068 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
12069 return rsltmp;
12070#endif
e518068a 12071} /* end of my_gmtime() */
e518068a 12072/*}}}*/
12073
12074
ff0cee69 12075/*{{{struct tm *my_localtime(const time_t *timep)*/
12076struct tm *
fd8cd3a3 12077Perl_my_localtime(pTHX_ const time_t *timep)
ff0cee69 12078{
22d4bb9c 12079 time_t when, whenutc;
61bb5906 12080 struct tm *rsltmp;
22d4bb9c 12081 int dst, offset;
ff0cee69 12082
68dc0745 12083 if (timep == NULL) {
12084 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12085 return NULL;
12086 }
12087 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
f7ddb74a 12088 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
ff0cee69 12089
12090 when = *timep;
61bb5906 12091# ifdef RTL_USES_UTC
ff0cee69 12092# ifdef VMSISH_TIME
61bb5906 12093 if (VMSISH_TIME) when = _toutc(when);
ff0cee69 12094# endif
61bb5906 12095 /* CRTL localtime() wants UTC as input, does tz correction itself */
ff0cee69 12096 return localtime(&when);
22d4bb9c
CB
12097
12098# else /* !RTL_USES_UTC */
12099 whenutc = when;
61bb5906 12100# ifdef VMSISH_TIME
22d4bb9c
CB
12101 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
12102 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
61bb5906 12103# endif
22d4bb9c
CB
12104 dst = -1;
12105#ifndef RTL_USES_UTC
32af7c23 12106 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
22d4bb9c
CB
12107 when = whenutc - offset; /* pseudolocal time*/
12108 }
61bb5906
CB
12109# endif
12110 /* CRTL localtime() wants local time as input, so does no tz correction */
12111 rsltmp = localtime(&when);
22d4bb9c 12112 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
61bb5906 12113 return rsltmp;
22d4bb9c 12114# endif
ff0cee69 12115
12116} /* end of my_localtime() */
12117/*}}}*/
12118
12119/* Reset definitions for later calls */
12120#define gmtime(t) my_gmtime(t)
12121#define localtime(t) my_localtime(t)
12122#define time(t) my_time(t)
12123
12124
941b3de1
CB
12125/* my_utime - update modification/access time of a file
12126 *
12127 * VMS 7.3 and later implementation
12128 * Only the UTC translation is home-grown. The rest is handled by the
12129 * CRTL utime(), which will take into account the relevant feature
12130 * logicals and ODS-5 volume characteristics for true access times.
12131 *
12132 * pre VMS 7.3 implementation:
12133 * The calling sequence is identical to POSIX utime(), but under
12134 * VMS with ODS-2, only the modification time is changed; ODS-2 does
12135 * not maintain access times. Restrictions differ from the POSIX
ff0cee69 12136 * definition in that the time can be changed as long as the
12137 * caller has permission to execute the necessary IO$_MODIFY $QIO;
12138 * no separate checks are made to insure that the caller is the
12139 * owner of the file or has special privs enabled.
12140 * Code here is based on Joe Meadows' FILE utility.
941b3de1 12141 *
ff0cee69 12142 */
12143
12144/* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12145 * to VMS epoch (01-JAN-1858 00:00:00.00)
12146 * in 100 ns intervals.
12147 */
12148static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12149
94a11853
CB
12150/*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12151int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
ff0cee69 12152{
941b3de1
CB
12153#if __CRTL_VER >= 70300000
12154 struct utimbuf utc_utimes, *utc_utimesp;
12155
12156 if (utimes != NULL) {
12157 utc_utimes.actime = utimes->actime;
12158 utc_utimes.modtime = utimes->modtime;
12159# ifdef VMSISH_TIME
12160 /* If input was local; convert to UTC for sys svc */
12161 if (VMSISH_TIME) {
12162 utc_utimes.actime = _toutc(utimes->actime);
12163 utc_utimes.modtime = _toutc(utimes->modtime);
12164 }
12165# endif
12166 utc_utimesp = &utc_utimes;
12167 }
12168 else {
12169 utc_utimesp = NULL;
12170 }
12171
12172 return utime(file, utc_utimesp);
12173
12174#else /* __CRTL_VER < 70300000 */
12175
ff0cee69 12176 register int i;
f7ddb74a 12177 int sts;
ff0cee69 12178 long int bintime[2], len = 2, lowbit, unixtime,
12179 secscale = 10000000; /* seconds --> 100 ns intervals */
12180 unsigned long int chan, iosb[2], retsts;
12181 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12182 struct FAB myfab = cc$rms_fab;
12183 struct NAM mynam = cc$rms_nam;
12184#if defined (__DECC) && defined (__VAX)
12185 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12186 * at least through VMS V6.1, which causes a type-conversion warning.
12187 */
12188# pragma message save
12189# pragma message disable cvtdiftypes
12190#endif
12191 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12192 struct fibdef myfib;
12193#if defined (__DECC) && defined (__VAX)
12194 /* This should be right after the declaration of myatr, but due
12195 * to a bug in VAX DEC C, this takes effect a statement early.
12196 */
12197# pragma message restore
12198#endif
f7ddb74a 12199 /* cast ok for read only parameter */
ff0cee69 12200 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12201 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12202 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
704c2eb3 12203
ff0cee69 12204 if (file == NULL || *file == '\0') {
941b3de1 12205 SETERRNO(ENOENT, LIB$_INVARG);
ff0cee69 12206 return -1;
12207 }
704c2eb3
JM
12208
12209 /* Convert to VMS format ensuring that it will fit in 255 characters */
6fb6c614 12210 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
941b3de1
CB
12211 SETERRNO(ENOENT, LIB$_INVARG);
12212 return -1;
12213 }
ff0cee69 12214 if (utimes != NULL) {
12215 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
12216 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12217 * Since time_t is unsigned long int, and lib$emul takes a signed long int
12218 * as input, we force the sign bit to be clear by shifting unixtime right
12219 * one bit, then multiplying by an extra factor of 2 in lib$emul().
12220 */
12221 lowbit = (utimes->modtime & 1) ? secscale : 0;
12222 unixtime = (long int) utimes->modtime;
61bb5906
CB
12223# ifdef VMSISH_TIME
12224 /* If input was UTC; convert to local for sys svc */
12225 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
ff0cee69 12226# endif
1a6334fb 12227 unixtime >>= 1; secscale <<= 1;
ff0cee69 12228 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12229 if (!(retsts & 1)) {
941b3de1 12230 SETERRNO(EVMSERR, retsts);
ff0cee69 12231 return -1;
12232 }
12233 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12234 if (!(retsts & 1)) {
941b3de1 12235 SETERRNO(EVMSERR, retsts);
ff0cee69 12236 return -1;
12237 }
12238 }
12239 else {
12240 /* Just get the current time in VMS format directly */
12241 retsts = sys$gettim(bintime);
12242 if (!(retsts & 1)) {
941b3de1 12243 SETERRNO(EVMSERR, retsts);
ff0cee69 12244 return -1;
12245 }
12246 }
12247
12248 myfab.fab$l_fna = vmsspec;
12249 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12250 myfab.fab$l_nam = &mynam;
12251 mynam.nam$l_esa = esa;
12252 mynam.nam$b_ess = (unsigned char) sizeof esa;
12253 mynam.nam$l_rsa = rsa;
12254 mynam.nam$b_rss = (unsigned char) sizeof rsa;
f7ddb74a
JM
12255 if (decc_efs_case_preserve)
12256 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
ff0cee69 12257
12258 /* Look for the file to be affected, letting RMS parse the file
12259 * specification for us as well. I have set errno using only
12260 * values documented in the utime() man page for VMS POSIX.
12261 */
12262 retsts = sys$parse(&myfab,0,0);
12263 if (!(retsts & 1)) {
12264 set_vaxc_errno(retsts);
12265 if (retsts == RMS$_PRV) set_errno(EACCES);
12266 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12267 else set_errno(EVMSERR);
12268 return -1;
12269 }
12270 retsts = sys$search(&myfab,0,0);
12271 if (!(retsts & 1)) {
752635ea 12272 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12273 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 12274 set_vaxc_errno(retsts);
12275 if (retsts == RMS$_PRV) set_errno(EACCES);
12276 else if (retsts == RMS$_FNF) set_errno(ENOENT);
12277 else set_errno(EVMSERR);
12278 return -1;
12279 }
12280
12281 devdsc.dsc$w_length = mynam.nam$b_dev;
f7ddb74a 12282 /* cast ok for read only parameter */
ff0cee69 12283 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12284
12285 retsts = sys$assign(&devdsc,&chan,0,0);
12286 if (!(retsts & 1)) {
752635ea 12287 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12288 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 12289 set_vaxc_errno(retsts);
12290 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
12291 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
12292 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
12293 else set_errno(EVMSERR);
12294 return -1;
12295 }
12296
12297 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12298 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12299
12300 memset((void *) &myfib, 0, sizeof myfib);
22d4bb9c 12301#if defined(__DECC) || defined(__DECCXX)
ff0cee69 12302 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12303 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12304 /* This prevents the revision time of the file being reset to the current
12305 * time as a result of our IO$_MODIFY $QIO. */
12306 myfib.fib$l_acctl = FIB$M_NORECORD;
12307#else
12308 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12309 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12310 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12311#endif
12312 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
752635ea 12313 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12314 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 12315 _ckvmssts(sys$dassgn(chan));
12316 if (retsts & 1) retsts = iosb[0];
12317 if (!(retsts & 1)) {
12318 set_vaxc_errno(retsts);
12319 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12320 else set_errno(EVMSERR);
12321 return -1;
12322 }
12323
12324 return 0;
941b3de1
CB
12325
12326#endif /* #if __CRTL_VER >= 70300000 */
12327
ff0cee69 12328} /* end of my_utime() */
12329/*}}}*/
12330
748a9306 12331/*
2497a41f 12332 * flex_stat, flex_lstat, flex_fstat
748a9306
LW
12333 * basic stat, but gets it right when asked to stat
12334 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12335 */
12336
2497a41f 12337#ifndef _USE_STD_STAT
748a9306
LW
12338/* encode_dev packs a VMS device name string into an integer to allow
12339 * simple comparisons. This can be used, for example, to check whether two
12340 * files are located on the same device, by comparing their encoded device
12341 * names. Even a string comparison would not do, because stat() reuses the
12342 * device name buffer for each call; so without encode_dev, it would be
12343 * necessary to save the buffer and use strcmp (this would mean a number of
12344 * changes to the standard Perl code, to say nothing of what a Perl script
12345 * would have to do.
12346 *
12347 * The device lock id, if it exists, should be unique (unless perhaps compared
12348 * with lock ids transferred from other nodes). We have a lock id if the disk is
12349 * mounted cluster-wide, which is when we tend to get long (host-qualified)
12350 * device names. Thus we use the lock id in preference, and only if that isn't
12351 * available, do we try to pack the device name into an integer (flagged by
12352 * the sign bit (LOCKID_MASK) being set).
12353 *
e518068a 12354 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
748a9306
LW
12355 * name and its encoded form, but it seems very unlikely that we will find
12356 * two files on different disks that share the same encoded device names,
12357 * and even more remote that they will share the same file id (if the test
12358 * is to check for the same file).
12359 *
12360 * A better method might be to use sys$device_scan on the first call, and to
12361 * search for the device, returning an index into the cached array.
cb9e088c 12362 * The number returned would be more intelligible.
748a9306
LW
12363 * This is probably not worth it, and anyway would take quite a bit longer
12364 * on the first call.
12365 */
12366#define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
fd8cd3a3 12367static mydev_t encode_dev (pTHX_ const char *dev)
748a9306
LW
12368{
12369 int i;
12370 unsigned long int f;
aa689395 12371 mydev_t enc;
748a9306
LW
12372 char c;
12373 const char *q;
12374
12375 if (!dev || !dev[0]) return 0;
12376
12377#if LOCKID_MASK
12378 {
12379 struct dsc$descriptor_s dev_desc;
cb9e088c 12380 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
748a9306
LW
12381
12382 /* For cluster-mounted disks, the disk lock identifier is unique, so we
12383 can try that first. */
12384 dev_desc.dsc$w_length = strlen (dev);
12385 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
12386 dev_desc.dsc$b_class = DSC$K_CLASS_S;
f7ddb74a 12387 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
cb9e088c 12388 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
360732b5 12389 if (!$VMS_STATUS_SUCCESS(status)) {
cb9e088c
CB
12390 switch (status) {
12391 case SS$_NOSUCHDEV:
12392 SETERRNO(ENODEV, status);
12393 return 0;
12394 default:
12395 _ckvmssts(status);
12396 }
12397 }
748a9306
LW
12398 if (lockid) return (lockid & ~LOCKID_MASK);
12399 }
a0d0e21e 12400#endif
748a9306
LW
12401
12402 /* Otherwise we try to encode the device name */
12403 enc = 0;
12404 f = 1;
12405 i = 0;
12406 for (q = dev + strlen(dev); q--; q >= dev) {
988c775c
JM
12407 if (*q == ':')
12408 break;
748a9306
LW
12409 if (isdigit (*q))
12410 c= (*q) - '0';
12411 else if (isalpha (toupper (*q)))
12412 c= toupper (*q) - 'A' + (char)10;
12413 else
12414 continue; /* Skip '$'s */
12415 i++;
12416 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
12417 if (i>1) f *= 36;
12418 enc += f * (unsigned long int) c;
12419 }
12420 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
12421
12422} /* end of encode_dev() */
cfcfe586
JM
12423#define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12424 device_no = encode_dev(aTHX_ devname)
12425#else
12426#define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12427 device_no = new_dev_no
2497a41f 12428#endif
748a9306 12429
748a9306 12430static int
135577da 12431is_null_device(const char *name)
748a9306 12432{
2497a41f 12433 if (decc_bug_devnull != 0) {
682e4b71 12434 if (strncmp("/dev/null", name, 9) == 0)
2497a41f
JM
12435 return 1;
12436 }
748a9306
LW
12437 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12438 The underscore prefix, controller letter, and unit number are
12439 independently optional; for our purposes, the colon punctuation
12440 is not. The colon can be trailed by optional directory and/or
12441 filename, but two consecutive colons indicates a nodename rather
12442 than a device. [pr] */
12443 if (*name == '_') ++name;
12444 if (tolower(*name++) != 'n') return 0;
12445 if (tolower(*name++) != 'l') return 0;
12446 if (tolower(*name) == 'a') ++name;
12447 if (*name == '0') ++name;
12448 return (*name++ == ':') && (*name != ':');
12449}
12450
312ac60b
JM
12451static int
12452Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
c07a80fd 12453
46c05374
CB
12454#define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12455
a1887106
JM
12456static I32
12457Perl_cando_by_name_int
12458 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
748a9306 12459{
e538e23f
CB
12460 char usrname[L_cuserid];
12461 struct dsc$descriptor_s usrdsc =
748a9306 12462 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
e538e23f 12463 char *vmsname = NULL, *fileified = NULL;
597c27e2 12464 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
2d9f3838 12465 unsigned short int retlen, trnlnm_iter_count;
748a9306
LW
12466 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12467 union prvdef curprv;
597c27e2
CB
12468 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12469 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12470 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
ada67d10
CB
12471 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12472 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12473 {0,0,0,0}};
12474 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
748a9306 12475 {0,0,0,0}};
ada67d10 12476 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
858aded6 12477 Stat_t st;
6151c65c 12478 static int profile_context = -1;
748a9306
LW
12479
12480 if (!fname || !*fname) return FALSE;
a1887106 12481
e538e23f
CB
12482 /* Make sure we expand logical names, since sys$check_access doesn't */
12483 fileified = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12484 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f 12485 if (!strpbrk(fname,"/]>:")) {
a1887106
JM
12486 strcpy(fileified,fname);
12487 trnlnm_iter_count = 0;
e538e23f 12488 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
2d9f3838
CB
12489 trnlnm_iter_count++;
12490 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
a1887106
JM
12491 }
12492 fname = fileified;
e538e23f
CB
12493 }
12494
12495 vmsname = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12496 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f
CB
12497 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12498 /* Don't know if already in VMS format, so make sure */
360732b5 12499 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
a1887106 12500 PerlMem_free(fileified);
e538e23f 12501 PerlMem_free(vmsname);
a1887106
JM
12502 return FALSE;
12503 }
a1887106
JM
12504 }
12505 else {
e538e23f 12506 strcpy(vmsname,fname);
a5f75d66
AD
12507 }
12508
858aded6 12509 /* sys$check_access needs a file spec, not a directory spec.
312ac60b 12510 * flex_stat now will handle a null thread context during startup.
858aded6 12511 */
e538e23f
CB
12512
12513 retlen = namdsc.dsc$w_length = strlen(vmsname);
12514 if (vmsname[retlen-1] == ']'
12515 || vmsname[retlen-1] == '>'
858aded6 12516 || vmsname[retlen-1] == ':'
46c05374 12517 || (!flex_stat_int(vmsname, &st, 1) &&
312ac60b 12518 S_ISDIR(st.st_mode))) {
e538e23f 12519
a979ce91 12520 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
e538e23f
CB
12521 PerlMem_free(fileified);
12522 PerlMem_free(vmsname);
12523 return FALSE;
12524 }
12525 fname = fileified;
12526 }
858aded6
CB
12527 else {
12528 fname = vmsname;
12529 }
e538e23f
CB
12530
12531 retlen = namdsc.dsc$w_length = strlen(fname);
12532 namdsc.dsc$a_pointer = (char *)fname;
12533
748a9306 12534 switch (bit) {
f282b18d 12535 case S_IXUSR: case S_IXGRP: case S_IXOTH:
360732b5 12536 access = ARM$M_EXECUTE;
597c27e2
CB
12537 flags = CHP$M_READ;
12538 break;
f282b18d 12539 case S_IRUSR: case S_IRGRP: case S_IROTH:
360732b5 12540 access = ARM$M_READ;
597c27e2
CB
12541 flags = CHP$M_READ | CHP$M_USEREADALL;
12542 break;
f282b18d 12543 case S_IWUSR: case S_IWGRP: case S_IWOTH:
360732b5 12544 access = ARM$M_WRITE;
597c27e2
CB
12545 flags = CHP$M_READ | CHP$M_WRITE;
12546 break;
f282b18d 12547 case S_IDUSR: case S_IDGRP: case S_IDOTH:
360732b5 12548 access = ARM$M_DELETE;
597c27e2
CB
12549 flags = CHP$M_READ | CHP$M_WRITE;
12550 break;
748a9306 12551 default:
a1887106
JM
12552 if (fileified != NULL)
12553 PerlMem_free(fileified);
e538e23f
CB
12554 if (vmsname != NULL)
12555 PerlMem_free(vmsname);
748a9306
LW
12556 return FALSE;
12557 }
12558
ada67d10
CB
12559 /* Before we call $check_access, create a user profile with the current
12560 * process privs since otherwise it just uses the default privs from the
baf3cf9c
CB
12561 * UAF and might give false positives or negatives. This only works on
12562 * VMS versions v6.0 and later since that's when sys$create_user_profile
12563 * became available.
ada67d10
CB
12564 */
12565
12566 /* get current process privs and username */
ebd4d70b
JM
12567 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12568 _ckvmssts_noperl(iosb[0]);
ada67d10 12569
baf3cf9c
CB
12570#if defined(__VMS_VER) && __VMS_VER >= 60000000
12571
ada67d10 12572 /* find out the space required for the profile */
ebd4d70b 12573 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
6151c65c 12574 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12575
12576 /* allocate space for the profile and get it filled in */
c5375c28 12577 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
ebd4d70b
JM
12578 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12579 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
6151c65c 12580 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12581
12582 /* use the profile to check access to the file; free profile & analyze results */
6151c65c 12583 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
c5375c28 12584 PerlMem_free(usrprodsc.dsc$a_pointer);
ada67d10 12585 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
baf3cf9c
CB
12586
12587#else
12588
12589 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12590
12591#endif
12592
bbce6d69 12593 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
61bb5906 12594 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
22d4bb9c 12595 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
bbce6d69 12596 set_vaxc_errno(retsts);
12597 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12598 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12599 else set_errno(ENOENT);
a1887106
JM
12600 if (fileified != NULL)
12601 PerlMem_free(fileified);
e538e23f
CB
12602 if (vmsname != NULL)
12603 PerlMem_free(vmsname);
a3e9d8c9 12604 return FALSE;
12605 }
ada67d10 12606 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
a1887106
JM
12607 if (fileified != NULL)
12608 PerlMem_free(fileified);
e538e23f
CB
12609 if (vmsname != NULL)
12610 PerlMem_free(vmsname);
3a385817
GS
12611 return TRUE;
12612 }
ebd4d70b 12613 _ckvmssts_noperl(retsts);
748a9306 12614
a1887106
JM
12615 if (fileified != NULL)
12616 PerlMem_free(fileified);
e538e23f
CB
12617 if (vmsname != NULL)
12618 PerlMem_free(vmsname);
748a9306
LW
12619 return FALSE; /* Should never get here */
12620
a1887106
JM
12621}
12622
12623/* Do the permissions allow some operation? Assumes PL_statcache already set. */
12624/* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12625 * subset of the applicable information.
12626 */
12627bool
12628Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12629{
12630 return cando_by_name_int
12631 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12632} /* end of cando() */
12633/*}}}*/
12634
12635
12636/*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12637I32
12638Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12639{
12640 return cando_by_name_int(bit, effective, fname, 0);
12641
748a9306
LW
12642} /* end of cando_by_name() */
12643/*}}}*/
12644
12645
61bb5906 12646/*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
748a9306 12647int
fd8cd3a3 12648Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
748a9306 12649{
312ac60b 12650 if (!fstat(fd, &statbufp->crtl_stat)) {
75796008 12651 char *cptr;
988c775c
JM
12652 char *vms_filename;
12653 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12654 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
75796008 12655
988c775c
JM
12656 /* Save name for cando by name in VMS format */
12657 cptr = getname(fd, vms_filename, 1);
75796008 12658
988c775c
JM
12659 /* This should not happen, but just in case */
12660 if (cptr == NULL) {
12661 statbufp->st_devnam[0] = 0;
12662 }
12663 else {
12664 /* Make sure that the saved name fits in 255 characters */
6fb6c614 12665 cptr = int_rmsexpand_vms
988c775c
JM
12666 (vms_filename,
12667 statbufp->st_devnam,
6fb6c614 12668 0);
75796008 12669 if (cptr == NULL)
988c775c 12670 statbufp->st_devnam[0] = 0;
75796008 12671 }
988c775c 12672 PerlMem_free(vms_filename);
682e4b71
JM
12673
12674 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12675 VMS_DEVICE_ENCODE
12676 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
2497a41f 12677
61bb5906
CB
12678# ifdef RTL_USES_UTC
12679# ifdef VMSISH_TIME
12680 if (VMSISH_TIME) {
12681 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12682 statbufp->st_atime = _toloc(statbufp->st_atime);
12683 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12684 }
12685# endif
12686# else
ff0cee69 12687# ifdef VMSISH_TIME
12688 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12689# else
12690 if (1) {
12691# endif
61bb5906
CB
12692 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12693 statbufp->st_atime = _toutc(statbufp->st_atime);
12694 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 12695 }
61bb5906 12696#endif
b7ae7a0d 12697 return 0;
12698 }
12699 return -1;
748a9306
LW
12700
12701} /* end of flex_fstat() */
12702/*}}}*/
12703
2497a41f
JM
12704static int
12705Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
748a9306 12706{
312ac60b
JM
12707 char *fileified;
12708 char *temp_fspec;
12709 const char *save_spec;
12710 char *ret_spec;
bbce6d69 12711 int retval = -1;
312ac60b 12712 int efs_hack = 0;
4ee39169 12713 dSAVEDERRNO;
748a9306 12714
312ac60b
JM
12715 if (!fspec) {
12716 errno = EINVAL;
12717 return retval;
12718 }
988c775c 12719
2497a41f 12720 if (decc_bug_devnull != 0) {
312ac60b 12721 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
2497a41f 12722 memset(statbufp,0,sizeof *statbufp);
cfcfe586 12723 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
2497a41f
JM
12724 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12725 statbufp->st_uid = 0x00010001;
12726 statbufp->st_gid = 0x0001;
12727 time((time_t *)&statbufp->st_mtime);
12728 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12729 return 0;
12730 }
748a9306
LW
12731 }
12732
bbce6d69 12733 /* Try for a directory name first. If fspec contains a filename without
61bb5906 12734 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
bbce6d69 12735 * and sea:[wine.dark]water. exist, we prefer the directory here.
12736 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12737 * not sea:[wine.dark]., if the latter exists. If the intended target is
12738 * the file with null type, specify this by calling flex_stat() with
12739 * a '.' at the end of fspec.
2497a41f
JM
12740 *
12741 * If we are in Posix filespec mode, accept the filename as is.
bbce6d69 12742 */
f36b279d
CB
12743
12744
312ac60b
JM
12745 fileified = PerlMem_malloc(VMS_MAXRSS);
12746 if (fileified == NULL)
12747 _ckvmssts_noperl(SS$_INSFMEM);
12748
12749 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12750 if (temp_fspec == NULL)
12751 _ckvmssts_noperl(SS$_INSFMEM);
12752
12753 strcpy(temp_fspec, fspec);
12754
12755 SAVE_ERRNO;
f36b279d 12756
2497a41f
JM
12757#if __CRTL_VER >= 80200000 && !defined(__VAX)
12758 if (decc_posix_compliant_pathnames == 0) {
12759#endif
312ac60b
JM
12760
12761 /* We may be able to optimize this, but in order for fileify_dirspec to
12762 * always return a usuable answer, we have to call vmspath first to
12763 * make sure that it is in VMS directory format, as stat/lstat on 8.3
12764 * can not handle directories in unix format that it does not have read
12765 * access to. Vmspath handles the case where a bare name which could be
12766 * a logical name gets passed.
12767 */
12768 ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12769 if (ret_spec != NULL) {
d94c5a78 12770 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
312ac60b
JM
12771 if (ret_spec != NULL) {
12772 if (lstat_flag == 0)
12773 retval = stat(fileified, &statbufp->crtl_stat);
12774 else
12775 retval = lstat(fileified, &statbufp->crtl_stat);
12776 save_spec = fileified;
12777 }
748a9306 12778 }
312ac60b
JM
12779
12780 if (retval && vms_bug_stat_filename) {
12781
12782 /* We should try again as a vmsified file specification */
12783 /* However Perl traditionally has not done this, which */
12784 /* causes problems with existing tests */
12785
12786 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12787 if (ret_spec != NULL) {
12788 if (lstat_flag == 0)
12789 retval = stat(temp_fspec, &statbufp->crtl_stat);
12790 else
12791 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12792 save_spec = temp_fspec;
12793 }
2497a41f 12794 }
312ac60b 12795
f1db9cda 12796 if (retval) {
312ac60b
JM
12797 /* Last chance - allow multiple dots with out EFS CHARSET */
12798 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12799 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12800 * enable it if it isn't already.
12801 */
12802#if __CRTL_VER >= 70300000 && !defined(__VAX)
12803 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12804 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12805#endif
12806 if (lstat_flag == 0)
12807 retval = stat(fspec, &statbufp->crtl_stat);
12808 else
12809 retval = lstat(fspec, &statbufp->crtl_stat);
12810 save_spec = fspec;
12811#if __CRTL_VER >= 70300000 && !defined(__VAX)
12812 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12813 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12814 efs_hack = 1;
12815 }
12816#endif
f1db9cda 12817 }
312ac60b 12818
2497a41f
JM
12819#if __CRTL_VER >= 80200000 && !defined(__VAX)
12820 } else {
12821 if (lstat_flag == 0)
312ac60b 12822 retval = stat(temp_fspec, &statbufp->crtl_stat);
2497a41f 12823 else
312ac60b 12824 retval = lstat(temp_fspec, &statbufp->crtl_stat);
988c775c 12825 save_spec = temp_fspec;
2497a41f
JM
12826 }
12827#endif
f36b279d
CB
12828
12829#if __CRTL_VER >= 70300000 && !defined(__VAX)
12830 /* As you were... */
12831 if (!decc_efs_charset)
12832 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12833#endif
12834
ff0cee69 12835 if (!retval) {
988c775c 12836 char * cptr;
d584a1c6
JM
12837 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12838
12839 /* If this is an lstat, do not follow the link */
12840 if (lstat_flag)
12841 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12842
312ac60b
JM
12843#if __CRTL_VER >= 70300000 && !defined(__VAX)
12844 /* If we used the efs_hack above, we must also use it here for */
12845 /* perl_cando to work */
12846 if (efs_hack && (decc_efs_charset_index > 0)) {
12847 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12848 }
12849#endif
6fb6c614 12850 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
312ac60b
JM
12851#if __CRTL_VER >= 70300000 && !defined(__VAX)
12852 if (efs_hack && (decc_efs_charset_index > 0)) {
12853 decc$feature_set_value(decc_efs_charset, 1, 0);
12854 }
12855#endif
12856
12857 /* Fix me: If this is NULL then stat found a file, and we could */
12858 /* not convert the specification to VMS - Should never happen */
988c775c
JM
12859 if (cptr == NULL)
12860 statbufp->st_devnam[0] = 0;
12861
682e4b71 12862 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12863 VMS_DEVICE_ENCODE
12864 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
61bb5906
CB
12865# ifdef RTL_USES_UTC
12866# ifdef VMSISH_TIME
12867 if (VMSISH_TIME) {
12868 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12869 statbufp->st_atime = _toloc(statbufp->st_atime);
12870 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12871 }
12872# endif
12873# else
ff0cee69 12874# ifdef VMSISH_TIME
12875 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12876# else
12877 if (1) {
12878# endif
61bb5906
CB
12879 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12880 statbufp->st_atime = _toutc(statbufp->st_atime);
12881 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 12882 }
61bb5906 12883# endif
ff0cee69 12884 }
9543c6b6 12885 /* If we were successful, leave errno where we found it */
4ee39169 12886 if (retval == 0) RESTORE_ERRNO;
acb491c0
CB
12887 PerlMem_free(temp_fspec);
12888 PerlMem_free(fileified);
748a9306
LW
12889 return retval;
12890
2497a41f
JM
12891} /* end of flex_stat_int() */
12892
12893
12894/*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12895int
12896Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12897{
7ded3206 12898 return flex_stat_int(fspec, statbufp, 0);
2497a41f
JM
12899}
12900/*}}}*/
12901
12902/*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12903int
12904Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12905{
7ded3206 12906 return flex_stat_int(fspec, statbufp, 1);
2497a41f 12907}
748a9306
LW
12908/*}}}*/
12909
b7ae7a0d 12910
c07a80fd 12911/*{{{char *my_getlogin()*/
12912/* VMS cuserid == Unix getlogin, except calling sequence */
12913char *
2fbb330f 12914my_getlogin(void)
c07a80fd 12915{
12916 static char user[L_cuserid];
12917 return cuserid(user);
12918}
12919/*}}}*/
12920
12921
a5f75d66
AD
12922/* rmscopy - copy a file using VMS RMS routines
12923 *
12924 * Copies contents and attributes of spec_in to spec_out, except owner
12925 * and protection information. Name and type of spec_in are used as
a3e9d8c9 12926 * defaults for spec_out. The third parameter specifies whether rmscopy()
12927 * should try to propagate timestamps from the input file to the output file.
12928 * If it is less than 0, no timestamps are preserved. If it is 0, then
12929 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12930 * propagated to the output file at creation iff the output file specification
12931 * did not contain an explicit name or type, and the revision date is always
12932 * updated at the end of the copy operation. If it is greater than 0, then
12933 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12934 * other than the revision date should be propagated, and bit 1 indicates
12935 * that the revision date should be propagated.
12936 *
12937 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
a5f75d66 12938 *
bd3fa61c 12939 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
a5f75d66 12940 * Incorporates, with permission, some code from EZCOPY by Tim Adye
01b8edb6 12941 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12942 * as part of the Perl standard distribution under the terms of the
12943 * GNU General Public License or the Perl Artistic License. Copies
12944 * of each may be found in the Perl standard distribution.
a480973c 12945 */ /* FIXME */
a3e9d8c9 12946/*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
a480973c
JM
12947int
12948Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12949{
d584a1c6
JM
12950 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12951 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
4e0c9737 12952 unsigned long int sts;
a1887106 12953 int dna_len;
a480973c
JM
12954 struct FAB fab_in, fab_out;
12955 struct RAB rab_in, rab_out;
a1887106
JM
12956 rms_setup_nam(nam);
12957 rms_setup_nam(nam_out);
a480973c
JM
12958 struct XABDAT xabdat;
12959 struct XABFHC xabfhc;
12960 struct XABRDT xabrdt;
12961 struct XABSUM xabsum;
12962
c5375c28 12963 vmsin = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12964 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 12965 vmsout = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12966 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
df278665
JM
12967 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12968 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
c5375c28
JM
12969 PerlMem_free(vmsin);
12970 PerlMem_free(vmsout);
a480973c
JM
12971 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12972 return 0;
12973 }
12974
b1a8dcd7 12975 esa = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12976 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12977 esal = NULL;
12978#if !defined(__VAX) && defined(NAML$C_MAXRSS)
12979 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12980 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 12981#endif
a480973c 12982 fab_in = cc$rms_fab;
a1887106 12983 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
a480973c
JM
12984 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12985 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12986 fab_in.fab$l_fop = FAB$M_SQO;
a1887106 12987 rms_bind_fab_nam(fab_in, nam);
a480973c
JM
12988 fab_in.fab$l_xab = (void *) &xabdat;
12989
b1a8dcd7 12990 rsa = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12991 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12992 rsal = NULL;
12993#if !defined(__VAX) && defined(NAML$C_MAXRSS)
12994 rsal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12995 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12996#endif
12997 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12998 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
a1887106
JM
12999 rms_nam_esl(nam) = 0;
13000 rms_nam_rsl(nam) = 0;
13001 rms_nam_esll(nam) = 0;
13002 rms_nam_rsll(nam) = 0;
a480973c
JM
13003#ifdef NAM$M_NO_SHORT_UPCASE
13004 if (decc_efs_case_preserve)
a1887106 13005 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
a480973c
JM
13006#endif
13007
13008 xabdat = cc$rms_xabdat; /* To get creation date */
13009 xabdat.xab$l_nxt = (void *) &xabfhc;
13010
13011 xabfhc = cc$rms_xabfhc; /* To get record length */
13012 xabfhc.xab$l_nxt = (void *) &xabsum;
13013
13014 xabsum = cc$rms_xabsum; /* To get key and area information */
13015
13016 if (!((sts = sys$open(&fab_in)) & 1)) {
c5375c28
JM
13017 PerlMem_free(vmsin);
13018 PerlMem_free(vmsout);
13019 PerlMem_free(esa);
d584a1c6
JM
13020 if (esal != NULL)
13021 PerlMem_free(esal);
c5375c28 13022 PerlMem_free(rsa);
d584a1c6
JM
13023 if (rsal != NULL)
13024 PerlMem_free(rsal);
a480973c
JM
13025 set_vaxc_errno(sts);
13026 switch (sts) {
13027 case RMS$_FNF: case RMS$_DNF:
13028 set_errno(ENOENT); break;
13029 case RMS$_DIR:
13030 set_errno(ENOTDIR); break;
13031 case RMS$_DEV:
13032 set_errno(ENODEV); break;
13033 case RMS$_SYN:
13034 set_errno(EINVAL); break;
13035 case RMS$_PRV:
13036 set_errno(EACCES); break;
13037 default:
13038 set_errno(EVMSERR);
13039 }
13040 return 0;
13041 }
13042
13043 nam_out = nam;
13044 fab_out = fab_in;
13045 fab_out.fab$w_ifi = 0;
13046 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13047 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13048 fab_out.fab$l_fop = FAB$M_SQO;
a1887106
JM
13049 rms_bind_fab_nam(fab_out, nam_out);
13050 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13051 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13052 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
d584a1c6 13053 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 13054 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13055 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 13056 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13057 esal_out = NULL;
13058 rsal_out = NULL;
13059#if !defined(__VAX) && defined(NAML$C_MAXRSS)
13060 esal_out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13061 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13062 rsal_out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13063 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13064#endif
13065 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13066 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
a480973c
JM
13067
13068 if (preserve_dates == 0) { /* Act like DCL COPY */
a1887106 13069 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
a480973c 13070 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
a1887106 13071 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
13072 PerlMem_free(vmsin);
13073 PerlMem_free(vmsout);
13074 PerlMem_free(esa);
d584a1c6
JM
13075 if (esal != NULL)
13076 PerlMem_free(esal);
c5375c28 13077 PerlMem_free(rsa);
d584a1c6
JM
13078 if (rsal != NULL)
13079 PerlMem_free(rsal);
c5375c28 13080 PerlMem_free(esa_out);
d584a1c6
JM
13081 if (esal_out != NULL)
13082 PerlMem_free(esal_out);
13083 PerlMem_free(rsa_out);
13084 if (rsal_out != NULL)
13085 PerlMem_free(rsal_out);
a480973c
JM
13086 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13087 set_vaxc_errno(sts);
13088 return 0;
13089 }
13090 fab_out.fab$l_xab = (void *) &xabdat;
a1887106
JM
13091 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13092 preserve_dates = 1;
a480973c
JM
13093 }
13094 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
13095 preserve_dates =0; /* bitmask from this point forward */
13096
13097 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
a1887106 13098 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
13099 PerlMem_free(vmsin);
13100 PerlMem_free(vmsout);
13101 PerlMem_free(esa);
d584a1c6
JM
13102 if (esal != NULL)
13103 PerlMem_free(esal);
c5375c28 13104 PerlMem_free(rsa);
d584a1c6
JM
13105 if (rsal != NULL)
13106 PerlMem_free(rsal);
c5375c28 13107 PerlMem_free(esa_out);
d584a1c6
JM
13108 if (esal_out != NULL)
13109 PerlMem_free(esal_out);
13110 PerlMem_free(rsa_out);
13111 if (rsal_out != NULL)
13112 PerlMem_free(rsal_out);
a480973c
JM
13113 set_vaxc_errno(sts);
13114 switch (sts) {
13115 case RMS$_DNF:
13116 set_errno(ENOENT); break;
13117 case RMS$_DIR:
13118 set_errno(ENOTDIR); break;
13119 case RMS$_DEV:
13120 set_errno(ENODEV); break;
13121 case RMS$_SYN:
13122 set_errno(EINVAL); break;
13123 case RMS$_PRV:
13124 set_errno(EACCES); break;
13125 default:
13126 set_errno(EVMSERR);
13127 }
13128 return 0;
13129 }
13130 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
13131 if (preserve_dates & 2) {
13132 /* sys$close() will process xabrdt, not xabdat */
13133 xabrdt = cc$rms_xabrdt;
13134#ifndef __GNUC__
13135 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13136#else
13137 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13138 * is unsigned long[2], while DECC & VAXC use a struct */
13139 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13140#endif
13141 fab_out.fab$l_xab = (void *) &xabrdt;
13142 }
13143
c5375c28 13144 ubf = PerlMem_malloc(32256);
ebd4d70b 13145 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c
JM
13146 rab_in = cc$rms_rab;
13147 rab_in.rab$l_fab = &fab_in;
13148 rab_in.rab$l_rop = RAB$M_BIO;
13149 rab_in.rab$l_ubf = ubf;
13150 rab_in.rab$w_usz = 32256;
13151 if (!((sts = sys$connect(&rab_in)) & 1)) {
13152 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
13153 PerlMem_free(vmsin);
13154 PerlMem_free(vmsout);
c5375c28 13155 PerlMem_free(ubf);
d584a1c6
JM
13156 PerlMem_free(esa);
13157 if (esal != NULL)
13158 PerlMem_free(esal);
c5375c28 13159 PerlMem_free(rsa);
d584a1c6
JM
13160 if (rsal != NULL)
13161 PerlMem_free(rsal);
c5375c28 13162 PerlMem_free(esa_out);
d584a1c6
JM
13163 if (esal_out != NULL)
13164 PerlMem_free(esal_out);
13165 PerlMem_free(rsa_out);
13166 if (rsal_out != NULL)
13167 PerlMem_free(rsal_out);
a480973c
JM
13168 set_errno(EVMSERR); set_vaxc_errno(sts);
13169 return 0;
13170 }
13171
13172 rab_out = cc$rms_rab;
13173 rab_out.rab$l_fab = &fab_out;
13174 rab_out.rab$l_rbf = ubf;
13175 if (!((sts = sys$connect(&rab_out)) & 1)) {
13176 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
13177 PerlMem_free(vmsin);
13178 PerlMem_free(vmsout);
c5375c28 13179 PerlMem_free(ubf);
d584a1c6
JM
13180 PerlMem_free(esa);
13181 if (esal != NULL)
13182 PerlMem_free(esal);
c5375c28 13183 PerlMem_free(rsa);
d584a1c6
JM
13184 if (rsal != NULL)
13185 PerlMem_free(rsal);
c5375c28 13186 PerlMem_free(esa_out);
d584a1c6
JM
13187 if (esal_out != NULL)
13188 PerlMem_free(esal_out);
13189 PerlMem_free(rsa_out);
13190 if (rsal_out != NULL)
13191 PerlMem_free(rsal_out);
a480973c
JM
13192 set_errno(EVMSERR); set_vaxc_errno(sts);
13193 return 0;
13194 }
13195
13196 while ((sts = sys$read(&rab_in))) { /* always true */
13197 if (sts == RMS$_EOF) break;
13198 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13199 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13200 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
13201 PerlMem_free(vmsin);
13202 PerlMem_free(vmsout);
c5375c28 13203 PerlMem_free(ubf);
d584a1c6
JM
13204 PerlMem_free(esa);
13205 if (esal != NULL)
13206 PerlMem_free(esal);
c5375c28 13207 PerlMem_free(rsa);
d584a1c6
JM
13208 if (rsal != NULL)
13209 PerlMem_free(rsal);
c5375c28 13210 PerlMem_free(esa_out);
d584a1c6
JM
13211 if (esal_out != NULL)
13212 PerlMem_free(esal_out);
13213 PerlMem_free(rsa_out);
13214 if (rsal_out != NULL)
13215 PerlMem_free(rsal_out);
a480973c
JM
13216 set_errno(EVMSERR); set_vaxc_errno(sts);
13217 return 0;
13218 }
13219 }
13220
13221
13222 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
13223 sys$close(&fab_in); sys$close(&fab_out);
13224 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
a480973c 13225
c5375c28
JM
13226 PerlMem_free(vmsin);
13227 PerlMem_free(vmsout);
c5375c28 13228 PerlMem_free(ubf);
d584a1c6
JM
13229 PerlMem_free(esa);
13230 if (esal != NULL)
13231 PerlMem_free(esal);
c5375c28 13232 PerlMem_free(rsa);
d584a1c6
JM
13233 if (rsal != NULL)
13234 PerlMem_free(rsal);
c5375c28 13235 PerlMem_free(esa_out);
d584a1c6
JM
13236 if (esal_out != NULL)
13237 PerlMem_free(esal_out);
13238 PerlMem_free(rsa_out);
13239 if (rsal_out != NULL)
13240 PerlMem_free(rsal_out);
13241
13242 if (!(sts & 1)) {
13243 set_errno(EVMSERR); set_vaxc_errno(sts);
13244 return 0;
13245 }
13246
a480973c
JM
13247 return 1;
13248
13249} /* end of rmscopy() */
a5f75d66
AD
13250/*}}}*/
13251
13252
748a9306
LW
13253/*** The following glue provides 'hooks' to make some of the routines
13254 * from this file available from Perl. These routines are sufficiently
13255 * basic, and are required sufficiently early in the build process,
13256 * that's it's nice to have them available to miniperl as well as the
13257 * full Perl, so they're set up here instead of in an extension. The
13258 * Perl code which handles importation of these names into a given
13259 * package lives in [.VMS]Filespec.pm in @INC.
13260 */
13261
13262void
5c84aa53 13263rmsexpand_fromperl(pTHX_ CV *cv)
01b8edb6 13264{
13265 dXSARGS;
bbce6d69 13266 char *fspec, *defspec = NULL, *rslt;
2d8e6c8d 13267 STRLEN n_a;
360732b5 13268 int fs_utf8, dfs_utf8;
01b8edb6 13269
360732b5
JM
13270 fs_utf8 = 0;
13271 dfs_utf8 = 0;
bbce6d69 13272 if (!items || items > 2)
5c84aa53 13273 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
2d8e6c8d 13274 fspec = SvPV(ST(0),n_a);
360732b5 13275 fs_utf8 = SvUTF8(ST(0));
bbce6d69 13276 if (!fspec || !*fspec) XSRETURN_UNDEF;
360732b5
JM
13277 if (items == 2) {
13278 defspec = SvPV(ST(1),n_a);
13279 dfs_utf8 = SvUTF8(ST(1));
13280 }
13281 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
bbce6d69 13282 ST(0) = sv_newmortal();
360732b5
JM
13283 if (rslt != NULL) {
13284 sv_usepvn(ST(0),rslt,strlen(rslt));
13285 if (fs_utf8) {
13286 SvUTF8_on(ST(0));
13287 }
13288 }
740ce14c 13289 XSRETURN(1);
01b8edb6 13290}
13291
13292void
5c84aa53 13293vmsify_fromperl(pTHX_ CV *cv)
748a9306
LW
13294{
13295 dXSARGS;
13296 char *vmsified;
2d8e6c8d 13297 STRLEN n_a;
360732b5 13298 int utf8_fl;
748a9306 13299
5c84aa53 13300 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
360732b5
JM
13301 utf8_fl = SvUTF8(ST(0));
13302 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13303 ST(0) = sv_newmortal();
360732b5
JM
13304 if (vmsified != NULL) {
13305 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13306 if (utf8_fl) {
13307 SvUTF8_on(ST(0));
13308 }
13309 }
748a9306
LW
13310 XSRETURN(1);
13311}
13312
13313void
5c84aa53 13314unixify_fromperl(pTHX_ CV *cv)
748a9306
LW
13315{
13316 dXSARGS;
13317 char *unixified;
2d8e6c8d 13318 STRLEN n_a;
360732b5 13319 int utf8_fl;
748a9306 13320
5c84aa53 13321 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
360732b5
JM
13322 utf8_fl = SvUTF8(ST(0));
13323 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13324 ST(0) = sv_newmortal();
360732b5
JM
13325 if (unixified != NULL) {
13326 sv_usepvn(ST(0),unixified,strlen(unixified));
13327 if (utf8_fl) {
13328 SvUTF8_on(ST(0));
13329 }
13330 }
748a9306
LW
13331 XSRETURN(1);
13332}
13333
13334void
5c84aa53 13335fileify_fromperl(pTHX_ CV *cv)
748a9306
LW
13336{
13337 dXSARGS;
13338 char *fileified;
2d8e6c8d 13339 STRLEN n_a;
360732b5 13340 int utf8_fl;
748a9306 13341
5c84aa53 13342 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
360732b5
JM
13343 utf8_fl = SvUTF8(ST(0));
13344 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13345 ST(0) = sv_newmortal();
360732b5
JM
13346 if (fileified != NULL) {
13347 sv_usepvn(ST(0),fileified,strlen(fileified));
13348 if (utf8_fl) {
13349 SvUTF8_on(ST(0));
13350 }
13351 }
748a9306
LW
13352 XSRETURN(1);
13353}
13354
13355void
5c84aa53 13356pathify_fromperl(pTHX_ CV *cv)
748a9306
LW
13357{
13358 dXSARGS;
13359 char *pathified;
2d8e6c8d 13360 STRLEN n_a;
360732b5 13361 int utf8_fl;
748a9306 13362
5c84aa53 13363 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
360732b5
JM
13364 utf8_fl = SvUTF8(ST(0));
13365 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13366 ST(0) = sv_newmortal();
360732b5
JM
13367 if (pathified != NULL) {
13368 sv_usepvn(ST(0),pathified,strlen(pathified));
13369 if (utf8_fl) {
13370 SvUTF8_on(ST(0));
13371 }
13372 }
748a9306
LW
13373 XSRETURN(1);
13374}
13375
13376void
5c84aa53 13377vmspath_fromperl(pTHX_ CV *cv)
748a9306
LW
13378{
13379 dXSARGS;
13380 char *vmspath;
2d8e6c8d 13381 STRLEN n_a;
360732b5 13382 int utf8_fl;
748a9306 13383
5c84aa53 13384 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
360732b5
JM
13385 utf8_fl = SvUTF8(ST(0));
13386 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13387 ST(0) = sv_newmortal();
360732b5
JM
13388 if (vmspath != NULL) {
13389 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13390 if (utf8_fl) {
13391 SvUTF8_on(ST(0));
13392 }
13393 }
748a9306
LW
13394 XSRETURN(1);
13395}
13396
13397void
5c84aa53 13398unixpath_fromperl(pTHX_ CV *cv)
748a9306
LW
13399{
13400 dXSARGS;
13401 char *unixpath;
2d8e6c8d 13402 STRLEN n_a;
360732b5 13403 int utf8_fl;
748a9306 13404
5c84aa53 13405 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
360732b5
JM
13406 utf8_fl = SvUTF8(ST(0));
13407 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13408 ST(0) = sv_newmortal();
360732b5
JM
13409 if (unixpath != NULL) {
13410 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13411 if (utf8_fl) {
13412 SvUTF8_on(ST(0));
13413 }
13414 }
748a9306
LW
13415 XSRETURN(1);
13416}
13417
13418void
5c84aa53 13419candelete_fromperl(pTHX_ CV *cv)
748a9306
LW
13420{
13421 dXSARGS;
988c775c 13422 char *fspec, *fsp;
a5f75d66
AD
13423 SV *mysv;
13424 IO *io;
2d8e6c8d 13425 STRLEN n_a;
748a9306 13426
5c84aa53 13427 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
a5f75d66
AD
13428
13429 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
988c775c
JM
13430 Newx(fspec, VMS_MAXRSS, char);
13431 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
6d24fbd1 13432 if (isGV_with_GP(mysv)) {
a15cef0c 13433 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
a5f75d66 13434 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13435 ST(0) = &PL_sv_no;
988c775c 13436 Safefree(fspec);
a5f75d66
AD
13437 XSRETURN(1);
13438 }
13439 fsp = fspec;
13440 }
13441 else {
2d8e6c8d 13442 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
a5f75d66 13443 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13444 ST(0) = &PL_sv_no;
988c775c 13445 Safefree(fspec);
a5f75d66
AD
13446 XSRETURN(1);
13447 }
13448 }
13449
54310121 13450 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
988c775c 13451 Safefree(fspec);
a5f75d66
AD
13452 XSRETURN(1);
13453}
13454
13455void
5c84aa53 13456rmscopy_fromperl(pTHX_ CV *cv)
a5f75d66
AD
13457{
13458 dXSARGS;
a480973c 13459 char *inspec, *outspec, *inp, *outp;
a3e9d8c9 13460 int date_flag;
a5f75d66
AD
13461 SV *mysv;
13462 IO *io;
2d8e6c8d 13463 STRLEN n_a;
a5f75d66 13464
a3e9d8c9 13465 if (items < 2 || items > 3)
5c84aa53 13466 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
a5f75d66
AD
13467
13468 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
a480973c 13469 Newx(inspec, VMS_MAXRSS, char);
6d24fbd1 13470 if (isGV_with_GP(mysv)) {
a15cef0c 13471 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
a5f75d66 13472 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13473 ST(0) = sv_2mortal(newSViv(0));
a480973c 13474 Safefree(inspec);
a5f75d66
AD
13475 XSRETURN(1);
13476 }
13477 inp = inspec;
13478 }
13479 else {
2d8e6c8d 13480 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
a5f75d66 13481 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13482 ST(0) = sv_2mortal(newSViv(0));
a480973c 13483 Safefree(inspec);
a5f75d66
AD
13484 XSRETURN(1);
13485 }
13486 }
13487 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
a480973c 13488 Newx(outspec, VMS_MAXRSS, char);
6d24fbd1 13489 if (isGV_with_GP(mysv)) {
a15cef0c 13490 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
a5f75d66 13491 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13492 ST(0) = sv_2mortal(newSViv(0));
a480973c
JM
13493 Safefree(inspec);
13494 Safefree(outspec);
a5f75d66
AD
13495 XSRETURN(1);
13496 }
13497 outp = outspec;
13498 }
13499 else {
2d8e6c8d 13500 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
a5f75d66 13501 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13502 ST(0) = sv_2mortal(newSViv(0));
a480973c
JM
13503 Safefree(inspec);
13504 Safefree(outspec);
a5f75d66
AD
13505 XSRETURN(1);
13506 }
13507 }
a3e9d8c9 13508 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
a5f75d66 13509
fd188159 13510 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
a480973c
JM
13511 Safefree(inspec);
13512 Safefree(outspec);
748a9306
LW
13513 XSRETURN(1);
13514}
13515
a480973c
JM
13516/* The mod2fname is limited to shorter filenames by design, so it should
13517 * not be modified to support longer EFS pathnames
13518 */
4b19af01 13519void
fd8cd3a3 13520mod2fname(pTHX_ CV *cv)
4b19af01
CB
13521{
13522 dXSARGS;
13523 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13524 workbuff[NAM$C_MAXRSS*1 + 1];
4e0c9737 13525 int counter, num_entries;
4b19af01
CB
13526 /* ODS-5 ups this, but we want to be consistent, so... */
13527 int max_name_len = 39;
13528 AV *in_array = (AV *)SvRV(ST(0));
13529
13530 num_entries = av_len(in_array);
13531
13532 /* All the names start with PL_. */
13533 strcpy(ultimate_name, "PL_");
13534
13535 /* Clean up our working buffer */
13536 Zero(work_name, sizeof(work_name), char);
13537
13538 /* Run through the entries and build up a working name */
13539 for(counter = 0; counter <= num_entries; counter++) {
13540 /* If it's not the first name then tack on a __ */
13541 if (counter) {
13542 strcat(work_name, "__");
13543 }
bfd025d9 13544 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
4b19af01
CB
13545 }
13546
13547 /* Check to see if we actually have to bother...*/
13548 if (strlen(work_name) + 3 <= max_name_len) {
13549 strcat(ultimate_name, work_name);
13550 } else {
13551 /* It's too darned big, so we need to go strip. We use the same */
13552 /* algorithm as xsubpp does. First, strip out doubled __ */
13553 char *source, *dest, last;
13554 dest = workbuff;
13555 last = 0;
13556 for (source = work_name; *source; source++) {
13557 if (last == *source && last == '_') {
13558 continue;
13559 }
13560 *dest++ = *source;
13561 last = *source;
13562 }
13563 /* Go put it back */
13564 strcpy(work_name, workbuff);
13565 /* Is it still too big? */
13566 if (strlen(work_name) + 3 > max_name_len) {
13567 /* Strip duplicate letters */
13568 last = 0;
13569 dest = workbuff;
13570 for (source = work_name; *source; source++) {
13571 if (last == toupper(*source)) {
13572 continue;
13573 }
13574 *dest++ = *source;
13575 last = toupper(*source);
13576 }
13577 strcpy(work_name, workbuff);
13578 }
13579
13580 /* Is it *still* too big? */
13581 if (strlen(work_name) + 3 > max_name_len) {
13582 /* Too bad, we truncate */
13583 work_name[max_name_len - 2] = 0;
13584 }
13585 strcat(ultimate_name, work_name);
13586 }
13587
13588 /* Okay, return it */
13589 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13590 XSRETURN(1);
13591}
13592
748a9306 13593void
96e176bf
CL
13594hushexit_fromperl(pTHX_ CV *cv)
13595{
13596 dXSARGS;
13597
13598 if (items > 0) {
13599 VMSISH_HUSHED = SvTRUE(ST(0));
13600 }
13601 ST(0) = boolSV(VMSISH_HUSHED);
13602 XSRETURN(1);
13603}
13604
dca5a913
JM
13605
13606PerlIO *
13607Perl_vms_start_glob
13608 (pTHX_ SV *tmpglob,
13609 IO *io)
13610{
13611 PerlIO *fp;
13612 struct vs_str_st *rslt;
13613 char *vmsspec;
13614 char *rstr;
13615 char *begin, *cp;
13616 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13617 PerlIO *tmpfp;
13618 STRLEN i;
13619 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13620 struct dsc$descriptor_vs rsdsc;
13621 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13622 unsigned long hasver = 0, isunix = 0;
13623 unsigned long int lff_flags = 0;
13624 int rms_sts;
85e7c9de 13625 int vms_old_glob = 1;
dca5a913 13626
83b907a4
CB
13627 if (!SvOK(tmpglob)) {
13628 SETERRNO(ENOENT,RMS$_FNF);
13629 return NULL;
13630 }
13631
85e7c9de
JM
13632 vms_old_glob = !decc_filename_unix_report;
13633
dca5a913
JM
13634#ifdef VMS_LONGNAME_SUPPORT
13635 lff_flags = LIB$M_FIL_LONG_NAMES;
13636#endif
13637 /* The Newx macro will not allow me to assign a smaller array
13638 * to the rslt pointer, so we will assign it to the begin char pointer
13639 * and then copy the value into the rslt pointer.
13640 */
13641 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13642 rslt = (struct vs_str_st *)begin;
13643 rslt->length = 0;
13644 rstr = &rslt->str[0];
13645 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13646 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13647 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13648 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13649
13650 Newx(vmsspec, VMS_MAXRSS, char);
13651
13652 /* We could find out if there's an explicit dev/dir or version
13653 by peeking into lib$find_file's internal context at
13654 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13655 but that's unsupported, so I don't want to do it now and
13656 have it bite someone in the future. */
13657 /* Fix-me: vms_split_path() is the only way to do this, the
13658 existing method will fail with many legal EFS or UNIX specifications
13659 */
13660
13661 cp = SvPV(tmpglob,i);
13662
13663 for (; i; i--) {
13664 if (cp[i] == ';') hasver = 1;
13665 if (cp[i] == '.') {
13666 if (sts) hasver = 1;
13667 else sts = 1;
13668 }
13669 if (cp[i] == '/') {
13670 hasdir = isunix = 1;
13671 break;
13672 }
13673 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13674 hasdir = 1;
13675 break;
13676 }
13677 }
85e7c9de
JM
13678
13679 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13680 if ((hasdir == 0) && decc_filename_unix_report) {
13681 isunix = 1;
13682 }
13683
dca5a913 13684 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
85e7c9de
JM
13685 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13686 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13687 int wildstar = 0;
13688 int wildquery = 0;
990cad08 13689 int found = 0;
dca5a913
JM
13690 Stat_t st;
13691 int stat_sts;
13692 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13693 if (!stat_sts && S_ISDIR(st.st_mode)) {
85e7c9de
JM
13694 char * vms_dir;
13695 const char * fname;
13696 STRLEN fname_len;
13697
13698 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13699 /* path delimiter of ':>]', if so, then the old behavior has */
94ae10c0 13700 /* obviously been specifically requested */
85e7c9de
JM
13701
13702 fname = SvPVX_const(tmpglob);
13703 fname_len = strlen(fname);
13704 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13705 if (vms_old_glob || (vms_dir != NULL)) {
13706 wilddsc.dsc$a_pointer = tovmspath_utf8(
13707 SvPVX(tmpglob),vmsspec,NULL);
13708 ok = (wilddsc.dsc$a_pointer != NULL);
13709 /* maybe passed 'foo' rather than '[.foo]', thus not
13710 detected above */
13711 hasdir = 1;
13712 } else {
13713 /* Operate just on the directory, the special stat/fstat for */
13714 /* leaves the fileified specification in the st_devnam */
13715 /* member. */
13716 wilddsc.dsc$a_pointer = st.st_devnam;
13717 ok = 1;
13718 }
dca5a913
JM
13719 }
13720 else {
360732b5 13721 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
dca5a913
JM
13722 ok = (wilddsc.dsc$a_pointer != NULL);
13723 }
13724 if (ok)
13725 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13726
13727 /* If not extended character set, replace ? with % */
13728 /* With extended character set, ? is a wildcard single character */
85e7c9de
JM
13729 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13730 if (*cp == '?') {
13731 wildquery = 1;
13732 if (!decc_efs_case_preserve)
13733 *cp = '%';
13734 } else if (*cp == '%') {
13735 wildquery = 1;
13736 } else if (*cp == '*') {
13737 wildstar = 1;
13738 }
dca5a913 13739 }
85e7c9de
JM
13740
13741 if (ok) {
13742 wv_sts = vms_split_path(
13743 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13744 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13745 &wvs_spec, &wvs_len);
13746 } else {
13747 wn_spec = NULL;
13748 wn_len = 0;
13749 we_spec = NULL;
13750 we_len = 0;
13751 }
13752
dca5a913
JM
13753 sts = SS$_NORMAL;
13754 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13755 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13756 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
85e7c9de 13757 int valid_find;
dca5a913 13758
85e7c9de 13759 valid_find = 0;
dca5a913
JM
13760 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13761 &dfltdsc,NULL,&rms_sts,&lff_flags);
13762 if (!$VMS_STATUS_SUCCESS(sts))
13763 break;
13764
13765 /* with varying string, 1st word of buffer contains result length */
13766 rstr[rslt->length] = '\0';
13767
13768 /* Find where all the components are */
13769 v_sts = vms_split_path
360732b5 13770 (rstr,
dca5a913
JM
13771 &v_spec,
13772 &v_len,
13773 &r_spec,
13774 &r_len,
13775 &d_spec,
13776 &d_len,
13777 &n_spec,
13778 &n_len,
13779 &e_spec,
13780 &e_len,
13781 &vs_spec,
13782 &vs_len);
13783
13784 /* If no version on input, truncate the version on output */
13785 if (!hasver && (vs_len > 0)) {
13786 *vs_spec = '\0';
13787 vs_len = 0;
85e7c9de
JM
13788 }
13789
13790 if (isunix) {
13791
13792 /* In Unix report mode, remove the ".dir;1" from the name */
13793 /* if it is a real directory */
13794 if (decc_filename_unix_report || decc_efs_charset) {
13795 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13796 Stat_t statbuf;
13797 int ret_sts;
13798
13799 ret_sts = flex_lstat(rstr, &statbuf);
13800 if ((ret_sts == 0) &&
13801 S_ISDIR(statbuf.st_mode)) {
13802 e_len = 0;
13803 e_spec[0] = 0;
13804 }
13805 }
13806 }
dca5a913
JM
13807
13808 /* No version & a null extension on UNIX handling */
85e7c9de 13809 if ((e_len == 1) && decc_readdir_dropdotnotype) {
dca5a913
JM
13810 e_len = 0;
13811 *e_spec = '\0';
13812 }
13813 }
13814
13815 if (!decc_efs_case_preserve) {
13816 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13817 }
13818
85e7c9de
JM
13819 /* Find File treats a Null extension as return all extensions */
13820 /* This is contrary to Perl expectations */
13821
13822 if (wildstar || wildquery || vms_old_glob) {
13823 /* really need to see if the returned file name matched */
13824 /* but for now will assume that it matches */
13825 valid_find = 1;
13826 } else {
13827 /* Exact Match requested */
13828 /* How are directories handled? - like a file */
13829 if ((e_len == we_len) && (n_len == wn_len)) {
13830 int t1;
13831 t1 = e_len;
13832 if (t1 > 0)
13833 t1 = strncmp(e_spec, we_spec, e_len);
13834 if (t1 == 0) {
13835 t1 = n_len;
13836 if (t1 > 0)
13837 t1 = strncmp(n_spec, we_spec, n_len);
13838 if (t1 == 0)
13839 valid_find = 1;
13840 }
13841 }
13842 }
13843
13844 if (valid_find) {
13845 found++;
13846
13847 if (hasdir) {
13848 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13849 begin = rstr;
13850 }
13851 else {
13852 /* Start with the name */
13853 begin = n_spec;
13854 }
13855 strcat(begin,"\n");
13856 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13857 }
dca5a913
JM
13858 }
13859 if (cxt) (void)lib$find_file_end(&cxt);
990cad08
CB
13860
13861 if (!found) {
13862 /* Be POSIXish: return the input pattern when no matches */
2da7a6b5
CB
13863 strcpy(rstr,SvPVX(tmpglob));
13864 strcat(rstr,"\n");
13865 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
990cad08
CB
13866 }
13867
dca5a913
JM
13868 if (ok && sts != RMS$_NMF &&
13869 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13870 if (!ok) {
13871 if (!(sts & 1)) {
13872 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13873 }
13874 PerlIO_close(tmpfp);
13875 fp = NULL;
13876 }
13877 else {
13878 PerlIO_rewind(tmpfp);
13879 IoTYPE(io) = IoTYPE_RDONLY;
13880 IoIFP(io) = fp = tmpfp;
13881 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13882 }
13883 }
13884 Safefree(vmsspec);
13885 Safefree(rslt);
13886 return fp;
13887}
13888
cd1191f1 13889
2497a41f 13890static char *
5c4d031a 13891mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
d584a1c6 13892 int *utf8_fl);
2497a41f
JM
13893
13894void
4d8d3a9c 13895unixrealpath_fromperl(pTHX_ CV *cv)
2497a41f 13896{
d584a1c6
JM
13897 dXSARGS;
13898 char *fspec, *rslt_spec, *rslt;
13899 STRLEN n_a;
2497a41f 13900
d584a1c6 13901 if (!items || items != 1)
4d8d3a9c 13902 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
2497a41f 13903
d584a1c6
JM
13904 fspec = SvPV(ST(0),n_a);
13905 if (!fspec || !*fspec) XSRETURN_UNDEF;
2497a41f 13906
d584a1c6
JM
13907 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13908 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13909
13910 ST(0) = sv_newmortal();
13911 if (rslt != NULL)
13912 sv_usepvn(ST(0),rslt,strlen(rslt));
13913 else
13914 Safefree(rslt_spec);
13915 XSRETURN(1);
2497a41f 13916}
2ee6e19d 13917
b1a8dcd7
JM
13918static char *
13919mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13920 int *utf8_fl);
13921
13922void
4d8d3a9c 13923vmsrealpath_fromperl(pTHX_ CV *cv)
b1a8dcd7
JM
13924{
13925 dXSARGS;
13926 char *fspec, *rslt_spec, *rslt;
13927 STRLEN n_a;
13928
13929 if (!items || items != 1)
4d8d3a9c 13930 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
b1a8dcd7
JM
13931
13932 fspec = SvPV(ST(0),n_a);
13933 if (!fspec || !*fspec) XSRETURN_UNDEF;
13934
13935 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13936 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13937
13938 ST(0) = sv_newmortal();
13939 if (rslt != NULL)
13940 sv_usepvn(ST(0),rslt,strlen(rslt));
13941 else
13942 Safefree(rslt_spec);
13943 XSRETURN(1);
13944}
13945
13946#ifdef HAS_SYMLINK
2ee6e19d
CB
13947/*
13948 * A thin wrapper around decc$symlink to make sure we follow the
13949 * standard and do not create a symlink with a zero-length name.
4148925f
JM
13950 *
13951 * Also in ODS-2 mode, existing tests assume that the link target
13952 * will be converted to UNIX format.
2ee6e19d 13953 */
4148925f
JM
13954/*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13955int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13956 if (!link_name || !*link_name) {
2ee6e19d
CB
13957 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13958 return -1;
13959 }
4148925f
JM
13960
13961 if (decc_efs_charset) {
13962 return symlink(contents, link_name);
13963 } else {
13964 int sts;
13965 char * utarget;
13966
13967 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13968 /* because in order to work, the symlink target must be in UNIX format */
13969
13970 /* As symbolic links can hold things other than files, we will only do */
13971 /* the conversion in in ODS-2 mode */
13972
4d9538c1 13973 utarget = PerlMem_malloc(VMS_MAXRSS + 1);
0e5ce2c7 13974 if (int_tounixspec(contents, utarget, NULL) == NULL) {
4148925f
JM
13975
13976 /* This should not fail, as an untranslatable filename */
13977 /* should be passed through */
13978 utarget = (char *)contents;
13979 }
13980 sts = symlink(utarget, link_name);
4d9538c1 13981 PerlMem_free(utarget);
4148925f
JM
13982 return sts;
13983 }
13984
2ee6e19d
CB
13985}
13986/*}}}*/
13987
13988#endif /* HAS_SYMLINK */
2497a41f 13989
2497a41f
JM
13990int do_vms_case_tolerant(void);
13991
13992void
4d8d3a9c 13993case_tolerant_process_fromperl(pTHX_ CV *cv)
2497a41f
JM
13994{
13995 dXSARGS;
13996 ST(0) = boolSV(do_vms_case_tolerant());
13997 XSRETURN(1);
13998}
2497a41f 13999
9ec7171b
CB
14000#ifdef USE_ITHREADS
14001
96e176bf
CL
14002void
14003Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14004 struct interp_intern *dst)
14005{
7918f24d
NC
14006 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14007
96e176bf
CL
14008 memcpy(dst,src,sizeof(struct interp_intern));
14009}
14010
9ec7171b
CB
14011#endif
14012
96e176bf
CL
14013void
14014Perl_sys_intern_clear(pTHX)
14015{
14016}
14017
14018void
14019Perl_sys_intern_init(pTHX)
14020{
3ff49832
CL
14021 unsigned int ix = RAND_MAX;
14022 double x;
96e176bf
CL
14023
14024 VMSISH_HUSHED = 0;
14025
1a3aec58 14026 MY_POSIX_EXIT = vms_posix_exit;
7a7fd8e0 14027
96e176bf
CL
14028 x = (float)ix;
14029 MY_INV_RAND_MAX = 1./x;
ff7adb52 14030}
96e176bf
CL
14031
14032void
f7ddb74a 14033init_os_extras(void)
748a9306 14034{
a69a6dba 14035 dTHX;
748a9306 14036 char* file = __FILE__;
988c775c 14037 if (decc_disable_to_vms_logname_translation) {
93948341
CB
14038 no_translate_barewords = TRUE;
14039 } else {
14040 no_translate_barewords = FALSE;
14041 }
748a9306 14042
740ce14c 14043 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
a5f75d66
AD
14044 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14045 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14046 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14047 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14048 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14049 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14050 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
4b19af01 14051 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
a5f75d66 14052 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
96e176bf 14053 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
4d8d3a9c
CB
14054 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14055 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14056 newXSproto("VMS::Filespec::case_tolerant_process",
14057 case_tolerant_process_fromperl,file,"");
17f28c40 14058
afd8f436 14059 store_pipelocs(aTHX); /* will redo any earlier attempts */
22d4bb9c 14060
748a9306
LW
14061 return;
14062}
14063
f7ddb74a
JM
14064#if __CRTL_VER == 80200000
14065/* This missed getting in to the DECC SDK for 8.2 */
14066char *realpath(const char *file_name, char * resolved_name, ...);
14067#endif
14068
14069/*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14070/* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14071 * The perl fallback routine to provide realpath() is not as efficient
14072 * on OpenVMS.
14073 */
d584a1c6
JM
14074
14075/* Hack, use old stat() as fastest way of getting ino_t and device */
14076int decc$stat(const char *name, void * statbuf);
312ac60b
JM
14077#if !defined(__VAX) && __CRTL_VER >= 80200000
14078int decc$lstat(const char *name, void * statbuf);
14079#else
14080#define decc$lstat decc$stat
14081#endif
d584a1c6
JM
14082
14083
14084/* Realpath is fragile. In 8.3 it does not work if the feature
14085 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14086 * links are implemented in RMS, not the CRTL. It also can fail if the
14087 * user does not have read/execute access to some of the directories.
14088 * So in order for Do What I Mean mode to work, if realpath() fails,
14089 * fall back to looking up the filename by the device name and FID.
14090 */
14091
312ac60b
JM
14092int vms_fid_to_name(char * outname, int outlen,
14093 const char * name, int lstat_flag, mode_t * mode)
d584a1c6 14094{
312ac60b
JM
14095#pragma message save
14096#pragma message disable MISALGNDSTRCT
14097#pragma message disable MISALGNDMEM
14098#pragma member_alignment save
14099#pragma nomember_alignment
d584a1c6
JM
14100struct statbuf_t {
14101 char * st_dev;
b1a8dcd7 14102 unsigned short st_ino[3];
312ac60b 14103 unsigned short old_st_mode;
d584a1c6
JM
14104 unsigned long padl[30]; /* plenty of room */
14105} statbuf;
312ac60b
JM
14106#pragma message restore
14107#pragma member_alignment restore
14108
14109 int sts;
14110 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14111 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14112 char *fileified;
14113 char *temp_fspec;
14114 char *ret_spec;
14115
14116 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14117 * unexpected answers
14118 */
14119
14120 fileified = PerlMem_malloc(VMS_MAXRSS);
14121 if (fileified == NULL)
14122 _ckvmssts_noperl(SS$_INSFMEM);
14123
14124 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14125 if (temp_fspec == NULL)
14126 _ckvmssts_noperl(SS$_INSFMEM);
14127
14128 sts = -1;
14129 /* First need to try as a directory */
14130 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14131 if (ret_spec != NULL) {
14132 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14133 if (ret_spec != NULL) {
14134 if (lstat_flag == 0)
14135 sts = decc$stat(fileified, &statbuf);
14136 else
14137 sts = decc$lstat(fileified, &statbuf);
14138 }
14139 }
14140
14141 /* Then as a VMS file spec */
14142 if (sts != 0) {
14143 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14144 if (ret_spec != NULL) {
14145 if (lstat_flag == 0) {
14146 sts = decc$stat(temp_fspec, &statbuf);
14147 } else {
14148 sts = decc$lstat(temp_fspec, &statbuf);
14149 }
14150 }
14151 }
14152
14153 if (sts) {
14154 /* Next try - allow multiple dots with out EFS CHARSET */
14155 /* The CRTL stat() falls down hard on multi-dot filenames in unix
14156 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14157 * enable it if it isn't already.
14158 */
14159#if __CRTL_VER >= 70300000 && !defined(__VAX)
14160 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14161 decc$feature_set_value(decc_efs_charset_index, 1, 1);
14162#endif
14163 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14164 if (lstat_flag == 0) {
14165 sts = decc$stat(name, &statbuf);
14166 } else {
14167 sts = decc$lstat(name, &statbuf);
14168 }
14169#if __CRTL_VER >= 70300000 && !defined(__VAX)
14170 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14171 decc$feature_set_value(decc_efs_charset_index, 1, 0);
14172#endif
14173 }
14174
14175
14176 /* and then because the Perl Unix to VMS conversion is not perfect */
14177 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14178 /* characters from filenames so we need to try it as-is */
14179 if (sts) {
14180 if (lstat_flag == 0) {
14181 sts = decc$stat(name, &statbuf);
14182 } else {
14183 sts = decc$lstat(name, &statbuf);
14184 }
14185 }
d584a1c6 14186
d584a1c6 14187 if (sts == 0) {
312ac60b 14188 int vms_sts;
d584a1c6
JM
14189
14190 dvidsc.dsc$a_pointer=statbuf.st_dev;
d94c5a78 14191 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
d584a1c6
JM
14192
14193 specdsc.dsc$a_pointer = outname;
14194 specdsc.dsc$w_length = outlen-1;
14195
d94c5a78 14196 vms_sts = lib$fid_to_name
d584a1c6 14197 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
d94c5a78 14198 if ($VMS_STATUS_SUCCESS(vms_sts)) {
d584a1c6 14199 outname[specdsc.dsc$w_length] = 0;
312ac60b
JM
14200
14201 /* Return the mode */
14202 if (mode) {
14203 *mode = statbuf.old_st_mode;
14204 }
d584a1c6
JM
14205 }
14206 }
9e2bec02
CB
14207 PerlMem_free(temp_fspec);
14208 PerlMem_free(fileified);
d584a1c6
JM
14209 return sts;
14210}
14211
14212
14213
f7ddb74a 14214static char *
5c4d031a 14215mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
d584a1c6 14216 int *utf8_fl)
f7ddb74a 14217{
d584a1c6
JM
14218 char * rslt = NULL;
14219
b1a8dcd7
JM
14220#ifdef HAS_SYMLINK
14221 if (decc_posix_compliant_pathnames > 0 ) {
14222 /* realpath currently only works if posix compliant pathnames are
14223 * enabled. It may start working when they are not, but in that
14224 * case we still want the fallback behavior for backwards compatibility
14225 */
d584a1c6 14226 rslt = realpath(filespec, outbuf);
b1a8dcd7
JM
14227 }
14228#endif
d584a1c6
JM
14229
14230 if (rslt == NULL) {
14231 char * vms_spec;
14232 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14233 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
312ac60b 14234 mode_t my_mode;
d584a1c6
JM
14235
14236 /* Fall back to fid_to_name */
14237
14238 Newx(vms_spec, VMS_MAXRSS + 1, char);
14239
312ac60b 14240 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
4d8d3a9c 14241 if (sts == 0) {
d584a1c6
JM
14242
14243
14244 /* Now need to trim the version off */
14245 sts = vms_split_path
14246 (vms_spec,
14247 &v_spec,
14248 &v_len,
14249 &r_spec,
14250 &r_len,
14251 &d_spec,
14252 &d_len,
14253 &n_spec,
14254 &n_len,
14255 &e_spec,
14256 &e_len,
14257 &vs_spec,
14258 &vs_len);
14259
14260
4d8d3a9c
CB
14261 if (sts == 0) {
14262 int haslower = 0;
14263 const char *cp;
d584a1c6 14264
4d8d3a9c
CB
14265 /* Trim off the version */
14266 int file_len = v_len + r_len + d_len + n_len + e_len;
14267 vms_spec[file_len] = 0;
d584a1c6 14268
f785e3a1
JM
14269 /* Trim off the .DIR if this is a directory */
14270 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
14271 if (S_ISDIR(my_mode)) {
14272 e_len = 0;
14273 e_spec[0] = 0;
14274 }
14275 }
14276
14277 /* Drop NULL extensions on UNIX file specification */
14278 if ((e_len == 1) && decc_readdir_dropdotnotype) {
14279 e_len = 0;
14280 e_spec[0] = '\0';
14281 }
14282
4d8d3a9c 14283 /* The result is expected to be in UNIX format */
0e5ce2c7 14284 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
4d8d3a9c
CB
14285
14286 /* Downcase if input had any lower case letters and
14287 * case preservation is not in effect.
14288 */
14289 if (!decc_efs_case_preserve) {
14290 for (cp = filespec; *cp; cp++)
14291 if (islower(*cp)) { haslower = 1; break; }
14292
14293 if (haslower) __mystrtolower(rslt);
14294 }
14295 }
643f470b
CB
14296 } else {
14297
14298 /* Now for some hacks to deal with backwards and forward */
94ae10c0 14299 /* compatibility */
643f470b
CB
14300 if (!decc_efs_charset) {
14301
14302 /* 1. ODS-2 mode wants to do a syntax only translation */
6fb6c614
JM
14303 rslt = int_rmsexpand(filespec, outbuf,
14304 NULL, 0, NULL, utf8_fl);
643f470b
CB
14305
14306 } else {
14307 if (decc_filename_unix_report) {
14308 char * dir_name;
14309 char * vms_dir_name;
14310 char * file_name;
14311
14312 /* 2. ODS-5 / UNIX report mode should return a failure */
14313 /* if the parent directory also does not exist */
14314 /* Otherwise, get the real path for the parent */
14315 /* and add the child to it.
14316
14317 /* basename / dirname only available for VMS 7.0+ */
14318 /* So we may need to implement them as common routines */
14319
14320 Newx(dir_name, VMS_MAXRSS + 1, char);
14321 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14322 dir_name[0] = '\0';
14323 file_name = NULL;
14324
14325 /* First try a VMS parse */
14326 sts = vms_split_path
14327 (filespec,
14328 &v_spec,
14329 &v_len,
14330 &r_spec,
14331 &r_len,
14332 &d_spec,
14333 &d_len,
14334 &n_spec,
14335 &n_len,
14336 &e_spec,
14337 &e_len,
14338 &vs_spec,
14339 &vs_len);
14340
14341 if (sts == 0) {
14342 /* This is VMS */
14343
14344 int dir_len = v_len + r_len + d_len + n_len;
14345 if (dir_len > 0) {
14346 strncpy(dir_name, filespec, dir_len);
14347 dir_name[dir_len] = '\0';
14348 file_name = (char *)&filespec[dir_len + 1];
14349 }
14350 } else {
14351 /* This must be UNIX */
14352 char * tchar;
14353
14354 tchar = strrchr(filespec, '/');
14355
4148925f
JM
14356 if (tchar != NULL) {
14357 int dir_len = tchar - filespec;
14358 strncpy(dir_name, filespec, dir_len);
14359 dir_name[dir_len] = '\0';
14360 file_name = (char *) &filespec[dir_len + 1];
14361 }
14362 }
14363
14364 /* Dir name is defaulted */
14365 if (dir_name[0] == 0) {
14366 dir_name[0] = '.';
14367 dir_name[1] = '\0';
14368 }
14369
14370 /* Need realpath for the directory */
14371 sts = vms_fid_to_name(vms_dir_name,
14372 VMS_MAXRSS + 1,
312ac60b 14373 dir_name, 0, NULL);
4148925f
JM
14374
14375 if (sts == 0) {
14376 /* Now need to pathify it.
1fe570cc
JM
14377 char *tdir = int_pathify_dirspec(vms_dir_name,
14378 outbuf);
4148925f
JM
14379
14380 /* And now add the original filespec to it */
14381 if (file_name != NULL) {
14382 strcat(outbuf, file_name);
14383 }
14384 return outbuf;
14385 }
14386 Safefree(vms_dir_name);
14387 Safefree(dir_name);
14388 }
14389 }
643f470b 14390 }
d584a1c6
JM
14391 Safefree(vms_spec);
14392 }
14393 return rslt;
f7ddb74a
JM
14394}
14395
b1a8dcd7
JM
14396static char *
14397mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14398 int *utf8_fl)
14399{
14400 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14401 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
b1a8dcd7
JM
14402
14403 /* Fall back to fid_to_name */
14404
312ac60b 14405 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
cd43acd7
CB
14406 if (sts != 0) {
14407 return NULL;
14408 }
14409 else {
b1a8dcd7
JM
14410
14411
14412 /* Now need to trim the version off */
14413 sts = vms_split_path
14414 (outbuf,
14415 &v_spec,
14416 &v_len,
14417 &r_spec,
14418 &r_len,
14419 &d_spec,
14420 &d_len,
14421 &n_spec,
14422 &n_len,
14423 &e_spec,
14424 &e_len,
14425 &vs_spec,
14426 &vs_len);
14427
14428
14429 if (sts == 0) {
4d8d3a9c
CB
14430 int haslower = 0;
14431 const char *cp;
14432
14433 /* Trim off the version */
14434 int file_len = v_len + r_len + d_len + n_len + e_len;
14435 outbuf[file_len] = 0;
b1a8dcd7 14436
4d8d3a9c
CB
14437 /* Downcase if input had any lower case letters and
14438 * case preservation is not in effect.
14439 */
14440 if (!decc_efs_case_preserve) {
14441 for (cp = filespec; *cp; cp++)
14442 if (islower(*cp)) { haslower = 1; break; }
14443
14444 if (haslower) __mystrtolower(outbuf);
14445 }
b1a8dcd7
JM
14446 }
14447 }
14448 return outbuf;
14449}
14450
14451
f7ddb74a
JM
14452/*}}}*/
14453/* External entry points */
360732b5
JM
14454char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14455{ return do_vms_realpath(filespec, outbuf, utf8_fl); }
f7ddb74a 14456
b1a8dcd7
JM
14457char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14458{ return do_vms_realname(filespec, outbuf, utf8_fl); }
f7ddb74a 14459
f7ddb74a
JM
14460/* case_tolerant */
14461
14462/*{{{int do_vms_case_tolerant(void)*/
14463/* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14464 * controlled by a process setting.
14465 */
14466int do_vms_case_tolerant(void)
14467{
14468 return vms_process_case_tolerant;
14469}
14470/*}}}*/
14471/* External entry points */
b1a8dcd7 14472#if __CRTL_VER >= 70301000 && !defined(__VAX)
f7ddb74a
JM
14473int Perl_vms_case_tolerant(void)
14474{ return do_vms_case_tolerant(); }
14475#else
14476int Perl_vms_case_tolerant(void)
14477{ return vms_process_case_tolerant; }
14478#endif
14479
14480
14481 /* Start of DECC RTL Feature handling */
14482
14483static int sys_trnlnm
14484 (const char * logname,
14485 char * value,
14486 int value_len)
14487{
14488 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14489 const unsigned long attr = LNM$M_CASE_BLIND;
14490 struct dsc$descriptor_s name_dsc;
14491 int status;
14492 unsigned short result;
14493 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14494 {0, 0, 0, 0}};
14495
14496 name_dsc.dsc$w_length = strlen(logname);
14497 name_dsc.dsc$a_pointer = (char *)logname;
14498 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14499 name_dsc.dsc$b_class = DSC$K_CLASS_S;
14500
14501 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14502
14503 if ($VMS_STATUS_SUCCESS(status)) {
14504
14505 /* Null terminate and return the string */
14506 /*--------------------------------------*/
14507 value[result] = 0;
14508 }
14509
14510 return status;
14511}
14512
14513static int sys_crelnm
14514 (const char * logname,
14515 const char * value)
14516{
14517 int ret_val;
14518 const char * proc_table = "LNM$PROCESS_TABLE";
14519 struct dsc$descriptor_s proc_table_dsc;
14520 struct dsc$descriptor_s logname_dsc;
14521 struct itmlst_3 item_list[2];
14522
14523 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14524 proc_table_dsc.dsc$w_length = strlen(proc_table);
14525 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14526 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14527
14528 logname_dsc.dsc$a_pointer = (char *) logname;
14529 logname_dsc.dsc$w_length = strlen(logname);
14530 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14531 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14532
14533 item_list[0].buflen = strlen(value);
14534 item_list[0].itmcode = LNM$_STRING;
14535 item_list[0].bufadr = (char *)value;
14536 item_list[0].retlen = NULL;
14537
14538 item_list[1].buflen = 0;
14539 item_list[1].itmcode = 0;
14540
14541 ret_val = sys$crelnm
14542 (NULL,
14543 (const struct dsc$descriptor_s *)&proc_table_dsc,
14544 (const struct dsc$descriptor_s *)&logname_dsc,
14545 NULL,
14546 (const struct item_list_3 *) item_list);
14547
14548 return ret_val;
14549}
14550
f7ddb74a
JM
14551/* C RTL Feature settings */
14552
14553static int set_features
14554 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
14555 int (* cli_routine)(void), /* Not documented */
14556 void *image_info) /* Not documented */
14557{
14558 int status;
14559 int s;
f7ddb74a 14560 char val_str[10];
3c841f20 14561#if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
f7ddb74a
JM
14562 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14563 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14564 unsigned long case_perm;
14565 unsigned long case_image;
3c841f20 14566#endif
f7ddb74a 14567
9c1171d1
JM
14568 /* Allow an exception to bring Perl into the VMS debugger */
14569 vms_debug_on_exception = 0;
14570 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14571 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14572 val_str[0] = _toupper(val_str[0]);
9c1171d1
JM
14573 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14574 vms_debug_on_exception = 1;
14575 else
14576 vms_debug_on_exception = 0;
14577 }
14578
b53f3677
JM
14579 /* Debug unix/vms file translation routines */
14580 vms_debug_fileify = 0;
14581 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14582 if ($VMS_STATUS_SUCCESS(status)) {
14583 val_str[0] = _toupper(val_str[0]);
14584 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14585 vms_debug_fileify = 1;
14586 else
14587 vms_debug_fileify = 0;
14588 }
14589
14590
14591 /* Historically PERL has been doing vmsify / stat differently than */
14592 /* the CRTL. In particular, under some conditions the CRTL will */
14593 /* remove some illegal characters like spaces from filenames */
14594 /* resulting in some differences. The stat()/lstat() wrapper has */
14595 /* been reporting such file names as invalid and fails to stat them */
14596 /* fixing this bug so that stat()/lstat() accept these like the */
14597 /* CRTL does will result in several tests failing. */
14598 /* This should really be fixed, but for now, set up a feature to */
14599 /* enable it so that the impact can be studied. */
14600 vms_bug_stat_filename = 0;
14601 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14602 if ($VMS_STATUS_SUCCESS(status)) {
14603 val_str[0] = _toupper(val_str[0]);
14604 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14605 vms_bug_stat_filename = 1;
14606 else
14607 vms_bug_stat_filename = 0;
14608 }
14609
14610
38a44b82 14611 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
360732b5
JM
14612 vms_vtf7_filenames = 0;
14613 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14614 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14615 val_str[0] = _toupper(val_str[0]);
360732b5
JM
14616 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14617 vms_vtf7_filenames = 1;
14618 else
14619 vms_vtf7_filenames = 0;
14620 }
14621
e0e5e8d6 14622 /* unlink all versions on unlink() or rename() */
d584a1c6 14623 vms_unlink_all_versions = 0;
e0e5e8d6
JM
14624 status = sys_trnlnm
14625 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14626 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14627 val_str[0] = _toupper(val_str[0]);
e0e5e8d6
JM
14628 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14629 vms_unlink_all_versions = 1;
14630 else
14631 vms_unlink_all_versions = 0;
14632 }
14633
360732b5
JM
14634 /* Dectect running under GNV Bash or other UNIX like shell */
14635#if __CRTL_VER >= 70300000 && !defined(__VAX)
14636 gnv_unix_shell = 0;
14637 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14638 if ($VMS_STATUS_SUCCESS(status)) {
360732b5
JM
14639 gnv_unix_shell = 1;
14640 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14641 set_feature_default("DECC$EFS_CHARSET", 1);
14642 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14643 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14644 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14645 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
e0e5e8d6 14646 vms_unlink_all_versions = 1;
1a3aec58 14647 vms_posix_exit = 1;
360732b5
JM
14648 }
14649#endif
9c1171d1 14650
2497a41f
JM
14651 /* hacks to see if known bugs are still present for testing */
14652
2497a41f 14653 /* PCP mode requires creating /dev/null special device file */
2623a4a6 14654 decc_bug_devnull = 0;
2497a41f
JM
14655 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14656 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14657 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
14658 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14659 decc_bug_devnull = 1;
682e4b71
JM
14660 else
14661 decc_bug_devnull = 0;
2497a41f
JM
14662 }
14663
2497a41f
JM
14664 /* UNIX directory names with no paths are broken in a lot of places */
14665 decc_dir_barename = 1;
14666 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14667 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14668 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
14669 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14670 decc_dir_barename = 1;
14671 else
14672 decc_dir_barename = 0;
14673 }
14674
f7ddb74a
JM
14675#if __CRTL_VER >= 70300000 && !defined(__VAX)
14676 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14677 if (s >= 0) {
14678 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14679 if (decc_disable_to_vms_logname_translation < 0)
14680 decc_disable_to_vms_logname_translation = 0;
14681 }
14682
14683 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14684 if (s >= 0) {
14685 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14686 if (decc_efs_case_preserve < 0)
14687 decc_efs_case_preserve = 0;
14688 }
14689
14690 s = decc$feature_get_index("DECC$EFS_CHARSET");
b53f3677 14691 decc_efs_charset_index = s;
f7ddb74a
JM
14692 if (s >= 0) {
14693 decc_efs_charset = decc$feature_get_value(s, 1);
14694 if (decc_efs_charset < 0)
14695 decc_efs_charset = 0;
14696 }
14697
14698 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14699 if (s >= 0) {
14700 decc_filename_unix_report = decc$feature_get_value(s, 1);
1a3aec58 14701 if (decc_filename_unix_report > 0) {
f7ddb74a 14702 decc_filename_unix_report = 1;
1a3aec58
JM
14703 vms_posix_exit = 1;
14704 }
f7ddb74a
JM
14705 else
14706 decc_filename_unix_report = 0;
14707 }
14708
14709 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14710 if (s >= 0) {
14711 decc_filename_unix_only = decc$feature_get_value(s, 1);
14712 if (decc_filename_unix_only > 0) {
14713 decc_filename_unix_only = 1;
14714 }
14715 else {
14716 decc_filename_unix_only = 0;
14717 }
14718 }
14719
14720 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14721 if (s >= 0) {
14722 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14723 if (decc_filename_unix_no_version < 0)
14724 decc_filename_unix_no_version = 0;
14725 }
14726
14727 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14728 if (s >= 0) {
14729 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14730 if (decc_readdir_dropdotnotype < 0)
14731 decc_readdir_dropdotnotype = 0;
14732 }
14733
f7ddb74a
JM
14734#if __CRTL_VER >= 80200000
14735 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14736 if (s >= 0) {
14737 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14738 if (decc_posix_compliant_pathnames < 0)
14739 decc_posix_compliant_pathnames = 0;
14740 if (decc_posix_compliant_pathnames > 4)
14741 decc_posix_compliant_pathnames = 0;
14742 }
14743
14744#endif
14745#else
14746 status = sys_trnlnm
14747 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14748 if ($VMS_STATUS_SUCCESS(status)) {
14749 val_str[0] = _toupper(val_str[0]);
14750 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14751 decc_disable_to_vms_logname_translation = 1;
14752 }
14753 }
14754
14755#ifndef __VAX
14756 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14757 if ($VMS_STATUS_SUCCESS(status)) {
14758 val_str[0] = _toupper(val_str[0]);
14759 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14760 decc_efs_case_preserve = 1;
14761 }
14762 }
14763#endif
14764
14765 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14766 if ($VMS_STATUS_SUCCESS(status)) {
14767 val_str[0] = _toupper(val_str[0]);
14768 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14769 decc_filename_unix_report = 1;
14770 }
14771 }
14772 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14773 if ($VMS_STATUS_SUCCESS(status)) {
14774 val_str[0] = _toupper(val_str[0]);
14775 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14776 decc_filename_unix_only = 1;
14777 decc_filename_unix_report = 1;
14778 }
14779 }
14780 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14781 if ($VMS_STATUS_SUCCESS(status)) {
14782 val_str[0] = _toupper(val_str[0]);
14783 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14784 decc_filename_unix_no_version = 1;
14785 }
14786 }
14787 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14788 if ($VMS_STATUS_SUCCESS(status)) {
14789 val_str[0] = _toupper(val_str[0]);
14790 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14791 decc_readdir_dropdotnotype = 1;
14792 }
14793 }
14794#endif
14795
28ff9735 14796#if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
f7ddb74a
JM
14797
14798 /* Report true case tolerance */
14799 /*----------------------------*/
14800 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14801 if (!$VMS_STATUS_SUCCESS(status))
14802 case_perm = PPROP$K_CASE_BLIND;
14803 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14804 if (!$VMS_STATUS_SUCCESS(status))
14805 case_image = PPROP$K_CASE_BLIND;
14806 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14807 (case_image == PPROP$K_CASE_SENSITIVE))
14808 vms_process_case_tolerant = 0;
14809
14810#endif
14811
1a3aec58 14812 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
94ae10c0 14813 /* for strict backward compatibility */
1a3aec58
JM
14814 status = sys_trnlnm
14815 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14816 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14817 val_str[0] = _toupper(val_str[0]);
1a3aec58
JM
14818 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14819 vms_posix_exit = 1;
14820 else
14821 vms_posix_exit = 0;
14822 }
14823
f7ddb74a
JM
14824
14825 /* CRTL can be initialized past this point, but not before. */
14826/* DECC$CRTL_INIT(); */
14827
14828 return SS$_NORMAL;
14829}
14830
14831#ifdef __DECC
f7ddb74a
JM
14832#pragma nostandard
14833#pragma extern_model save
14834#pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
f7ddb74a 14835 const __align (LONGWORD) int spare[8] = {0};
dfffea70
CB
14836
14837/* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14838#if __DECC_VER >= 60560002
14839#pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14840#else
14841#pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
f7ddb74a 14842#endif
dfffea70
CB
14843#endif /* __DECC */
14844
f7ddb74a
JM
14845const long vms_cc_features = (const long)set_features;
14846
14847/*
14848** Force a reference to LIB$INITIALIZE to ensure it
14849** exists in the image.
14850*/
17072196 14851#define lib$initialize LIB$INITIALIZE
f7ddb74a
JM
14852int lib$initialize(void);
14853#ifdef __DECC
14854#pragma extern_model strict_refdef
14855#endif
14856 int lib_init_ref = (int) lib$initialize;
14857
14858#ifdef __DECC
14859#pragma extern_model restore
14860#pragma standard
14861#endif
14862
748a9306 14863/* End of vms.c */