This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
perldelta: remove 2 commits from the list
[perl5.git] / vms / vms.c
CommitLineData
b429d381 1/* vms.c
a0d0e21e 2 *
82dd182c 3 * VMS-specific routines for perl5
748a9306 4 *
82dd182c
CB
5 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6 * 2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
7 *
8 * You may distribute under the terms of either the GNU General Public
9 * License or the Artistic License, as specified in the README file.
10 *
11 * Please see Changes*.* or the Perl Repository Browser for revision history.
a0d0e21e
LW
12 */
13
7c884029 14/*
4ac71550
TC
15 * Yet small as was their hunted band
16 * still fell and fearless was each hand,
17 * and strong deeds they wrought yet oft,
18 * and loved the woods, whose ways more soft
19 * them seemed than thralls of that black throne
20 * to live and languish in halls of stone.
21 * "The Lay of Leithian", Canto II, lines 135-40
7c884029 22 *
4ac71550 23 * [p.162 of _The Lays of Beleriand_]
7c884029
CB
24 */
25
a0d0e21e
LW
26#include <acedef.h>
27#include <acldef.h>
28#include <armdef.h>
748a9306 29#include <atrdef.h>
a0d0e21e 30#include <chpdef.h>
8fde5078 31#include <clidef.h>
a3e9d8c9 32#include <climsgdef.h>
cd1191f1 33#include <dcdef.h>
a0d0e21e 34#include <descrip.h>
22d4bb9c 35#include <devdef.h>
a0d0e21e 36#include <dvidef.h>
748a9306 37#include <fibdef.h>
a0d0e21e
LW
38#include <float.h>
39#include <fscndef.h>
40#include <iodef.h>
41#include <jpidef.h>
61bb5906 42#include <kgbdef.h>
f675dbe5 43#include <libclidef.h>
a0d0e21e
LW
44#include <libdef.h>
45#include <lib$routines.h>
46#include <lnmdef.h>
aeb5cf3c 47#include <msgdef.h>
4fdf8f88 48#include <ossdef.h>
f7ddb74a
JM
49#if __CRTL_VER >= 70301000 && !defined(__VAX)
50#include <ppropdef.h>
51#endif
748a9306 52#include <prvdef.h>
a0d0e21e
LW
53#include <psldef.h>
54#include <rms.h>
55#include <shrdef.h>
56#include <ssdef.h>
57#include <starlet.h>
f86702cc 58#include <strdef.h>
59#include <str$routines.h>
a0d0e21e 60#include <syidef.h>
748a9306
LW
61#include <uaidef.h>
62#include <uicdef.h>
2fbb330f
JM
63#include <stsdef.h>
64#include <rmsdef.h>
cd1191f1 65#include <smgdef.h>
cfcfe586
JM
66#if __CRTL_VER >= 70000000 /* FIXME to earliest version */
67#include <efndef.h>
68#define NO_EFN EFN$C_ENF
69#else
70#define NO_EFN 0;
71#endif
a0d0e21e 72
f7ddb74a
JM
73#if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
74int decc$feature_get_index(const char *name);
75char* decc$feature_get_name(int index);
76int decc$feature_get_value(int index, int mode);
77int decc$feature_set_value(int index, int mode, int value);
78#else
79#include <unixlib.h>
80#endif
81
cfcfe586
JM
82#pragma member_alignment save
83#pragma nomember_alignment longword
84struct item_list_3 {
85 unsigned short len;
86 unsigned short code;
87 void * bufadr;
88 unsigned short * retadr;
89};
90#pragma member_alignment restore
91
7a7fd8e0 92#if __CRTL_VER >= 70300000 && !defined(__VAX)
f7ddb74a
JM
93
94static int set_feature_default(const char *name, int value)
95{
96 int status;
97 int index;
98
99 index = decc$feature_get_index(name);
100
101 status = decc$feature_set_value(index, 1, value);
102 if (index == -1 || (status == -1)) {
103 return -1;
104 }
105
106 status = decc$feature_get_value(index, 1);
107 if (status != value) {
108 return -1;
109 }
110
111return 0;
112}
113#endif
f7ddb74a 114
740ce14c 115/* Older versions of ssdef.h don't have these */
116#ifndef SS$_INVFILFOROP
117# define SS$_INVFILFOROP 3930
118#endif
119#ifndef SS$_NOSUCHOBJECT
b7ae7a0d 120# define SS$_NOSUCHOBJECT 2696
121#endif
122
a15cef0c
CB
123/* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
124#define PERLIO_NOT_STDIO 0
125
2497a41f 126/* Don't replace system definitions of vfork, getenv, lstat, and stat,
aa689395 127 * code below needs to get to the underlying CRTL routines. */
128#define DONT_MASK_RTL_CALLS
a0d0e21e
LW
129#include "EXTERN.h"
130#include "perl.h"
748a9306 131#include "XSUB.h"
3eeba6fb
CB
132/* Anticipating future expansion in lexical warnings . . . */
133#ifndef WARN_INTERNAL
134# define WARN_INTERNAL WARN_MISC
135#endif
a0d0e21e 136
988c775c
JM
137#ifdef VMS_LONGNAME_SUPPORT
138#include <libfildef.h>
139#endif
140
22d4bb9c
CB
141#if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
142# define RTL_USES_UTC 1
143#endif
144
58472d87
CB
145#if !defined(__VAX) && __CRTL_VER >= 80200000
146#ifdef lstat
147#undef lstat
148#endif
149#else
150#ifdef lstat
151#undef lstat
152#endif
153#define lstat(_x, _y) stat(_x, _y)
154#endif
155
5f1992ed
CB
156/* Routine to create a decterm for use with the Perl debugger */
157/* No headers, this information was found in the Programming Concepts Manual */
158
8cb5d3d5 159static int (*decw_term_port)
5f1992ed
CB
160 (const struct dsc$descriptor_s * display,
161 const struct dsc$descriptor_s * setup_file,
162 const struct dsc$descriptor_s * customization,
163 struct dsc$descriptor_s * result_device_name,
164 unsigned short * result_device_name_length,
165 void * controller,
166 void * char_buffer,
8cb5d3d5 167 void * char_change_buffer) = 0;
22d4bb9c 168
c07a80fd 169/* gcc's header files don't #define direct access macros
170 * corresponding to VAXC's variant structs */
171#ifdef __GNUC__
482b294c 172# define uic$v_format uic$r_uic_form.uic$v_format
173# define uic$v_group uic$r_uic_form.uic$v_group
174# define uic$v_member uic$r_uic_form.uic$v_member
c07a80fd 175# define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
176# define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
177# define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
178# define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
179#endif
180
c645ec3f
GS
181#if defined(NEED_AN_H_ERRNO)
182dEXT int h_errno;
183#endif
c07a80fd 184
f7ddb74a
JM
185#ifdef __DECC
186#pragma message disable pragma
187#pragma member_alignment save
188#pragma nomember_alignment longword
189#pragma message save
190#pragma message disable misalgndmem
191#endif
a0d0e21e
LW
192struct itmlst_3 {
193 unsigned short int buflen;
194 unsigned short int itmcode;
195 void *bufadr;
748a9306 196 unsigned short int *retlen;
a0d0e21e 197};
657054d4
JM
198
199struct filescan_itmlst_2 {
200 unsigned short length;
201 unsigned short itmcode;
202 char * component;
203};
204
dca5a913
JM
205struct vs_str_st {
206 unsigned short length;
207 char str[65536];
208};
209
f7ddb74a
JM
210#ifdef __DECC
211#pragma message restore
212#pragma member_alignment restore
213#endif
a0d0e21e 214
360732b5
JM
215#define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
216#define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
217#define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
218#define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
219#define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
220#define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
b1a8dcd7 221#define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
360732b5
JM
222#define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
223#define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
f7ddb74a 224#define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
4b19af01
CB
225#define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
226#define getredirection(a,b) mp_getredirection(aTHX_ a,b)
227
360732b5
JM
228static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
229static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
230static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
231static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
f7ddb74a 232
6fb6c614
JM
233static char * int_rmsexpand_vms(
234 const char * filespec, char * outbuf, unsigned opts);
235static char * int_rmsexpand_tovms(
236 const char * filespec, char * outbuf, unsigned opts);
df278665
JM
237static char *int_tovmsspec
238 (const char *path, char *buf, int dir_flag, int * utf8_flag);
a979ce91 239static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
0e5ce2c7 240static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
4846f1d7 241static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
df278665 242
0e06870b
CB
243/* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
244#define PERL_LNM_MAX_ALLOWED_INDEX 127
245
2d9f3838
CB
246/* OpenVMS User's Guide says at least 9 iterative translations will be performed,
247 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
248 * the Perl facility.
249 */
250#define PERL_LNM_MAX_ITER 10
251
2497a41f
JM
252 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
253#if __CRTL_VER >= 70302000 && !defined(__VAX)
254#define MAX_DCL_SYMBOL (8192)
255#define MAX_DCL_LINE_LENGTH (4096 - 4)
256#else
257#define MAX_DCL_SYMBOL (1024)
258#define MAX_DCL_LINE_LENGTH (1024 - 4)
259#endif
ff7adb52 260
01b8edb6 261static char *__mystrtolower(char *str)
262{
263 if (str) for (; *str; ++str) *str= tolower(*str);
264 return str;
265}
266
f675dbe5
CB
267static struct dsc$descriptor_s fildevdsc =
268 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
269static struct dsc$descriptor_s crtlenvdsc =
270 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
271static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
272static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
273static struct dsc$descriptor_s **env_tables = defenv;
274static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
275
93948341
CB
276/* True if we shouldn't treat barewords as logicals during directory */
277/* munching */
278static int no_translate_barewords;
279
22d4bb9c
CB
280#ifndef RTL_USES_UTC
281static int tz_updated = 1;
282#endif
283
f7ddb74a
JM
284/* DECC Features that may need to affect how Perl interprets
285 * displays filename information
286 */
287static int decc_disable_to_vms_logname_translation = 1;
288static int decc_disable_posix_root = 1;
289int decc_efs_case_preserve = 0;
290static int decc_efs_charset = 0;
b53f3677 291static int decc_efs_charset_index = -1;
f7ddb74a
JM
292static int decc_filename_unix_no_version = 0;
293static int decc_filename_unix_only = 0;
294int decc_filename_unix_report = 0;
295int decc_posix_compliant_pathnames = 0;
296int decc_readdir_dropdotnotype = 0;
297static int vms_process_case_tolerant = 1;
360732b5
JM
298int vms_vtf7_filenames = 0;
299int gnv_unix_shell = 0;
e0e5e8d6 300static int vms_unlink_all_versions = 0;
1a3aec58 301static int vms_posix_exit = 0;
f7ddb74a 302
2497a41f 303/* bug workarounds if needed */
682e4b71 304int decc_bug_devnull = 1;
2497a41f 305int decc_dir_barename = 0;
b53f3677 306int vms_bug_stat_filename = 0;
2497a41f 307
9c1171d1 308static int vms_debug_on_exception = 0;
b53f3677
JM
309static int vms_debug_fileify = 0;
310
311/* Simple logical name translation */
312static int simple_trnlnm
313 (const char * logname,
314 char * value,
315 int value_len)
316{
317 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
318 const unsigned long attr = LNM$M_CASE_BLIND;
319 struct dsc$descriptor_s name_dsc;
320 int status;
321 unsigned short result;
322 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
323 {0, 0, 0, 0}};
324
325 name_dsc.dsc$w_length = strlen(logname);
326 name_dsc.dsc$a_pointer = (char *)logname;
327 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
328 name_dsc.dsc$b_class = DSC$K_CLASS_S;
329
330 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
331
332 if ($VMS_STATUS_SUCCESS(status)) {
333
334 /* Null terminate and return the string */
335 /*--------------------------------------*/
336 value[result] = 0;
337 return result;
338 }
339
340 return 0;
341}
342
9c1171d1 343
f7ddb74a
JM
344/* Is this a UNIX file specification?
345 * No longer a simple check with EFS file specs
346 * For now, not a full check, but need to
347 * handle POSIX ^UP^ specifications
348 * Fixing to handle ^/ cases would require
349 * changes to many other conversion routines.
350 */
351
657054d4 352static int is_unix_filespec(const char *path)
f7ddb74a
JM
353{
354int ret_val;
355const char * pch1;
356
357 ret_val = 0;
358 if (strncmp(path,"\"^UP^",5) != 0) {
359 pch1 = strchr(path, '/');
360 if (pch1 != NULL)
361 ret_val = 1;
362 else {
363
364 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
365 if (decc_filename_unix_report || decc_filename_unix_only) {
366 if (strcmp(path,".") == 0)
367 ret_val = 1;
368 }
369 }
370 }
371 return ret_val;
372}
373
360732b5
JM
374/* This routine converts a UCS-2 character to be VTF-7 encoded.
375 */
376
377static void ucs2_to_vtf7
378 (char *outspec,
379 unsigned long ucs2_char,
380 int * output_cnt)
381{
382unsigned char * ucs_ptr;
383int hex;
384
385 ucs_ptr = (unsigned char *)&ucs2_char;
386
387 outspec[0] = '^';
388 outspec[1] = 'U';
389 hex = (ucs_ptr[1] >> 4) & 0xf;
390 if (hex < 0xA)
391 outspec[2] = hex + '0';
392 else
393 outspec[2] = (hex - 9) + 'A';
394 hex = ucs_ptr[1] & 0xF;
395 if (hex < 0xA)
396 outspec[3] = hex + '0';
397 else {
398 outspec[3] = (hex - 9) + 'A';
399 }
400 hex = (ucs_ptr[0] >> 4) & 0xf;
401 if (hex < 0xA)
402 outspec[4] = hex + '0';
403 else
404 outspec[4] = (hex - 9) + 'A';
405 hex = ucs_ptr[1] & 0xF;
406 if (hex < 0xA)
407 outspec[5] = hex + '0';
408 else {
409 outspec[5] = (hex - 9) + 'A';
410 }
411 *output_cnt = 6;
412}
413
414
415/* This handles the conversion of a UNIX extended character set to a ^
416 * escaped VMS character.
417 * in a UNIX file specification.
418 *
419 * The output count variable contains the number of characters added
420 * to the output string.
421 *
422 * The return value is the number of characters read from the input string
423 */
424static int copy_expand_unix_filename_escape
425 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
426{
427int count;
428int scnt;
429int utf8_flag;
430
431 utf8_flag = 0;
432 if (utf8_fl)
433 utf8_flag = *utf8_fl;
434
435 count = 0;
436 *output_cnt = 0;
437 if (*inspec >= 0x80) {
438 if (utf8_fl && vms_vtf7_filenames) {
439 unsigned long ucs_char;
440
441 ucs_char = 0;
442
443 if ((*inspec & 0xE0) == 0xC0) {
444 /* 2 byte Unicode */
445 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
446 if (ucs_char >= 0x80) {
447 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
448 return 2;
449 }
450 } else if ((*inspec & 0xF0) == 0xE0) {
451 /* 3 byte Unicode */
452 ucs_char = ((inspec[0] & 0xF) << 12) +
453 ((inspec[1] & 0x3f) << 6) +
454 (inspec[2] & 0x3f);
455 if (ucs_char >= 0x800) {
456 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
457 return 3;
458 }
459
460#if 0 /* I do not see longer sequences supported by OpenVMS */
461 /* Maybe some one can fix this later */
462 } else if ((*inspec & 0xF8) == 0xF0) {
463 /* 4 byte Unicode */
464 /* UCS-4 to UCS-2 */
465 } else if ((*inspec & 0xFC) == 0xF8) {
466 /* 5 byte Unicode */
467 /* UCS-4 to UCS-2 */
468 } else if ((*inspec & 0xFE) == 0xFC) {
469 /* 6 byte Unicode */
470 /* UCS-4 to UCS-2 */
471#endif
472 }
473 }
474
38a44b82 475 /* High bit set, but not a Unicode character! */
360732b5
JM
476
477 /* Non printing DECMCS or ISO Latin-1 character? */
478 if (*inspec <= 0x9F) {
479 int hex;
480 outspec[0] = '^';
481 outspec++;
482 hex = (*inspec >> 4) & 0xF;
483 if (hex < 0xA)
484 outspec[1] = hex + '0';
485 else {
486 outspec[1] = (hex - 9) + 'A';
487 }
488 hex = *inspec & 0xF;
489 if (hex < 0xA)
490 outspec[2] = hex + '0';
491 else {
492 outspec[2] = (hex - 9) + 'A';
493 }
494 *output_cnt = 3;
495 return 1;
496 } else if (*inspec == 0xA0) {
497 outspec[0] = '^';
498 outspec[1] = 'A';
499 outspec[2] = '0';
500 *output_cnt = 3;
501 return 1;
502 } else if (*inspec == 0xFF) {
503 outspec[0] = '^';
504 outspec[1] = 'F';
505 outspec[2] = 'F';
506 *output_cnt = 3;
507 return 1;
508 }
509 *outspec = *inspec;
510 *output_cnt = 1;
511 return 1;
512 }
513
514 /* Is this a macro that needs to be passed through?
515 * Macros start with $( and an alpha character, followed
516 * by a string of alpha numeric characters ending with a )
517 * If this does not match, then encode it as ODS-5.
518 */
519 if ((inspec[0] == '$') && (inspec[1] == '(')) {
520 int tcnt;
521
522 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
523 tcnt = 3;
524 outspec[0] = inspec[0];
525 outspec[1] = inspec[1];
526 outspec[2] = inspec[2];
527
528 while(isalnum(inspec[tcnt]) ||
529 (inspec[2] == '.') || (inspec[2] == '_')) {
530 outspec[tcnt] = inspec[tcnt];
531 tcnt++;
532 }
533 if (inspec[tcnt] == ')') {
534 outspec[tcnt] = inspec[tcnt];
535 tcnt++;
536 *output_cnt = tcnt;
537 return tcnt;
538 }
539 }
540 }
541
542 switch (*inspec) {
543 case 0x7f:
544 outspec[0] = '^';
545 outspec[1] = '7';
546 outspec[2] = 'F';
547 *output_cnt = 3;
548 return 1;
549 break;
550 case '?':
551 if (decc_efs_charset == 0)
552 outspec[0] = '%';
553 else
554 outspec[0] = '?';
555 *output_cnt = 1;
556 return 1;
557 break;
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 ']':
575 case '%':
576 case '^':
449de3c2 577 case '\\':
adc11f0b
CB
578 /* Don't escape again if following character is
579 * already something we escape.
580 */
449de3c2 581 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
adc11f0b
CB
582 *outspec = *inspec;
583 *output_cnt = 1;
584 return 1;
585 break;
586 }
587 /* But otherwise fall through and escape it. */
360732b5
JM
588 case '=':
589 /* Assume that this is to be escaped */
590 outspec[0] = '^';
591 outspec[1] = *inspec;
592 *output_cnt = 2;
593 return 1;
594 break;
595 case ' ': /* space */
596 /* Assume that this is to be escaped */
597 outspec[0] = '^';
598 outspec[1] = '_';
599 *output_cnt = 2;
600 return 1;
601 break;
602 default:
603 *outspec = *inspec;
604 *output_cnt = 1;
605 return 1;
606 break;
607 }
608}
609
610
657054d4
JM
611/* This handles the expansion of a '^' prefix to the proper character
612 * in a UNIX file specification.
613 *
614 * The output count variable contains the number of characters added
615 * to the output string.
616 *
617 * The return value is the number of characters read from the input
618 * string
619 */
620static int copy_expand_vms_filename_escape
621 (char *outspec, const char *inspec, int *output_cnt)
622{
623int count;
624int scnt;
625
626 count = 0;
627 *output_cnt = 0;
628 if (*inspec == '^') {
629 inspec++;
630 switch (*inspec) {
adc11f0b
CB
631 /* Spaces and non-trailing dots should just be passed through,
632 * but eat the escape character.
633 */
657054d4 634 case '.':
657054d4 635 *outspec = *inspec;
adc11f0b
CB
636 count += 2;
637 (*output_cnt)++;
657054d4
JM
638 break;
639 case '_': /* space */
640 *outspec = ' ';
adc11f0b 641 count += 2;
657054d4
JM
642 (*output_cnt)++;
643 break;
adc11f0b
CB
644 case '^':
645 /* Hmm. Better leave the escape escaped. */
646 outspec[0] = '^';
647 outspec[1] = '^';
648 count += 2;
649 (*output_cnt) += 2;
650 break;
360732b5 651 case 'U': /* Unicode - FIX-ME this is wrong. */
657054d4
JM
652 inspec++;
653 count++;
654 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
655 if (scnt == 4) {
2f4077ca
JM
656 unsigned int c1, c2;
657 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
9960802c
NK
658 outspec[0] = c1 & 0xff;
659 outspec[1] = c2 & 0xff;
657054d4
JM
660 if (scnt > 1) {
661 (*output_cnt) += 2;
662 count += 4;
663 }
664 }
665 else {
666 /* Error - do best we can to continue */
667 *outspec = 'U';
668 outspec++;
669 (*output_cnt++);
670 *outspec = *inspec;
671 count++;
672 (*output_cnt++);
673 }
674 break;
675 default:
676 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
677 if (scnt == 2) {
678 /* Hex encoded */
2f4077ca
JM
679 unsigned int c1;
680 scnt = sscanf(inspec, "%2x", &c1);
681 outspec[0] = c1 & 0xff;
657054d4
JM
682 if (scnt > 0) {
683 (*output_cnt++);
684 count += 2;
685 }
686 }
687 else {
688 *outspec = *inspec;
689 count++;
690 (*output_cnt++);
691 }
692 }
693 }
694 else {
695 *outspec = *inspec;
696 count++;
697 (*output_cnt)++;
698 }
699 return count;
700}
701
657054d4
JM
702/* vms_split_path - Verify that the input file specification is a
703 * VMS format file specification, and provide pointers to the components of
704 * it. With EFS format filenames, this is virtually the only way to
705 * parse a VMS path specification into components.
706 *
707 * If the sum of the components do not add up to the length of the
708 * string, then the passed file specification is probably a UNIX style
709 * path.
710 */
711static int vms_split_path
360732b5 712 (const char * path,
dca5a913 713 char * * volume,
657054d4 714 int * vol_len,
dca5a913 715 char * * root,
657054d4 716 int * root_len,
dca5a913 717 char * * dir,
657054d4 718 int * dir_len,
dca5a913 719 char * * name,
657054d4 720 int * name_len,
dca5a913 721 char * * ext,
657054d4 722 int * ext_len,
dca5a913 723 char * * version,
657054d4
JM
724 int * ver_len)
725{
726struct dsc$descriptor path_desc;
727int status;
728unsigned long flags;
729int ret_stat;
730struct filescan_itmlst_2 item_list[9];
731const int filespec = 0;
732const int nodespec = 1;
733const int devspec = 2;
734const int rootspec = 3;
735const int dirspec = 4;
736const int namespec = 5;
737const int typespec = 6;
738const int verspec = 7;
739
740 /* Assume the worst for an easy exit */
741 ret_stat = -1;
742 *volume = NULL;
743 *vol_len = 0;
744 *root = NULL;
745 *root_len = 0;
746 *dir = NULL;
747 *dir_len;
748 *name = NULL;
749 *name_len = 0;
750 *ext = NULL;
751 *ext_len = 0;
752 *version = NULL;
753 *ver_len = 0;
754
755 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
756 path_desc.dsc$w_length = strlen(path);
757 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
758 path_desc.dsc$b_class = DSC$K_CLASS_S;
759
760 /* Get the total length, if it is shorter than the string passed
761 * then this was probably not a VMS formatted file specification
762 */
763 item_list[filespec].itmcode = FSCN$_FILESPEC;
764 item_list[filespec].length = 0;
765 item_list[filespec].component = NULL;
766
767 /* If the node is present, then it gets considered as part of the
768 * volume name to hopefully make things simple.
769 */
770 item_list[nodespec].itmcode = FSCN$_NODE;
771 item_list[nodespec].length = 0;
772 item_list[nodespec].component = NULL;
773
774 item_list[devspec].itmcode = FSCN$_DEVICE;
775 item_list[devspec].length = 0;
776 item_list[devspec].component = NULL;
777
778 /* root is a special case, adding it to either the directory or
779 * the device components will probalby complicate things for the
780 * callers of this routine, so leave it separate.
781 */
782 item_list[rootspec].itmcode = FSCN$_ROOT;
783 item_list[rootspec].length = 0;
784 item_list[rootspec].component = NULL;
785
786 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
787 item_list[dirspec].length = 0;
788 item_list[dirspec].component = NULL;
789
790 item_list[namespec].itmcode = FSCN$_NAME;
791 item_list[namespec].length = 0;
792 item_list[namespec].component = NULL;
793
794 item_list[typespec].itmcode = FSCN$_TYPE;
795 item_list[typespec].length = 0;
796 item_list[typespec].component = NULL;
797
798 item_list[verspec].itmcode = FSCN$_VERSION;
799 item_list[verspec].length = 0;
800 item_list[verspec].component = NULL;
801
802 item_list[8].itmcode = 0;
803 item_list[8].length = 0;
804 item_list[8].component = NULL;
805
7566800d 806 status = sys$filescan
657054d4
JM
807 ((const struct dsc$descriptor_s *)&path_desc, item_list,
808 &flags, NULL, NULL);
360732b5 809 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
657054d4
JM
810
811 /* If we parsed it successfully these two lengths should be the same */
812 if (path_desc.dsc$w_length != item_list[filespec].length)
813 return ret_stat;
814
815 /* If we got here, then it is a VMS file specification */
816 ret_stat = 0;
817
818 /* set the volume name */
819 if (item_list[nodespec].length > 0) {
820 *volume = item_list[nodespec].component;
821 *vol_len = item_list[nodespec].length + item_list[devspec].length;
822 }
823 else {
824 *volume = item_list[devspec].component;
825 *vol_len = item_list[devspec].length;
826 }
827
828 *root = item_list[rootspec].component;
829 *root_len = item_list[rootspec].length;
830
831 *dir = item_list[dirspec].component;
832 *dir_len = item_list[dirspec].length;
833
834 /* Now fun with versions and EFS file specifications
835 * The parser can not tell the difference when a "." is a version
836 * delimiter or a part of the file specification.
837 */
838 if ((decc_efs_charset) &&
839 (item_list[verspec].length > 0) &&
840 (item_list[verspec].component[0] == '.')) {
841 *name = item_list[namespec].component;
842 *name_len = item_list[namespec].length + item_list[typespec].length;
843 *ext = item_list[verspec].component;
844 *ext_len = item_list[verspec].length;
845 *version = NULL;
846 *ver_len = 0;
847 }
848 else {
849 *name = item_list[namespec].component;
850 *name_len = item_list[namespec].length;
851 *ext = item_list[typespec].component;
852 *ext_len = item_list[typespec].length;
853 *version = item_list[verspec].component;
854 *ver_len = item_list[verspec].length;
855 }
856 return ret_stat;
857}
858
df278665
JM
859/* Routine to determine if the file specification ends with .dir */
860static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
861
862 /* e_len must be 4, and version must be <= 2 characters */
863 if (e_len != 4 || vs_len > 2)
864 return 0;
865
866 /* If a version number is present, it needs to be one */
867 if ((vs_len == 2) && (vs_spec[1] != '1'))
868 return 0;
869
870 /* Look for the DIR on the extension */
871 if (vms_process_case_tolerant) {
872 if ((toupper(e_spec[1]) == 'D') &&
873 (toupper(e_spec[2]) == 'I') &&
874 (toupper(e_spec[3]) == 'R')) {
875 return 1;
876 }
877 } else {
878 /* Directory extensions are supposed to be in upper case only */
879 /* I would not be surprised if this rule can not be enforced */
880 /* if and when someone fully debugs the case sensitive mode */
881 if ((e_spec[1] == 'D') &&
882 (e_spec[2] == 'I') &&
883 (e_spec[3] == 'R')) {
884 return 1;
885 }
886 }
887 return 0;
888}
889
f7ddb74a 890
fa537f88
CB
891/* my_maxidx
892 * Routine to retrieve the maximum equivalence index for an input
893 * logical name. Some calls to this routine have no knowledge if
894 * the variable is a logical or not. So on error we return a max
895 * index of zero.
896 */
f7ddb74a 897/*{{{int my_maxidx(const char *lnm) */
fa537f88 898static int
f7ddb74a 899my_maxidx(const char *lnm)
fa537f88
CB
900{
901 int status;
902 int midx;
903 int attr = LNM$M_CASE_BLIND;
904 struct dsc$descriptor lnmdsc;
905 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
906 {0, 0, 0, 0}};
907
908 lnmdsc.dsc$w_length = strlen(lnm);
909 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
910 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
f7ddb74a 911 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
fa537f88
CB
912
913 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
914 if ((status & 1) == 0)
915 midx = 0;
916
917 return (midx);
918}
919/*}}}*/
920
f675dbe5 921/*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
c07a80fd 922int
fd8cd3a3 923Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
f675dbe5 924 struct dsc$descriptor_s **tabvec, unsigned long int flags)
748a9306 925{
f7ddb74a
JM
926 const char *cp1;
927 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
f675dbe5 928 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
748a9306 929 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
fa537f88 930 int midx;
f675dbe5
CB
931 unsigned char acmode;
932 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
933 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
934 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
935 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
748a9306 936 {0, 0, 0, 0}};
f675dbe5 937 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
fd8cd3a3
DS
938#if defined(PERL_IMPLICIT_CONTEXT)
939 pTHX = NULL;
fd8cd3a3
DS
940 if (PL_curinterp) {
941 aTHX = PERL_GET_INTERP;
cc077a9f 942 } else {
fd8cd3a3 943 aTHX = NULL;
cc077a9f
HM
944 }
945#endif
748a9306 946
fa537f88 947 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
b7ae7a0d 948 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
949 }
f7ddb74a 950 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
951 *cp2 = _toupper(*cp1);
952 if (cp1 - lnm > LNM$C_NAMLENGTH) {
953 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
954 return 0;
955 }
956 }
957 lnmdsc.dsc$w_length = cp1 - lnm;
958 lnmdsc.dsc$a_pointer = uplnm;
fd7385b9 959 uplnm[lnmdsc.dsc$w_length] = '\0';
f675dbe5
CB
960 secure = flags & PERL__TRNENV_SECURE;
961 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
962 if (!tabvec || !*tabvec) tabvec = env_tables;
963
964 for (curtab = 0; tabvec[curtab]; curtab++) {
965 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
966 if (!ivenv && !secure) {
967 char *eq, *end;
968 int i;
969 if (!environ) {
970 ivenv = 1;
ebd4d70b
JM
971#if defined(PERL_IMPLICIT_CONTEXT)
972 if (aTHX == NULL) {
973 fprintf(stderr,
873f5ddf 974 "Can't read CRTL environ\n");
ebd4d70b
JM
975 } else
976#endif
977 Perl_warn(aTHX_ "Can't read CRTL environ\n");
f675dbe5
CB
978 continue;
979 }
980 retsts = SS$_NOLOGNAM;
981 for (i = 0; environ[i]; i++) {
982 if ((eq = strchr(environ[i],'=')) &&
299d126a 983 lnmdsc.dsc$w_length == (eq - environ[i]) &&
f675dbe5
CB
984 !strncmp(environ[i],uplnm,eq - environ[i])) {
985 eq++;
986 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
987 if (!eqvlen) continue;
988 retsts = SS$_NORMAL;
989 break;
990 }
991 }
992 if (retsts != SS$_NOLOGNAM) break;
993 }
994 }
995 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
996 !str$case_blind_compare(&tmpdsc,&clisym)) {
997 if (!ivsym && !secure) {
998 unsigned short int deflen = LNM$C_NAMLENGTH;
999 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1000 /* dynamic dsc to accomodate possible long value */
ebd4d70b 1001 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
f675dbe5
CB
1002 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1003 if (retsts & 1) {
2497a41f 1004 if (eqvlen > MAX_DCL_SYMBOL) {
f675dbe5 1005 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
2497a41f 1006 eqvlen = MAX_DCL_SYMBOL;
cc077a9f
HM
1007 /* Special hack--we might be called before the interpreter's */
1008 /* fully initialized, in which case either thr or PL_curcop */
1009 /* might be bogus. We have to check, since ckWARN needs them */
1010 /* both to be valid if running threaded */
8a646e0b
JM
1011#if defined(PERL_IMPLICIT_CONTEXT)
1012 if (aTHX == NULL) {
1013 fprintf(stderr,
873f5ddf 1014 "Value of CLI symbol \"%s\" too long",lnm);
8a646e0b
JM
1015 } else
1016#endif
cc077a9f 1017 if (ckWARN(WARN_MISC)) {
f98bc0c6 1018 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
cc077a9f 1019 }
f675dbe5
CB
1020 }
1021 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1022 }
ebd4d70b 1023 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
f675dbe5
CB
1024 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1025 if (retsts == LIB$_NOSUCHSYM) continue;
1026 break;
1027 }
1028 }
1029 else if (!ivlnm) {
843027b0 1030 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
f7ddb74a
JM
1031 midx = my_maxidx(lnm);
1032 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1033 lnmlst[1].bufadr = cp2;
fa537f88
CB
1034 eqvlen = 0;
1035 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1036 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1037 if (retsts == SS$_NOLOGNAM) break;
1038 /* PPFs have a prefix */
1039 if (
fd7385b9 1040#if INTSIZE == 4
fa537f88 1041 *((int *)uplnm) == *((int *)"SYS$") &&
fd7385b9 1042#endif
fa537f88
CB
1043 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1044 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1045 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1046 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1047 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
18a3d61e 1048 memmove(eqv,eqv+4,eqvlen-4);
fa537f88
CB
1049 eqvlen -= 4;
1050 }
f7ddb74a
JM
1051 cp2 += eqvlen;
1052 *cp2 = '\0';
fa537f88
CB
1053 }
1054 if ((retsts == SS$_IVLOGNAM) ||
1055 (retsts == SS$_NOLOGNAM)) { continue; }
fd7385b9 1056 }
fa537f88 1057 else {
fa537f88
CB
1058 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1059 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1060 if (retsts == SS$_NOLOGNAM) continue;
1061 eqv[eqvlen] = '\0';
1062 }
1063 eqvlen = strlen(eqv);
f675dbe5
CB
1064 break;
1065 }
c07a80fd 1066 }
f675dbe5
CB
1067 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1068 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1069 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1070 retsts == SS$_NOLOGNAM) {
1071 set_errno(EINVAL); set_vaxc_errno(retsts);
748a9306 1072 }
ebd4d70b 1073 else _ckvmssts_noperl(retsts);
f675dbe5
CB
1074 return 0;
1075} /* end of vmstrnenv */
1076/*}}}*/
c07a80fd 1077
f675dbe5
CB
1078/*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1079/* Define as a function so we can access statics. */
4b19af01 1080int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
f675dbe5 1081{
8a646e0b
JM
1082 int flags = 0;
1083
1084#if defined(PERL_IMPLICIT_CONTEXT)
1085 if (aTHX != NULL)
1086#endif
f675dbe5 1087#ifdef SECURE_INTERNAL_GETENV
8a646e0b
JM
1088 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1089 PERL__TRNENV_SECURE : 0;
f675dbe5 1090#endif
8a646e0b
JM
1091
1092 return vmstrnenv(lnm, eqv, idx, fildev, flags);
f675dbe5
CB
1093}
1094/*}}}*/
a0d0e21e
LW
1095
1096/* my_getenv
61bb5906
CB
1097 * Note: Uses Perl temp to store result so char * can be returned to
1098 * caller; this pointer will be invalidated at next Perl statement
1099 * transition.
a6c40364 1100 * We define this as a function rather than a macro in terms of my_getenv_len()
f675dbe5
CB
1101 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1102 * allocate SVs).
a0d0e21e 1103 */
f675dbe5 1104/*{{{ char *my_getenv(const char *lnm, bool sys)*/
a0d0e21e 1105char *
5c84aa53 1106Perl_my_getenv(pTHX_ const char *lnm, bool sys)
a0d0e21e 1107{
f7ddb74a 1108 const char *cp1;
fa537f88 1109 static char *__my_getenv_eqv = NULL;
f7ddb74a 1110 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
c07a80fd 1111 unsigned long int idx = 0;
bc10a425 1112 int trnsuccess, success, secure, saverr, savvmserr;
843027b0 1113 int midx, flags;
61bb5906 1114 SV *tmpsv;
a0d0e21e 1115
f7ddb74a 1116 midx = my_maxidx(lnm) + 1;
fa537f88 1117
6b88bc9c 1118 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
61bb5906
CB
1119 /* Set up a temporary buffer for the return value; Perl will
1120 * clean it up at the next statement transition */
fa537f88 1121 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
61bb5906
CB
1122 if (!tmpsv) return NULL;
1123 eqv = SvPVX(tmpsv);
1124 }
fa537f88
CB
1125 else {
1126 /* Assume no interpreter ==> single thread */
1127 if (__my_getenv_eqv != NULL) {
1128 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1129 }
1130 else {
a02a5408 1131 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1132 }
1133 eqv = __my_getenv_eqv;
1134 }
1135
f7ddb74a 1136 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1137 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
2497a41f 1138 int len;
61bb5906 1139 getcwd(eqv,LNM$C_NAMLENGTH);
2497a41f
JM
1140
1141 len = strlen(eqv);
1142
1143 /* Get rid of "000000/ in rooted filespecs */
1144 if (len > 7) {
1145 char * zeros;
1146 zeros = strstr(eqv, "/000000/");
1147 if (zeros != NULL) {
1148 int mlen;
1149 mlen = len - (zeros - eqv) - 7;
1150 memmove(zeros, &zeros[7], mlen);
1151 len = len - 7;
1152 eqv[len] = '\0';
1153 }
1154 }
61bb5906 1155 return eqv;
748a9306 1156 }
a0d0e21e 1157 else {
2512681b 1158 /* Impose security constraints only if tainting */
bc10a425
CB
1159 if (sys) {
1160 /* Impose security constraints only if tainting */
1161 secure = PL_curinterp ? PL_tainting : will_taint;
1162 saverr = errno; savvmserr = vaxc$errno;
1163 }
843027b0
CB
1164 else {
1165 secure = 0;
1166 }
1167
1168 flags =
f675dbe5 1169#ifdef SECURE_INTERNAL_GETENV
843027b0 1170 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1171#else
843027b0 1172 0
f675dbe5 1173#endif
843027b0
CB
1174 ;
1175
1176 /* For the getenv interface we combine all the equivalence names
1177 * of a search list logical into one value to acquire a maximum
1178 * value length of 255*128 (assuming %ENV is using logicals).
1179 */
1180 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1181
1182 /* If the name contains a semicolon-delimited index, parse it
1183 * off and make sure we only retrieve the equivalence name for
1184 * that index. */
1185 if ((cp2 = strchr(lnm,';')) != NULL) {
1186 strcpy(uplnm,lnm);
1187 uplnm[cp2-lnm] = '\0';
1188 idx = strtoul(cp2+1,NULL,0);
1189 lnm = uplnm;
1190 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1191 }
1192
1193 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1194
bc10a425
CB
1195 /* Discard NOLOGNAM on internal calls since we're often looking
1196 * for an optional name, and this "error" often shows up as the
1197 * (bogus) exit status for a die() call later on. */
1198 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
4e205ed6 1199 return success ? eqv : NULL;
a0d0e21e 1200 }
a0d0e21e
LW
1201
1202} /* end of my_getenv() */
1203/*}}}*/
1204
f675dbe5 1205
a6c40364
GS
1206/*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1207char *
fd8cd3a3 1208Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
f675dbe5 1209{
f7ddb74a
JM
1210 const char *cp1;
1211 char *buf, *cp2;
a6c40364 1212 unsigned long idx = 0;
843027b0 1213 int midx, flags;
fa537f88 1214 static char *__my_getenv_len_eqv = NULL;
bc10a425 1215 int secure, saverr, savvmserr;
cc077a9f
HM
1216 SV *tmpsv;
1217
f7ddb74a 1218 midx = my_maxidx(lnm) + 1;
fa537f88 1219
cc077a9f
HM
1220 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1221 /* Set up a temporary buffer for the return value; Perl will
1222 * clean it up at the next statement transition */
fa537f88 1223 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
cc077a9f
HM
1224 if (!tmpsv) return NULL;
1225 buf = SvPVX(tmpsv);
1226 }
fa537f88
CB
1227 else {
1228 /* Assume no interpreter ==> single thread */
1229 if (__my_getenv_len_eqv != NULL) {
1230 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1231 }
1232 else {
a02a5408 1233 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1234 }
1235 buf = __my_getenv_len_eqv;
1236 }
1237
f7ddb74a 1238 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1239 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
f7ddb74a
JM
1240 char * zeros;
1241
f675dbe5 1242 getcwd(buf,LNM$C_NAMLENGTH);
a6c40364 1243 *len = strlen(buf);
f7ddb74a
JM
1244
1245 /* Get rid of "000000/ in rooted filespecs */
1246 if (*len > 7) {
1247 zeros = strstr(buf, "/000000/");
1248 if (zeros != NULL) {
1249 int mlen;
1250 mlen = *len - (zeros - buf) - 7;
1251 memmove(zeros, &zeros[7], mlen);
1252 *len = *len - 7;
1253 buf[*len] = '\0';
1254 }
1255 }
a6c40364 1256 return buf;
f675dbe5
CB
1257 }
1258 else {
bc10a425
CB
1259 if (sys) {
1260 /* Impose security constraints only if tainting */
1261 secure = PL_curinterp ? PL_tainting : will_taint;
1262 saverr = errno; savvmserr = vaxc$errno;
1263 }
843027b0
CB
1264 else {
1265 secure = 0;
1266 }
1267
1268 flags =
f675dbe5 1269#ifdef SECURE_INTERNAL_GETENV
843027b0 1270 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1271#else
843027b0 1272 0
f675dbe5 1273#endif
843027b0
CB
1274 ;
1275
1276 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1277
1278 if ((cp2 = strchr(lnm,';')) != NULL) {
1279 strcpy(buf,lnm);
1280 buf[cp2-lnm] = '\0';
1281 idx = strtoul(cp2+1,NULL,0);
1282 lnm = buf;
1283 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1284 }
1285
1286 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1287
f7ddb74a
JM
1288 /* Get rid of "000000/ in rooted filespecs */
1289 if (*len > 7) {
1290 char * zeros;
1291 zeros = strstr(buf, "/000000/");
1292 if (zeros != NULL) {
1293 int mlen;
1294 mlen = *len - (zeros - buf) - 7;
1295 memmove(zeros, &zeros[7], mlen);
1296 *len = *len - 7;
1297 buf[*len] = '\0';
1298 }
1299 }
1300
bc10a425
CB
1301 /* Discard NOLOGNAM on internal calls since we're often looking
1302 * for an optional name, and this "error" often shows up as the
1303 * (bogus) exit status for a die() call later on. */
1304 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
4e205ed6 1305 return *len ? buf : NULL;
f675dbe5
CB
1306 }
1307
a6c40364 1308} /* end of my_getenv_len() */
f675dbe5
CB
1309/*}}}*/
1310
8a646e0b 1311static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
8fde5078
CB
1312
1313static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1e422769 1314
740ce14c 1315/*{{{ void prime_env_iter() */
1316void
1317prime_env_iter(void)
1318/* Fill the %ENV associative array with all logical names we can
1319 * find, in preparation for iterating over it.
1320 */
1321{
17f28c40 1322 static int primed = 0;
3eeba6fb 1323 HV *seenhv = NULL, *envhv;
22be8b3c 1324 SV *sv = NULL;
4e205ed6 1325 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
8fde5078
CB
1326 unsigned short int chan;
1327#ifndef CLI$M_TRUSTED
1328# define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1329#endif
f675dbe5
CB
1330 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1331 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1332 long int i;
1333 bool have_sym = FALSE, have_lnm = FALSE;
1334 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1335 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1336 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1337 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1338 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
fd8cd3a3
DS
1339#if defined(PERL_IMPLICIT_CONTEXT)
1340 pTHX;
1341#endif
3db8f154 1342#if defined(USE_ITHREADS)
b2b3adea
HM
1343 static perl_mutex primenv_mutex;
1344 MUTEX_INIT(&primenv_mutex);
61bb5906 1345#endif
740ce14c 1346
fd8cd3a3
DS
1347#if defined(PERL_IMPLICIT_CONTEXT)
1348 /* We jump through these hoops because we can be called at */
1349 /* platform-specific initialization time, which is before anything is */
1350 /* set up--we can't even do a plain dTHX since that relies on the */
1351 /* interpreter structure to be initialized */
fd8cd3a3
DS
1352 if (PL_curinterp) {
1353 aTHX = PERL_GET_INTERP;
1354 } else {
ebd4d70b
JM
1355 /* we never get here because the NULL pointer will cause the */
1356 /* several of the routines called by this routine to access violate */
1357
1358 /* This routine is only called by hv.c/hv_iterinit which has a */
1359 /* context, so the real fix may be to pass it through instead of */
1360 /* the hoops above */
fd8cd3a3
DS
1361 aTHX = NULL;
1362 }
1363#endif
fd8cd3a3 1364
3eeba6fb 1365 if (primed || !PL_envgv) return;
61bb5906
CB
1366 MUTEX_LOCK(&primenv_mutex);
1367 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
3eeba6fb 1368 envhv = GvHVn(PL_envgv);
740ce14c 1369 /* Perform a dummy fetch as an lval to insure that the hash table is
8fde5078 1370 * set up. Otherwise, the hv_store() will turn into a nullop. */
740ce14c 1371 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
740ce14c 1372
f675dbe5
CB
1373 for (i = 0; env_tables[i]; i++) {
1374 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1375 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
f02a1854 1376 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
8fde5078 1377 }
f675dbe5
CB
1378 if (have_sym || have_lnm) {
1379 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1380 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1381 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1382 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
61bb5906 1383 }
f675dbe5
CB
1384
1385 for (i--; i >= 0; i--) {
1386 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1387 char *start;
1388 int j;
1389 for (j = 0; environ[j]; j++) {
1390 if (!(start = strchr(environ[j],'='))) {
3eeba6fb 1391 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1392 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
f675dbe5
CB
1393 }
1394 else {
1395 start++;
22be8b3c
CB
1396 sv = newSVpv(start,0);
1397 SvTAINTED_on(sv);
1398 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
f675dbe5
CB
1399 }
1400 }
1401 continue;
740ce14c 1402 }
f675dbe5
CB
1403 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1404 !str$case_blind_compare(&tmpdsc,&clisym)) {
1405 strcpy(cmd,"Show Symbol/Global *");
1406 cmddsc.dsc$w_length = 20;
1407 if (env_tables[i]->dsc$w_length == 12 &&
1408 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1409 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1410 flags = defflags | CLI$M_NOLOGNAM;
1411 }
1412 else {
1413 strcpy(cmd,"Show Logical *");
1414 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1415 strcat(cmd," /Table=");
1416 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1417 cmddsc.dsc$w_length = strlen(cmd);
1418 }
1419 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1420 flags = defflags | CLI$M_NOCLISYM;
1421 }
1422
1423 /* Create a new subprocess to execute each command, to exclude the
1424 * remote possibility that someone could subvert a mbx or file used
1425 * to write multiple commands to a single subprocess.
1426 */
1427 do {
1428 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1429 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1430 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1431 defflags &= ~CLI$M_TRUSTED;
1432 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1433 _ckvmssts(retsts);
a02a5408 1434 if (!buf) Newx(buf,mbxbufsiz + 1,char);
f675dbe5
CB
1435 if (seenhv) SvREFCNT_dec(seenhv);
1436 seenhv = newHV();
1437 while (1) {
1438 char *cp1, *cp2, *key;
1439 unsigned long int sts, iosb[2], retlen, keylen;
1440 register U32 hash;
1441
1442 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1443 if (sts & 1) sts = iosb[0] & 0xffff;
1444 if (sts == SS$_ENDOFFILE) {
1445 int wakect = 0;
1446 while (substs == 0) { sys$hiber(); wakect++;}
1447 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1448 _ckvmssts(substs);
1449 break;
1450 }
1451 _ckvmssts(sts);
1452 retlen = iosb[0] >> 16;
1453 if (!retlen) continue; /* blank line */
1454 buf[retlen] = '\0';
1455 if (iosb[1] != subpid) {
1456 if (iosb[1]) {
5c84aa53 1457 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
f675dbe5
CB
1458 }
1459 continue;
1460 }
3eeba6fb 1461 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
f98bc0c6 1462 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
f675dbe5
CB
1463
1464 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1465 if (*cp1 == '(' || /* Logical name table name */
1466 *cp1 == '=' /* Next eqv of searchlist */) continue;
1467 if (*cp1 == '"') cp1++;
1468 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1469 key = cp1; keylen = cp2 - cp1;
1470 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1471 while (*cp2 && *cp2 != '=') cp2++;
1f47e8e2
CB
1472 while (*cp2 && *cp2 == '=') cp2++;
1473 while (*cp2 && *cp2 == ' ') cp2++;
1474 if (*cp2 == '"') { /* String translation; may embed "" */
1475 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1476 cp2++; cp1--; /* Skip "" surrounding translation */
1477 }
1478 else { /* Numeric translation */
1479 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1480 cp1--; /* stop on last non-space char */
1481 }
1482 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
f98bc0c6 1483 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
edc7bc49
CB
1484 continue;
1485 }
5afd6d42 1486 PERL_HASH(hash,key,keylen);
ff79d39d
CB
1487
1488 if (cp1 == cp2 && *cp2 == '.') {
1489 /* A single dot usually means an unprintable character, such as a null
1490 * to indicate a zero-length value. Get the actual value to make sure.
1491 */
1492 char lnm[LNM$C_NAMLENGTH+1];
2497a41f 1493 char eqv[MAX_DCL_SYMBOL+1];
0faef845 1494 int trnlen;
ff79d39d 1495 strncpy(lnm, key, keylen);
0faef845 1496 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
ff79d39d
CB
1497 sv = newSVpvn(eqv, strlen(eqv));
1498 }
1499 else {
1500 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1501 }
1502
22be8b3c
CB
1503 SvTAINTED_on(sv);
1504 hv_store(envhv,key,keylen,sv,hash);
f675dbe5 1505 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
edc7bc49 1506 }
f675dbe5
CB
1507 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1508 /* get the PPFs for this process, not the subprocess */
f7ddb74a 1509 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
f675dbe5
CB
1510 char eqv[LNM$C_NAMLENGTH+1];
1511 int trnlen, i;
1512 for (i = 0; ppfs[i]; i++) {
1513 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
22be8b3c
CB
1514 sv = newSVpv(eqv,trnlen);
1515 SvTAINTED_on(sv);
1516 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
f675dbe5 1517 }
740ce14c 1518 }
1519 }
f675dbe5
CB
1520 primed = 1;
1521 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1522 if (buf) Safefree(buf);
1523 if (seenhv) SvREFCNT_dec(seenhv);
1524 MUTEX_UNLOCK(&primenv_mutex);
1525 return;
1526
740ce14c 1527} /* end of prime_env_iter */
1528/*}}}*/
740ce14c 1529
f675dbe5 1530
2c590a56 1531/*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
1532/* Define or delete an element in the same "environment" as
1533 * vmstrnenv(). If an element is to be deleted, it's removed from
1534 * the first place it's found. If it's to be set, it's set in the
1535 * place designated by the first element of the table vector.
3eeba6fb 1536 * Like setenv() returns 0 for success, non-zero on error.
a0d0e21e 1537 */
f675dbe5 1538int
2c590a56 1539Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
a0d0e21e 1540{
f7ddb74a
JM
1541 const char *cp1;
1542 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
f675dbe5 1543 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
fa537f88 1544 int nseg = 0, j;
a0d0e21e 1545 unsigned long int retsts, usermode = PSL$C_USER;
fa537f88 1546 struct itmlst_3 *ile, *ilist;
a0d0e21e 1547 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
f675dbe5
CB
1548 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1549 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1550 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1551 $DESCRIPTOR(local,"_LOCAL");
1552
ed253963
CB
1553 if (!lnm) {
1554 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1555 return SS$_IVLOGNAM;
1556 }
1557
f7ddb74a 1558 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
1559 *cp2 = _toupper(*cp1);
1560 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1561 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1562 return SS$_IVLOGNAM;
1563 }
1564 }
a0d0e21e 1565 lnmdsc.dsc$w_length = cp1 - lnm;
f675dbe5
CB
1566 if (!tabvec || !*tabvec) tabvec = env_tables;
1567
3eeba6fb 1568 if (!eqv) { /* we're deleting n element */
f675dbe5
CB
1569 for (curtab = 0; tabvec[curtab]; curtab++) {
1570 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1571 int i;
299d126a 1572 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
f675dbe5 1573 if ((cp1 = strchr(environ[i],'=')) &&
299d126a 1574 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
f675dbe5 1575 !strncmp(environ[i],lnm,cp1 - environ[i])) {
3eeba6fb 1576#ifdef HAS_SETENV
0e06870b 1577 return setenv(lnm,"",1) ? vaxc$errno : 0;
f675dbe5
CB
1578 }
1579 }
1580 ivenv = 1; retsts = SS$_NOLOGNAM;
1581#else
3eeba6fb 1582 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1583 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
3eeba6fb
CB
1584 ivenv = 1; retsts = SS$_NOSUCHPGM;
1585 break;
1586 }
1587 }
f675dbe5
CB
1588#endif
1589 }
1590 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1591 !str$case_blind_compare(&tmpdsc,&clisym)) {
1592 unsigned int symtype;
1593 if (tabvec[curtab]->dsc$w_length == 12 &&
1594 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1595 !str$case_blind_compare(&tmpdsc,&local))
1596 symtype = LIB$K_CLI_LOCAL_SYM;
1597 else symtype = LIB$K_CLI_GLOBAL_SYM;
1598 retsts = lib$delete_symbol(&lnmdsc,&symtype);
3eeba6fb
CB
1599 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1600 if (retsts == LIB$_NOSUCHSYM) continue;
f675dbe5
CB
1601 break;
1602 }
1603 else if (!ivlnm) {
1604 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1605 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1606 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1607 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1608 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1609 }
a0d0e21e
LW
1610 }
1611 }
f675dbe5
CB
1612 else { /* we're defining a value */
1613 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1614#ifdef HAS_SETENV
3eeba6fb 1615 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
f675dbe5 1616#else
3eeba6fb 1617 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1618 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
f675dbe5
CB
1619 retsts = SS$_NOSUCHPGM;
1620#endif
1621 }
1622 else {
f7ddb74a 1623 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
f675dbe5
CB
1624 eqvdsc.dsc$w_length = strlen(eqv);
1625 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1626 !str$case_blind_compare(&tmpdsc,&clisym)) {
1627 unsigned int symtype;
1628 if (tabvec[0]->dsc$w_length == 12 &&
1629 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1630 !str$case_blind_compare(&tmpdsc,&local))
1631 symtype = LIB$K_CLI_LOCAL_SYM;
1632 else symtype = LIB$K_CLI_GLOBAL_SYM;
1633 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1634 }
3eeba6fb
CB
1635 else {
1636 if (!*eqv) eqvdsc.dsc$w_length = 1;
a1dfe751 1637 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
fa537f88
CB
1638
1639 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1640 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1641 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1642 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1643 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1644 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1645 }
1646
a02a5408 1647 Newx(ilist,nseg+1,struct itmlst_3);
fa537f88
CB
1648 ile = ilist;
1649 if (!ile) {
1650 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1651 return SS$_INSFMEM;
a1dfe751 1652 }
fa537f88
CB
1653 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1654
1655 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1656 ile->itmcode = LNM$_STRING;
1657 ile->bufadr = c;
1658 if ((j+1) == nseg) {
1659 ile->buflen = strlen(c);
1660 /* in case we are truncating one that's too long */
1661 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1662 }
1663 else {
1664 ile->buflen = LNM$C_NAMLENGTH;
1665 }
1666 }
1667
1668 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1669 Safefree (ilist);
1670 }
1671 else {
1672 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
a1dfe751 1673 }
3eeba6fb 1674 }
f675dbe5
CB
1675 }
1676 }
1677 if (!(retsts & 1)) {
1678 switch (retsts) {
1679 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1680 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1681 set_errno(EVMSERR); break;
1682 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1683 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1684 set_errno(EINVAL); break;
1685 case SS$_NOPRIV:
7d2497bf 1686 set_errno(EACCES); break;
f675dbe5
CB
1687 default:
1688 _ckvmssts(retsts);
1689 set_errno(EVMSERR);
1690 }
1691 set_vaxc_errno(retsts);
1692 return (int) retsts || 44; /* retsts should never be 0, but just in case */
a0d0e21e 1693 }
3eeba6fb
CB
1694 else {
1695 /* We reset error values on success because Perl does an hv_fetch()
1696 * before each hv_store(), and if the thing we're setting didn't
1697 * previously exist, we've got a leftover error message. (Of course,
1698 * this fails in the face of
1699 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1700 * in that the error reported in $! isn't spurious,
1701 * but it's right more often than not.)
1702 */
f675dbe5
CB
1703 set_errno(0); set_vaxc_errno(retsts);
1704 return 0;
1705 }
1706
1707} /* end of vmssetenv() */
1708/*}}}*/
a0d0e21e 1709
2c590a56 1710/*{{{ void my_setenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
1711/* This has to be a function since there's a prototype for it in proto.h */
1712void
2c590a56 1713Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
f675dbe5 1714{
bc10a425
CB
1715 if (lnm && *lnm) {
1716 int len = strlen(lnm);
1717 if (len == 7) {
1718 char uplnm[8];
22d4bb9c
CB
1719 int i;
1720 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
bc10a425 1721 if (!strcmp(uplnm,"DEFAULT")) {
7ded3206 1722 if (eqv && *eqv) my_chdir(eqv);
bc10a425
CB
1723 return;
1724 }
1725 }
1726#ifndef RTL_USES_UTC
1727 if (len == 6 || len == 2) {
1728 char uplnm[7];
1729 int i;
1730 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1731 uplnm[len] = '\0';
1732 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1733 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
22d4bb9c
CB
1734 }
1735#endif
1736 }
f675dbe5
CB
1737 (void) vmssetenv(lnm,eqv,NULL);
1738}
a0d0e21e
LW
1739/*}}}*/
1740
27c67b75 1741/*{{{static void vmssetuserlnm(char *name, char *eqv); */
0e06870b
CB
1742/* vmssetuserlnm
1743 * sets a user-mode logical in the process logical name table
1744 * used for redirection of sys$error
4d9538c1
JM
1745 *
1746 * Fix-me: The pTHX is not needed for this routine, however doio.c
1747 * is calling it with one instead of using a macro.
1748 * A macro needs to be added to vmsish.h and doio.c updated to use it.
1749 *
0e06870b
CB
1750 */
1751void
2fbb330f 1752Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
0e06870b
CB
1753{
1754 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1755 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
2d5e9e5d 1756 unsigned long int iss, attr = LNM$M_CONFINE;
0e06870b
CB
1757 unsigned char acmode = PSL$C_USER;
1758 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1759 {0, 0, 0, 0}};
2fbb330f 1760 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
0e06870b
CB
1761 d_name.dsc$w_length = strlen(name);
1762
1763 lnmlst[0].buflen = strlen(eqv);
2fbb330f 1764 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
0e06870b
CB
1765
1766 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1767 if (!(iss&1)) lib$signal(iss);
1768}
1769/*}}}*/
c07a80fd 1770
f675dbe5 1771
c07a80fd 1772/*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1773/* my_crypt - VMS password hashing
1774 * my_crypt() provides an interface compatible with the Unix crypt()
1775 * C library function, and uses sys$hash_password() to perform VMS
1776 * password hashing. The quadword hashed password value is returned
1777 * as a NUL-terminated 8 character string. my_crypt() does not change
1778 * the case of its string arguments; in order to match the behavior
1779 * of LOGINOUT et al., alphabetic characters in both arguments must
1780 * be upcased by the caller.
2497a41f
JM
1781 *
1782 * - fix me to call ACM services when available
c07a80fd 1783 */
1784char *
fd8cd3a3 1785Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
c07a80fd 1786{
1787# ifndef UAI$C_PREFERRED_ALGORITHM
1788# define UAI$C_PREFERRED_ALGORITHM 127
1789# endif
1790 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1791 unsigned short int salt = 0;
1792 unsigned long int sts;
1793 struct const_dsc {
1794 unsigned short int dsc$w_length;
1795 unsigned char dsc$b_type;
1796 unsigned char dsc$b_class;
1797 const char * dsc$a_pointer;
1798 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1799 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1800 struct itmlst_3 uailst[3] = {
1801 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1802 { sizeof salt, UAI$_SALT, &salt, 0},
1803 { 0, 0, NULL, NULL}};
1804 static char hash[9];
1805
1806 usrdsc.dsc$w_length = strlen(usrname);
1807 usrdsc.dsc$a_pointer = usrname;
1808 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1809 switch (sts) {
f282b18d 1810 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
c07a80fd 1811 set_errno(EACCES);
1812 break;
1813 case RMS$_RNF:
1814 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1815 break;
1816 default:
1817 set_errno(EVMSERR);
1818 }
1819 set_vaxc_errno(sts);
1820 if (sts != RMS$_RNF) return NULL;
1821 }
1822
1823 txtdsc.dsc$w_length = strlen(textpasswd);
1824 txtdsc.dsc$a_pointer = textpasswd;
1825 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1826 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1827 }
1828
1829 return (char *) hash;
1830
1831} /* end of my_crypt() */
1832/*}}}*/
1833
1834
360732b5
JM
1835static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1836static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1837static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
a0d0e21e 1838
2497a41f
JM
1839/* fixup barenames that are directories for internal use.
1840 * There have been problems with the consistent handling of UNIX
1841 * style directory names when routines are presented with a name that
1842 * has no directory delimitors at all. So this routine will eventually
1843 * fix the issue.
1844 */
1845static char * fixup_bare_dirnames(const char * name)
1846{
1847 if (decc_disable_to_vms_logname_translation) {
1848/* fix me */
1849 }
1850 return NULL;
1851}
1852
e0e5e8d6
JM
1853/* 8.3, remove() is now broken on symbolic links */
1854static int rms_erase(const char * vmsname);
1855
1856
2497a41f
JM
1857/* mp_do_kill_file
1858 * A little hack to get around a bug in some implemenation of remove()
1859 * that do not know how to delete a directory
1860 *
1861 * Delete any file to which user has control access, regardless of whether
1862 * delete access is explicitly allowed.
1863 * Limitations: User must have write access to parent directory.
1864 * Does not block signals or ASTs; if interrupted in midstream
1865 * may leave file with an altered ACL.
1866 * HANDLE WITH CARE!
1867 */
1868/*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1869static int
1870mp_do_kill_file(pTHX_ const char *name, int dirflag)
1871{
e0e5e8d6
JM
1872 char *vmsname;
1873 char *rslt;
2497a41f
JM
1874 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1875 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1876 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1877 struct myacedef {
1878 unsigned char myace$b_length;
1879 unsigned char myace$b_type;
1880 unsigned short int myace$w_flags;
1881 unsigned long int myace$l_access;
1882 unsigned long int myace$l_ident;
1883 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1884 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1885 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1886 struct itmlst_3
1887 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1888 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1889 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1890 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1891 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1892 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1893
1894 /* Expand the input spec using RMS, since the CRTL remove() and
1895 * system services won't do this by themselves, so we may miss
1896 * a file "hiding" behind a logical name or search list. */
c5375c28 1897 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
ebd4d70b 1898 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 1899
6fb6c614 1900 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
e0e5e8d6 1901 if (rslt == NULL) {
c5375c28 1902 PerlMem_free(vmsname);
2497a41f
JM
1903 return -1;
1904 }
c5375c28 1905
e0e5e8d6
JM
1906 /* Erase the file */
1907 rmsts = rms_erase(vmsname);
2497a41f 1908
e0e5e8d6
JM
1909 /* Did it succeed */
1910 if ($VMS_STATUS_SUCCESS(rmsts)) {
1911 PerlMem_free(vmsname);
1912 return 0;
2497a41f
JM
1913 }
1914
1915 /* If not, can changing protections help? */
e0e5e8d6
JM
1916 if (rmsts != RMS$_PRV) {
1917 set_vaxc_errno(rmsts);
1918 PerlMem_free(vmsname);
2497a41f
JM
1919 return -1;
1920 }
1921
1922 /* No, so we get our own UIC to use as a rights identifier,
1923 * and the insert an ACE at the head of the ACL which allows us
1924 * to delete the file.
1925 */
ebd4d70b 1926 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
e0e5e8d6
JM
1927 fildsc.dsc$w_length = strlen(vmsname);
1928 fildsc.dsc$a_pointer = vmsname;
2497a41f
JM
1929 cxt = 0;
1930 newace.myace$l_ident = oldace.myace$l_ident;
e0e5e8d6 1931 rmsts = -1;
2497a41f
JM
1932 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1933 switch (aclsts) {
1934 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1935 set_errno(ENOENT); break;
1936 case RMS$_DIR:
1937 set_errno(ENOTDIR); break;
1938 case RMS$_DEV:
1939 set_errno(ENODEV); break;
1940 case RMS$_SYN: case SS$_INVFILFOROP:
1941 set_errno(EINVAL); break;
1942 case RMS$_PRV:
1943 set_errno(EACCES); break;
1944 default:
ebd4d70b 1945 _ckvmssts_noperl(aclsts);
2497a41f
JM
1946 }
1947 set_vaxc_errno(aclsts);
e0e5e8d6 1948 PerlMem_free(vmsname);
2497a41f
JM
1949 return -1;
1950 }
1951 /* Grab any existing ACEs with this identifier in case we fail */
1952 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1953 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1954 || fndsts == SS$_NOMOREACE ) {
1955 /* Add the new ACE . . . */
1956 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1957 goto yourroom;
1958
e0e5e8d6
JM
1959 rmsts = rms_erase(vmsname);
1960 if ($VMS_STATUS_SUCCESS(rmsts)) {
1961 rmsts = 0;
2497a41f
JM
1962 }
1963 else {
e0e5e8d6 1964 rmsts = -1;
2497a41f
JM
1965 /* We blew it - dir with files in it, no write priv for
1966 * parent directory, etc. Put things back the way they were. */
1967 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1968 goto yourroom;
1969 if (fndsts & 1) {
1970 addlst[0].bufadr = &oldace;
1971 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1972 goto yourroom;
1973 }
1974 }
1975 }
1976
1977 yourroom:
1978 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1979 /* We just deleted it, so of course it's not there. Some versions of
1980 * VMS seem to return success on the unlock operation anyhow (after all
1981 * the unlock is successful), but others don't.
1982 */
1983 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1984 if (aclsts & 1) aclsts = fndsts;
1985 if (!(aclsts & 1)) {
1986 set_errno(EVMSERR);
1987 set_vaxc_errno(aclsts);
2497a41f
JM
1988 }
1989
e0e5e8d6 1990 PerlMem_free(vmsname);
2497a41f
JM
1991 return rmsts;
1992
1993} /* end of kill_file() */
1994/*}}}*/
1995
1996
a0d0e21e
LW
1997/*{{{int do_rmdir(char *name)*/
1998int
b8ffc8df 1999Perl_do_rmdir(pTHX_ const char *name)
a0d0e21e 2000{
e0e5e8d6 2001 char * dirfile;
a0d0e21e 2002 int retval;
61bb5906 2003 Stat_t st;
a0d0e21e 2004
d94c5a78
JM
2005 /* lstat returns a VMS fileified specification of the name */
2006 /* that is looked up, and also lets verifies that this is a directory */
e0e5e8d6 2007
46c05374 2008 retval = flex_lstat(name, &st);
d94c5a78
JM
2009 if (retval != 0) {
2010 char * ret_spec;
2011
2012 /* Due to a historical feature, flex_stat/lstat can not see some */
2013 /* Unix format file names that the rest of the CRTL can see */
2014 /* Fixing that feature will cause some perl tests to fail */
2015 /* So try this one more time. */
2016
2017 retval = lstat(name, &st.crtl_stat);
2018 if (retval != 0)
2019 return -1;
2020
2021 /* force it to a file spec for the kill file to work. */
2022 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
2023 if (ret_spec == NULL) {
2024 errno = EIO;
2025 return -1;
2026 }
e0e5e8d6 2027 }
d94c5a78
JM
2028
2029 if (!S_ISDIR(st.st_mode)) {
e0e5e8d6
JM
2030 errno = ENOTDIR;
2031 retval = -1;
2032 }
d94c5a78
JM
2033 else {
2034 dirfile = st.st_devnam;
2035
2036 /* It may be possible for flex_stat to find a file and vmsify() to */
2037 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
2038 /* with that case, so fail it */
2039 if (dirfile[0] == 0) {
2040 errno = EIO;
2041 return -1;
2042 }
2043
e0e5e8d6 2044 retval = mp_do_kill_file(aTHX_ dirfile, 1);
d94c5a78 2045 }
e0e5e8d6 2046
a0d0e21e
LW
2047 return retval;
2048
2049} /* end of do_rmdir */
2050/*}}}*/
2051
2052/* kill_file
2053 * Delete any file to which user has control access, regardless of whether
2054 * delete access is explicitly allowed.
2055 * Limitations: User must have write access to parent directory.
2056 * Does not block signals or ASTs; if interrupted in midstream
2057 * may leave file with an altered ACL.
2058 * HANDLE WITH CARE!
2059 */
2060/*{{{int kill_file(char *name)*/
2061int
b8ffc8df 2062Perl_kill_file(pTHX_ const char *name)
a0d0e21e 2063{
d94c5a78 2064 char * vmsfile;
e0e5e8d6
JM
2065 Stat_t st;
2066 int rmsts;
a0d0e21e 2067
d94c5a78
JM
2068 /* Convert the filename to VMS format and see if it is a directory */
2069 /* flex_lstat returns a vmsified file specification */
46c05374 2070 rmsts = flex_lstat(name, &st);
d94c5a78
JM
2071 if (rmsts != 0) {
2072
2073 /* Due to a historical feature, flex_stat/lstat can not see some */
2074 /* Unix format file names that the rest of the CRTL can see when */
2075 /* ODS-2 file specifications are in use. */
2076 /* Fixing that feature will cause some perl tests to fail */
2077 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2078 st.st_mode = 0;
2079 vmsfile = (char *) name; /* cast ok */
2080
2081 } else {
2082 vmsfile = st.st_devnam;
2083 if (vmsfile[0] == 0) {
2084 /* It may be possible for flex_stat to find a file and vmsify() */
2085 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2086 /* deal with that case, so fail it */
2087 errno = EIO;
2088 return -1;
2089 }
2090 }
2091
2092 /* Remove() is allowed to delete directories, according to the X/Open
2093 * specifications.
2094 * This may need special handling to work with the ACL hacks.
a0d0e21e 2095 */
d94c5a78
JM
2096 if (S_ISDIR(st.st_mode)) {
2097 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2098 return rmsts;
a0d0e21e
LW
2099 }
2100
d94c5a78
JM
2101 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2102
2103 /* Need to delete all versions ? */
2104 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2105 int i = 0;
2106
2107 /* Just use lstat() here as do not need st_dev */
2108 /* and we know that the file is in VMS format or that */
2109 /* because of a historical bug, flex_stat can not see the file */
2110 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2111 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2112 if (rmsts != 0)
2113 break;
2114 i++;
2115
2116 /* Make sure that we do not loop forever */
2117 if (i > 32767) {
2118 errno = EIO;
2119 rmsts = -1;
2120 break;
2121 }
2122 }
2123 }
a0d0e21e
LW
2124
2125 return rmsts;
2126
2127} /* end of kill_file() */
2128/*}}}*/
2129
8cc95fdb 2130
84902520 2131/*{{{int my_mkdir(char *,Mode_t)*/
8cc95fdb 2132int
b8ffc8df 2133Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
8cc95fdb 2134{
2135 STRLEN dirlen = strlen(dir);
2136
a2a90019
CB
2137 /* zero length string sometimes gives ACCVIO */
2138 if (dirlen == 0) return -1;
2139
8cc95fdb 2140 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2141 * null file name/type. However, it's commonplace under Unix,
2142 * so we'll allow it for a gain in portability.
2143 */
2144 if (dir[dirlen-1] == '/') {
2145 char *newdir = savepvn(dir,dirlen-1);
2146 int ret = mkdir(newdir,mode);
2147 Safefree(newdir);
2148 return ret;
2149 }
2150 else return mkdir(dir,mode);
2151} /* end of my_mkdir */
2152/*}}}*/
2153
ee8c7f54
CB
2154/*{{{int my_chdir(char *)*/
2155int
b8ffc8df 2156Perl_my_chdir(pTHX_ const char *dir)
ee8c7f54
CB
2157{
2158 STRLEN dirlen = strlen(dir);
ee8c7f54
CB
2159
2160 /* zero length string sometimes gives ACCVIO */
2161 if (dirlen == 0) return -1;
f7ddb74a
JM
2162 const char *dir1;
2163
2164 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2165 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2166 * so that existing scripts do not need to be changed.
2167 */
2168 dir1 = dir;
2169 while ((dirlen > 0) && (*dir1 == ' ')) {
2170 dir1++;
2171 dirlen--;
2172 }
ee8c7f54
CB
2173
2174 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2175 * that implies
2176 * null file name/type. However, it's commonplace under Unix,
2177 * so we'll allow it for a gain in portability.
f7ddb74a 2178 *
4d9538c1 2179 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
ee8c7f54 2180 */
f7ddb74a 2181 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
4d9538c1
JM
2182 char *newdir;
2183 int ret;
2184 newdir = PerlMem_malloc(dirlen);
2185 if (newdir ==NULL)
2186 _ckvmssts_noperl(SS$_INSFMEM);
2187 strncpy(newdir, dir1, dirlen-1);
2188 newdir[dirlen-1] = '\0';
2189 ret = chdir(newdir);
2190 PerlMem_free(newdir);
2191 return ret;
ee8c7f54 2192 }
dca5a913 2193 else return chdir(dir1);
ee8c7f54
CB
2194} /* end of my_chdir */
2195/*}}}*/
8cc95fdb 2196
674d6c38 2197
f1db9cda
JM
2198/*{{{int my_chmod(char *, mode_t)*/
2199int
2200Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2201{
4d9538c1
JM
2202 Stat_t st;
2203 int ret = -1;
2204 char * changefile;
f1db9cda
JM
2205 STRLEN speclen = strlen(file_spec);
2206
2207 /* zero length string sometimes gives ACCVIO */
2208 if (speclen == 0) return -1;
2209
2210 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2211 * that implies null file name/type. However, it's commonplace under Unix,
2212 * so we'll allow it for a gain in portability.
2213 *
2214 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2215 * in VMS file.dir notation.
2216 */
4d9538c1
JM
2217 changefile = (char *) file_spec; /* cast ok */
2218 ret = flex_lstat(file_spec, &st);
2219 if (ret != 0) {
f1db9cda 2220
4d9538c1
JM
2221 /* Due to a historical feature, flex_stat/lstat can not see some */
2222 /* Unix format file names that the rest of the CRTL can see when */
2223 /* ODS-2 file specifications are in use. */
2224 /* Fixing that feature will cause some perl tests to fail */
2225 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2226 st.st_mode = 0;
f1db9cda 2227
4d9538c1
JM
2228 } else {
2229 /* It may be possible to get here with nothing in st_devname */
2230 /* chmod still may work though */
2231 if (st.st_devnam[0] != 0) {
2232 changefile = st.st_devnam;
2233 }
f1db9cda 2234 }
4d9538c1
JM
2235 ret = chmod(changefile, mode);
2236 return ret;
f1db9cda
JM
2237} /* end of my_chmod */
2238/*}}}*/
2239
2240
674d6c38
CB
2241/*{{{FILE *my_tmpfile()*/
2242FILE *
2243my_tmpfile(void)
2244{
2245 FILE *fp;
2246 char *cp;
674d6c38
CB
2247
2248 if ((fp = tmpfile())) return fp;
2249
c5375c28
JM
2250 cp = PerlMem_malloc(L_tmpnam+24);
2251 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2252
2497a41f
JM
2253 if (decc_filename_unix_only == 0)
2254 strcpy(cp,"Sys$Scratch:");
2255 else
2256 strcpy(cp,"/tmp/");
674d6c38
CB
2257 tmpnam(cp+strlen(cp));
2258 strcat(cp,".Perltmp");
2259 fp = fopen(cp,"w+","fop=dlt");
c5375c28 2260 PerlMem_free(cp);
674d6c38
CB
2261 return fp;
2262}
2263/*}}}*/
2264
5c2d7af2
CB
2265
2266#ifndef HOMEGROWN_POSIX_SIGNALS
2267/*
2268 * The C RTL's sigaction fails to check for invalid signal numbers so we
2269 * help it out a bit. The docs are correct, but the actual routine doesn't
2270 * do what the docs say it will.
2271 */
2272/*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2273int
2274Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2275 struct sigaction* oact)
2276{
2277 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2278 SETERRNO(EINVAL, SS$_INVARG);
2279 return -1;
2280 }
2281 return sigaction(sig, act, oact);
2282}
2283/*}}}*/
2284#endif
2285
f2610a60
CL
2286#ifdef KILL_BY_SIGPRC
2287#include <errnodef.h>
2288
05c058bc
CB
2289/* We implement our own kill() using the undocumented system service
2290 sys$sigprc for one of two reasons:
2291
2292 1.) If the kill() in an older CRTL uses sys$forcex, causing the
f2610a60
CL
2293 target process to do a sys$exit, which usually can't be handled
2294 gracefully...certainly not by Perl and the %SIG{} mechanism.
2295
05c058bc
CB
2296 2.) If the kill() in the CRTL can't be called from a signal
2297 handler without disappearing into the ether, i.e., the signal
2298 it purportedly sends is never trapped. Still true as of VMS 7.3.
2299
2300 sys$sigprc has the same parameters as sys$forcex, but throws an exception
f2610a60
CL
2301 in the target process rather than calling sys$exit.
2302
2303 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2304 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2305 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2306 with condition codes C$_SIG0+nsig*8, catching the exception on the
2307 target process and resignaling with appropriate arguments.
2308
2309 But we don't have that VMS 7.0+ exception handler, so if you
2310 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2311
2312 Also note that SIGTERM is listed in the docs as being "unimplemented",
2313 yet always seems to be signaled with a VMS condition code of 4 (and
2314 correctly handled for that code). So we hardwire it in.
2315
2316 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2317 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2318 than signalling with an unrecognized (and unhandled by CRTL) code.
2319*/
2320
fe1de8ce 2321#define _MY_SIG_MAX 28
f2610a60 2322
9c1171d1
JM
2323static unsigned int
2324Perl_sig_to_vmscondition_int(int sig)
f2610a60 2325{
2e34cc90 2326 static unsigned int sig_code[_MY_SIG_MAX+1] =
f2610a60
CL
2327 {
2328 0, /* 0 ZERO */
2329 SS$_HANGUP, /* 1 SIGHUP */
2330 SS$_CONTROLC, /* 2 SIGINT */
2331 SS$_CONTROLY, /* 3 SIGQUIT */
2332 SS$_RADRMOD, /* 4 SIGILL */
2333 SS$_BREAK, /* 5 SIGTRAP */
2334 SS$_OPCCUS, /* 6 SIGABRT */
2335 SS$_COMPAT, /* 7 SIGEMT */
2336#ifdef __VAX
2337 SS$_FLTOVF, /* 8 SIGFPE VAX */
2338#else
2339 SS$_HPARITH, /* 8 SIGFPE AXP */
2340#endif
2341 SS$_ABORT, /* 9 SIGKILL */
2342 SS$_ACCVIO, /* 10 SIGBUS */
2343 SS$_ACCVIO, /* 11 SIGSEGV */
2344 SS$_BADPARAM, /* 12 SIGSYS */
2345 SS$_NOMBX, /* 13 SIGPIPE */
2346 SS$_ASTFLT, /* 14 SIGALRM */
2347 4, /* 15 SIGTERM */
2348 0, /* 16 SIGUSR1 */
fe1de8ce
CB
2349 0, /* 17 SIGUSR2 */
2350 0, /* 18 */
2351 0, /* 19 */
2352 0, /* 20 SIGCHLD */
2353 0, /* 21 SIGCONT */
2354 0, /* 22 SIGSTOP */
2355 0, /* 23 SIGTSTP */
2356 0, /* 24 SIGTTIN */
2357 0, /* 25 SIGTTOU */
2358 0, /* 26 */
2359 0, /* 27 */
2360 0 /* 28 SIGWINCH */
f2610a60
CL
2361 };
2362
2363#if __VMS_VER >= 60200000
2364 static int initted = 0;
2365 if (!initted) {
2366 initted = 1;
2367 sig_code[16] = C$_SIGUSR1;
2368 sig_code[17] = C$_SIGUSR2;
fe1de8ce
CB
2369#if __CRTL_VER >= 70000000
2370 sig_code[20] = C$_SIGCHLD;
2371#endif
2372#if __CRTL_VER >= 70300000
2373 sig_code[28] = C$_SIGWINCH;
2374#endif
f2610a60
CL
2375 }
2376#endif
2377
2e34cc90
CL
2378 if (sig < _SIG_MIN) return 0;
2379 if (sig > _MY_SIG_MAX) return 0;
2380 return sig_code[sig];
2381}
2382
9c1171d1
JM
2383unsigned int
2384Perl_sig_to_vmscondition(int sig)
2385{
2386#ifdef SS$_DEBUG
2387 if (vms_debug_on_exception != 0)
2388 lib$signal(SS$_DEBUG);
2389#endif
2390 return Perl_sig_to_vmscondition_int(sig);
2391}
2392
2393
2e34cc90
CL
2394int
2395Perl_my_kill(int pid, int sig)
2396{
218fdd94 2397 dTHX;
2e34cc90
CL
2398 int iss;
2399 unsigned int code;
17072196 2400#define sys$sigprc SYS$SIGPRC
2e34cc90
CL
2401 int sys$sigprc(unsigned int *pidadr,
2402 struct dsc$descriptor_s *prcname,
2403 unsigned int code);
2404
7a7fd8e0
JM
2405 /* sig 0 means validate the PID */
2406 /*------------------------------*/
2407 if (sig == 0) {
2408 const unsigned long int jpicode = JPI$_PID;
2409 pid_t ret_pid;
2410 int status;
2411 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2412 if ($VMS_STATUS_SUCCESS(status))
2413 return 0;
2414 switch (status) {
2415 case SS$_NOSUCHNODE:
2416 case SS$_UNREACHABLE:
2417 case SS$_NONEXPR:
2418 errno = ESRCH;
2419 break;
2420 case SS$_NOPRIV:
2421 errno = EPERM;
2422 break;
2423 default:
2424 errno = EVMSERR;
2425 }
2426 vaxc$errno=status;
2427 return -1;
2428 }
2429
9c1171d1 2430 code = Perl_sig_to_vmscondition_int(sig);
2e34cc90 2431
7a7fd8e0
JM
2432 if (!code) {
2433 SETERRNO(EINVAL, SS$_BADPARAM);
2434 return -1;
2435 }
2436
2437 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2438 * signals are to be sent to multiple processes.
2439 * pid = 0 - all processes in group except ones that the system exempts
2440 * pid = -1 - all processes except ones that the system exempts
2441 * pid = -n - all processes in group (abs(n)) except ...
2442 * For now, just report as not supported.
2443 */
2444
2445 if (pid <= 0) {
2446 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
f2610a60
CL
2447 return -1;
2448 }
2449
2e34cc90 2450 iss = sys$sigprc((unsigned int *)&pid,0,code);
f2610a60
CL
2451 if (iss&1) return 0;
2452
2453 switch (iss) {
2454 case SS$_NOPRIV:
2455 set_errno(EPERM); break;
2456 case SS$_NONEXPR:
2457 case SS$_NOSUCHNODE:
2458 case SS$_UNREACHABLE:
2459 set_errno(ESRCH); break;
2460 case SS$_INSFMEM:
2461 set_errno(ENOMEM); break;
2462 default:
ebd4d70b 2463 _ckvmssts_noperl(iss);
f2610a60
CL
2464 set_errno(EVMSERR);
2465 }
2466 set_vaxc_errno(iss);
2467
2468 return -1;
2469}
2470#endif
2471
2fbb330f
JM
2472/* Routine to convert a VMS status code to a UNIX status code.
2473** More tricky than it appears because of conflicting conventions with
2474** existing code.
2475**
2476** VMS status codes are a bit mask, with the least significant bit set for
2477** success.
2478**
2479** Special UNIX status of EVMSERR indicates that no translation is currently
2480** available, and programs should check the VMS status code.
2481**
2482** Programs compiled with _POSIX_EXIT have a special encoding that requires
2483** decoding.
2484*/
2485
2486#ifndef C_FACILITY_NO
2487#define C_FACILITY_NO 0x350000
2488#endif
2489#ifndef DCL_IVVERB
2490#define DCL_IVVERB 0x38090
2491#endif
2492
7a7fd8e0 2493int Perl_vms_status_to_unix(int vms_status, int child_flag)
2fbb330f
JM
2494{
2495int facility;
2496int fac_sp;
2497int msg_no;
2498int msg_status;
2499int unix_status;
2500
2501 /* Assume the best or the worst */
2502 if (vms_status & STS$M_SUCCESS)
2503 unix_status = 0;
2504 else
2505 unix_status = EVMSERR;
2506
2507 msg_status = vms_status & ~STS$M_CONTROL;
2508
2509 facility = vms_status & STS$M_FAC_NO;
2510 fac_sp = vms_status & STS$M_FAC_SP;
2511 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2512
0968cdad 2513 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2fbb330f
JM
2514 switch(msg_no) {
2515 case SS$_NORMAL:
2516 unix_status = 0;
2517 break;
2518 case SS$_ACCVIO:
2519 unix_status = EFAULT;
2520 break;
7a7fd8e0
JM
2521 case SS$_DEVOFFLINE:
2522 unix_status = EBUSY;
2523 break;
2524 case SS$_CLEARED:
2525 unix_status = ENOTCONN;
2526 break;
2527 case SS$_IVCHAN:
2fbb330f
JM
2528 case SS$_IVLOGNAM:
2529 case SS$_BADPARAM:
2530 case SS$_IVLOGTAB:
2531 case SS$_NOLOGNAM:
2532 case SS$_NOLOGTAB:
2533 case SS$_INVFILFOROP:
2534 case SS$_INVARG:
2535 case SS$_NOSUCHID:
2536 case SS$_IVIDENT:
2537 unix_status = EINVAL;
2538 break;
7a7fd8e0
JM
2539 case SS$_UNSUPPORTED:
2540 unix_status = ENOTSUP;
2541 break;
2fbb330f
JM
2542 case SS$_FILACCERR:
2543 case SS$_NOGRPPRV:
2544 case SS$_NOSYSPRV:
2545 unix_status = EACCES;
2546 break;
2547 case SS$_DEVICEFULL:
2548 unix_status = ENOSPC;
2549 break;
2550 case SS$_NOSUCHDEV:
2551 unix_status = ENODEV;
2552 break;
2553 case SS$_NOSUCHFILE:
2554 case SS$_NOSUCHOBJECT:
2555 unix_status = ENOENT;
2556 break;
fb38d079
JM
2557 case SS$_ABORT: /* Fatal case */
2558 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2559 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2fbb330f
JM
2560 unix_status = EINTR;
2561 break;
2562 case SS$_BUFFEROVF:
2563 unix_status = E2BIG;
2564 break;
2565 case SS$_INSFMEM:
2566 unix_status = ENOMEM;
2567 break;
2568 case SS$_NOPRIV:
2569 unix_status = EPERM;
2570 break;
2571 case SS$_NOSUCHNODE:
2572 case SS$_UNREACHABLE:
2573 unix_status = ESRCH;
2574 break;
2575 case SS$_NONEXPR:
2576 unix_status = ECHILD;
2577 break;
2578 default:
2579 if ((facility == 0) && (msg_no < 8)) {
2580 /* These are not real VMS status codes so assume that they are
2581 ** already UNIX status codes
2582 */
2583 unix_status = msg_no;
2584 break;
2585 }
2586 }
2587 }
2588 else {
2589 /* Translate a POSIX exit code to a UNIX exit code */
2590 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
7a7fd8e0 2591 unix_status = (msg_no & 0x07F8) >> 3;
2fbb330f
JM
2592 }
2593 else {
7a7fd8e0
JM
2594
2595 /* Documented traditional behavior for handling VMS child exits */
2596 /*--------------------------------------------------------------*/
2597 if (child_flag != 0) {
2598
2599 /* Success / Informational return 0 */
2600 /*----------------------------------*/
2601 if (msg_no & STS$K_SUCCESS)
2602 return 0;
2603
2604 /* Warning returns 1 */
2605 /*-------------------*/
2606 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2607 return 1;
2608
2609 /* Everything else pass through the severity bits */
2610 /*------------------------------------------------*/
2611 return (msg_no & STS$M_SEVERITY);
2612 }
2613
2614 /* Normal VMS status to ERRNO mapping attempt */
2615 /*--------------------------------------------*/
2fbb330f
JM
2616 switch(msg_status) {
2617 /* case RMS$_EOF: */ /* End of File */
2618 case RMS$_FNF: /* File Not Found */
2619 case RMS$_DNF: /* Dir Not Found */
2620 unix_status = ENOENT;
2621 break;
2622 case RMS$_RNF: /* Record Not Found */
2623 unix_status = ESRCH;
2624 break;
2625 case RMS$_DIR:
2626 unix_status = ENOTDIR;
2627 break;
2628 case RMS$_DEV:
2629 unix_status = ENODEV;
2630 break;
7a7fd8e0
JM
2631 case RMS$_IFI:
2632 case RMS$_FAC:
2633 case RMS$_ISI:
2634 unix_status = EBADF;
2635 break;
2636 case RMS$_FEX:
2637 unix_status = EEXIST;
2638 break;
2fbb330f
JM
2639 case RMS$_SYN:
2640 case RMS$_FNM:
2641 case LIB$_INVSTRDES:
2642 case LIB$_INVARG:
2643 case LIB$_NOSUCHSYM:
2644 case LIB$_INVSYMNAM:
2645 case DCL_IVVERB:
2646 unix_status = EINVAL;
2647 break;
2648 case CLI$_BUFOVF:
2649 case RMS$_RTB:
2650 case CLI$_TKNOVF:
2651 case CLI$_RSLOVF:
2652 unix_status = E2BIG;
2653 break;
2654 case RMS$_PRV: /* No privilege */
2655 case RMS$_ACC: /* ACP file access failed */
2656 case RMS$_WLK: /* Device write locked */
2657 unix_status = EACCES;
2658 break;
ed1b9de0
JM
2659 case RMS$_MKD: /* Failed to mark for delete */
2660 unix_status = EPERM;
2661 break;
2fbb330f
JM
2662 /* case RMS$_NMF: */ /* No more files */
2663 }
2664 }
2665 }
2666
2667 return unix_status;
2668}
2669
7a7fd8e0
JM
2670/* Try to guess at what VMS error status should go with a UNIX errno
2671 * value. This is hard to do as there could be many possible VMS
2672 * error statuses that caused the errno value to be set.
2673 */
2674
2675int Perl_unix_status_to_vms(int unix_status)
2676{
2677int test_unix_status;
2678
2679 /* Trivial cases first */
2680 /*---------------------*/
2681 if (unix_status == EVMSERR)
2682 return vaxc$errno;
2683
2684 /* Is vaxc$errno sane? */
2685 /*---------------------*/
2686 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2687 if (test_unix_status == unix_status)
2688 return vaxc$errno;
2689
2690 /* If way out of range, must be VMS code already */
2691 /*-----------------------------------------------*/
2692 if (unix_status > EVMSERR)
2693 return unix_status;
2694
2695 /* If out of range, punt */
2696 /*-----------------------*/
2697 if (unix_status > __ERRNO_MAX)
2698 return SS$_ABORT;
2699
2700
2701 /* Ok, now we have to do it the hard way. */
2702 /*----------------------------------------*/
2703 switch(unix_status) {
2704 case 0: return SS$_NORMAL;
2705 case EPERM: return SS$_NOPRIV;
2706 case ENOENT: return SS$_NOSUCHOBJECT;
2707 case ESRCH: return SS$_UNREACHABLE;
2708 case EINTR: return SS$_ABORT;
2709 /* case EIO: */
2710 /* case ENXIO: */
2711 case E2BIG: return SS$_BUFFEROVF;
2712 /* case ENOEXEC */
2713 case EBADF: return RMS$_IFI;
2714 case ECHILD: return SS$_NONEXPR;
2715 /* case EAGAIN */
2716 case ENOMEM: return SS$_INSFMEM;
2717 case EACCES: return SS$_FILACCERR;
2718 case EFAULT: return SS$_ACCVIO;
2719 /* case ENOTBLK */
0968cdad 2720 case EBUSY: return SS$_DEVOFFLINE;
7a7fd8e0
JM
2721 case EEXIST: return RMS$_FEX;
2722 /* case EXDEV */
2723 case ENODEV: return SS$_NOSUCHDEV;
2724 case ENOTDIR: return RMS$_DIR;
2725 /* case EISDIR */
2726 case EINVAL: return SS$_INVARG;
2727 /* case ENFILE */
2728 /* case EMFILE */
2729 /* case ENOTTY */
2730 /* case ETXTBSY */
2731 /* case EFBIG */
2732 case ENOSPC: return SS$_DEVICEFULL;
2733 case ESPIPE: return LIB$_INVARG;
2734 /* case EROFS: */
2735 /* case EMLINK: */
2736 /* case EPIPE: */
2737 /* case EDOM */
2738 case ERANGE: return LIB$_INVARG;
2739 /* case EWOULDBLOCK */
2740 /* case EINPROGRESS */
2741 /* case EALREADY */
2742 /* case ENOTSOCK */
2743 /* case EDESTADDRREQ */
2744 /* case EMSGSIZE */
2745 /* case EPROTOTYPE */
2746 /* case ENOPROTOOPT */
2747 /* case EPROTONOSUPPORT */
2748 /* case ESOCKTNOSUPPORT */
2749 /* case EOPNOTSUPP */
2750 /* case EPFNOSUPPORT */
2751 /* case EAFNOSUPPORT */
2752 /* case EADDRINUSE */
2753 /* case EADDRNOTAVAIL */
2754 /* case ENETDOWN */
2755 /* case ENETUNREACH */
2756 /* case ENETRESET */
2757 /* case ECONNABORTED */
2758 /* case ECONNRESET */
2759 /* case ENOBUFS */
2760 /* case EISCONN */
2761 case ENOTCONN: return SS$_CLEARED;
2762 /* case ESHUTDOWN */
2763 /* case ETOOMANYREFS */
2764 /* case ETIMEDOUT */
2765 /* case ECONNREFUSED */
2766 /* case ELOOP */
2767 /* case ENAMETOOLONG */
2768 /* case EHOSTDOWN */
2769 /* case EHOSTUNREACH */
2770 /* case ENOTEMPTY */
2771 /* case EPROCLIM */
2772 /* case EUSERS */
2773 /* case EDQUOT */
2774 /* case ENOMSG */
2775 /* case EIDRM */
2776 /* case EALIGN */
2777 /* case ESTALE */
2778 /* case EREMOTE */
2779 /* case ENOLCK */
2780 /* case ENOSYS */
2781 /* case EFTYPE */
2782 /* case ECANCELED */
2783 /* case EFAIL */
2784 /* case EINPROG */
2785 case ENOTSUP:
2786 return SS$_UNSUPPORTED;
2787 /* case EDEADLK */
2788 /* case ENWAIT */
2789 /* case EILSEQ */
2790 /* case EBADCAT */
2791 /* case EBADMSG */
2792 /* case EABANDONED */
2793 default:
2794 return SS$_ABORT; /* punt */
2795 }
2796
2797 return SS$_ABORT; /* Should not get here */
2798}
2fbb330f
JM
2799
2800
22d4bb9c 2801/* default piping mailbox size */
df17c887
CB
2802#ifdef __VAX
2803# define PERL_BUFSIZ 512
2804#else
2805# define PERL_BUFSIZ 8192
2806#endif
22d4bb9c 2807
674d6c38 2808
a0d0e21e 2809static void
8a646e0b 2810create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
a0d0e21e 2811{
22d4bb9c
CB
2812 unsigned long int mbxbufsiz;
2813 static unsigned long int syssize = 0;
2814 unsigned long int dviitm = DVI$_DEVNAM;
22d4bb9c 2815 char csize[LNM$C_NAMLENGTH+1];
f7ddb74a
JM
2816 int sts;
2817
22d4bb9c
CB
2818 if (!syssize) {
2819 unsigned long syiitm = SYI$_MAXBUF;
a0d0e21e 2820 /*
22d4bb9c
CB
2821 * Get the SYSGEN parameter MAXBUF
2822 *
2823 * If the logical 'PERL_MBX_SIZE' is defined
2824 * use the value of the logical instead of PERL_BUFSIZ, but
2825 * keep the size between 128 and MAXBUF.
2826 *
a0d0e21e 2827 */
ebd4d70b 2828 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
22d4bb9c
CB
2829 }
2830
2831 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2832 mbxbufsiz = atoi(csize);
2833 } else {
2834 mbxbufsiz = PERL_BUFSIZ;
a0d0e21e 2835 }
22d4bb9c
CB
2836 if (mbxbufsiz < 128) mbxbufsiz = 128;
2837 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2838
ebd4d70b 2839 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
a0d0e21e 2840
ebd4d70b
JM
2841 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2842 _ckvmssts_noperl(sts);
a0d0e21e
LW
2843 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2844
2845} /* end of create_mbx() */
2846
22d4bb9c 2847
a0d0e21e 2848/*{{{ my_popen and my_pclose*/
22d4bb9c
CB
2849
2850typedef struct _iosb IOSB;
2851typedef struct _iosb* pIOSB;
2852typedef struct _pipe Pipe;
2853typedef struct _pipe* pPipe;
2854typedef struct pipe_details Info;
2855typedef struct pipe_details* pInfo;
2856typedef struct _srqp RQE;
2857typedef struct _srqp* pRQE;
2858typedef struct _tochildbuf CBuf;
2859typedef struct _tochildbuf* pCBuf;
2860
2861struct _iosb {
2862 unsigned short status;
2863 unsigned short count;
2864 unsigned long dvispec;
2865};
2866
2867#pragma member_alignment save
2868#pragma nomember_alignment quadword
2869struct _srqp { /* VMS self-relative queue entry */
2870 unsigned long qptr[2];
2871};
2872#pragma member_alignment restore
2873static RQE RQE_ZERO = {0,0};
2874
2875struct _tochildbuf {
2876 RQE q;
2877 int eof;
2878 unsigned short size;
2879 char *buf;
2880};
2881
2882struct _pipe {
2883 RQE free;
2884 RQE wait;
2885 int fd_out;
2886 unsigned short chan_in;
2887 unsigned short chan_out;
2888 char *buf;
2889 unsigned int bufsize;
2890 IOSB iosb;
2891 IOSB iosb2;
2892 int *pipe_done;
2893 int retry;
2894 int type;
2895 int shut_on_empty;
2896 int need_wake;
2897 pPipe *home;
2898 pInfo info;
2899 pCBuf curr;
2900 pCBuf curr2;
fd8cd3a3
DS
2901#if defined(PERL_IMPLICIT_CONTEXT)
2902 void *thx; /* Either a thread or an interpreter */
2903 /* pointer, depending on how we're built */
2904#endif
22d4bb9c
CB
2905};
2906
2907
a0d0e21e
LW
2908struct pipe_details
2909{
22d4bb9c 2910 pInfo next;
ff7adb52
CL
2911 PerlIO *fp; /* file pointer to pipe mailbox */
2912 int useFILE; /* using stdio, not perlio */
748a9306
LW
2913 int pid; /* PID of subprocess */
2914 int mode; /* == 'r' if pipe open for reading */
2915 int done; /* subprocess has completed */
ff7adb52 2916 int waiting; /* waiting for completion/closure */
22d4bb9c
CB
2917 int closing; /* my_pclose is closing this pipe */
2918 unsigned long completion; /* termination status of subprocess */
2919 pPipe in; /* pipe in to sub */
2920 pPipe out; /* pipe out of sub */
2921 pPipe err; /* pipe of sub's sys$error */
2922 int in_done; /* true when in pipe finished */
2923 int out_done;
2924 int err_done;
cd1191f1
CB
2925 unsigned short xchan; /* channel to debug xterm */
2926 unsigned short xchan_valid; /* channel is assigned */
a0d0e21e
LW
2927};
2928
748a9306
LW
2929struct exit_control_block
2930{
2931 struct exit_control_block *flink;
2932 unsigned long int (*exit_routine)();
2933 unsigned long int arg_count;
2934 unsigned long int *status_address;
2935 unsigned long int exit_status;
2936};
2937
d85f548a
JH
2938typedef struct _closed_pipes Xpipe;
2939typedef struct _closed_pipes* pXpipe;
2940
2941struct _closed_pipes {
2942 int pid; /* PID of subprocess */
2943 unsigned long completion; /* termination status of subprocess */
2944};
2945#define NKEEPCLOSED 50
2946static Xpipe closed_list[NKEEPCLOSED];
2947static int closed_index = 0;
2948static int closed_num = 0;
2949
22d4bb9c
CB
2950#define RETRY_DELAY "0 ::0.20"
2951#define MAX_RETRY 50
a0d0e21e 2952
22d4bb9c
CB
2953static int pipe_ef = 0; /* first call to safe_popen inits these*/
2954static unsigned long mypid;
2955static unsigned long delaytime[2];
2956
2957static pInfo open_pipes = NULL;
2958static $DESCRIPTOR(nl_desc, "NL:");
3eeba6fb 2959
ff7adb52
CL
2960#define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2961
2962
3eeba6fb 2963
748a9306 2964static unsigned long int
ebd4d70b 2965pipe_exit_routine()
748a9306 2966{
22d4bb9c 2967 pInfo info;
1e422769 2968 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
ff7adb52
CL
2969 int sts, did_stuff, need_eof, j;
2970
5ce486e0
CB
2971 /*
2972 * Flush any pending i/o, but since we are in process run-down, be
2973 * careful about referencing PerlIO structures that may already have
2974 * been deallocated. We may not even have an interpreter anymore.
ff7adb52
CL
2975 */
2976 info = open_pipes;
2977 while (info) {
2978 if (info->fp) {
ebd4d70b
JM
2979#if defined(PERL_IMPLICIT_CONTEXT)
2980 /* We need to use the Perl context of the thread that created */
2981 /* the pipe. */
2982 pTHX;
2983 if (info->err)
2984 aTHX = info->err->thx;
2985 else if (info->out)
2986 aTHX = info->out->thx;
2987 else if (info->in)
2988 aTHX = info->in->thx;
2989#endif
5ce486e0
CB
2990 if (!info->useFILE
2991#if defined(USE_ITHREADS)
2992 && my_perl
2993#endif
a24c654f
CB
2994#ifdef USE_PERLIO
2995 && PL_perlio_fd_refcnt
2996#endif
2997 )
5ce486e0 2998 PerlIO_flush(info->fp);
ff7adb52
CL
2999 else
3000 fflush((FILE *)info->fp);
3001 }
3002 info = info->next;
3003 }
3eeba6fb
CB
3004
3005 /*
ff7adb52 3006 next we try sending an EOF...ignore if doesn't work, make sure we
3eeba6fb
CB
3007 don't hang
3008 */
3009 did_stuff = 0;
3010 info = open_pipes;
748a9306 3011
3eeba6fb 3012 while (info) {
b2b89246 3013 int need_eof;
d4c83939 3014 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 3015 if (info->in && !info->in->shut_on_empty) {
d4c83939 3016 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
ebd4d70b 3017 0, 0, 0, 0, 0, 0));
ff7adb52 3018 info->waiting = 1;
22d4bb9c 3019 did_stuff = 1;
748a9306 3020 }
d4c83939 3021 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3022 info = info->next;
3023 }
ff7adb52
CL
3024
3025 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3026
3027 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3028 int nwait = 0;
3029
3030 info = open_pipes;
3031 while (info) {
d4c83939 3032 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3033 if (info->waiting && info->done)
3034 info->waiting = 0;
3035 nwait += info->waiting;
d4c83939 3036 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3037 info = info->next;
3038 }
3039 if (!nwait) break;
3040 sleep(1);
3041 }
3eeba6fb
CB
3042
3043 did_stuff = 0;
3044 info = open_pipes;
3045 while (info) {
d4c83939 3046 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3047 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3048 sts = sys$forcex(&info->pid,0,&abort);
d4c83939 3049 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3eeba6fb
CB
3050 did_stuff = 1;
3051 }
d4c83939 3052 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3053 info = info->next;
3054 }
ff7adb52
CL
3055
3056 /* again, wait for effect */
3057
3058 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3059 int nwait = 0;
3060
3061 info = open_pipes;
3062 while (info) {
d4c83939 3063 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3064 if (info->waiting && info->done)
3065 info->waiting = 0;
3066 nwait += info->waiting;
d4c83939 3067 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3068 info = info->next;
3069 }
3070 if (!nwait) break;
3071 sleep(1);
3072 }
3eeba6fb
CB
3073
3074 info = open_pipes;
3075 while (info) {
d4c83939 3076 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3077 if (!info->done) { /* We tried to be nice . . . */
3078 sts = sys$delprc(&info->pid,0);
d4c83939 3079 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2f1dcba4 3080 info->done = 1; /* sys$delprc is as done as we're going to get. */
3eeba6fb 3081 }
d4c83939 3082 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3083 info = info->next;
3084 }
3085
3086 while(open_pipes) {
ebd4d70b
JM
3087
3088#if defined(PERL_IMPLICIT_CONTEXT)
3089 /* We need to use the Perl context of the thread that created */
3090 /* the pipe. */
3091 pTHX;
36b6faa8
CB
3092 if (open_pipes->err)
3093 aTHX = open_pipes->err->thx;
3094 else if (open_pipes->out)
3095 aTHX = open_pipes->out->thx;
3096 else if (open_pipes->in)
3097 aTHX = open_pipes->in->thx;
ebd4d70b 3098#endif
1e422769 3099 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3100 else if (!(sts & 1)) retsts = sts;
748a9306
LW
3101 }
3102 return retsts;
3103}
3104
3105static struct exit_control_block pipe_exitblock =
3106 {(struct exit_control_block *) 0,
3107 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3108
22d4bb9c
CB
3109static void pipe_mbxtofd_ast(pPipe p);
3110static void pipe_tochild1_ast(pPipe p);
3111static void pipe_tochild2_ast(pPipe p);
748a9306 3112
a0d0e21e 3113static void
22d4bb9c 3114popen_completion_ast(pInfo info)
a0d0e21e 3115{
22d4bb9c
CB
3116 pInfo i = open_pipes;
3117 int iss;
f7ddb74a 3118 int sts;
d85f548a
JH
3119 pXpipe x;
3120
3121 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3122 closed_list[closed_index].pid = info->pid;
3123 closed_list[closed_index].completion = info->completion;
3124 closed_index++;
3125 if (closed_index == NKEEPCLOSED)
3126 closed_index = 0;
3127 closed_num++;
22d4bb9c
CB
3128
3129 while (i) {
3130 if (i == info) break;
3131 i = i->next;
3132 }
3133 if (!i) return; /* unlinked, probably freed too */
3134
22d4bb9c
CB
3135 info->done = TRUE;
3136
3137/*
3138 Writing to subprocess ...
3139 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3140
3141 chan_out may be waiting for "done" flag, or hung waiting
3142 for i/o completion to child...cancel the i/o. This will
3143 put it into "snarf mode" (done but no EOF yet) that discards
3144 input.
3145
3146 Output from subprocess (stdout, stderr) needs to be flushed and
3147 shut down. We try sending an EOF, but if the mbx is full the pipe
3148 routine should still catch the "shut_on_empty" flag, telling it to
3149 use immediate-style reads so that "mbx empty" -> EOF.
3150
3151
3152*/
3153 if (info->in && !info->in_done) { /* only for mode=w */
3154 if (info->in->shut_on_empty && info->in->need_wake) {
3155 info->in->need_wake = FALSE;
fd8cd3a3 3156 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
22d4bb9c 3157 } else {
fd8cd3a3 3158 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
22d4bb9c
CB
3159 }
3160 }
3161
3162 if (info->out && !info->out_done) { /* were we also piping output? */
3163 info->out->shut_on_empty = TRUE;
3164 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3165 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
fd8cd3a3 3166 _ckvmssts_noperl(iss);
22d4bb9c
CB
3167 }
3168
3169 if (info->err && !info->err_done) { /* we were piping stderr */
3170 info->err->shut_on_empty = TRUE;
3171 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3172 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
fd8cd3a3 3173 _ckvmssts_noperl(iss);
a0d0e21e 3174 }
fd8cd3a3 3175 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3176
a0d0e21e
LW
3177}
3178
2fbb330f 3179static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
218fdd94 3180static void vms_execfree(struct dsc$descriptor_s *vmscmd);
aa779de1 3181
22d4bb9c
CB
3182/*
3183 we actually differ from vmstrnenv since we use this to
3184 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3185 are pointing to the same thing
3186*/
3187
3188static unsigned short
fd8cd3a3 3189popen_translate(pTHX_ char *logical, char *result)
22d4bb9c
CB
3190{
3191 int iss;
3192 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3193 $DESCRIPTOR(d_log,"");
3194 struct _il3 {
3195 unsigned short length;
3196 unsigned short code;
3197 char * buffer_addr;
3198 unsigned short *retlenaddr;
3199 } itmlst[2];
3200 unsigned short l, ifi;
3201
3202 d_log.dsc$a_pointer = logical;
3203 d_log.dsc$w_length = strlen(logical);
3204
3205 itmlst[0].code = LNM$_STRING;
3206 itmlst[0].length = 255;
3207 itmlst[0].buffer_addr = result;
3208 itmlst[0].retlenaddr = &l;
3209
3210 itmlst[1].code = 0;
3211 itmlst[1].length = 0;
3212 itmlst[1].buffer_addr = 0;
3213 itmlst[1].retlenaddr = 0;
3214
3215 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3216 if (iss == SS$_NOLOGNAM) {
3217 iss = SS$_NORMAL;
3218 l = 0;
3219 }
3220 if (!(iss&1)) lib$signal(iss);
3221 result[l] = '\0';
3222/*
3223 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3224 strip it off and return the ifi, if any
3225*/
3226 ifi = 0;
3227 if (result[0] == 0x1b && result[1] == 0x00) {
18a3d61e 3228 memmove(&ifi,result+2,2);
22d4bb9c
CB
3229 strcpy(result,result+4);
3230 }
3231 return ifi; /* this is the RMS internal file id */
3232}
3233
22d4bb9c
CB
3234static void pipe_infromchild_ast(pPipe p);
3235
3236/*
3237 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3238 inside an AST routine without worrying about reentrancy and which Perl
3239 memory allocator is being used.
3240
3241 We read data and queue up the buffers, then spit them out one at a
3242 time to the output mailbox when the output mailbox is ready for one.
3243
3244*/
3245#define INITIAL_TOCHILDQUEUE 2
3246
3247static pPipe
fd8cd3a3 3248pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3249{
22d4bb9c
CB
3250 pPipe p;
3251 pCBuf b;
3252 char mbx1[64], mbx2[64];
3253 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3254 DSC$K_CLASS_S, mbx1},
3255 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3256 DSC$K_CLASS_S, mbx2};
3257 unsigned int dviitm = DVI$_DEVBUFSIZ;
3258 int j, n;
3259
d4c83939 3260 n = sizeof(Pipe);
ebd4d70b 3261 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3262
8a646e0b
JM
3263 create_mbx(&p->chan_in , &d_mbx1);
3264 create_mbx(&p->chan_out, &d_mbx2);
ebd4d70b 3265 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
22d4bb9c
CB
3266
3267 p->buf = 0;
3268 p->shut_on_empty = FALSE;
3269 p->need_wake = FALSE;
3270 p->type = 0;
3271 p->retry = 0;
3272 p->iosb.status = SS$_NORMAL;
3273 p->iosb2.status = SS$_NORMAL;
3274 p->free = RQE_ZERO;
3275 p->wait = RQE_ZERO;
3276 p->curr = 0;
3277 p->curr2 = 0;
3278 p->info = 0;
fd8cd3a3
DS
3279#ifdef PERL_IMPLICIT_CONTEXT
3280 p->thx = aTHX;
3281#endif
22d4bb9c
CB
3282
3283 n = sizeof(CBuf) + p->bufsize;
3284
3285 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
ebd4d70b 3286 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c 3287 b->buf = (char *) b + sizeof(CBuf);
ebd4d70b 3288 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
3289 }
3290
3291 pipe_tochild2_ast(p);
3292 pipe_tochild1_ast(p);
3293 strcpy(wmbx, mbx1);
3294 strcpy(rmbx, mbx2);
3295 return p;
3296}
3297
3298/* reads the MBX Perl is writing, and queues */
3299
3300static void
3301pipe_tochild1_ast(pPipe p)
3302{
22d4bb9c
CB
3303 pCBuf b = p->curr;
3304 int iss = p->iosb.status;
3305 int eof = (iss == SS$_ENDOFFILE);
f7ddb74a 3306 int sts;
fd8cd3a3
DS
3307#ifdef PERL_IMPLICIT_CONTEXT
3308 pTHX = p->thx;
3309#endif
22d4bb9c
CB
3310
3311 if (p->retry) {
3312 if (eof) {
3313 p->shut_on_empty = TRUE;
3314 b->eof = TRUE;
ebd4d70b 3315 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c 3316 } else {
ebd4d70b 3317 _ckvmssts_noperl(iss);
22d4bb9c
CB
3318 }
3319
3320 b->eof = eof;
3321 b->size = p->iosb.count;
ebd4d70b 3322 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
22d4bb9c
CB
3323 if (p->need_wake) {
3324 p->need_wake = FALSE;
ebd4d70b 3325 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
22d4bb9c
CB
3326 }
3327 } else {
3328 p->retry = 1; /* initial call */
3329 }
3330
3331 if (eof) { /* flush the free queue, return when done */
3332 int n = sizeof(CBuf) + p->bufsize;
3333 while (1) {
3334 iss = lib$remqti(&p->free, &b);
3335 if (iss == LIB$_QUEWASEMP) return;
ebd4d70b
JM
3336 _ckvmssts_noperl(iss);
3337 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c
CB
3338 }
3339 }
3340
3341 iss = lib$remqti(&p->free, &b);
3342 if (iss == LIB$_QUEWASEMP) {
3343 int n = sizeof(CBuf) + p->bufsize;
ebd4d70b 3344 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c
CB
3345 b->buf = (char *) b + sizeof(CBuf);
3346 } else {
ebd4d70b 3347 _ckvmssts_noperl(iss);
22d4bb9c
CB
3348 }
3349
3350 p->curr = b;
3351 iss = sys$qio(0,p->chan_in,
3352 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3353 &p->iosb,
3354 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3355 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
ebd4d70b 3356 _ckvmssts_noperl(iss);
22d4bb9c
CB
3357}
3358
3359
3360/* writes queued buffers to output, waits for each to complete before
3361 doing the next */
3362
3363static void
3364pipe_tochild2_ast(pPipe p)
3365{
22d4bb9c
CB
3366 pCBuf b = p->curr2;
3367 int iss = p->iosb2.status;
3368 int n = sizeof(CBuf) + p->bufsize;
3369 int done = (p->info && p->info->done) ||
3370 iss == SS$_CANCEL || iss == SS$_ABORT;
fd8cd3a3
DS
3371#if defined(PERL_IMPLICIT_CONTEXT)
3372 pTHX = p->thx;
3373#endif
22d4bb9c
CB
3374
3375 do {
3376 if (p->type) { /* type=1 has old buffer, dispose */
3377 if (p->shut_on_empty) {
ebd4d70b 3378 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c 3379 } else {
ebd4d70b 3380 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
3381 }
3382 p->type = 0;
3383 }
3384
3385 iss = lib$remqti(&p->wait, &b);
3386 if (iss == LIB$_QUEWASEMP) {
3387 if (p->shut_on_empty) {
3388 if (done) {
ebd4d70b 3389 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c 3390 *p->pipe_done = TRUE;
ebd4d70b 3391 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3392 } else {
ebd4d70b 3393 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
3394 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3395 }
3396 return;
3397 }
3398 p->need_wake = TRUE;
3399 return;
3400 }
ebd4d70b 3401 _ckvmssts_noperl(iss);
22d4bb9c
CB
3402 p->type = 1;
3403 } while (done);
3404
3405
3406 p->curr2 = b;
3407 if (b->eof) {
ebd4d70b 3408 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
3409 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3410 } else {
ebd4d70b 3411 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
22d4bb9c
CB
3412 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3413 }
3414
3415 return;
3416
3417}
3418
3419
3420static pPipe
fd8cd3a3 3421pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3422{
22d4bb9c
CB
3423 pPipe p;
3424 char mbx1[64], mbx2[64];
3425 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3426 DSC$K_CLASS_S, mbx1},
3427 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3428 DSC$K_CLASS_S, mbx2};
3429 unsigned int dviitm = DVI$_DEVBUFSIZ;
3430
d4c83939 3431 int n = sizeof(Pipe);
ebd4d70b 3432 _ckvmssts_noperl(lib$get_vm(&n, &p));
8a646e0b
JM
3433 create_mbx(&p->chan_in , &d_mbx1);
3434 create_mbx(&p->chan_out, &d_mbx2);
22d4bb9c 3435
ebd4d70b 3436 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3437 n = p->bufsize * sizeof(char);
ebd4d70b 3438 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3439 p->shut_on_empty = FALSE;
3440 p->info = 0;
3441 p->type = 0;
3442 p->iosb.status = SS$_NORMAL;
fd8cd3a3
DS
3443#if defined(PERL_IMPLICIT_CONTEXT)
3444 p->thx = aTHX;
3445#endif
22d4bb9c
CB
3446 pipe_infromchild_ast(p);
3447
3448 strcpy(wmbx, mbx1);
3449 strcpy(rmbx, mbx2);
3450 return p;
3451}
3452
3453static void
3454pipe_infromchild_ast(pPipe p)
3455{
22d4bb9c
CB
3456 int iss = p->iosb.status;
3457 int eof = (iss == SS$_ENDOFFILE);
3458 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3459 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
fd8cd3a3
DS
3460#if defined(PERL_IMPLICIT_CONTEXT)
3461 pTHX = p->thx;
3462#endif
22d4bb9c
CB
3463
3464 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
ebd4d70b 3465 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c
CB
3466 p->chan_out = 0;
3467 }
3468
3469 /* read completed:
3470 input shutdown if EOF from self (done or shut_on_empty)
3471 output shutdown if closing flag set (my_pclose)
3472 send data/eof from child or eof from self
3473 otherwise, re-read (snarf of data from child)
3474 */
3475
3476 if (p->type == 1) {
3477 p->type = 0;
3478 if (myeof && p->chan_in) { /* input shutdown */
ebd4d70b 3479 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c
CB
3480 p->chan_in = 0;
3481 }
3482
3483 if (p->chan_out) {
3484 if (myeof || kideof) { /* pass EOF to parent */
ebd4d70b
JM
3485 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3486 pipe_infromchild_ast, p,
3487 0, 0, 0, 0, 0, 0));
22d4bb9c
CB
3488 return;
3489 } else if (eof) { /* eat EOF --- fall through to read*/
3490
3491 } else { /* transmit data */
ebd4d70b
JM
3492 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3493 pipe_infromchild_ast,p,
3494 p->buf, p->iosb.count, 0, 0, 0, 0));
22d4bb9c
CB
3495 return;
3496 }
3497 }
3498 }
3499
3500 /* everything shut? flag as done */
3501
3502 if (!p->chan_in && !p->chan_out) {
3503 *p->pipe_done = TRUE;
ebd4d70b 3504 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
3505 return;
3506 }
3507
3508 /* write completed (or read, if snarfing from child)
3509 if still have input active,
3510 queue read...immediate mode if shut_on_empty so we get EOF if empty
3511 otherwise,
3512 check if Perl reading, generate EOFs as needed
3513 */
3514
3515 if (p->type == 0) {
3516 p->type = 1;
3517 if (p->chan_in) {
3518 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3519 pipe_infromchild_ast,p,
3520 p->buf, p->bufsize, 0, 0, 0, 0);
3521 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
ebd4d70b 3522 _ckvmssts_noperl(iss);
22d4bb9c
CB
3523 } else { /* send EOFs for extra reads */
3524 p->iosb.status = SS$_ENDOFFILE;
3525 p->iosb.dvispec = 0;
ebd4d70b
JM
3526 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3527 0, 0, 0,
3528 pipe_infromchild_ast, p, 0, 0, 0, 0));
22d4bb9c
CB
3529 }
3530 }
3531}
3532
3533static pPipe
fd8cd3a3 3534pipe_mbxtofd_setup(pTHX_ int fd, char *out)
22d4bb9c 3535{
22d4bb9c
CB
3536 pPipe p;
3537 char mbx[64];
3538 unsigned long dviitm = DVI$_DEVBUFSIZ;
3539 struct stat s;
3540 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3541 DSC$K_CLASS_S, mbx};
a480973c 3542 int n = sizeof(Pipe);
22d4bb9c
CB
3543
3544 /* things like terminals and mbx's don't need this filter */
3545 if (fd && fstat(fd,&s) == 0) {
3546 unsigned long dviitm = DVI$_DEVCHAR, devchar;
cfcfe586
JM
3547 char device[65];
3548 unsigned short dev_len;
3549 struct dsc$descriptor_s d_dev;
3550 char * cptr;
3551 struct item_list_3 items[3];
3552 int status;
3553 unsigned short dvi_iosb[4];
3554
3555 cptr = getname(fd, out, 1);
ebd4d70b 3556 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
cfcfe586
JM
3557 d_dev.dsc$a_pointer = out;
3558 d_dev.dsc$w_length = strlen(out);
3559 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3560 d_dev.dsc$b_class = DSC$K_CLASS_S;
3561
3562 items[0].len = 4;
3563 items[0].code = DVI$_DEVCHAR;
3564 items[0].bufadr = &devchar;
3565 items[0].retadr = NULL;
3566 items[1].len = 64;
3567 items[1].code = DVI$_FULLDEVNAM;
3568 items[1].bufadr = device;
3569 items[1].retadr = &dev_len;
3570 items[2].len = 0;
3571 items[2].code = 0;
3572
3573 status = sys$getdviw
3574 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
ebd4d70b 3575 _ckvmssts_noperl(status);
cfcfe586
JM
3576 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3577 device[dev_len] = 0;
3578
3579 if (!(devchar & DEV$M_DIR)) {
3580 strcpy(out, device);
3581 return 0;
3582 }
3583 }
22d4bb9c
CB
3584 }
3585
ebd4d70b 3586 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3587 p->fd_out = dup(fd);
8a646e0b 3588 create_mbx(&p->chan_in, &d_mbx);
ebd4d70b 3589 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3590 n = (p->bufsize+1) * sizeof(char);
ebd4d70b 3591 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3592 p->shut_on_empty = FALSE;
3593 p->retry = 0;
3594 p->info = 0;
3595 strcpy(out, mbx);
3596
ebd4d70b
JM
3597 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3598 pipe_mbxtofd_ast, p,
3599 p->buf, p->bufsize, 0, 0, 0, 0));
22d4bb9c
CB
3600
3601 return p;
3602}
3603
3604static void
3605pipe_mbxtofd_ast(pPipe p)
3606{
22d4bb9c
CB
3607 int iss = p->iosb.status;
3608 int done = p->info->done;
3609 int iss2;
3610 int eof = (iss == SS$_ENDOFFILE);
3611 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3612 int err = !(iss&1) && !eof;
fd8cd3a3
DS
3613#if defined(PERL_IMPLICIT_CONTEXT)
3614 pTHX = p->thx;
3615#endif
22d4bb9c
CB
3616
3617 if (done && myeof) { /* end piping */
3618 close(p->fd_out);
3619 sys$dassgn(p->chan_in);
3620 *p->pipe_done = TRUE;
ebd4d70b 3621 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
3622 return;
3623 }
3624
3625 if (!err && !eof) { /* good data to send to file */
3626 p->buf[p->iosb.count] = '\n';
3627 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3628 if (iss2 < 0) {
3629 p->retry++;
3630 if (p->retry < MAX_RETRY) {
ebd4d70b 3631 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
22d4bb9c
CB
3632 return;
3633 }
3634 }
3635 p->retry = 0;
3636 } else if (err) {
ebd4d70b 3637 _ckvmssts_noperl(iss);
22d4bb9c
CB
3638 }
3639
3640
3641 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3642 pipe_mbxtofd_ast, p,
3643 p->buf, p->bufsize, 0, 0, 0, 0);
3644 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
ebd4d70b 3645 _ckvmssts_noperl(iss);
22d4bb9c
CB
3646}
3647
3648
3649typedef struct _pipeloc PLOC;
3650typedef struct _pipeloc* pPLOC;
3651
3652struct _pipeloc {
3653 pPLOC next;
3654 char dir[NAM$C_MAXRSS+1];
3655};
3656static pPLOC head_PLOC = 0;
3657
5c0ae288 3658void
fd8cd3a3 3659free_pipelocs(pTHX_ void *head)
5c0ae288
CL
3660{
3661 pPLOC p, pnext;
ff7adb52 3662 pPLOC *pHead = (pPLOC *)head;
5c0ae288 3663
ff7adb52 3664 p = *pHead;
5c0ae288
CL
3665 while (p) {
3666 pnext = p->next;
e0ef6b43 3667 PerlMem_free(p);
5c0ae288
CL
3668 p = pnext;
3669 }
ff7adb52 3670 *pHead = 0;
5c0ae288 3671}
22d4bb9c
CB
3672
3673static void
fd8cd3a3 3674store_pipelocs(pTHX)
22d4bb9c
CB
3675{
3676 int i;
3677 pPLOC p;
ff7adb52 3678 AV *av = 0;
22d4bb9c
CB
3679 SV *dirsv;
3680 GV *gv;
3681 char *dir, *x;
3682 char *unixdir;
3683 char temp[NAM$C_MAXRSS+1];
3684 STRLEN n_a;
3685
ff7adb52 3686 if (head_PLOC)
218fdd94 3687 free_pipelocs(aTHX_ &head_PLOC);
ff7adb52 3688
22d4bb9c
CB
3689/* the . directory from @INC comes last */
3690
e0ef6b43 3691 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3692 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3693 p->next = head_PLOC;
3694 head_PLOC = p;
3695 strcpy(p->dir,"./");
3696
3697/* get the directory from $^X */
3698
c5375c28 3699 unixdir = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 3700 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 3701
218fdd94
CL
3702#ifdef PERL_IMPLICIT_CONTEXT
3703 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3704#else
22d4bb9c 3705 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
218fdd94 3706#endif
22d4bb9c
CB
3707 strcpy(temp, PL_origargv[0]);
3708 x = strrchr(temp,']');
2497a41f
JM
3709 if (x == NULL) {
3710 x = strrchr(temp,'>');
3711 if (x == NULL) {
3712 /* It could be a UNIX path */
3713 x = strrchr(temp,'/');
3714 }
3715 }
3716 if (x)
3717 x[1] = '\0';
3718 else {
3719 /* Got a bare name, so use default directory */
3720 temp[0] = '.';
3721 temp[1] = '\0';
3722 }
22d4bb9c 3723
4e205ed6 3724 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
e0ef6b43 3725 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3726 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3727 p->next = head_PLOC;
3728 head_PLOC = p;
3729 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3730 p->dir[NAM$C_MAXRSS] = '\0';
c5375c28 3731 }
22d4bb9c
CB
3732 }
3733
3734/* reverse order of @INC entries, skip "." since entered above */
3735
218fdd94
CL
3736#ifdef PERL_IMPLICIT_CONTEXT
3737 if (aTHX)
3738#endif
ff7adb52
CL
3739 if (PL_incgv) av = GvAVn(PL_incgv);
3740
3741 for (i = 0; av && i <= AvFILL(av); i++) {
22d4bb9c
CB
3742 dirsv = *av_fetch(av,i,TRUE);
3743
3744 if (SvROK(dirsv)) continue;
3745 dir = SvPVx(dirsv,n_a);
3746 if (strcmp(dir,".") == 0) continue;
4e205ed6 3747 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
22d4bb9c
CB
3748 continue;
3749
e0ef6b43 3750 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
22d4bb9c
CB
3751 p->next = head_PLOC;
3752 head_PLOC = p;
3753 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3754 p->dir[NAM$C_MAXRSS] = '\0';
3755 }
3756
3757/* most likely spot (ARCHLIB) put first in the list */
3758
3759#ifdef ARCHLIB_EXP
4e205ed6 3760 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
e0ef6b43 3761 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3762 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3763 p->next = head_PLOC;
3764 head_PLOC = p;
3765 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3766 p->dir[NAM$C_MAXRSS] = '\0';
3767 }
3768#endif
c5375c28 3769 PerlMem_free(unixdir);
22d4bb9c
CB
3770}
3771
a1887106
JM
3772static I32
3773Perl_cando_by_name_int
3774 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3775#if !defined(PERL_IMPLICIT_CONTEXT)
3776#define cando_by_name_int Perl_cando_by_name_int
3777#else
3778#define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3779#endif
22d4bb9c
CB
3780
3781static char *
fd8cd3a3 3782find_vmspipe(pTHX)
22d4bb9c
CB
3783{
3784 static int vmspipe_file_status = 0;
3785 static char vmspipe_file[NAM$C_MAXRSS+1];
3786
3787 /* already found? Check and use ... need read+execute permission */
3788
3789 if (vmspipe_file_status == 1) {
a1887106
JM
3790 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3791 && cando_by_name_int
3792 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
22d4bb9c
CB
3793 return vmspipe_file;
3794 }
3795 vmspipe_file_status = 0;
3796 }
3797
3798 /* scan through stored @INC, $^X */
3799
3800 if (vmspipe_file_status == 0) {
3801 char file[NAM$C_MAXRSS+1];
3802 pPLOC p = head_PLOC;
3803
3804 while (p) {
2f4077ca 3805 char * exp_res;
4d743a9b 3806 int dirlen;
22d4bb9c 3807 strcpy(file, p->dir);
4d743a9b
JM
3808 dirlen = strlen(file);
3809 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
22d4bb9c
CB
3810 file[NAM$C_MAXRSS] = '\0';
3811 p = p->next;
3812
6fb6c614 3813 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
2f4077ca 3814 if (!exp_res) continue;
22d4bb9c 3815
a1887106
JM
3816 if (cando_by_name_int
3817 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3818 && cando_by_name_int
3819 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
22d4bb9c
CB
3820 vmspipe_file_status = 1;
3821 return vmspipe_file;
3822 }
3823 }
3824 vmspipe_file_status = -1; /* failed, use tempfiles */
3825 }
3826
3827 return 0;
3828}
3829
3830static FILE *
fd8cd3a3 3831vmspipe_tempfile(pTHX)
22d4bb9c
CB
3832{
3833 char file[NAM$C_MAXRSS+1];
3834 FILE *fp;
3835 static int index = 0;
2497a41f
JM
3836 Stat_t s0, s1;
3837 int cmp_result;
22d4bb9c
CB
3838
3839 /* create a tempfile */
3840
3841 /* we can't go from W, shr=get to R, shr=get without
3842 an intermediate vulnerable state, so don't bother trying...
3843
3844 and lib$spawn doesn't shr=put, so have to close the write
3845
3846 So... match up the creation date/time and the FID to
3847 make sure we're dealing with the same file
3848
3849 */
3850
3851 index++;
2497a41f
JM
3852 if (!decc_filename_unix_only) {
3853 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3854 fp = fopen(file,"w");
3855 if (!fp) {
22d4bb9c
CB
3856 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3857 fp = fopen(file,"w");
3858 if (!fp) {
3859 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3860 fp = fopen(file,"w");
2497a41f
JM
3861 }
3862 }
3863 }
3864 else {
3865 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3866 fp = fopen(file,"w");
3867 if (!fp) {
3868 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3869 fp = fopen(file,"w");
3870 if (!fp) {
3871 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3872 fp = fopen(file,"w");
3873 }
3874 }
22d4bb9c
CB
3875 }
3876 if (!fp) return 0; /* we're hosed */
3877
f9ecfa39 3878 fprintf(fp,"$! 'f$verify(0)'\n");
22d4bb9c
CB
3879 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3880 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3881 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3882 fprintf(fp,"$ perl_on = \"set noon\"\n");
3883 fprintf(fp,"$ perl_exit = \"exit\"\n");
3884 fprintf(fp,"$ perl_del = \"delete\"\n");
3885 fprintf(fp,"$ pif = \"if\"\n");
3886 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
2d5e9e5d
JH
3887 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3888 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
0e06870b 3889 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
48b5a746
CL
3890 fprintf(fp,"$! --- build command line to get max possible length\n");
3891 fprintf(fp,"$c=perl_popen_cmd0\n");
3892 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3893 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3894 fprintf(fp,"$x=perl_popen_cmd3\n");
3895 fprintf(fp,"$c=c+x\n");
22d4bb9c 3896 fprintf(fp,"$ perl_on\n");
f9ecfa39 3897 fprintf(fp,"$ 'c'\n");
22d4bb9c 3898 fprintf(fp,"$ perl_status = $STATUS\n");
0e06870b 3899 fprintf(fp,"$ perl_del 'perl_cfile'\n");
22d4bb9c
CB
3900 fprintf(fp,"$ perl_exit 'perl_status'\n");
3901 fsync(fileno(fp));
3902
3903 fgetname(fp, file, 1);
312ac60b 3904 fstat(fileno(fp), &s0.crtl_stat);
22d4bb9c
CB
3905 fclose(fp);
3906
2497a41f 3907 if (decc_filename_unix_only)
0e5ce2c7 3908 int_tounixspec(file, file, NULL);
22d4bb9c
CB
3909 fp = fopen(file,"r","shr=get");
3910 if (!fp) return 0;
312ac60b 3911 fstat(fileno(fp), &s1.crtl_stat);
2497a41f 3912
682e4b71 3913 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
2497a41f 3914 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
22d4bb9c
CB
3915 fclose(fp);
3916 return 0;
3917 }
3918
3919 return fp;
3920}
3921
3922
cd1191f1
CB
3923static int vms_is_syscommand_xterm(void)
3924{
3925 const static struct dsc$descriptor_s syscommand_dsc =
3926 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3927
3928 const static struct dsc$descriptor_s decwdisplay_dsc =
3929 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3930
3931 struct item_list_3 items[2];
3932 unsigned short dvi_iosb[4];
3933 unsigned long devchar;
3934 unsigned long devclass;
3935 int status;
3936
3937 /* Very simple check to guess if sys$command is a decterm? */
3938 /* First see if the DECW$DISPLAY: device exists */
3939 items[0].len = 4;
3940 items[0].code = DVI$_DEVCHAR;
3941 items[0].bufadr = &devchar;
3942 items[0].retadr = NULL;
3943 items[1].len = 0;
3944 items[1].code = 0;
3945
3946 status = sys$getdviw
3947 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3948
3949 if ($VMS_STATUS_SUCCESS(status)) {
3950 status = dvi_iosb[0];
3951 }
3952
3953 if (!$VMS_STATUS_SUCCESS(status)) {
3954 SETERRNO(EVMSERR, status);
3955 return -1;
3956 }
3957
3958 /* If it does, then for now assume that we are on a workstation */
3959 /* Now verify that SYS$COMMAND is a terminal */
3960 /* for creating the debugger DECTerm */
3961
3962 items[0].len = 4;
3963 items[0].code = DVI$_DEVCLASS;
3964 items[0].bufadr = &devclass;
3965 items[0].retadr = NULL;
3966 items[1].len = 0;
3967 items[1].code = 0;
3968
3969 status = sys$getdviw
3970 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3971
3972 if ($VMS_STATUS_SUCCESS(status)) {
3973 status = dvi_iosb[0];
3974 }
3975
3976 if (!$VMS_STATUS_SUCCESS(status)) {
3977 SETERRNO(EVMSERR, status);
3978 return -1;
3979 }
3980 else {
3981 if (devclass == DC$_TERM) {
3982 return 0;
3983 }
3984 }
3985 return -1;
3986}
3987
3988/* If we are on a DECTerm, we can pretend to fork xterms when requested */
3989static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3990{
3991 int status;
3992 int ret_stat;
3993 char * ret_char;
3994 char device_name[65];
3995 unsigned short device_name_len;
3996 struct dsc$descriptor_s customization_dsc;
3997 struct dsc$descriptor_s device_name_dsc;
3998 const char * cptr;
3999 char * tptr;
4000 char customization[200];
4001 char title[40];
4002 pInfo info = NULL;
4003 char mbx1[64];
4004 unsigned short p_chan;
4005 int n;
4006 unsigned short iosb[4];
4007 struct item_list_3 items[2];
4008 const char * cust_str =
4009 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4010 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4011 DSC$K_CLASS_S, mbx1};
4012
8cb5d3d5
JM
4013 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4014 /*---------------------------------------*/
d30c1055 4015 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
8cb5d3d5
JM
4016
4017
4018 /* Make sure that this is from the Perl debugger */
cd1191f1
CB
4019 ret_char = strstr(cmd," xterm ");
4020 if (ret_char == NULL)
4021 return NULL;
4022 cptr = ret_char + 7;
4023 ret_char = strstr(cmd,"tty");
4024 if (ret_char == NULL)
4025 return NULL;
4026 ret_char = strstr(cmd,"sleep");
4027 if (ret_char == NULL)
4028 return NULL;
4029
8cb5d3d5
JM
4030 if (decw_term_port == 0) {
4031 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4032 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4033 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4034
d30c1055 4035 status = lib$find_image_symbol
8cb5d3d5
JM
4036 (&filename1_dsc,
4037 &decw_term_port_dsc,
4038 (void *)&decw_term_port,
4039 NULL,
4040 0);
4041
4042 /* Try again with the other image name */
4043 if (!$VMS_STATUS_SUCCESS(status)) {
4044
d30c1055 4045 status = lib$find_image_symbol
8cb5d3d5
JM
4046 (&filename2_dsc,
4047 &decw_term_port_dsc,
4048 (void *)&decw_term_port,
4049 NULL,
4050 0);
4051
4052 }
4053
4054 }
4055
4056
4057 /* No decw$term_port, give it up */
4058 if (!$VMS_STATUS_SUCCESS(status))
4059 return NULL;
4060
cd1191f1
CB
4061 /* Are we on a workstation? */
4062 /* to do: capture the rows / columns and pass their properties */
4063 ret_stat = vms_is_syscommand_xterm();
4064 if (ret_stat < 0)
4065 return NULL;
4066
4067 /* Make the title: */
4068 ret_char = strstr(cptr,"-title");
4069 if (ret_char != NULL) {
4070 while ((*cptr != 0) && (*cptr != '\"')) {
4071 cptr++;
4072 }
4073 if (*cptr == '\"')
4074 cptr++;
4075 n = 0;
4076 while ((*cptr != 0) && (*cptr != '\"')) {
4077 title[n] = *cptr;
4078 n++;
4079 if (n == 39) {
4080 title[39] == 0;
4081 break;
4082 }
4083 cptr++;
4084 }
4085 title[n] = 0;
4086 }
4087 else {
4088 /* Default title */
4089 strcpy(title,"Perl Debug DECTerm");
4090 }
4091 sprintf(customization, cust_str, title);
4092
4093 customization_dsc.dsc$a_pointer = customization;
4094 customization_dsc.dsc$w_length = strlen(customization);
4095 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4096 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4097
4098 device_name_dsc.dsc$a_pointer = device_name;
4099 device_name_dsc.dsc$w_length = sizeof device_name -1;
4100 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4101 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4102
4103 device_name_len = 0;
4104
4105 /* Try to create the window */
8cb5d3d5 4106 status = (*decw_term_port)
cd1191f1
CB
4107 (NULL,
4108 NULL,
4109 &customization_dsc,
4110 &device_name_dsc,
4111 &device_name_len,
4112 NULL,
4113 NULL,
4114 NULL);
4115 if (!$VMS_STATUS_SUCCESS(status)) {
4116 SETERRNO(EVMSERR, status);
4117 return NULL;
4118 }
4119
4120 device_name[device_name_len] = '\0';
4121
4122 /* Need to set this up to look like a pipe for cleanup */
4123 n = sizeof(Info);
4124 status = lib$get_vm(&n, &info);
4125 if (!$VMS_STATUS_SUCCESS(status)) {
4126 SETERRNO(ENOMEM, status);
4127 return NULL;
4128 }
4129
4130 info->mode = *mode;
4131 info->done = FALSE;
4132 info->completion = 0;
4133 info->closing = FALSE;
4134 info->in = 0;
4135 info->out = 0;
4136 info->err = 0;
4e205ed6 4137 info->fp = NULL;
cd1191f1
CB
4138 info->useFILE = 0;
4139 info->waiting = 0;
4140 info->in_done = TRUE;
4141 info->out_done = TRUE;
4142 info->err_done = TRUE;
4143
4144 /* Assign a channel on this so that it will persist, and not login */
4145 /* We stash this channel in the info structure for reference. */
4146 /* The created xterm self destructs when the last channel is removed */
4147 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4148 /* So leave this assigned. */
4149 device_name_dsc.dsc$w_length = device_name_len;
4150 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4151 if (!$VMS_STATUS_SUCCESS(status)) {
4152 SETERRNO(EVMSERR, status);
4153 return NULL;
4154 }
4155 info->xchan_valid = 1;
4156
4157 /* Now create a mailbox to be read by the application */
4158
8a646e0b 4159 create_mbx(&p_chan, &d_mbx1);
cd1191f1
CB
4160
4161 /* write the name of the created terminal to the mailbox */
4162 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4163 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4164
4165 if (!$VMS_STATUS_SUCCESS(status)) {
4166 SETERRNO(EVMSERR, status);
4167 return NULL;
4168 }
4169
4170 info->fp = PerlIO_open(mbx1, mode);
4171
4172 /* Done with this channel */
4173 sys$dassgn(p_chan);
4174
4175 /* If any errors, then clean up */
4176 if (!info->fp) {
4177 n = sizeof(Info);
ebd4d70b 4178 _ckvmssts_noperl(lib$free_vm(&n, &info));
cd1191f1
CB
4179 return NULL;
4180 }
4181
4182 /* All done */
4183 return info->fp;
4184}
22d4bb9c 4185
ebd4d70b
JM
4186static I32 my_pclose_pinfo(pTHX_ pInfo info);
4187
8fde5078 4188static PerlIO *
2fbb330f 4189safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
a0d0e21e 4190{
748a9306 4191 static int handler_set_up = FALSE;
ebd4d70b 4192 PerlIO * ret_fp;
55f2b99c 4193 unsigned long int sts, flags = CLI$M_NOWAIT;
f9ecfa39
PP
4194 /* The use of a GLOBAL table (as was done previously) rendered
4195 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4196 * environment. Hence we've switched to LOCAL symbol table.
4197 */
4198 unsigned int table = LIB$K_CLI_LOCAL_SYM;
d4c83939 4199 int j, wait = 0, n;
ff7adb52 4200 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
cfcfe586 4201 char *in, *out, *err, mbx[512];
22d4bb9c
CB
4202 FILE *tpipe = 0;
4203 char tfilebuf[NAM$C_MAXRSS+1];
d4c83939 4204 pInfo info = NULL;
48b5a746 4205 char cmd_sym_name[20];
22d4bb9c
CB
4206 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4207 DSC$K_CLASS_S, symbol};
22d4bb9c 4208 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
a0d0e21e 4209 DSC$K_CLASS_S, 0};
48b5a746
CL
4210 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4211 DSC$K_CLASS_S, cmd_sym_name};
218fdd94 4212 struct dsc$descriptor_s *vmscmd;
22d4bb9c 4213 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
0e06870b 4214 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
22d4bb9c 4215 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
cd1191f1 4216
cd1191f1
CB
4217 /* Check here for Xterm create request. This means looking for
4218 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4219 * is possible to create an xterm.
4220 */
4221 if (*in_mode == 'r') {
4222 PerlIO * xterm_fd;
4223
4d9538c1
JM
4224#if defined(PERL_IMPLICIT_CONTEXT)
4225 /* Can not fork an xterm with a NULL context */
4226 /* This probably could never happen */
4227 xterm_fd = NULL;
4228 if (aTHX != NULL)
4229#endif
cd1191f1 4230 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4e205ed6 4231 if (xterm_fd != NULL)
cd1191f1
CB
4232 return xterm_fd;
4233 }
cd1191f1 4234
afd8f436
JH
4235 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4236
22d4bb9c
CB
4237 /* once-per-program initialization...
4238 note that the SETAST calls and the dual test of pipe_ef
4239 makes sure that only the FIRST thread through here does
4240 the initialization...all other threads wait until it's
4241 done.
4242
4243 Yeah, uglier than a pthread call, it's got all the stuff inline
4244 rather than in a separate routine.
4245 */
4246
4247 if (!pipe_ef) {
ebd4d70b 4248 _ckvmssts_noperl(sys$setast(0));
22d4bb9c
CB
4249 if (!pipe_ef) {
4250 unsigned long int pidcode = JPI$_PID;
4251 $DESCRIPTOR(d_delay, RETRY_DELAY);
ebd4d70b
JM
4252 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4253 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4254 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
22d4bb9c
CB
4255 }
4256 if (!handler_set_up) {
ebd4d70b 4257 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
22d4bb9c
CB
4258 handler_set_up = TRUE;
4259 }
ebd4d70b 4260 _ckvmssts_noperl(sys$setast(1));
22d4bb9c
CB
4261 }
4262
4263 /* see if we can find a VMSPIPE.COM */
4264
4265 tfilebuf[0] = '@';
fd8cd3a3 4266 vmspipe = find_vmspipe(aTHX);
22d4bb9c
CB
4267 if (vmspipe) {
4268 strcpy(tfilebuf+1,vmspipe);
4269 } else { /* uh, oh...we're in tempfile hell */
fd8cd3a3 4270 tpipe = vmspipe_tempfile(aTHX);
22d4bb9c
CB
4271 if (!tpipe) { /* a fish popular in Boston */
4272 if (ckWARN(WARN_PIPE)) {
f98bc0c6 4273 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
22d4bb9c 4274 }
4e205ed6 4275 return NULL;
22d4bb9c
CB
4276 }
4277 fgetname(tpipe,tfilebuf+1,1);
4278 }
4279 vmspipedsc.dsc$a_pointer = tfilebuf;
4280 vmspipedsc.dsc$w_length = strlen(tfilebuf);
a0d0e21e 4281
218fdd94 4282 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
a2669cfc
JH
4283 if (!(sts & 1)) {
4284 switch (sts) {
4285 case RMS$_FNF: case RMS$_DNF:
4286 set_errno(ENOENT); break;
4287 case RMS$_DIR:
4288 set_errno(ENOTDIR); break;
4289 case RMS$_DEV:
4290 set_errno(ENODEV); break;
4291 case RMS$_PRV:
4292 set_errno(EACCES); break;
4293 case RMS$_SYN:
4294 set_errno(EINVAL); break;
4295 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4296 set_errno(E2BIG); break;
4297 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 4298 _ckvmssts_noperl(sts); /* fall through */
a2669cfc
JH
4299 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4300 set_errno(EVMSERR);
4301 }
4302 set_vaxc_errno(sts);
cd1191f1 4303 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
f98bc0c6 4304 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
a2669cfc 4305 }
ff7adb52 4306 *psts = sts;
4e205ed6 4307 return NULL;
a2669cfc 4308 }
d4c83939 4309 n = sizeof(Info);
ebd4d70b 4310 _ckvmssts_noperl(lib$get_vm(&n, &info));
22d4bb9c 4311
ff7adb52 4312 strcpy(mode,in_mode);
22d4bb9c
CB
4313 info->mode = *mode;
4314 info->done = FALSE;
4315 info->completion = 0;
4316 info->closing = FALSE;
4317 info->in = 0;
4318 info->out = 0;
4319 info->err = 0;
4e205ed6 4320 info->fp = NULL;
ff7adb52
CL
4321 info->useFILE = 0;
4322 info->waiting = 0;
22d4bb9c
CB
4323 info->in_done = TRUE;
4324 info->out_done = TRUE;
4325 info->err_done = TRUE;
cd1191f1
CB
4326 info->xchan = 0;
4327 info->xchan_valid = 0;
cfcfe586
JM
4328
4329 in = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4330 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4331 out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4332 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4333 err = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4334 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4335
0e06870b 4336 in[0] = out[0] = err[0] = '\0';
22d4bb9c 4337
ff7adb52
CL
4338 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4339 info->useFILE = 1;
4340 strcpy(p,p+1);
4341 }
4342 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4343 wait = 1;
4344 strcpy(p,p+1);
4345 }
4346
22d4bb9c 4347 if (*mode == 'r') { /* piping from subroutine */
22d4bb9c 4348
fd8cd3a3 4349 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
22d4bb9c
CB
4350 if (info->out) {
4351 info->out->pipe_done = &info->out_done;
4352 info->out_done = FALSE;
4353 info->out->info = info;
4354 }
ff7adb52 4355 if (!info->useFILE) {
cd1191f1 4356 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4357 } else {
4358 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4359 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4360 }
4361
22d4bb9c
CB
4362 if (!info->fp && info->out) {
4363 sys$cancel(info->out->chan_out);
4364
4365 while (!info->out_done) {
4366 int done;
ebd4d70b 4367 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4368 done = info->out_done;
ebd4d70b
JM
4369 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4370 _ckvmssts_noperl(sys$setast(1));
4371 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
0e06870b 4372 }
22d4bb9c 4373
d4c83939
CB
4374 if (info->out->buf) {
4375 n = info->out->bufsize * sizeof(char);
ebd4d70b 4376 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
d4c83939
CB
4377 }
4378 n = sizeof(Pipe);
ebd4d70b 4379 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
d4c83939 4380 n = sizeof(Info);
ebd4d70b 4381 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4382 *psts = RMS$_FNF;
4e205ed6 4383 return NULL;
0e06870b 4384 }
22d4bb9c 4385
fd8cd3a3 4386 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
22d4bb9c
CB
4387 if (info->err) {
4388 info->err->pipe_done = &info->err_done;
4389 info->err_done = FALSE;
4390 info->err->info = info;
4391 }
a0d0e21e 4392
ff7adb52
CL
4393 } else if (*mode == 'w') { /* piping to subroutine */
4394
4395 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4396 if (info->out) {
4397 info->out->pipe_done = &info->out_done;
4398 info->out_done = FALSE;
4399 info->out->info = info;
4400 }
4401
4402 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4403 if (info->err) {
4404 info->err->pipe_done = &info->err_done;
4405 info->err_done = FALSE;
4406 info->err->info = info;
4407 }
a0d0e21e 4408
fd8cd3a3 4409 info->in = pipe_tochild_setup(aTHX_ in,mbx);
ff7adb52 4410 if (!info->useFILE) {
a480973c 4411 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4412 } else {
4413 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4414 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4415 }
4416
22d4bb9c
CB
4417 if (info->in) {
4418 info->in->pipe_done = &info->in_done;
4419 info->in_done = FALSE;
4420 info->in->info = info;
4421 }
a0d0e21e 4422
22d4bb9c
CB
4423 /* error cleanup */
4424 if (!info->fp && info->in) {
4425 info->done = TRUE;
ebd4d70b
JM
4426 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4427 0, 0, 0, 0, 0, 0, 0, 0));
22d4bb9c
CB
4428
4429 while (!info->in_done) {
4430 int done;
ebd4d70b 4431 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4432 done = info->in_done;
ebd4d70b
JM
4433 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4434 _ckvmssts_noperl(sys$setast(1));
4435 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
22d4bb9c 4436 }
a0d0e21e 4437
d4c83939
CB
4438 if (info->in->buf) {
4439 n = info->in->bufsize * sizeof(char);
ebd4d70b 4440 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
d4c83939
CB
4441 }
4442 n = sizeof(Pipe);
ebd4d70b 4443 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
d4c83939 4444 n = sizeof(Info);
ebd4d70b 4445 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4446 *psts = RMS$_FNF;
4e205ed6 4447 return NULL;
22d4bb9c 4448 }
a0d0e21e 4449
22d4bb9c 4450
ff7adb52 4451 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
fd8cd3a3 4452 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
22d4bb9c
CB
4453 if (info->out) {
4454 info->out->pipe_done = &info->out_done;
4455 info->out_done = FALSE;
4456 info->out->info = info;
4457 }
0e06870b 4458
fd8cd3a3 4459 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
0e06870b
CB
4460 if (info->err) {
4461 info->err->pipe_done = &info->err_done;
4462 info->err_done = FALSE;
4463 info->err->info = info;
4464 }
748a9306 4465 }
22d4bb9c
CB
4466
4467 symbol[MAX_DCL_SYMBOL] = '\0';
4468
4469 strncpy(symbol, in, MAX_DCL_SYMBOL);
4470 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4471 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
22d4bb9c
CB
4472
4473 strncpy(symbol, err, MAX_DCL_SYMBOL);
4474 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4475 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
22d4bb9c 4476
0e06870b
CB
4477 strncpy(symbol, out, MAX_DCL_SYMBOL);
4478 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4479 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
22d4bb9c 4480
cfcfe586
JM
4481 /* Done with the names for the pipes */
4482 PerlMem_free(err);
4483 PerlMem_free(out);
4484 PerlMem_free(in);
4485
218fdd94 4486 p = vmscmd->dsc$a_pointer;
22d4bb9c
CB
4487 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4488 if (*p == '$') p++; /* remove leading $ */
4489 while (*p == ' ' || *p == '\t') p++;
48b5a746
CL
4490
4491 for (j = 0; j < 4; j++) {
4492 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4493 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4494
22d4bb9c
CB
4495 strncpy(symbol, p, MAX_DCL_SYMBOL);
4496 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4497 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
22d4bb9c 4498
48b5a746
CL
4499 if (strlen(p) > MAX_DCL_SYMBOL) {
4500 p += MAX_DCL_SYMBOL;
4501 } else {
4502 p += strlen(p);
4503 }
4504 }
ebd4d70b 4505 _ckvmssts_noperl(sys$setast(0));
a0d0e21e
LW
4506 info->next=open_pipes; /* prepend to list */
4507 open_pipes=info;
ebd4d70b 4508 _ckvmssts_noperl(sys$setast(1));
55f2b99c
CB
4509 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4510 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4511 * have SYS$COMMAND if we need it.
4512 */
ebd4d70b 4513 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
22d4bb9c
CB
4514 0, &info->pid, &info->completion,
4515 0, popen_completion_ast,info,0,0,0));
4516
4517 /* if we were using a tempfile, close it now */
4518
4519 if (tpipe) fclose(tpipe);
4520
ff7adb52 4521 /* once the subprocess is spawned, it has copied the symbols and
22d4bb9c
CB
4522 we can get rid of ours */
4523
48b5a746
CL
4524 for (j = 0; j < 4; j++) {
4525 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4526 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
ebd4d70b 4527 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
48b5a746 4528 }
ebd4d70b
JM
4529 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4530 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4531 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
218fdd94 4532 vms_execfree(vmscmd);
a0d0e21e 4533
218fdd94
CL
4534#ifdef PERL_IMPLICIT_CONTEXT
4535 if (aTHX)
4536#endif
6b88bc9c 4537 PL_forkprocess = info->pid;
218fdd94 4538
ebd4d70b 4539 ret_fp = info->fp;
ff7adb52 4540 if (wait) {
ebd4d70b 4541 dSAVEDERRNO;
ff7adb52
CL
4542 int done = 0;
4543 while (!done) {
ebd4d70b 4544 _ckvmssts_noperl(sys$setast(0));
ff7adb52 4545 done = info->done;
ebd4d70b
JM
4546 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4547 _ckvmssts_noperl(sys$setast(1));
4548 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
ff7adb52
CL
4549 }
4550 *psts = info->completion;
2fbb330f
JM
4551/* Caller thinks it is open and tries to close it. */
4552/* This causes some problems, as it changes the error status */
4553/* my_pclose(info->fp); */
ebd4d70b
JM
4554
4555 /* If we did not have a file pointer open, then we have to */
4556 /* clean up here or eventually we will run out of something */
4557 SAVE_ERRNO;
4558 if (info->fp == NULL) {
4559 my_pclose_pinfo(aTHX_ info);
4560 }
4561 RESTORE_ERRNO;
4562
ff7adb52 4563 } else {
eed5d6a1 4564 *psts = info->pid;
ff7adb52 4565 }
ebd4d70b 4566 return ret_fp;
1e422769 4567} /* end of safe_popen */
4568
4569
a15cef0c
CB
4570/*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4571PerlIO *
2fbb330f 4572Perl_my_popen(pTHX_ const char *cmd, const char *mode)
1e422769 4573{
ff7adb52 4574 int sts;
1e422769 4575 TAINT_ENV();
4576 TAINT_PROPER("popen");
45bc9206 4577 PERL_FLUSHALL_FOR_CHILD;
ff7adb52 4578 return safe_popen(aTHX_ cmd,mode,&sts);
a0d0e21e 4579}
1e422769 4580
a0d0e21e
LW
4581/*}}}*/
4582
ebd4d70b
JM
4583
4584/* Routine to close and cleanup a pipe info structure */
4585
4586static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4587
748a9306 4588 unsigned long int retsts;
d4c83939 4589 int done, iss, n;
cd1191f1 4590 int status;
ebd4d70b 4591 pInfo next, last;
748a9306 4592
bbce6d69 4593 /* If we were writing to a subprocess, insure that someone reading from
4594 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
22d4bb9c
CB
4595 * produce an EOF record in the mailbox.
4596 *
4597 * well, at least sometimes it *does*, so we have to watch out for
4598 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4599 */
ff7adb52 4600 if (info->fp) {
5ce486e0
CB
4601 if (!info->useFILE
4602#if defined(USE_ITHREADS)
4603 && my_perl
4604#endif
a24c654f
CB
4605#ifdef USE_PERLIO
4606 && PL_perlio_fd_refcnt
4607#endif
4608 )
5ce486e0 4609 PerlIO_flush(info->fp);
ff7adb52
CL
4610 else
4611 fflush((FILE *)info->fp);
4612 }
22d4bb9c 4613
b08af3f0 4614 _ckvmssts(sys$setast(0));
22d4bb9c
CB
4615 info->closing = TRUE;
4616 done = info->done && info->in_done && info->out_done && info->err_done;
4617 /* hanging on write to Perl's input? cancel it */
4618 if (info->mode == 'r' && info->out && !info->out_done) {
4619 if (info->out->chan_out) {
4620 _ckvmssts(sys$cancel(info->out->chan_out));
4621 if (!info->out->chan_in) { /* EOF generation, need AST */
4622 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4623 }
4624 }
4625 }
4626 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4627 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4628 0, 0, 0, 0, 0, 0));
b08af3f0 4629 _ckvmssts(sys$setast(1));
ff7adb52 4630 if (info->fp) {
5ce486e0
CB
4631 if (!info->useFILE
4632#if defined(USE_ITHREADS)
4633 && my_perl
4634#endif
a24c654f
CB
4635#ifdef USE_PERLIO
4636 && PL_perlio_fd_refcnt
4637#endif
4638 )
d4c83939 4639 PerlIO_close(info->fp);
ff7adb52
CL
4640 else
4641 fclose((FILE *)info->fp);
4642 }
22d4bb9c
CB
4643 /*
4644 we have to wait until subprocess completes, but ALSO wait until all
4645 the i/o completes...otherwise we'll be freeing the "info" structure
4646 that the i/o ASTs could still be using...
4647 */
4648
4649 while (!done) {
4650 _ckvmssts(sys$setast(0));
4651 done = info->done && info->in_done && info->out_done && info->err_done;
4652 if (!done) _ckvmssts(sys$clref(pipe_ef));
4653 _ckvmssts(sys$setast(1));
4654 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4655 }
4656 retsts = info->completion;
a0d0e21e 4657
a0d0e21e 4658 /* remove from list of open pipes */
b08af3f0 4659 _ckvmssts(sys$setast(0));
ebd4d70b
JM
4660 last = NULL;
4661 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4662 if (next == info)
4663 break;
4664 }
4665
4666 if (last)
4667 last->next = info->next;
4668 else
4669 open_pipes = info->next;
b08af3f0 4670 _ckvmssts(sys$setast(1));
22d4bb9c
CB
4671
4672 /* free buffers and structures */
4673
4674 if (info->in) {
d4c83939
CB
4675 if (info->in->buf) {
4676 n = info->in->bufsize * sizeof(char);
4677 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4678 }
4679 n = sizeof(Pipe);
4680 _ckvmssts(lib$free_vm(&n, &info->in));
22d4bb9c
CB
4681 }
4682 if (info->out) {
d4c83939
CB
4683 if (info->out->buf) {
4684 n = info->out->bufsize * sizeof(char);
4685 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4686 }
4687 n = sizeof(Pipe);
4688 _ckvmssts(lib$free_vm(&n, &info->out));
22d4bb9c
CB
4689 }
4690 if (info->err) {
d4c83939
CB
4691 if (info->err->buf) {
4692 n = info->err->bufsize * sizeof(char);
4693 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4694 }
4695 n = sizeof(Pipe);
4696 _ckvmssts(lib$free_vm(&n, &info->err));
22d4bb9c 4697 }
d4c83939
CB
4698 n = sizeof(Info);
4699 _ckvmssts(lib$free_vm(&n, &info));
a0d0e21e
LW
4700
4701 return retsts;
ebd4d70b
JM
4702}
4703
4704
4705/*{{{ I32 my_pclose(PerlIO *fp)*/
4706I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4707{
4708 pInfo info, last = NULL;
4709 I32 ret_status;
4710
4711 /* Fixme - need ast and mutex protection here */
4712 for (info = open_pipes; info != NULL; last = info, info = info->next)
4713 if (info->fp == fp) break;
4714
4715 if (info == NULL) { /* no such pipe open */
4716 set_errno(ECHILD); /* quoth POSIX */
4717 set_vaxc_errno(SS$_NONEXPR);
4718 return -1;
4719 }
4720
4721 ret_status = my_pclose_pinfo(aTHX_ info);
4722
4723 return ret_status;
748a9306 4724
a0d0e21e
LW
4725} /* end of my_pclose() */
4726
119586db 4727#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
4728 /* Roll our own prototype because we want this regardless of whether
4729 * _VMS_WAIT is defined.
4730 */
4731 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4732#endif
4733/* sort-of waitpid; special handling of pipe clean-up for subprocesses
4734 created with popen(); otherwise partially emulate waitpid() unless
4735 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4736 Also check processes not considered by the CRTL waitpid().
4737 */
4fdae800 4738/*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4739Pid_t
fd8cd3a3 4740Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
a0d0e21e 4741{
22d4bb9c
CB
4742 pInfo info;
4743 int done;
aeb5cf3c 4744 int sts;
d85f548a 4745 int j;
aeb5cf3c
CB
4746
4747 if (statusp) *statusp = 0;
a0d0e21e
LW
4748
4749 for (info = open_pipes; info != NULL; info = info->next)
4750 if (info->pid == pid) break;
4751
4752 if (info != NULL) { /* we know about this child */
748a9306 4753 while (!info->done) {
22d4bb9c
CB
4754 _ckvmssts(sys$setast(0));
4755 done = info->done;
4756 if (!done) _ckvmssts(sys$clref(pipe_ef));
4757 _ckvmssts(sys$setast(1));
4758 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
a0d0e21e
LW
4759 }
4760
aeb5cf3c 4761 if (statusp) *statusp = info->completion;
a0d0e21e 4762 return pid;
d85f548a
JH
4763 }
4764
4765 /* child that already terminated? */
aeb5cf3c 4766
d85f548a
JH
4767 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4768 if (closed_list[j].pid == pid) {
4769 if (statusp) *statusp = closed_list[j].completion;
4770 return pid;
4771 }
a0d0e21e 4772 }
d85f548a
JH
4773
4774 /* fall through if this child is not one of our own pipe children */
aeb5cf3c 4775
119586db 4776#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
4777
4778 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4779 * in 7.2 did we get a version that fills in the VMS completion
4780 * status as Perl has always tried to do.
4781 */
4782
4783 sts = __vms_waitpid( pid, statusp, flags );
4784
4785 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4786 return sts;
4787
4788 /* If the real waitpid tells us the child does not exist, we
4789 * fall through here to implement waiting for a child that
4790 * was created by some means other than exec() (say, spawned
4791 * from DCL) or to wait for a process that is not a subprocess
4792 * of the current process.
4793 */
4794
119586db 4795#endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
aeb5cf3c 4796
21bc9d50 4797 {
a0d0e21e 4798 $DESCRIPTOR(intdsc,"0 00:00:01");
aeb5cf3c
CB
4799 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4800 unsigned long int pidcode = JPI$_PID, mypid;
4801 unsigned long int interval[2];
aeb5cf3c 4802 unsigned int jpi_iosb[2];
d85f548a 4803 struct itmlst_3 jpilist[2] = {
aeb5cf3c 4804 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
aeb5cf3c
CB
4805 { 0, 0, 0, 0}
4806 };
aeb5cf3c
CB
4807
4808 if (pid <= 0) {
4809 /* Sorry folks, we don't presently implement rooting around for
4810 the first child we can find, and we definitely don't want to
4811 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4812 */
4813 set_errno(ENOTSUP);
4814 return -1;
4815 }
4816
d85f548a
JH
4817 /* Get the owner of the child so I can warn if it's not mine. If the
4818 * process doesn't exist or I don't have the privs to look at it,
4819 * I can go home early.
aeb5cf3c
CB
4820 */
4821 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4822 if (sts & 1) sts = jpi_iosb[0];
4823 if (!(sts & 1)) {
4824 switch (sts) {
4825 case SS$_NONEXPR:
4826 set_errno(ECHILD);
4827 break;
4828 case SS$_NOPRIV:
4829 set_errno(EACCES);
4830 break;
4831 default:
4832 _ckvmssts(sts);
4833 }
4834 set_vaxc_errno(sts);
4835 return -1;
4836 }
a0d0e21e 4837
3eeba6fb 4838 if (ckWARN(WARN_EXEC)) {
aeb5cf3c
CB
4839 /* remind folks they are asking for non-standard waitpid behavior */
4840 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
748a9306 4841 if (ownerpid != mypid)
f98bc0c6 4842 Perl_warner(aTHX_ packWARN(WARN_EXEC),
aeb5cf3c
CB
4843 "waitpid: process %x is not a child of process %x",
4844 pid,mypid);
748a9306 4845 }
a0d0e21e 4846
d85f548a
JH
4847 /* simply check on it once a second until it's not there anymore. */
4848
4849 _ckvmssts(sys$bintim(&intdsc,interval));
4850 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
aeb5cf3c
CB
4851 _ckvmssts(sys$schdwk(0,0,interval,0));
4852 _ckvmssts(sys$hiber());
d85f548a
JH
4853 }
4854 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
aeb5cf3c
CB
4855
4856 _ckvmssts(sts);
a0d0e21e 4857 return pid;
21bc9d50 4858 }
a0d0e21e 4859} /* end of waitpid() */
a0d0e21e
LW
4860/*}}}*/
4861/*}}}*/
4862/*}}}*/
4863
4864/*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4865char *
4866my_gconvert(double val, int ndig, int trail, char *buf)
4867{
4868 static char __gcvtbuf[DBL_DIG+1];
4869 char *loc;
4870
4871 loc = buf ? buf : __gcvtbuf;
71be2cbc 4872
4873#ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4874 if (val < 1) {
4875 sprintf(loc,"%.*g",ndig,val);
4876 return loc;
4877 }
4878#endif
4879
a0d0e21e
LW
4880 if (val) {
4881 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4882 return gcvt(val,ndig,loc);
4883 }
4884 else {
4885 loc[0] = '0'; loc[1] = '\0';
4886 return loc;
4887 }
4888
4889}
4890/*}}}*/
4891
988c775c 4892#if defined(__VAX) || !defined(NAML$C_MAXRSS)
a480973c
JM
4893static int rms_free_search_context(struct FAB * fab)
4894{
4895struct NAM * nam;
4896
4897 nam = fab->fab$l_nam;
4898 nam->nam$b_nop |= NAM$M_SYNCHK;
4899 nam->nam$l_rlf = NULL;
4900 fab->fab$b_dns = 0;
4901 return sys$parse(fab, NULL, NULL);
4902}
4903
4904#define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4d743a9b 4905#define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
a480973c
JM
4906#define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4907#define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4908#define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4909#define rms_nam_esll(nam) nam.nam$b_esl
4910#define rms_nam_esl(nam) nam.nam$b_esl
4911#define rms_nam_name(nam) nam.nam$l_name
4912#define rms_nam_namel(nam) nam.nam$l_name
4913#define rms_nam_type(nam) nam.nam$l_type
4914#define rms_nam_typel(nam) nam.nam$l_type
4915#define rms_nam_ver(nam) nam.nam$l_ver
4916#define rms_nam_verl(nam) nam.nam$l_ver
4917#define rms_nam_rsll(nam) nam.nam$b_rsl
4918#define rms_nam_rsl(nam) nam.nam$b_rsl
4919#define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4920#define rms_set_fna(fab, nam, name, size) \
a1887106 4921 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
a480973c
JM
4922#define rms_get_fna(fab, nam) fab.fab$l_fna
4923#define rms_set_dna(fab, nam, name, size) \
a1887106
JM
4924 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4925#define rms_nam_dns(fab, nam) fab.fab$b_dns
d584a1c6 4926#define rms_set_esa(nam, name, size) \
a1887106 4927 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
a480973c 4928#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 4929 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
a480973c 4930#define rms_set_rsa(nam, name, size) \
a1887106 4931 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
a480973c 4932#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106
JM
4933 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4934#define rms_nam_name_type_l_size(nam) \
4935 (nam.nam$b_name + nam.nam$b_type)
a480973c
JM
4936#else
4937static int rms_free_search_context(struct FAB * fab)
4938{
4939struct NAML * nam;
4940
4941 nam = fab->fab$l_naml;
4942 nam->naml$b_nop |= NAM$M_SYNCHK;
4943 nam->naml$l_rlf = NULL;
4944 nam->naml$l_long_defname_size = 0;
988c775c 4945
a480973c
JM
4946 fab->fab$b_dns = 0;
4947 return sys$parse(fab, NULL, NULL);
4948}
4949
4950#define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4d743a9b 4951#define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
a480973c
JM
4952#define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4953#define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4954#define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4955#define rms_nam_esll(nam) nam.naml$l_long_expand_size
4956#define rms_nam_esl(nam) nam.naml$b_esl
4957#define rms_nam_name(nam) nam.naml$l_name
4958#define rms_nam_namel(nam) nam.naml$l_long_name
4959#define rms_nam_type(nam) nam.naml$l_type
4960#define rms_nam_typel(nam) nam.naml$l_long_type
4961#define rms_nam_ver(nam) nam.naml$l_ver
4962#define rms_nam_verl(nam) nam.naml$l_long_ver
4963#define rms_nam_rsll(nam) nam.naml$l_long_result_size
4964#define rms_nam_rsl(nam) nam.naml$b_rsl
4965#define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4966#define rms_set_fna(fab, nam, name, size) \
a1887106 4967 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
a480973c 4968 nam.naml$l_long_filename_size = size; \
a1887106 4969 nam.naml$l_long_filename = name;}
a480973c
JM
4970#define rms_get_fna(fab, nam) nam.naml$l_long_filename
4971#define rms_set_dna(fab, nam, name, size) \
a1887106 4972 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
a480973c 4973 nam.naml$l_long_defname_size = size; \
a1887106 4974 nam.naml$l_long_defname = name; }
a480973c 4975#define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
d584a1c6 4976#define rms_set_esa(nam, name, size) \
a1887106 4977 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
a480973c 4978 nam.naml$l_long_expand_alloc = size; \
a1887106 4979 nam.naml$l_long_expand = name; }
a480973c 4980#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 4981 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
a480973c 4982 nam.naml$l_long_expand = l_name; \
a1887106 4983 nam.naml$l_long_expand_alloc = l_size; }
a480973c 4984#define rms_set_rsa(nam, name, size) \
a1887106 4985 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
a480973c 4986 nam.naml$l_long_result = name; \
a1887106 4987 nam.naml$l_long_result_alloc = size; }
a480973c 4988#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106 4989 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
a480973c 4990 nam.naml$l_long_result = l_name; \
a1887106
JM
4991 nam.naml$l_long_result_alloc = l_size; }
4992#define rms_nam_name_type_l_size(nam) \
4993 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
a480973c
JM
4994#endif
4995
4fdf8f88 4996
e0e5e8d6
JM
4997/* rms_erase
4998 * The CRTL for 8.3 and later can create symbolic links in any mode,
4fdf8f88 4999 * however in 8.3 the unlink/remove/delete routines will only properly handle
e0e5e8d6 5000 * them if one of the PCP modes is active.
e0e5e8d6
JM
5001 */
5002static int rms_erase(const char * vmsname)
5003{
5004 int status;
5005 struct FAB myfab = cc$rms_fab;
5006 rms_setup_nam(mynam);
5007
5008 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
5009 rms_bind_fab_nam(myfab, mynam);
4fdf8f88 5010
e0e5e8d6
JM
5011#ifdef NAML$M_OPEN_SPECIAL
5012 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5013#endif
5014
d30c1055 5015 status = sys$erase(&myfab, 0, 0);
e0e5e8d6
JM
5016
5017 return status;
5018}
5019
bbce6d69 5020
4fdf8f88
JM
5021static int
5022vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5023 const struct dsc$descriptor_s * vms_dst_dsc,
5024 unsigned long flags)
5025{
5026 /* VMS and UNIX handle file permissions differently and the
5027 * the same ACL trick may be needed for renaming files,
5028 * especially if they are directories.
5029 */
5030
5031 /* todo: get kill_file and rename to share common code */
5032 /* I can not find online documentation for $change_acl
5033 * it appears to be replaced by $set_security some time ago */
5034
5035const unsigned int access_mode = 0;
5036$DESCRIPTOR(obj_file_dsc,"FILE");
5037char *vmsname;
5038char *rslt;
5039unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5040int aclsts, fndsts, rnsts = -1;
5041unsigned int ctx = 0;
5042struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5043struct dsc$descriptor_s * clean_dsc;
5044
5045struct myacedef {
5046 unsigned char myace$b_length;
5047 unsigned char myace$b_type;
5048 unsigned short int myace$w_flags;
5049 unsigned long int myace$l_access;
5050 unsigned long int myace$l_ident;
5051} newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5052 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5053 0},
5054 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5055
5056struct item_list_3
5057 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5058 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5059 {0,0,0,0}},
5060 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5061 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5062 {0,0,0,0}};
5063
5064
5065 /* Expand the input spec using RMS, since we do not want to put
5066 * ACLs on the target of a symbolic link */
5067 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5068 if (vmsname == NULL)
5069 return SS$_INSFMEM;
5070
6fb6c614 5071 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4fdf8f88 5072 vmsname,
6fb6c614 5073 PERL_RMSEXPAND_M_SYMLINK);
4fdf8f88
JM
5074 if (rslt == NULL) {
5075 PerlMem_free(vmsname);
5076 return SS$_INSFMEM;
5077 }
5078
5079 /* So we get our own UIC to use as a rights identifier,
5080 * and the insert an ACE at the head of the ACL which allows us
5081 * to delete the file.
5082 */
ebd4d70b 5083 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4fdf8f88
JM
5084
5085 fildsc.dsc$w_length = strlen(vmsname);
5086 fildsc.dsc$a_pointer = vmsname;
5087 ctx = 0;
5088 newace.myace$l_ident = oldace.myace$l_ident;
5089 rnsts = SS$_ABORT;
5090
5091 /* Grab any existing ACEs with this identifier in case we fail */
5092 clean_dsc = &fildsc;
5093 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5094 &fildsc,
5095 NULL,
5096 OSS$M_WLOCK,
5097 findlst,
5098 &ctx,
5099 &access_mode);
5100
5101 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5102 /* Add the new ACE . . . */
5103
5104 /* if the sys$get_security succeeded, then ctx is valid, and the
5105 * object/file descriptors will be ignored. But otherwise they
5106 * are needed
5107 */
5108 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5109 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5110 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5111 set_errno(EVMSERR);
5112 set_vaxc_errno(aclsts);
5113 PerlMem_free(vmsname);
5114 return aclsts;
5115 }
5116
5117 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5118 NULL, NULL,
5119 &flags,
5120 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5121
5122 if ($VMS_STATUS_SUCCESS(rnsts)) {
5123 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5124 }
5125
5126 /* Put things back the way they were. */
5127 ctx = 0;
5128 aclsts = sys$get_security(&obj_file_dsc,
5129 clean_dsc,
5130 NULL,
5131 OSS$M_WLOCK,
5132 findlst,
5133 &ctx,
5134 &access_mode);
5135
5136 if ($VMS_STATUS_SUCCESS(aclsts)) {
5137 int sec_flags;
5138
5139 sec_flags = 0;
5140 if (!$VMS_STATUS_SUCCESS(fndsts))
5141 sec_flags = OSS$M_RELCTX;
5142
5143 /* Get rid of the new ACE */
5144 aclsts = sys$set_security(NULL, NULL, NULL,
5145 sec_flags, dellst, &ctx, &access_mode);
5146
5147 /* If there was an old ACE, put it back */
5148 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5149 addlst[0].bufadr = &oldace;
5150 aclsts = sys$set_security(NULL, NULL, NULL,
5151 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5152 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5153 set_errno(EVMSERR);
5154 set_vaxc_errno(aclsts);
5155 rnsts = aclsts;
5156 }
5157 } else {
5158 int aclsts2;
5159
5160 /* Try to clear the lock on the ACL list */
5161 aclsts2 = sys$set_security(NULL, NULL, NULL,
5162 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5163
5164 /* Rename errors are most important */
5165 if (!$VMS_STATUS_SUCCESS(rnsts))
5166 aclsts = rnsts;
5167 set_errno(EVMSERR);
5168 set_vaxc_errno(aclsts);
5169 rnsts = aclsts;
5170 }
5171 }
5172 else {
5173 if (aclsts != SS$_ACLEMPTY)
5174 rnsts = aclsts;
5175 }
5176 }
5177 else
5178 rnsts = fndsts;
5179
5180 PerlMem_free(vmsname);
5181 return rnsts;
5182}
5183
5184
5185/*{{{int rename(const char *, const char * */
5186/* Not exactly what X/Open says to do, but doing it absolutely right
5187 * and efficiently would require a lot more work. This should be close
5188 * enough to pass all but the most strict X/Open compliance test.
5189 */
5190int
5191Perl_rename(pTHX_ const char *src, const char * dst)
5192{
5193int retval;
5194int pre_delete = 0;
5195int src_sts;
5196int dst_sts;
5197Stat_t src_st;
5198Stat_t dst_st;
5199
5200 /* Validate the source file */
46c05374 5201 src_sts = flex_lstat(src, &src_st);
4fdf8f88
JM
5202 if (src_sts != 0) {
5203
5204 /* No source file or other problem */
5205 return src_sts;
5206 }
b94a8c49
JM
5207 if (src_st.st_devnam[0] == 0) {
5208 /* This may be possible so fail if it is seen. */
5209 errno = EIO;
5210 return -1;
5211 }
4fdf8f88 5212
46c05374 5213 dst_sts = flex_lstat(dst, &dst_st);
4fdf8f88
JM
5214 if (dst_sts == 0) {
5215
5216 if (dst_st.st_dev != src_st.st_dev) {
5217 /* Must be on the same device */
5218 errno = EXDEV;
5219 return -1;
5220 }
5221
5222 /* VMS_INO_T_COMPARE is true if the inodes are different
5223 * to match the output of memcmp
5224 */
5225
5226 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5227 /* That was easy, the files are the same! */
5228 return 0;
5229 }
5230
5231 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5232 /* If source is a directory, so must be dest */
5233 errno = EISDIR;
5234 return -1;
5235 }
5236
5237 }
5238
5239
5240 if ((dst_sts == 0) &&
5241 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5242
5243 /* We have issues here if vms_unlink_all_versions is set
5244 * If the destination exists, and is not a directory, then
5245 * we must delete in advance.
5246 *
5247 * If the src is a directory, then we must always pre-delete
5248 * the destination.
5249 *
5250 * If we successfully delete the dst in advance, and the rename fails
5251 * X/Open requires that errno be EIO.
5252 *
5253 */
5254
5255 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5256 int d_sts;
46c05374 5257 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
b94a8c49
JM
5258 S_ISDIR(dst_st.st_mode));
5259
5260 /* Need to delete all versions ? */
5261 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5262 int i = 0;
5263
5264 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
46c05374 5265 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
b94a8c49
JM
5266 if (d_sts != 0)
5267 break;
5268 i++;
5269
5270 /* Make sure that we do not loop forever */
5271 if (i > 32767) {
5272 errno = EIO;
5273 d_sts = -1;
5274 break;
5275 }
5276 }
5277 }
5278
4fdf8f88
JM
5279 if (d_sts != 0)
5280 return d_sts;
5281
5282 /* We killed the destination, so only errno now is EIO */
5283 pre_delete = 1;
5284 }
5285 }
5286
5287 /* Originally the idea was to call the CRTL rename() and only
5288 * try the lib$rename_file if it failed.
5289 * It turns out that there are too many variants in what the
5290 * the CRTL rename might do, so only use lib$rename_file
5291 */
5292 retval = -1;
5293
5294 {
5295 /* Is the source and dest both in VMS format */
5296 /* if the source is a directory, then need to fileify */
5297 /* and dest must be a directory or non-existant. */
5298
4fdf8f88
JM
5299 char * vms_dst;
5300 int sts;
5301 char * ret_str;
5302 unsigned long flags;
5303 struct dsc$descriptor_s old_file_dsc;
5304 struct dsc$descriptor_s new_file_dsc;
5305
5306 /* We need to modify the src and dst depending
5307 * on if one or more of them are directories.
5308 */
5309
4fdf8f88
JM
5310 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5311 if (vms_dst == NULL)
ebd4d70b 5312 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88
JM
5313
5314 if (S_ISDIR(src_st.st_mode)) {
5315 char * ret_str;
5316 char * vms_dir_file;
5317
5318 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5319 if (vms_dir_file == NULL)
ebd4d70b 5320 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88 5321
4fdf8f88
JM
5322 /* If the dest is a directory, we must remove it
5323 if (dst_sts == 0) {
5324 int d_sts;
46c05374 5325 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
4fdf8f88 5326 if (d_sts != 0) {
4fdf8f88
JM
5327 PerlMem_free(vms_dst);
5328 errno = EIO;
5329 return sts;
5330 }
5331
5332 pre_delete = 1;
5333 }
5334
5335 /* The dest must be a VMS file specification */
df278665 5336 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5337 if (ret_str == NULL) {
4fdf8f88
JM
5338 PerlMem_free(vms_dst);
5339 errno = EIO;
5340 return -1;
5341 }
5342
5343 /* The source must be a file specification */
4fdf8f88
JM
5344 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5345 if (ret_str == NULL) {
4fdf8f88
JM
5346 PerlMem_free(vms_dst);
5347 PerlMem_free(vms_dir_file);
5348 errno = EIO;
5349 return -1;
5350 }
5351 PerlMem_free(vms_dst);
5352 vms_dst = vms_dir_file;
5353
5354 } else {
5355 /* File to file or file to new dir */
5356
5357 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5358 /* VMS pathify a dir target */
4846f1d7 5359 ret_str = int_tovmspath(dst, vms_dst, NULL);
4fdf8f88 5360 if (ret_str == NULL) {
4fdf8f88
JM
5361 PerlMem_free(vms_dst);
5362 errno = EIO;
5363 return -1;
5364 }
5365 } else {
b94a8c49
JM
5366 char * v_spec, * r_spec, * d_spec, * n_spec;
5367 char * e_spec, * vs_spec;
5368 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
4fdf8f88
JM
5369
5370 /* fileify a target VMS file specification */
df278665 5371 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5372 if (ret_str == NULL) {
4fdf8f88
JM
5373 PerlMem_free(vms_dst);
5374 errno = EIO;
5375 return -1;
5376 }
b94a8c49
JM
5377
5378 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5379 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5380 &e_len, &vs_spec, &vs_len);
5381 if (sts == 0) {
5382 if (e_len == 0) {
5383 /* Get rid of the version */
5384 if (vs_len != 0) {
5385 *vs_spec = '\0';
5386 }
5387 /* Need to specify a '.' so that the extension */
5388 /* is not inherited */
5389 strcat(vms_dst,".");
5390 }
5391 }
4fdf8f88
JM
5392 }
5393 }
5394
b94a8c49
JM
5395 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5396 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
4fdf8f88
JM
5397 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5398 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5399
5400 new_file_dsc.dsc$a_pointer = vms_dst;
5401 new_file_dsc.dsc$w_length = strlen(vms_dst);
5402 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5403 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5404
5405 flags = 0;
5406#if !defined(__VAX) && defined(NAML$C_MAXRSS)
449de3c2 5407 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
4fdf8f88
JM
5408#endif
5409
5410 sts = lib$rename_file(&old_file_dsc,
5411 &new_file_dsc,
5412 NULL, NULL,
5413 &flags,
5414 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5415 if (!$VMS_STATUS_SUCCESS(sts)) {
5416
5417 /* We could have failed because VMS style permissions do not
5418 * permit renames that UNIX will allow. Just like the hack
5419 * in for kill_file.
5420 */
5421 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5422 }
5423
4fdf8f88
JM
5424 PerlMem_free(vms_dst);
5425 if (!$VMS_STATUS_SUCCESS(sts)) {
5426 errno = EIO;
5427 return -1;
5428 }
5429 retval = 0;
5430 }
5431
5432 if (vms_unlink_all_versions) {
5433 /* Now get rid of any previous versions of the source file that
5434 * might still exist
5435 */
b94a8c49
JM
5436 int i = 0;
5437 dSAVEDERRNO;
5438 SAVE_ERRNO;
46c05374 5439 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
5440 S_ISDIR(src_st.st_mode));
5441 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
46c05374 5442 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
5443 S_ISDIR(src_st.st_mode));
5444 if (src_sts != 0)
5445 break;
5446 i++;
5447
5448 /* Make sure that we do not loop forever */
5449 if (i > 32767) {
5450 src_sts = -1;
5451 break;
5452 }
5453 }
5454 RESTORE_ERRNO;
4fdf8f88
JM
5455 }
5456
5457 /* We deleted the destination, so must force the error to be EIO */
5458 if ((retval != 0) && (pre_delete != 0))
5459 errno = EIO;
5460
5461 return retval;
5462}
5463/*}}}*/
5464
5465
bbce6d69 5466/*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5467/* Shortcut for common case of simple calls to $PARSE and $SEARCH
5468 * to expand file specification. Allows for a single default file
5469 * specification and a simple mask of options. If outbuf is non-NULL,
5470 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5471 * the resultant file specification is placed. If outbuf is NULL, the
5472 * resultant file specification is placed into a static buffer.
5473 * The third argument, if non-NULL, is taken to be a default file
5474 * specification string. The fourth argument is unused at present.
5475 * rmesexpand() returns the address of the resultant string if
5476 * successful, and NULL on error.
e886094b
JM
5477 *
5478 * New functionality for previously unused opts value:
5479 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
a1887106
JM
5480 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5481 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
e0e5e8d6 5482 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
bbce6d69 5483 */
360732b5 5484static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
96e4d5b1 5485
bbce6d69 5486static char *
6fb6c614
JM
5487int_rmsexpand
5488 (const char *filespec,
360732b5 5489 char *outbuf,
360732b5
JM
5490 const char *defspec,
5491 unsigned opts,
5492 int * fs_utf8,
5493 int * dfs_utf8)
bbce6d69 5494{
6fb6c614
JM
5495 char * ret_spec;
5496 const char * in_spec;
5497 char * spec_buf;
5498 const char * def_spec;
5499 char * vmsfspec, *vmsdefspec;
5500 char * esa;
7566800d 5501 char * esal = NULL;
18a3d61e
JM
5502 char * outbufl;
5503 struct FAB myfab = cc$rms_fab;
a480973c 5504 rms_setup_nam(mynam);
18a3d61e
JM
5505 STRLEN speclen;
5506 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5507 int sts;
5508
360732b5
JM
5509 /* temp hack until UTF8 is actually implemented */
5510 if (fs_utf8 != NULL)
5511 *fs_utf8 = 0;
5512
18a3d61e
JM
5513 if (!filespec || !*filespec) {
5514 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5515 return NULL;
5516 }
18a3d61e
JM
5517
5518 vmsfspec = NULL;
6fb6c614 5519 vmsdefspec = NULL;
18a3d61e 5520 outbufl = NULL;
a1887106 5521
6fb6c614 5522 in_spec = filespec;
a1887106
JM
5523 isunix = 0;
5524 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
6fb6c614
JM
5525 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5526 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5527
5528 /* If this is a UNIX file spec, convert it to VMS */
5529 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5530 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5531 &e_len, &vs_spec, &vs_len);
5532 if (sts != 0) {
5533 isunix = 1;
5534 char * ret_spec;
5535
5536 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5537 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5538 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5539 if (ret_spec == NULL) {
5540 PerlMem_free(vmsfspec);
5541 return NULL;
5542 }
5543 in_spec = (const char *)vmsfspec;
18a3d61e 5544
6fb6c614
JM
5545 /* Unless we are forcing to VMS format, a UNIX input means
5546 * UNIX output, and that requires long names to be used
5547 */
5548 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
b1a8dcd7 5549#if !defined(__VAX) && defined(NAML$C_MAXRSS)
6fb6c614 5550 opts |= PERL_RMSEXPAND_M_LONG;
778e045f
CB
5551#else
5552 NOOP;
b1a8dcd7 5553#endif
6fb6c614
JM
5554 else
5555 isunix = 0;
a1887106 5556 }
18a3d61e 5557
6fb6c614
JM
5558 }
5559
5560 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
a480973c 5561 rms_bind_fab_nam(myfab, mynam);
18a3d61e 5562
6fb6c614
JM
5563 /* Process the default file specification if present */
5564 def_spec = defspec;
18a3d61e
JM
5565 if (defspec && *defspec) {
5566 int t_isunix;
5567 t_isunix = is_unix_filespec(defspec);
5568 if (t_isunix) {
6fb6c614
JM
5569 vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5570 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5571 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5572
5573 if (ret_spec == NULL) {
5574 /* Clean up and bail */
5575 PerlMem_free(vmsdefspec);
5576 if (vmsfspec != NULL)
5577 PerlMem_free(vmsfspec);
5578 return NULL;
5579 }
5580 def_spec = (const char *)vmsdefspec;
18a3d61e 5581 }
6fb6c614
JM
5582 rms_set_dna(myfab, mynam,
5583 (char *)def_spec, strlen(def_spec)); /* cast ok */
18a3d61e
JM
5584 }
5585
6fb6c614 5586 /* Now we need the expansion buffers */
c5375c28 5587 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 5588 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5589#if !defined(__VAX) && defined(NAML$C_MAXRSS)
a1887106 5590 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5591 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5592#endif
a1887106 5593 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
18a3d61e 5594
d584a1c6
JM
5595 /* If a NAML block is used RMS always writes to the long and short
5596 * addresses unless you suppress the short name.
5597 */
a480973c 5598#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6 5599 outbufl = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5600 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5601#endif
d584a1c6 5602 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
18a3d61e 5603
f7ddb74a
JM
5604#ifdef NAM$M_NO_SHORT_UPCASE
5605 if (decc_efs_case_preserve)
a480973c 5606 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 5607#endif
18a3d61e 5608
e0e5e8d6
JM
5609 /* We may not want to follow symbolic links */
5610#ifdef NAML$M_OPEN_SPECIAL
5611 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5612 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5613#endif
5614
18a3d61e
JM
5615 /* First attempt to parse as an existing file */
5616 retsts = sys$parse(&myfab,0,0);
5617 if (!(retsts & STS$K_SUCCESS)) {
5618
5619 /* Could not find the file, try as syntax only if error is not fatal */
a480973c 5620 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
6fb6c614
JM
5621 if (retsts == RMS$_DNF ||
5622 retsts == RMS$_DIR ||
5623 retsts == RMS$_DEV ||
5624 retsts == RMS$_PRV) {
18a3d61e 5625 retsts = sys$parse(&myfab,0,0);
6fb6c614 5626 if (retsts & STS$K_SUCCESS) goto int_expanded;
18a3d61e
JM
5627 }
5628
5629 /* Still could not parse the file specification */
5630 /*----------------------------------------------*/
a480973c 5631 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5632 if (vmsdefspec != NULL)
5633 PerlMem_free(vmsdefspec);
18a3d61e 5634 if (vmsfspec != NULL)
c5375c28
JM
5635 PerlMem_free(vmsfspec);
5636 if (outbufl != NULL)
5637 PerlMem_free(outbufl);
5638 PerlMem_free(esa);
7566800d
CB
5639 if (esal != NULL)
5640 PerlMem_free(esal);
18a3d61e
JM
5641 set_vaxc_errno(retsts);
5642 if (retsts == RMS$_PRV) set_errno(EACCES);
5643 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5644 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5645 else set_errno(EVMSERR);
5646 return NULL;
5647 }
5648 retsts = sys$search(&myfab,0,0);
5649 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
a480973c 5650 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5651 if (vmsdefspec != NULL)
5652 PerlMem_free(vmsdefspec);
18a3d61e 5653 if (vmsfspec != NULL)
c5375c28
JM
5654 PerlMem_free(vmsfspec);
5655 if (outbufl != NULL)
5656 PerlMem_free(outbufl);
5657 PerlMem_free(esa);
7566800d
CB
5658 if (esal != NULL)
5659 PerlMem_free(esal);
18a3d61e
JM
5660 set_vaxc_errno(retsts);
5661 if (retsts == RMS$_PRV) set_errno(EACCES);
5662 else set_errno(EVMSERR);
5663 return NULL;
5664 }
5665
5666 /* If the input filespec contained any lowercase characters,
5667 * downcase the result for compatibility with Unix-minded code. */
6fb6c614 5668int_expanded:
18a3d61e 5669 if (!decc_efs_case_preserve) {
6fb6c614 5670 char * tbuf;
c5375c28
JM
5671 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5672 if (islower(*tbuf)) { haslower = 1; break; }
18a3d61e
JM
5673 }
5674
5675 /* Is a long or a short name expected */
5676 /*------------------------------------*/
6fb6c614 5677 spec_buf = NULL;
778e045f 5678#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5679 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5680 if (rms_nam_rsll(mynam)) {
6fb6c614 5681 spec_buf = outbufl;
a480973c 5682 speclen = rms_nam_rsll(mynam);
18a3d61e
JM
5683 }
5684 else {
6fb6c614 5685 spec_buf = esal; /* Not esa */
a480973c 5686 speclen = rms_nam_esll(mynam);
18a3d61e
JM
5687 }
5688 }
5689 else {
778e045f 5690#endif
a480973c 5691 if (rms_nam_rsl(mynam)) {
6fb6c614 5692 spec_buf = outbuf;
a480973c 5693 speclen = rms_nam_rsl(mynam);
18a3d61e
JM
5694 }
5695 else {
6fb6c614 5696 spec_buf = esa; /* Not esal */
a480973c 5697 speclen = rms_nam_esl(mynam);
18a3d61e 5698 }
778e045f 5699#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5700 }
778e045f 5701#endif
6fb6c614 5702 spec_buf[speclen] = '\0';
4d743a9b 5703
18a3d61e
JM
5704 /* Trim off null fields added by $PARSE
5705 * If type > 1 char, must have been specified in original or default spec
5706 * (not true for version; $SEARCH may have added version of existing file).
5707 */
a480973c 5708 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
18a3d61e 5709 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
5710 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5711 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
18a3d61e
JM
5712 }
5713 else {
a480973c
JM
5714 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5715 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
18a3d61e
JM
5716 }
5717 if (trimver || trimtype) {
5718 if (defspec && *defspec) {
5719 char *defesal = NULL;
d584a1c6
JM
5720 char *defesa = NULL;
5721 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5722 if (defesa != NULL) {
6fb6c614 5723 struct FAB deffab = cc$rms_fab;
d584a1c6
JM
5724#if !defined(__VAX) && defined(NAML$C_MAXRSS)
5725 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 5726 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 5727#endif
a480973c 5728 rms_setup_nam(defnam);
18a3d61e 5729
a480973c
JM
5730 rms_bind_fab_nam(deffab, defnam);
5731
5732 /* Cast ok */
5733 rms_set_fna
5734 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5735
d584a1c6
JM
5736 /* RMS needs the esa/esal as a work area if wildcards are involved */
5737 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
a480973c 5738
4d743a9b 5739 rms_clear_nam_nop(defnam);
a480973c 5740 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
18a3d61e
JM
5741#ifdef NAM$M_NO_SHORT_UPCASE
5742 if (decc_efs_case_preserve)
a480973c 5743 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
18a3d61e 5744#endif
e0e5e8d6
JM
5745#ifdef NAML$M_OPEN_SPECIAL
5746 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5747 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5748#endif
18a3d61e
JM
5749 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5750 if (trimver) {
a480973c 5751 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
18a3d61e
JM
5752 }
5753 if (trimtype) {
a480973c 5754 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
18a3d61e
JM
5755 }
5756 }
d584a1c6
JM
5757 if (defesal != NULL)
5758 PerlMem_free(defesal);
5759 PerlMem_free(defesa);
6fb6c614
JM
5760 } else {
5761 _ckvmssts_noperl(SS$_INSFMEM);
18a3d61e
JM
5762 }
5763 }
5764 if (trimver) {
5765 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5766 if (*(rms_nam_verl(mynam)) != '\"')
6fb6c614 5767 speclen = rms_nam_verl(mynam) - spec_buf;
18a3d61e
JM
5768 }
5769 else {
a480973c 5770 if (*(rms_nam_ver(mynam)) != '\"')
6fb6c614 5771 speclen = rms_nam_ver(mynam) - spec_buf;
18a3d61e
JM
5772 }
5773 }
5774 if (trimtype) {
5775 /* If we didn't already trim version, copy down */
5776 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
6fb6c614 5777 if (speclen > rms_nam_verl(mynam) - spec_buf)
18a3d61e 5778 memmove
a480973c
JM
5779 (rms_nam_typel(mynam),
5780 rms_nam_verl(mynam),
6fb6c614 5781 speclen - (rms_nam_verl(mynam) - spec_buf));
a480973c 5782 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
18a3d61e
JM
5783 }
5784 else {
6fb6c614 5785 if (speclen > rms_nam_ver(mynam) - spec_buf)
18a3d61e 5786 memmove
a480973c
JM
5787 (rms_nam_type(mynam),
5788 rms_nam_ver(mynam),
6fb6c614 5789 speclen - (rms_nam_ver(mynam) - spec_buf));
a480973c 5790 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
18a3d61e
JM
5791 }
5792 }
5793 }
5794
5795 /* Done with these copies of the input files */
5796 /*-------------------------------------------*/
5797 if (vmsfspec != NULL)
c5375c28 5798 PerlMem_free(vmsfspec);
6fb6c614
JM
5799 if (vmsdefspec != NULL)
5800 PerlMem_free(vmsdefspec);
18a3d61e
JM
5801
5802 /* If we just had a directory spec on input, $PARSE "helpfully"
5803 * adds an empty name and type for us */
d584a1c6 5804#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5805 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
5806 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5807 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5808 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
6fb6c614 5809 speclen = rms_nam_namel(mynam) - spec_buf;
18a3d61e 5810 }
d584a1c6
JM
5811 else
5812#endif
5813 {
a480973c
JM
5814 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5815 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5816 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
6fb6c614 5817 speclen = rms_nam_name(mynam) - spec_buf;
18a3d61e
JM
5818 }
5819
5820 /* Posix format specifications must have matching quotes */
4d743a9b 5821 if (speclen < (VMS_MAXRSS - 1)) {
6fb6c614
JM
5822 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5823 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5824 spec_buf[speclen] = '\"';
4d743a9b
JM
5825 speclen++;
5826 }
18a3d61e
JM
5827 }
5828 }
6fb6c614
JM
5829 spec_buf[speclen] = '\0';
5830 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
18a3d61e
JM
5831
5832 /* Have we been working with an expanded, but not resultant, spec? */
5833 /* Also, convert back to Unix syntax if necessary. */
d584a1c6
JM
5834 {
5835 int rsl;
18a3d61e 5836
d584a1c6
JM
5837#if !defined(__VAX) && defined(NAML$C_MAXRSS)
5838 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5839 rsl = rms_nam_rsll(mynam);
5840 } else
5841#endif
5842 {
5843 rsl = rms_nam_rsl(mynam);
5844 }
5845 if (!rsl) {
6fb6c614
JM
5846 /* rsl is not present, it means that spec_buf is either */
5847 /* esa or esal, and needs to be copied to outbuf */
5848 /* convert to Unix if desired */
d584a1c6 5849 if (isunix) {
6fb6c614
JM
5850 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5851 } else {
5852 /* VMS file specs are not in UTF-8 */
5853 if (fs_utf8 != NULL)
5854 *fs_utf8 = 0;
5855 strcpy(outbuf, spec_buf);
5856 ret_spec = outbuf;
18a3d61e
JM
5857 }
5858 }
6fb6c614
JM
5859 else {
5860 /* Now spec_buf is either outbuf or outbufl */
5861 /* We need the result into outbuf */
5862 if (isunix) {
5863 /* If we need this in UNIX, then we need another buffer */
5864 /* to keep things in order */
5865 char * src;
5866 char * new_src = NULL;
5867 if (spec_buf == outbuf) {
5868 new_src = PerlMem_malloc(VMS_MAXRSS);
5869 strcpy(new_src, spec_buf);
5870 } else {
5871 src = spec_buf;
5872 }
5873 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5874 if (new_src) {
5875 PerlMem_free(new_src);
5876 }
5877 } else {
5878 /* VMS file specs are not in UTF-8 */
5879 if (fs_utf8 != NULL)
5880 *fs_utf8 = 0;
5881
5882 /* Copy the buffer if needed */
5883 if (outbuf != spec_buf)
5884 strcpy(outbuf, spec_buf);
5885 ret_spec = outbuf;
d584a1c6 5886 }
18a3d61e 5887 }
18a3d61e 5888 }
6fb6c614
JM
5889
5890 /* Need to clean up the search context */
a480973c
JM
5891 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5892 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5893
5894 /* Clean up the extra buffers */
7566800d 5895 if (esal != NULL)
6fb6c614
JM
5896 PerlMem_free(esal);
5897 PerlMem_free(esa);
c5375c28
JM
5898 if (outbufl != NULL)
5899 PerlMem_free(outbufl);
6fb6c614
JM
5900
5901 /* Return the result */
5902 return ret_spec;
5903}
5904
5905/* Common simple case - Expand an already VMS spec */
5906static char *
5907int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5908 opts |= PERL_RMSEXPAND_M_VMS_IN;
5909 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5910}
5911
5912/* Common simple case - Expand to a VMS spec */
5913static char *
5914int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5915 opts |= PERL_RMSEXPAND_M_VMS;
5916 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5917}
5918
5919
5920/* Entry point used by perl routines */
5921static char *
5922mp_do_rmsexpand
5923 (pTHX_ const char *filespec,
5924 char *outbuf,
5925 int ts,
5926 const char *defspec,
5927 unsigned opts,
5928 int * fs_utf8,
5929 int * dfs_utf8)
5930{
5931 static char __rmsexpand_retbuf[VMS_MAXRSS];
5932 char * expanded, *ret_spec, *ret_buf;
5933
5934 expanded = NULL;
5935 ret_buf = outbuf;
5936 if (ret_buf == NULL) {
5937 if (ts) {
5938 Newx(expanded, VMS_MAXRSS, char);
5939 if (expanded == NULL)
5940 _ckvmssts(SS$_INSFMEM);
5941 ret_buf = expanded;
5942 } else {
5943 ret_buf = __rmsexpand_retbuf;
5944 }
5945 }
5946
5947
5948 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5949 opts, fs_utf8, dfs_utf8);
5950
5951 if (ret_spec == NULL) {
5952 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5953 if (expanded)
5954 Safefree(expanded);
5955 }
5956
5957 return ret_spec;
bbce6d69 5958}
5959/*}}}*/
5960/* External entry points */
2fbb330f 5961char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5 5962{ return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
2fbb330f 5963char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5
JM
5964{ return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5965char *Perl_rmsexpand_utf8
5966 (pTHX_ const char *spec, char *buf, const char *def,
5967 unsigned opt, int * fs_utf8, int * dfs_utf8)
5968{ return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5969char *Perl_rmsexpand_utf8_ts
5970 (pTHX_ const char *spec, char *buf, const char *def,
5971 unsigned opt, int * fs_utf8, int * dfs_utf8)
5972{ return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
bbce6d69 5973
5974
a0d0e21e
LW
5975/*
5976** The following routines are provided to make life easier when
5977** converting among VMS-style and Unix-style directory specifications.
5978** All will take input specifications in either VMS or Unix syntax. On
5979** failure, all return NULL. If successful, the routines listed below
748a9306 5980** return a pointer to a buffer containing the appropriately
a0d0e21e
LW
5981** reformatted spec (and, therefore, subsequent calls to that routine
5982** will clobber the result), while the routines of the same names with
5983** a _ts suffix appended will return a pointer to a mallocd string
5984** containing the appropriately reformatted spec.
5985** In all cases, only explicit syntax is altered; no check is made that
5986** the resulting string is valid or that the directory in question
5987** actually exists.
5988**
5989** fileify_dirspec() - convert a directory spec into the name of the
5990** directory file (i.e. what you can stat() to see if it's a dir).
5991** The style (VMS or Unix) of the result is the same as the style
5992** of the parameter passed in.
5993** pathify_dirspec() - convert a directory spec into a path (i.e.
5994** what you prepend to a filename to indicate what directory it's in).
5995** The style (VMS or Unix) of the result is the same as the style
5996** of the parameter passed in.
5997** tounixpath() - convert a directory spec into a Unix-style path.
5998** tovmspath() - convert a directory spec into a VMS-style path.
5999** tounixspec() - convert any file spec into a Unix-style file spec.
6000** tovmsspec() - convert any file spec into a VMS-style spec.
360732b5 6001** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
e518068a 6002**
bd3fa61c 6003** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
01b8edb6 6004** Permission is given to distribute this code as part of the Perl
6005** standard distribution under the terms of the GNU General Public
6006** License or the Perl Artistic License. Copies of each may be
6007** found in the Perl standard distribution.
a0d0e21e
LW
6008 */
6009
a979ce91
JM
6010/*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6011static char *
6012int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
a0d0e21e 6013{
b7ae7a0d 6014 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
a979ce91 6015 char *cp1, *cp2, *lastdir;
a480973c 6016 char *trndir, *vmsdir;
2d9f3838 6017 unsigned short int trnlnm_iter_count;
df278665
JM
6018 int is_vms = 0;
6019 int is_unix = 0;
f7ddb74a 6020 int sts;
360732b5
JM
6021 if (utf8_fl != NULL)
6022 *utf8_fl = 0;
a0d0e21e 6023
c07a80fd 6024 if (!dir || !*dir) {
6025 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6026 }
a0d0e21e 6027 dirlen = strlen(dir);
a2a90019 6028 while (dirlen && dir[dirlen-1] == '/') --dirlen;
61bb5906 6029 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
f7ddb74a
JM
6030 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6031 dir = "/sys$disk";
6032 dirlen = 9;
6033 }
6034 else
6035 dirlen = 1;
61bb5906 6036 }
a480973c
JM
6037 if (dirlen > (VMS_MAXRSS - 1)) {
6038 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6039 return NULL;
c07a80fd 6040 }
c5375c28 6041 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 6042 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
6043 if (!strpbrk(dir+1,"/]>:") &&
6044 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
e518068a 6045 strcpy(trndir,*dir == '/' ? dir + 1: dir);
2d9f3838 6046 trnlnm_iter_count = 0;
b8486b9d 6047 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
2d9f3838
CB
6048 trnlnm_iter_count++;
6049 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6050 }
b8ffc8df 6051 dirlen = strlen(trndir);
e518068a 6052 }
01b8edb6 6053 else {
6054 strncpy(trndir,dir,dirlen);
6055 trndir[dirlen] = '\0';
01b8edb6 6056 }
b8ffc8df
RGS
6057
6058 /* At this point we are done with *dir and use *trndir which is a
6059 * copy that can be modified. *dir must not be modified.
6060 */
6061
c07a80fd 6062 /* If we were handed a rooted logical name or spec, treat it like a
6063 * simple directory, so that
6064 * $ Define myroot dev:[dir.]
6065 * ... do_fileify_dirspec("myroot",buf,1) ...
6066 * does something useful.
6067 */
b8ffc8df
RGS
6068 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6069 trndir[--dirlen] = '\0';
6070 trndir[dirlen-1] = ']';
c07a80fd 6071 }
b8ffc8df
RGS
6072 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6073 trndir[--dirlen] = '\0';
6074 trndir[dirlen-1] = '>';
46112e17 6075 }
e518068a 6076
b8ffc8df 6077 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
b7ae7a0d 6078 /* If we've got an explicit filename, we can just shuffle the string. */
6079 if (*(cp1+1)) hasfilename = 1;
6080 /* Similarly, we can just back up a level if we've got multiple levels
6081 of explicit directories in a VMS spec which ends with directories. */
6082 else {
b8ffc8df 6083 for (cp2 = cp1; cp2 > trndir; cp2--) {
f7ddb74a
JM
6084 if (*cp2 == '.') {
6085 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
657054d4 6086/* fix-me, can not scan EFS file specs backward like this */
f7ddb74a
JM
6087 *cp2 = *cp1; *cp1 = '\0';
6088 hasfilename = 1;
6089 break;
6090 }
b7ae7a0d 6091 }
6092 if (*cp2 == '[' || *cp2 == '<') break;
6093 }
6094 }
6095 }
6096
c5375c28 6097 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 6098 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 6099 cp1 = strpbrk(trndir,"]:>");
a979ce91
JM
6100 if (hasfilename || !cp1) { /* filename present or not VMS */
6101
6102 if (decc_efs_charset && !cp1) {
6103
6104 /* EFS handling for UNIX mode */
6105
6106 /* Just remove the trailing '/' and we should be done */
6107 STRLEN trndir_len;
6108 trndir_len = strlen(trndir);
6109
6110 if (trndir_len > 1) {
6111 trndir_len--;
6112 if (trndir[trndir_len] == '/') {
6113 trndir[trndir_len] = '\0';
6114 }
6115 }
6116 strcpy(buf, trndir);
6117 PerlMem_free(trndir);
6118 PerlMem_free(vmsdir);
6119 return buf;
6120 }
6121
6122 /* For non-EFS mode, this is left for backwards compatibility */
6123 /* For EFS mode, this is only done for VMS format filespecs as */
6124 /* Perl programs generally have problems when a UNIX format spec */
6125 /* returns a VMS format spec */
b8ffc8df 6126 if (trndir[0] == '.') {
a480973c 6127 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
c5375c28
JM
6128 PerlMem_free(trndir);
6129 PerlMem_free(vmsdir);
a979ce91 6130 return int_fileify_dirspec("[]", buf, NULL);
a480973c 6131 }
b8ffc8df 6132 else if (trndir[1] == '.' &&
a480973c 6133 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
c5375c28
JM
6134 PerlMem_free(trndir);
6135 PerlMem_free(vmsdir);
a979ce91 6136 return int_fileify_dirspec("[-]", buf, NULL);
a480973c 6137 }
748a9306 6138 }
b8ffc8df 6139 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
a0d0e21e 6140 dirlen -= 1; /* to last element */
b8ffc8df 6141 lastdir = strrchr(trndir,'/');
a0d0e21e 6142 }
b8ffc8df 6143 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
01b8edb6 6144 /* If we have "/." or "/..", VMSify it and let the VMS code
6145 * below expand it, rather than repeating the code to handle
6146 * relative components of a filespec here */
4633a7c4
LW
6147 do {
6148 if (*(cp1+2) == '.') cp1++;
6149 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
a480973c 6150 char * ret_chr;
df278665 6151 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
c5375c28
JM
6152 PerlMem_free(trndir);
6153 PerlMem_free(vmsdir);
a480973c
JM
6154 return NULL;
6155 }
fc1ce8cc 6156 if (strchr(vmsdir,'/') != NULL) {
df278665 6157 /* If int_tovmsspec() returned it, it must have VMS syntax
fc1ce8cc
CB
6158 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6159 * the time to check this here only so we avoid a recursion
6160 * loop; otherwise, gigo.
6161 */
c5375c28
JM
6162 PerlMem_free(trndir);
6163 PerlMem_free(vmsdir);
a480973c
JM
6164 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6165 return NULL;
fc1ce8cc 6166 }
a979ce91 6167 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6168 PerlMem_free(trndir);
6169 PerlMem_free(vmsdir);
a480973c
JM
6170 return NULL;
6171 }
0e5ce2c7 6172 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6173 PerlMem_free(trndir);
6174 PerlMem_free(vmsdir);
a480973c 6175 return ret_chr;
4633a7c4
LW
6176 }
6177 cp1++;
6178 } while ((cp1 = strstr(cp1,"/.")) != NULL);
b8ffc8df 6179 lastdir = strrchr(trndir,'/');
748a9306 6180 }
b8ffc8df 6181 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
a480973c 6182 char * ret_chr;
61bb5906
CB
6183 /* Ditto for specs that end in an MFD -- let the VMS code
6184 * figure out whether it's a real device or a rooted logical. */
f7ddb74a
JM
6185
6186 /* This should not happen any more. Allowing the fake /000000
6187 * in a UNIX pathname causes all sorts of problems when trying
6188 * to run in UNIX emulation. So the VMS to UNIX conversions
6189 * now remove the fake /000000 directories.
6190 */
6191
b8ffc8df 6192 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
df278665 6193 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
c5375c28
JM
6194 PerlMem_free(trndir);
6195 PerlMem_free(vmsdir);
a480973c
JM
6196 return NULL;
6197 }
a979ce91 6198 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6199 PerlMem_free(trndir);
6200 PerlMem_free(vmsdir);
a480973c
JM
6201 return NULL;
6202 }
0e5ce2c7 6203 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6204 PerlMem_free(trndir);
6205 PerlMem_free(vmsdir);
a480973c 6206 return ret_chr;
61bb5906 6207 }
a0d0e21e 6208 else {
f7ddb74a 6209
b8ffc8df
RGS
6210 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6211 !(lastdir = cp1 = strrchr(trndir,']')) &&
6212 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
f7ddb74a 6213
a979ce91
JM
6214 cp2 = strrchr(cp1,'.');
6215 if (cp2) {
6216 int e_len, vs_len = 0;
6217 int is_dir = 0;
6218 char * cp3;
6219 cp3 = strchr(cp2,';');
6220 e_len = strlen(cp2);
6221 if (cp3) {
6222 vs_len = strlen(cp3);
6223 e_len = e_len - vs_len;
6224 }
6225 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6226 if (!is_dir) {
6227 if (!decc_efs_charset) {
6228 /* If this is not EFS, then not a directory */
6229 PerlMem_free(trndir);
6230 PerlMem_free(vmsdir);
6231 set_errno(ENOTDIR);
6232 set_vaxc_errno(RMS$_DIR);
6233 return NULL;
6234 }
6235 } else {
6236 /* Ok, here we have an issue, technically if a .dir shows */
6237 /* from inside a directory, then we should treat it as */
6238 /* xxx^.dir.dir. But we do not have that context at this */
6239 /* point unless this is totally restructured, so we remove */
6240 /* The .dir for now, and fix this better later */
6241 dirlen = cp2 - trndir;
6242 }
a0d0e21e 6243 }
a979ce91 6244
748a9306 6245 }
f7ddb74a
JM
6246
6247 retlen = dirlen + 6;
a979ce91
JM
6248 memcpy(buf, trndir, dirlen);
6249 buf[dirlen] = '\0';
f7ddb74a 6250
a0d0e21e
LW
6251 /* We've picked up everything up to the directory file name.
6252 Now just add the type and version, and we're set. */
df278665
JM
6253
6254 /* We should only add type for VMS syntax, but historically Perl
6255 has added it for UNIX style also */
6256
6257 /* Fix me - we should not be using the same routine for VMS and
6258 UNIX format files. Things are too tangled so we need to lookup
6259 what syntax the output is */
6260
6261 is_unix = 0;
6262 is_vms = 0;
6263 lastdir = strrchr(trndir,'/');
6264 if (lastdir) {
6265 is_unix = 1;
6266 } else {
6267 lastdir = strpbrk(trndir,"]:>");
6268 if (lastdir) {
6269 is_vms = 1;
6270 }
6271 }
6272
6273 if ((is_vms == 0) && (is_unix == 0)) {
6274 /* We still do not know? */
6275 is_unix = decc_filename_unix_report;
6276 if (is_unix == 0)
6277 is_vms = 1;
6278 }
6279
6280 if ((is_unix && !decc_efs_charset) || is_vms) {
6281
6282 /* It is a bug to add a .dir to a UNIX format directory spec */
6283 /* However Perl on VMS may have programs that expect this so */
6284 /* If not using EFS character specifications allow it. */
6285
6286 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6287 /* Traditionally Perl expects filenames in lower case */
a979ce91 6288 strcat(buf, ".dir");
df278665
JM
6289 } else {
6290 /* VMS expects the .DIR to be in upper case */
a979ce91 6291 strcat(buf, ".DIR");
df278665
JM
6292 }
6293
6294 /* It is also a bug to put a VMS format version on a UNIX file */
6295 /* specification. Perl self tests are looking for this */
6296 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
a979ce91 6297 strcat(buf, ";1");
df278665 6298 }
c5375c28
JM
6299 PerlMem_free(trndir);
6300 PerlMem_free(vmsdir);
a979ce91 6301 return buf;
a0d0e21e
LW
6302 }
6303 else { /* VMS-style directory spec */
a480973c 6304
d584a1c6
JM
6305 char *esa, *esal, term, *cp;
6306 char *my_esa;
6307 int my_esa_len;
01b8edb6 6308 unsigned long int sts, cmplen, haslower = 0;
a480973c
JM
6309 unsigned int nam_fnb;
6310 char * nam_type;
a0d0e21e 6311 struct FAB dirfab = cc$rms_fab;
a480973c
JM
6312 rms_setup_nam(savnam);
6313 rms_setup_nam(dirnam);
6314
d584a1c6 6315 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 6316 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
6317 esal = NULL;
6318#if !defined(__VAX) && defined(NAML$C_MAXRSS)
6319 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 6320 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 6321#endif
a480973c
JM
6322 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6323 rms_bind_fab_nam(dirfab, dirnam);
6324 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
d584a1c6 6325 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
f7ddb74a
JM
6326#ifdef NAM$M_NO_SHORT_UPCASE
6327 if (decc_efs_case_preserve)
a480973c 6328 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6329#endif
01b8edb6 6330
b8ffc8df 6331 for (cp = trndir; *cp; cp++)
01b8edb6 6332 if (islower(*cp)) { haslower = 1; break; }
a480973c 6333 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
a979ce91
JM
6334 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6335 (dirfab.fab$l_sts == RMS$_DNF) ||
6336 (dirfab.fab$l_sts == RMS$_PRV)) {
6337 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6338 sts = sys$parse(&dirfab);
e518068a 6339 }
6340 if (!sts) {
c5375c28 6341 PerlMem_free(esa);
d584a1c6
JM
6342 if (esal != NULL)
6343 PerlMem_free(esal);
c5375c28
JM
6344 PerlMem_free(trndir);
6345 PerlMem_free(vmsdir);
748a9306
LW
6346 set_errno(EVMSERR);
6347 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
6348 return NULL;
6349 }
e518068a 6350 }
6351 else {
6352 savnam = dirnam;
a480973c
JM
6353 /* Does the file really exist? */
6354 if (sys$search(&dirfab)& STS$K_SUCCESS) {
e518068a 6355 /* Yes; fake the fnb bits so we'll check type below */
a979ce91 6356 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
e518068a 6357 }
752635ea
CB
6358 else { /* No; just work with potential name */
6359 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6360 else {
2623a4a6
JM
6361 int fab_sts;
6362 fab_sts = dirfab.fab$l_sts;
6363 sts = rms_free_search_context(&dirfab);
c5375c28 6364 PerlMem_free(esa);
d584a1c6
JM
6365 if (esal != NULL)
6366 PerlMem_free(esal);
c5375c28
JM
6367 PerlMem_free(trndir);
6368 PerlMem_free(vmsdir);
2623a4a6 6369 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
e518068a 6370 return NULL;
6371 }
e518068a 6372 }
a0d0e21e 6373 }
d584a1c6
JM
6374
6375 /* Make sure we are using the right buffer */
778e045f 6376#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6
JM
6377 if (esal != NULL) {
6378 my_esa = esal;
6379 my_esa_len = rms_nam_esll(dirnam);
6380 } else {
778e045f 6381#endif
d584a1c6
JM
6382 my_esa = esa;
6383 my_esa_len = rms_nam_esl(dirnam);
778e045f 6384#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6 6385 }
778e045f 6386#endif
d584a1c6 6387 my_esa[my_esa_len] = '\0';
a480973c 6388 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
d584a1c6
JM
6389 cp1 = strchr(my_esa,']');
6390 if (!cp1) cp1 = strchr(my_esa,'>');
748a9306 6391 if (cp1) { /* Should always be true */
d584a1c6
JM
6392 my_esa_len -= cp1 - my_esa - 1;
6393 memmove(my_esa, cp1 + 1, my_esa_len);
748a9306
LW
6394 }
6395 }
a480973c 6396 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
a0d0e21e 6397 /* Yep; check version while we're at it, if it's there. */
a480973c
JM
6398 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6399 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
a0d0e21e 6400 /* Something other than .DIR[;1]. Bzzt. */
a480973c 6401 sts = rms_free_search_context(&dirfab);
c5375c28 6402 PerlMem_free(esa);
d584a1c6
JM
6403 if (esal != NULL)
6404 PerlMem_free(esal);
c5375c28
JM
6405 PerlMem_free(trndir);
6406 PerlMem_free(vmsdir);
748a9306
LW
6407 set_errno(ENOTDIR);
6408 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
6409 return NULL;
6410 }
748a9306 6411 }
ae6d78fe 6412
a480973c 6413 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
748a9306 6414 /* They provided at least the name; we added the type, if necessary, */
a979ce91 6415 strcpy(buf, my_esa);
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);
a979ce91 6422 return buf;
748a9306 6423 }
c07a80fd 6424 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6425 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6426 *cp1 = '\0';
d584a1c6 6427 my_esa_len -= 9;
c07a80fd 6428 }
d584a1c6 6429 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
752635ea 6430 if (cp1 == NULL) { /* should never happen */
a480973c 6431 sts = rms_free_search_context(&dirfab);
c5375c28
JM
6432 PerlMem_free(trndir);
6433 PerlMem_free(esa);
d584a1c6
JM
6434 if (esal != NULL)
6435 PerlMem_free(esal);
c5375c28 6436 PerlMem_free(vmsdir);
752635ea
CB
6437 return NULL;
6438 }
748a9306
LW
6439 term = *cp1;
6440 *cp1 = '\0';
d584a1c6
JM
6441 retlen = strlen(my_esa);
6442 cp1 = strrchr(my_esa,'.');
f7ddb74a 6443 /* ODS-5 directory specifications can have extra "." in them. */
657054d4 6444 /* Fix-me, can not scan EFS file specifications backwards */
f7ddb74a 6445 while (cp1 != NULL) {
d584a1c6 6446 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
f7ddb74a
JM
6447 break;
6448 else {
6449 cp1--;
d584a1c6 6450 while ((cp1 > my_esa) && (*cp1 != '.'))
f7ddb74a
JM
6451 cp1--;
6452 }
d584a1c6 6453 if (cp1 == my_esa)
f7ddb74a
JM
6454 cp1 = NULL;
6455 }
6456
6457 if ((cp1) != NULL) {
748a9306
LW
6458 /* There's more than one directory in the path. Just roll back. */
6459 *cp1 = term;
a979ce91 6460 strcpy(buf, my_esa);
a0d0e21e
LW
6461 }
6462 else {
a480973c 6463 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
748a9306 6464 /* Go back and expand rooted logical name */
a480973c 6465 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
f7ddb74a
JM
6466#ifdef NAM$M_NO_SHORT_UPCASE
6467 if (decc_efs_case_preserve)
a480973c 6468 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6469#endif
a480973c
JM
6470 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6471 sts = rms_free_search_context(&dirfab);
c5375c28 6472 PerlMem_free(esa);
d584a1c6
JM
6473 if (esal != NULL)
6474 PerlMem_free(esal);
c5375c28
JM
6475 PerlMem_free(trndir);
6476 PerlMem_free(vmsdir);
748a9306
LW
6477 set_errno(EVMSERR);
6478 set_vaxc_errno(dirfab.fab$l_sts);
6479 return NULL;
6480 }
d584a1c6
JM
6481
6482 /* This changes the length of the string of course */
6483 if (esal != NULL) {
6484 my_esa_len = rms_nam_esll(dirnam);
6485 } else {
6486 my_esa_len = rms_nam_esl(dirnam);
6487 }
6488
6489 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
d584a1c6
JM
6490 cp1 = strstr(my_esa,"][");
6491 if (!cp1) cp1 = strstr(my_esa,"]<");
6492 dirlen = cp1 - my_esa;
a979ce91 6493 memcpy(buf, my_esa, dirlen);
748a9306 6494 if (!strncmp(cp1+2,"000000]",7)) {
a979ce91 6495 buf[dirlen-1] = '\0';
657054d4 6496 /* fix-me Not full ODS-5, just extra dots in directories for now */
a979ce91
JM
6497 cp1 = buf + dirlen - 1;
6498 while (cp1 > buf)
f7ddb74a
JM
6499 {
6500 if (*cp1 == '[')
6501 break;
6502 if (*cp1 == '.') {
6503 if (*(cp1-1) != '^')
6504 break;
6505 }
6506 cp1--;
6507 }
4633a7c4
LW
6508 if (*cp1 == '.') *cp1 = ']';
6509 else {
a979ce91 6510 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6511 memmove(cp1+1,"000000]",7);
4633a7c4 6512 }
748a9306
LW
6513 }
6514 else {
a979ce91
JM
6515 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6516 buf[retlen] = '\0';
748a9306 6517 /* Convert last '.' to ']' */
a979ce91 6518 cp1 = buf+retlen-1;
f7ddb74a
JM
6519 while (*cp != '[') {
6520 cp1--;
6521 if (*cp1 == '.') {
6522 /* Do not trip on extra dots in ODS-5 directories */
a979ce91 6523 if ((cp1 == buf) || (*(cp1-1) != '^'))
f7ddb74a
JM
6524 break;
6525 }
6526 }
4633a7c4
LW
6527 if (*cp1 == '.') *cp1 = ']';
6528 else {
a979ce91 6529 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6530 memmove(cp1+1,"000000]",7);
4633a7c4 6531 }
748a9306 6532 }
a0d0e21e 6533 }
748a9306 6534 else { /* This is a top-level dir. Add the MFD to the path. */
d584a1c6 6535 cp1 = my_esa;
a979ce91 6536 cp2 = buf;
bbdb6c9a 6537 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
a0d0e21e
LW
6538 strcpy(cp2,":[000000]");
6539 cp1 += 2;
6540 strcpy(cp2+9,cp1);
6541 }
748a9306 6542 }
a480973c 6543 sts = rms_free_search_context(&dirfab);
748a9306 6544 /* We've set up the string up through the filename. Add the
a0d0e21e 6545 type and version, and we're done. */
a979ce91 6546 strcat(buf,".DIR;1");
01b8edb6 6547
6548 /* $PARSE may have upcased filespec, so convert output to lower
6549 * case if input contained any lowercase characters. */
a979ce91 6550 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
c5375c28
JM
6551 PerlMem_free(trndir);
6552 PerlMem_free(esa);
d584a1c6
JM
6553 if (esal != NULL)
6554 PerlMem_free(esal);
c5375c28 6555 PerlMem_free(vmsdir);
a979ce91 6556 return buf;
a0d0e21e 6557 }
a979ce91
JM
6558} /* end of int_fileify_dirspec() */
6559
6560
6561/*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6562static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6563{
6564 static char __fileify_retbuf[VMS_MAXRSS];
6565 char * fileified, *ret_spec, *ret_buf;
6566
6567 fileified = NULL;
6568 ret_buf = buf;
6569 if (ret_buf == NULL) {
6570 if (ts) {
6571 Newx(fileified, VMS_MAXRSS, char);
6572 if (fileified == NULL)
6573 _ckvmssts(SS$_INSFMEM);
6574 ret_buf = fileified;
6575 } else {
6576 ret_buf = __fileify_retbuf;
6577 }
6578 }
6579
6580 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6581
6582 if (ret_spec == NULL) {
6583 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6584 if (fileified)
6585 Safefree(fileified);
6586 }
6587
6588 return ret_spec;
a0d0e21e
LW
6589} /* end of do_fileify_dirspec() */
6590/*}}}*/
a979ce91 6591
a0d0e21e 6592/* External entry points */
b8ffc8df 6593char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 6594{ return do_fileify_dirspec(dir,buf,0,NULL); }
b8ffc8df 6595char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
6596{ return do_fileify_dirspec(dir,buf,1,NULL); }
6597char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6598{ return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6599char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6600{ return do_fileify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 6601
1fe570cc
JM
6602static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6603 char * v_spec, int v_len, char * r_spec, int r_len,
6604 char * d_spec, int d_len, char * n_spec, int n_len,
6605 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6606
6607 /* VMS specification - Try to do this the simple way */
6608 if ((v_len + r_len > 0) || (d_len > 0)) {
6609 int is_dir;
6610
6611 /* No name or extension component, already a directory */
6612 if ((n_len + e_len + vs_len) == 0) {
6613 strcpy(buf, dir);
6614 return buf;
6615 }
6616
6617 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6618 /* This results from catfile() being used instead of catdir() */
6619 /* So even though it should not work, we need to allow it */
6620
6621 /* If this is .DIR;1 then do a simple conversion */
6622 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6623 if (is_dir || (e_len == 0) && (d_len > 0)) {
6624 int len;
6625 len = v_len + r_len + d_len - 1;
6626 char dclose = d_spec[d_len - 1];
6627 strncpy(buf, dir, len);
6628 buf[len] = '.';
6629 len++;
6630 strncpy(&buf[len], n_spec, n_len);
6631 len += n_len;
6632 buf[len] = dclose;
6633 buf[len + 1] = '\0';
6634 return buf;
6635 }
6636
6637#ifdef HAS_SYMLINK
6638 else if (d_len > 0) {
6639 /* In the olden days, a directory needed to have a .DIR */
6640 /* extension to be a valid directory, but now it could */
6641 /* be a symbolic link */
6642 int len;
6643 len = v_len + r_len + d_len - 1;
6644 char dclose = d_spec[d_len - 1];
6645 strncpy(buf, dir, len);
6646 buf[len] = '.';
6647 len++;
6648 strncpy(&buf[len], n_spec, n_len);
6649 len += n_len;
6650 if (e_len > 0) {
6651 if (decc_efs_charset) {
6652 buf[len] = '^';
6653 len++;
6654 strncpy(&buf[len], e_spec, e_len);
6655 len += e_len;
6656 } else {
6657 set_vaxc_errno(RMS$_DIR);
6658 set_errno(ENOTDIR);
6659 return NULL;
6660 }
6661 }
6662 buf[len] = dclose;
6663 buf[len + 1] = '\0';
6664 return buf;
6665 }
6666#else
6667 else {
6668 set_vaxc_errno(RMS$_DIR);
6669 set_errno(ENOTDIR);
6670 return NULL;
6671 }
6672#endif
6673 }
6674 set_vaxc_errno(RMS$_DIR);
6675 set_errno(ENOTDIR);
6676 return NULL;
6677}
6678
6679
6680/* Internal routine to make sure or convert a directory to be in a */
6681/* path specification. No utf8 flag because it is not changed or used */
6682static char *int_pathify_dirspec(const char *dir, char *buf)
a0d0e21e 6683{
1fe570cc
JM
6684 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6685 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6686 char * exp_spec, *ret_spec;
6687 char * trndir;
2d9f3838 6688 unsigned short int trnlnm_iter_count;
baf3cf9c 6689 STRLEN trnlen;
1fe570cc
JM
6690 int need_to_lower;
6691
6692 if (vms_debug_fileify) {
6693 if (dir == NULL)
6694 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6695 else
6696 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6697 }
6698
6699 /* We may need to lower case the result if we translated */
6700 /* a logical name or got the current working directory */
6701 need_to_lower = 0;
a0d0e21e 6702
c07a80fd 6703 if (!dir || !*dir) {
1fe570cc
JM
6704 set_errno(EINVAL);
6705 set_vaxc_errno(SS$_BADPARAM);
6706 return NULL;
c07a80fd 6707 }
6708
c5375c28 6709 trndir = PerlMem_malloc(VMS_MAXRSS);
1fe570cc
JM
6710 if (trndir == NULL)
6711 _ckvmssts_noperl(SS$_INSFMEM);
c07a80fd 6712
1fe570cc
JM
6713 /* If no directory specified use the current default */
6714 if (*dir)
6715 strcpy(trndir, dir);
6716 else {
6717 getcwd(trndir, VMS_MAXRSS - 1);
6718 need_to_lower = 1;
6719 }
6720
6721 /* now deal with bare names that could be logical names */
2d9f3838 6722 trnlnm_iter_count = 0;
93948341 6723 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
1fe570cc
JM
6724 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6725 trnlnm_iter_count++;
6726 need_to_lower = 1;
6727 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6728 break;
6729 trnlen = strlen(trndir);
6730
6731 /* Trap simple rooted lnms, and return lnm:[000000] */
6732 if (!strcmp(trndir+trnlen-2,".]")) {
6733 strcpy(buf, dir);
6734 strcat(buf, ":[000000]");
6735 PerlMem_free(trndir);
6736
6737 if (vms_debug_fileify) {
6738 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6739 }
6740 return buf;
6741 }
c07a80fd 6742 }
748a9306 6743
1fe570cc 6744 /* At this point we do not work with *dir, but the copy in *trndir */
b8ffc8df 6745
1fe570cc
JM
6746 if (need_to_lower && !decc_efs_case_preserve) {
6747 /* Legacy mode, lower case the returned value */
6748 __mystrtolower(trndir);
6749 }
f7ddb74a 6750
1fe570cc
JM
6751
6752 /* Some special cases, '..', '.' */
6753 sts = 0;
6754 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6755 /* Force UNIX filespec */
6756 sts = 1;
6757
6758 } else {
6759 /* Is this Unix or VMS format? */
6760 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6761 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6762 &e_len, &vs_spec, &vs_len);
6763 if (sts == 0) {
6764
6765 /* Just a filename? */
6766 if ((v_len + r_len + d_len) == 0) {
6767
6768 /* Now we have a problem, this could be Unix or VMS */
6769 /* We have to guess. .DIR usually means VMS */
6770
6771 /* In UNIX report mode, the .DIR extension is removed */
6772 /* if one shows up, it is for a non-directory or a directory */
6773 /* in EFS charset mode */
6774
6775 /* So if we are in Unix report mode, assume that this */
6776 /* is a relative Unix directory specification */
6777
6778 sts = 1;
6779 if (!decc_filename_unix_report && decc_efs_charset) {
6780 int is_dir;
6781 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6782
6783 if (is_dir) {
6784 /* Traditional mode, assume .DIR is directory */
6785 buf[0] = '[';
6786 buf[1] = '.';
6787 strncpy(&buf[2], n_spec, n_len);
6788 buf[n_len + 2] = ']';
6789 buf[n_len + 3] = '\0';
6790 PerlMem_free(trndir);
6791 if (vms_debug_fileify) {
6792 fprintf(stderr,
6793 "int_pathify_dirspec: buf = %s\n",
6794 buf);
6795 }
6796 return buf;
6797 }
6798 }
6799 }
a0d0e21e 6800 }
a0d0e21e 6801 }
1fe570cc
JM
6802 if (sts == 0) {
6803 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6804 v_spec, v_len, r_spec, r_len,
6805 d_spec, d_len, n_spec, n_len,
6806 e_spec, e_len, vs_spec, vs_len);
a0d0e21e 6807
1fe570cc
JM
6808 if (ret_spec != NULL) {
6809 PerlMem_free(trndir);
6810 if (vms_debug_fileify) {
6811 fprintf(stderr,
6812 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6813 }
6814 return ret_spec;
b7ae7a0d 6815 }
1fe570cc
JM
6816
6817 /* Simple way did not work, which means that a logical name */
6818 /* was present for the directory specification. */
6819 /* Need to use an rmsexpand variant to decode it completely */
6820 exp_spec = PerlMem_malloc(VMS_MAXRSS);
6821 if (exp_spec == NULL)
6822 _ckvmssts_noperl(SS$_INSFMEM);
6823
6824 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6825 if (ret_spec != NULL) {
6826 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6827 &r_spec, &r_len, &d_spec, &d_len,
6828 &n_spec, &n_len, &e_spec,
6829 &e_len, &vs_spec, &vs_len);
6830 if (sts == 0) {
6831 ret_spec = int_pathify_dirspec_simple(
6832 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6833 d_spec, d_len, n_spec, n_len,
6834 e_spec, e_len, vs_spec, vs_len);
6835
6836 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6837 /* Legacy mode, lower case the returned value */
6838 __mystrtolower(ret_spec);
6839 }
6840 } else {
6841 set_vaxc_errno(RMS$_DIR);
6842 set_errno(ENOTDIR);
6843 ret_spec = NULL;
6844 }
b7ae7a0d 6845 }
1fe570cc
JM
6846 PerlMem_free(exp_spec);
6847 PerlMem_free(trndir);
6848 if (vms_debug_fileify) {
6849 if (ret_spec == NULL)
6850 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6851 else
6852 fprintf(stderr,
6853 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6854 }
6855 return ret_spec;
a480973c 6856
1fe570cc
JM
6857 } else {
6858 /* Unix specification, Could be trivial conversion */
6859 STRLEN dir_len;
6860 dir_len = strlen(trndir);
6861
6862 /* If the extended file character set is in effect */
6863 /* then pathify is simple */
6864
6865 if (!decc_efs_charset) {
6866 /* Have to deal with traiing '.dir' or extra '.' */
6867 /* that should not be there in legacy mode, but is */
6868
6869 char * lastdot;
6870 char * lastslash;
6871 int is_dir;
6872
6873 lastslash = strrchr(trndir, '/');
6874 if (lastslash == NULL)
6875 lastslash = trndir;
6876 else
6877 lastslash++;
6878
6879 lastdot = NULL;
6880
6881 /* '..' or '.' are valid directory components */
6882 is_dir = 0;
6883 if (lastslash[0] == '.') {
6884 if (lastslash[1] == '\0') {
6885 is_dir = 1;
6886 } else if (lastslash[1] == '.') {
6887 if (lastslash[2] == '\0') {
6888 is_dir = 1;
6889 } else {
6890 /* And finally allow '...' */
6891 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6892 is_dir = 1;
6893 }
6894 }
6895 }
6896 }
01b8edb6 6897
1fe570cc
JM
6898 if (!is_dir) {
6899 lastdot = strrchr(lastslash, '.');
6900 }
6901 if (lastdot != NULL) {
6902 STRLEN e_len;
01b8edb6 6903
1fe570cc
JM
6904 /* '.dir' is discarded, and any other '.' is invalid */
6905 e_len = strlen(lastdot);
6906
6907 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6908
6909 if (is_dir) {
6910 dir_len = dir_len - 4;
6911
6912 }
6913 }
e518068a 6914 }
1fe570cc
JM
6915
6916 strcpy(buf, trndir);
6917 if (buf[dir_len - 1] != '/') {
6918 buf[dir_len] = '/';
6919 buf[dir_len + 1] = '\0';
a0d0e21e 6920 }
1fe570cc
JM
6921
6922 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6923 if (!decc_efs_charset) {
6924 int dir_start = 0;
6925 char * str = buf;
6926 if (str[0] == '.') {
6927 char * dots = str;
6928 int cnt = 1;
6929 while ((dots[cnt] == '.') && (cnt < 3))
6930 cnt++;
6931 if (cnt <= 3) {
6932 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6933 dir_start = 1;
6934 str += cnt;
6935 }
6936 }
6937 }
6938 for (; *str; ++str) {
6939 while (*str == '/') {
6940 dir_start = 1;
6941 *str++;
6942 }
6943 if (dir_start) {
6944
6945 /* Have to skip up to three dots which could be */
6946 /* directories, 3 dots being a VMS extension for Perl */
6947 char * dots = str;
6948 int cnt = 0;
6949 while ((dots[cnt] == '.') && (cnt < 3)) {
6950 cnt++;
6951 }
6952 if (dots[cnt] == '\0')
6953 break;
6954 if ((cnt > 1) && (dots[cnt] != '/')) {
6955 dir_start = 0;
6956 } else {
6957 str += cnt;
6958 }
6959
6960 /* too many dots? */
6961 if ((cnt == 0) || (cnt > 3)) {
6962 dir_start = 0;
6963 }
6964 }
6965 if (!dir_start && (*str == '.')) {
6966 *str = '_';
6967 }
6968 }
e518068a 6969 }
1fe570cc
JM
6970 PerlMem_free(trndir);
6971 ret_spec = buf;
6972 if (vms_debug_fileify) {
6973 if (ret_spec == NULL)
6974 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6975 else
6976 fprintf(stderr,
6977 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
a0d0e21e 6978 }
1fe570cc
JM
6979 return ret_spec;
6980 }
6981}
d584a1c6 6982
1fe570cc
JM
6983/*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6984static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6985{
6986 static char __pathify_retbuf[VMS_MAXRSS];
6987 char * pathified, *ret_spec, *ret_buf;
6988
6989 pathified = NULL;
6990 ret_buf = buf;
6991 if (ret_buf == NULL) {
6992 if (ts) {
6993 Newx(pathified, VMS_MAXRSS, char);
6994 if (pathified == NULL)
6995 _ckvmssts(SS$_INSFMEM);
6996 ret_buf = pathified;
6997 } else {
6998 ret_buf = __pathify_retbuf;
6999 }
7000 }
d584a1c6 7001
1fe570cc
JM
7002 ret_spec = int_pathify_dirspec(dir, ret_buf);
7003
7004 if (ret_spec == NULL) {
7005 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7006 if (pathified)
7007 Safefree(pathified);
a0d0e21e
LW
7008 }
7009
1fe570cc
JM
7010 return ret_spec;
7011
a0d0e21e 7012} /* end of do_pathify_dirspec() */
1fe570cc
JM
7013
7014
a0d0e21e 7015/* External entry points */
b8ffc8df 7016char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 7017{ return do_pathify_dirspec(dir,buf,0,NULL); }
b8ffc8df 7018char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
7019{ return do_pathify_dirspec(dir,buf,1,NULL); }
7020char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7021{ return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7022char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7023{ return do_pathify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 7024
0e5ce2c7
JM
7025/* Internal tounixspec routine that does not use a thread context */
7026/*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7027static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
a0d0e21e 7028{
0e5ce2c7 7029 char *dirend, *cp1, *cp3, *tmp;
b8ffc8df 7030 const char *cp2;
a480973c 7031 int devlen, dirlen, retlen = VMS_MAXRSS;
0f20d7df 7032 int expand = 1; /* guarantee room for leading and trailing slashes */
2d9f3838 7033 unsigned short int trnlnm_iter_count;
f7ddb74a 7034 int cmp_rslt;
360732b5
JM
7035 if (utf8_fl != NULL)
7036 *utf8_fl = 0;
a0d0e21e 7037
0e5ce2c7
JM
7038 if (vms_debug_fileify) {
7039 if (spec == NULL)
7040 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7041 else
7042 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7043 }
7044
7045
7046 if (spec == NULL) {
7047 set_errno(EINVAL);
7048 set_vaxc_errno(SS$_BADPARAM);
7049 return NULL;
7050 }
7051 if (strlen(spec) > (VMS_MAXRSS-1)) {
7052 set_errno(E2BIG);
7053 set_vaxc_errno(SS$_BUFFEROVF);
7054 return NULL;
e518068a 7055 }
f7ddb74a 7056
2497a41f
JM
7057 /* New VMS specific format needs translation
7058 * glob passes filenames with trailing '\n' and expects this preserved.
7059 */
7060 if (decc_posix_compliant_pathnames) {
7061 if (strncmp(spec, "\"^UP^", 5) == 0) {
7062 char * uspec;
7063 char *tunix;
7064 int tunix_len;
7065 int nl_flag;
7066
c5375c28 7067 tunix = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 7068 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2497a41f
JM
7069 strcpy(tunix, spec);
7070 tunix_len = strlen(tunix);
7071 nl_flag = 0;
7072 if (tunix[tunix_len - 1] == '\n') {
7073 tunix[tunix_len - 1] = '\"';
7074 tunix[tunix_len] = '\0';
7075 tunix_len--;
7076 nl_flag = 1;
7077 }
7078 uspec = decc$translate_vms(tunix);
367e4b85 7079 PerlMem_free(tunix);
2497a41f
JM
7080 if ((int)uspec > 0) {
7081 strcpy(rslt,uspec);
7082 if (nl_flag) {
7083 strcat(rslt,"\n");
7084 }
7085 else {
7086 /* If we can not translate it, makemaker wants as-is */
7087 strcpy(rslt, spec);
7088 }
7089 return rslt;
7090 }
7091 }
7092 }
7093
f7ddb74a
JM
7094 cmp_rslt = 0; /* Presume VMS */
7095 cp1 = strchr(spec, '/');
7096 if (cp1 == NULL)
7097 cmp_rslt = 0;
7098
7099 /* Look for EFS ^/ */
7100 if (decc_efs_charset) {
7101 while (cp1 != NULL) {
7102 cp2 = cp1 - 1;
7103 if (*cp2 != '^') {
7104 /* Found illegal VMS, assume UNIX */
7105 cmp_rslt = 1;
7106 break;
7107 }
7108 cp1++;
7109 cp1 = strchr(cp1, '/');
7110 }
7111 }
7112
7113 /* Look for "." and ".." */
7114 if (decc_filename_unix_report) {
7115 if (spec[0] == '.') {
7116 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7117 cmp_rslt = 1;
7118 }
7119 else {
7120 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7121 cmp_rslt = 1;
7122 }
7123 }
7124 }
7125 }
7126 /* This is already UNIX or at least nothing VMS understands */
7127 if (cmp_rslt) {
a0d0e21e 7128 strcpy(rslt,spec);
0e5ce2c7
JM
7129 if (vms_debug_fileify) {
7130 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7131 }
a0d0e21e
LW
7132 return rslt;
7133 }
7134
7135 cp1 = rslt;
7136 cp2 = spec;
7137 dirend = strrchr(spec,']');
7138 if (dirend == NULL) dirend = strrchr(spec,'>');
7139 if (dirend == NULL) dirend = strchr(spec,':');
7140 if (dirend == NULL) {
7141 strcpy(rslt,spec);
0e5ce2c7
JM
7142 if (vms_debug_fileify) {
7143 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7144 }
a0d0e21e
LW
7145 return rslt;
7146 }
f7ddb74a
JM
7147
7148 /* Special case 1 - sys$posix_root = / */
7149#if __CRTL_VER >= 70000000
7150 if (!decc_disable_posix_root) {
7151 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7152 *cp1 = '/';
7153 cp1++;
7154 cp2 = cp2 + 15;
7155 }
7156 }
7157#endif
7158
7159 /* Special case 2 - Convert NLA0: to /dev/null */
7160#if __CRTL_VER < 70000000
7161 cmp_rslt = strncmp(spec,"NLA0:", 5);
7162 if (cmp_rslt != 0)
7163 cmp_rslt = strncmp(spec,"nla0:", 5);
7164#else
7165 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7166#endif
7167 if (cmp_rslt == 0) {
7168 strcpy(rslt, "/dev/null");
7169 cp1 = cp1 + 9;
7170 cp2 = cp2 + 5;
7171 if (spec[6] != '\0') {
7172 cp1[9] == '/';
7173 cp1++;
7174 cp2++;
7175 }
7176 }
7177
7178 /* Also handle special case "SYS$SCRATCH:" */
7179#if __CRTL_VER < 70000000
7180 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7181 if (cmp_rslt != 0)
7182 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7183#else
7184 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7185#endif
c5375c28 7186 tmp = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 7187 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
7188 if (cmp_rslt == 0) {
7189 int islnm;
7190
b8486b9d 7191 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
f7ddb74a
JM
7192 if (!islnm) {
7193 strcpy(rslt, "/tmp");
7194 cp1 = cp1 + 4;
7195 cp2 = cp2 + 12;
7196 if (spec[12] != '\0') {
7197 cp1[4] == '/';
7198 cp1++;
7199 cp2++;
7200 }
7201 }
7202 }
7203
a5f75d66 7204 if (*cp2 != '[' && *cp2 != '<') {
a0d0e21e
LW
7205 *(cp1++) = '/';
7206 }
7207 else { /* the VMS spec begins with directories */
7208 cp2++;
a5f75d66 7209 if (*cp2 == ']' || *cp2 == '>') {
f86702cc 7210 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
367e4b85 7211 PerlMem_free(tmp);
a5f75d66
AD
7212 return rslt;
7213 }
f7ddb74a 7214 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
2f4077ca 7215 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
367e4b85 7216 PerlMem_free(tmp);
0e5ce2c7
JM
7217 if (vms_debug_fileify) {
7218 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7219 }
a0d0e21e
LW
7220 return NULL;
7221 }
2d9f3838 7222 trnlnm_iter_count = 0;
a0d0e21e
LW
7223 do {
7224 cp3 = tmp;
7225 while (*cp3 != ':' && *cp3) cp3++;
7226 *(cp3++) = '\0';
7227 if (strchr(cp3,']') != NULL) break;
2d9f3838
CB
7228 trnlnm_iter_count++;
7229 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
f675dbe5 7230 } while (vmstrnenv(tmp,tmp,0,fildev,0));
0e5ce2c7 7231 cp1 = rslt;
f86702cc 7232 cp3 = tmp;
7233 *(cp1++) = '/';
7234 while (*cp3) {
7235 *(cp1++) = *(cp3++);
0e5ce2c7 7236 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
367e4b85 7237 PerlMem_free(tmp);
0e5ce2c7
JM
7238 set_errno(ENAMETOOLONG);
7239 set_vaxc_errno(SS$_BUFFEROVF);
7240 if (vms_debug_fileify) {
7241 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7242 }
2f4077ca
JM
7243 return NULL; /* No room */
7244 }
a0d0e21e 7245 }
f86702cc 7246 *(cp1++) = '/';
7247 }
f7ddb74a
JM
7248 if ((*cp2 == '^')) {
7249 /* EFS file escape, pass the next character as is */
38a44b82 7250 /* Fix me: HEX encoding for Unicode not implemented */
f7ddb74a
JM
7251 cp2++;
7252 }
f86702cc 7253 else if ( *cp2 == '.') {
7254 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7255 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7256 cp2 += 3;
7257 }
7258 else cp2++;
a0d0e21e 7259 }
a0d0e21e 7260 }
367e4b85 7261 PerlMem_free(tmp);
a0d0e21e 7262 for (; cp2 <= dirend; cp2++) {
f7ddb74a
JM
7263 if ((*cp2 == '^')) {
7264 /* EFS file escape, pass the next character as is */
38a44b82 7265 /* Fix me: HEX encoding for Unicode not implemented */
42cd432e
CB
7266 *(cp1++) = *(++cp2);
7267 /* An escaped dot stays as is -- don't convert to slash */
7268 if (*cp2 == '.') cp2++;
f7ddb74a 7269 }
a0d0e21e
LW
7270 if (*cp2 == ':') {
7271 *(cp1++) = '/';
5ad5b34c 7272 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
a0d0e21e 7273 }
f86702cc 7274 else if (*cp2 == ']' || *cp2 == '>') {
7275 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7276 }
f7ddb74a 7277 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
a0d0e21e 7278 *(cp1++) = '/';
e518068a 7279 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7280 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7281 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7282 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7283 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7284 }
f86702cc 7285 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7286 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7287 cp2 += 2;
7288 }
a0d0e21e
LW
7289 }
7290 else if (*cp2 == '-') {
7291 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7292 while (*cp2 == '-') {
7293 cp2++;
7294 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7295 }
7296 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
0e5ce2c7 7297 /* filespecs like */
01b8edb6 7298 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
0e5ce2c7
JM
7299 if (vms_debug_fileify) {
7300 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7301 }
a0d0e21e
LW
7302 return NULL;
7303 }
a0d0e21e
LW
7304 }
7305 else *(cp1++) = *cp2;
7306 }
7307 else *(cp1++) = *cp2;
7308 }
0e5ce2c7 7309 /* Translate the rest of the filename. */
42cd432e 7310 while (*cp2) {
0e5ce2c7
JM
7311 int dot_seen;
7312 dot_seen = 0;
7313 switch(*cp2) {
7314 /* Fixme - for compatibility with the CRTL we should be removing */
7315 /* spaces from the file specifications, but this may show that */
7316 /* some tests that were appearing to pass are not really passing */
7317 case '%':
7318 cp2++;
7319 *(cp1++) = '?';
7320 break;
7321 case '^':
7322 /* Fix me hex expansions not implemented */
7323 cp2++; /* '^.' --> '.' and other. */
7324 if (*cp2) {
7325 if (*cp2 == '_') {
7326 cp2++;
7327 *(cp1++) = ' ';
7328 } else {
7329 *(cp1++) = *(cp2++);
7330 }
7331 }
7332 break;
7333 case ';':
7334 if (decc_filename_unix_no_version) {
7335 /* Easy, drop the version */
7336 while (*cp2)
7337 cp2++;
7338 break;
7339 } else {
7340 /* Punt - passing the version as a dot will probably */
7341 /* break perl in weird ways, but so did passing */
7342 /* through the ; as a version. Follow the CRTL and */
7343 /* hope for the best. */
7344 cp2++;
7345 *(cp1++) = '.';
7346 }
7347 break;
7348 case '.':
7349 if (dot_seen) {
7350 /* We will need to fix this properly later */
7351 /* As Perl may be installed on an ODS-5 volume, but not */
7352 /* have the EFS_CHARSET enabled, it still may encounter */
7353 /* filenames with extra dots in them, and a precedent got */
7354 /* set which allowed them to work, that we will uphold here */
7355 /* If extra dots are present in a name and no ^ is on them */
7356 /* VMS assumes that the first one is the extension delimiter */
7357 /* the rest have an implied ^. */
7358
7359 /* this is also a conflict as the . is also a version */
7360 /* delimiter in VMS, */
7361
7362 *(cp1++) = *(cp2++);
7363 break;
7364 }
7365 dot_seen = 1;
7366 /* This is an extension */
7367 if (decc_readdir_dropdotnotype) {
7368 cp2++;
7369 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7370 /* Drop the dot for the extension */
7371 break;
7372 } else {
7373 *(cp1++) = '.';
7374 }
7375 break;
7376 }
7377 default:
7378 *(cp1++) = *(cp2++);
7379 }
42cd432e 7380 }
a0d0e21e
LW
7381 *cp1 = '\0';
7382
f7ddb74a
JM
7383 /* This still leaves /000000/ when working with a
7384 * VMS device root or concealed root.
7385 */
7386 {
7387 int ulen;
7388 char * zeros;
7389
7390 ulen = strlen(rslt);
7391
7392 /* Get rid of "000000/ in rooted filespecs */
7393 if (ulen > 7) {
7394 zeros = strstr(rslt, "/000000/");
7395 if (zeros != NULL) {
7396 int mlen;
7397 mlen = ulen - (zeros - rslt) - 7;
7398 memmove(zeros, &zeros[7], mlen);
7399 ulen = ulen - 7;
7400 rslt[ulen] = '\0';
7401 }
7402 }
7403 }
7404
0e5ce2c7
JM
7405 if (vms_debug_fileify) {
7406 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7407 }
a0d0e21e
LW
7408 return rslt;
7409
0e5ce2c7
JM
7410} /* end of int_tounixspec() */
7411
7412
7413/*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7414static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7415{
7416 static char __tounixspec_retbuf[VMS_MAXRSS];
7417 char * unixspec, *ret_spec, *ret_buf;
7418
7419 unixspec = NULL;
7420 ret_buf = buf;
7421 if (ret_buf == NULL) {
7422 if (ts) {
7423 Newx(unixspec, VMS_MAXRSS, char);
7424 if (unixspec == NULL)
7425 _ckvmssts(SS$_INSFMEM);
7426 ret_buf = unixspec;
7427 } else {
7428 ret_buf = __tounixspec_retbuf;
7429 }
7430 }
7431
7432 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7433
7434 if (ret_spec == NULL) {
7435 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7436 if (unixspec)
7437 Safefree(unixspec);
7438 }
7439
7440 return ret_spec;
7441
a0d0e21e
LW
7442} /* end of do_tounixspec() */
7443/*}}}*/
7444/* External entry points */
360732b5
JM
7445char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7446 { return do_tounixspec(spec,buf,0, NULL); }
7447char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7448 { return do_tounixspec(spec,buf,1, NULL); }
7449char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7450 { return do_tounixspec(spec,buf,0, utf8_fl); }
7451char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7452 { return do_tounixspec(spec,buf,1, utf8_fl); }
a0d0e21e 7453
360732b5 7454#if __CRTL_VER >= 70200000 && !defined(__VAX)
2497a41f 7455
360732b5
JM
7456/*
7457 This procedure is used to identify if a path is based in either
7458 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7459 it returns the OpenVMS format directory for it.
7460
7461 It is expecting specifications of only '/' or '/xxxx/'
7462
7463 If a posix root does not exist, or 'xxxx' is not a directory
7464 in the posix root, it returns a failure.
7465
7466 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7467
7468 It is used only internally by posix_to_vmsspec_hardway().
7469 */
7470
7471static int posix_root_to_vms
7472 (char *vmspath, int vmspath_len,
7473 const char *unixpath,
d584a1c6
JM
7474 const int * utf8_fl)
7475{
2497a41f
JM
7476int sts;
7477struct FAB myfab = cc$rms_fab;
d584a1c6 7478rms_setup_nam(mynam);
2497a41f 7479struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
d584a1c6
JM
7480struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7481char * esa, * esal, * rsa, * rsal;
2497a41f
JM
7482char *vms_delim;
7483int dir_flag;
7484int unixlen;
7485
360732b5 7486 dir_flag = 0;
d584a1c6 7487 vmspath[0] = '\0';
360732b5
JM
7488 unixlen = strlen(unixpath);
7489 if (unixlen == 0) {
360732b5
JM
7490 return RMS$_FNF;
7491 }
7492
7493#if __CRTL_VER >= 80200000
2497a41f 7494 /* If not a posix spec already, convert it */
360732b5
JM
7495 if (decc_posix_compliant_pathnames) {
7496 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7497 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7498 }
7499 else {
7500 /* This is already a VMS specification, no conversion */
7501 unixlen--;
7502 strncpy(vmspath,unixpath, vmspath_len);
7503 }
2497a41f 7504 }
360732b5
JM
7505 else
7506#endif
7507 {
7508 int path_len;
7509 int i,j;
7510
7511 /* Check to see if this is under the POSIX root */
7512 if (decc_disable_posix_root) {
7513 return RMS$_FNF;
7514 }
7515
7516 /* Skip leading / */
7517 if (unixpath[0] == '/') {
7518 unixpath++;
7519 unixlen--;
7520 }
7521
7522
7523 strcpy(vmspath,"SYS$POSIX_ROOT:");
7524
7525 /* If this is only the / , or blank, then... */
7526 if (unixpath[0] == '\0') {
7527 /* by definition, this is the answer */
7528 return SS$_NORMAL;
7529 }
7530
7531 /* Need to look up a directory */
7532 vmspath[15] = '[';
7533 vmspath[16] = '\0';
7534
7535 /* Copy and add '^' escape characters as needed */
7536 j = 16;
7537 i = 0;
7538 while (unixpath[i] != 0) {
7539 int k;
7540
7541 j += copy_expand_unix_filename_escape
7542 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7543 i += k;
7544 }
7545
7546 path_len = strlen(vmspath);
7547 if (vmspath[path_len - 1] == '/')
7548 path_len--;
7549 vmspath[path_len] = ']';
7550 path_len++;
7551 vmspath[path_len] = '\0';
7552
2497a41f
JM
7553 }
7554 vmspath[vmspath_len] = 0;
7555 if (unixpath[unixlen - 1] == '/')
7556 dir_flag = 1;
d584a1c6
JM
7557 esal = PerlMem_malloc(VMS_MAXRSS);
7558 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7559 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
c5375c28 7560 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
7561 rsal = PerlMem_malloc(VMS_MAXRSS);
7562 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7563 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7564 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7565 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7566 rms_bind_fab_nam(myfab, mynam);
7567 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7568 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
2497a41f
JM
7569 if (decc_efs_case_preserve)
7570 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
ea0c9945 7571#ifdef NAML$M_OPEN_SPECIAL
2497a41f 7572 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
ea0c9945 7573#endif
2497a41f
JM
7574
7575 /* Set up the remaining naml fields */
7576 sts = sys$parse(&myfab);
7577
7578 /* It failed! Try again as a UNIX filespec */
7579 if (!(sts & 1)) {
d584a1c6 7580 PerlMem_free(esal);
367e4b85 7581 PerlMem_free(esa);
d584a1c6
JM
7582 PerlMem_free(rsal);
7583 PerlMem_free(rsa);
2497a41f
JM
7584 return sts;
7585 }
7586
7587 /* get the Device ID and the FID */
7588 sts = sys$search(&myfab);
d584a1c6
JM
7589
7590 /* These are no longer needed */
7591 PerlMem_free(esa);
7592 PerlMem_free(rsal);
7593 PerlMem_free(rsa);
7594
2497a41f
JM
7595 /* on any failure, returned the POSIX ^UP^ filespec */
7596 if (!(sts & 1)) {
d584a1c6 7597 PerlMem_free(esal);
2497a41f
JM
7598 return sts;
7599 }
7600 specdsc.dsc$a_pointer = vmspath;
7601 specdsc.dsc$w_length = vmspath_len;
7602
7603 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7604 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7605 sts = lib$fid_to_name
7606 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7607
7608 /* on any failure, returned the POSIX ^UP^ filespec */
7609 if (!(sts & 1)) {
7610 /* This can happen if user does not have permission to read directories */
7611 if (strncmp(unixpath,"\"^UP^",5) != 0)
7612 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7613 else
7614 strcpy(vmspath, unixpath);
7615 }
7616 else {
7617 vmspath[specdsc.dsc$w_length] = 0;
7618
7619 /* Are we expecting a directory? */
7620 if (dir_flag != 0) {
7621 int i;
7622 char *eptr;
7623
7624 eptr = NULL;
7625
7626 i = specdsc.dsc$w_length - 1;
7627 while (i > 0) {
7628 int zercnt;
7629 zercnt = 0;
7630 /* Version must be '1' */
7631 if (vmspath[i--] != '1')
7632 break;
7633 /* Version delimiter is one of ".;" */
7634 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7635 break;
7636 i--;
7637 if (vmspath[i--] != 'R')
7638 break;
7639 if (vmspath[i--] != 'I')
7640 break;
7641 if (vmspath[i--] != 'D')
7642 break;
7643 if (vmspath[i--] != '.')
7644 break;
7645 eptr = &vmspath[i+1];
7646 while (i > 0) {
7647 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7648 if (vmspath[i-1] != '^') {
7649 if (zercnt != 6) {
7650 *eptr = vmspath[i];
7651 eptr[1] = '\0';
7652 vmspath[i] = '.';
7653 break;
7654 }
7655 else {
7656 /* Get rid of 6 imaginary zero directory filename */
7657 vmspath[i+1] = '\0';
7658 }
7659 }
7660 }
7661 if (vmspath[i] == '0')
7662 zercnt++;
7663 else
7664 zercnt = 10;
7665 i--;
7666 }
7667 break;
7668 }
7669 }
7670 }
d584a1c6 7671 PerlMem_free(esal);
2497a41f
JM
7672 return sts;
7673}
7674
360732b5
JM
7675/* /dev/mumble needs to be handled special.
7676 /dev/null becomes NLA0:, And there is the potential for other stuff
7677 like /dev/tty which may need to be mapped to something.
7678*/
7679
7680static int
7681slash_dev_special_to_vms
7682 (const char * unixptr,
7683 char * vmspath,
7684 int vmspath_len)
7685{
7686char * nextslash;
7687int len;
7688int cmp;
7689int islnm;
7690
7691 unixptr += 4;
7692 nextslash = strchr(unixptr, '/');
7693 len = strlen(unixptr);
7694 if (nextslash != NULL)
7695 len = nextslash - unixptr;
7696 cmp = strncmp("null", unixptr, 5);
7697 if (cmp == 0) {
7698 if (vmspath_len >= 6) {
7699 strcpy(vmspath, "_NLA0:");
7700 return SS$_NORMAL;
7701 }
7702 }
7703}
7704
7705
7706/* The built in routines do not understand perl's special needs, so
7707 doing a manual conversion from UNIX to VMS
7708
7709 If the utf8_fl is not null and points to a non-zero value, then
7710 treat 8 bit characters as UTF-8.
7711
7712 The sequence starting with '$(' and ending with ')' will be passed
7713 through with out interpretation instead of being escaped.
7714
7715 */
2497a41f 7716static int posix_to_vmsspec_hardway
360732b5
JM
7717 (char *vmspath, int vmspath_len,
7718 const char *unixpath,
7719 int dir_flag,
7720 int * utf8_fl) {
2497a41f
JM
7721
7722char *esa;
7723const char *unixptr;
360732b5 7724const char *unixend;
2497a41f
JM
7725char *vmsptr;
7726const char *lastslash;
7727const char *lastdot;
7728int unixlen;
7729int vmslen;
7730int dir_start;
7731int dir_dot;
7732int quoted;
360732b5
JM
7733char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7734int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
2497a41f 7735
360732b5
JM
7736 if (utf8_fl != NULL)
7737 *utf8_fl = 0;
2497a41f
JM
7738
7739 unixptr = unixpath;
7740 dir_dot = 0;
7741
7742 /* Ignore leading "/" characters */
7743 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7744 unixptr++;
7745 }
7746 unixlen = strlen(unixptr);
7747
7748 /* Do nothing with blank paths */
7749 if (unixlen == 0) {
7750 vmspath[0] = '\0';
7751 return SS$_NORMAL;
7752 }
7753
360732b5
JM
7754 quoted = 0;
7755 /* This could have a "^UP^ on the front */
7756 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7757 quoted = 1;
7758 unixptr+= 5;
7759 unixlen-= 5;
7760 }
7761
2497a41f
JM
7762 lastslash = strrchr(unixptr,'/');
7763 lastdot = strrchr(unixptr,'.');
360732b5
JM
7764 unixend = strrchr(unixptr,'\"');
7765 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7766 unixend = unixptr + unixlen;
7767 }
2497a41f
JM
7768
7769 /* last dot is last dot or past end of string */
7770 if (lastdot == NULL)
7771 lastdot = unixptr + unixlen;
7772
7773 /* if no directories, set last slash to beginning of string */
7774 if (lastslash == NULL) {
7775 lastslash = unixptr;
7776 }
7777 else {
7778 /* Watch out for trailing "." after last slash, still a directory */
7779 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7780 lastslash = unixptr + unixlen;
7781 }
7782
7783 /* Watch out for traiing ".." after last slash, still a directory */
7784 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7785 lastslash = unixptr + unixlen;
7786 }
7787
7788 /* dots in directories are aways escaped */
7789 if (lastdot < lastslash)
7790 lastdot = unixptr + unixlen;
7791 }
7792
7793 /* if (unixptr < lastslash) then we are in a directory */
7794
7795 dir_start = 0;
2497a41f
JM
7796
7797 vmsptr = vmspath;
7798 vmslen = 0;
7799
2497a41f
JM
7800 /* Start with the UNIX path */
7801 if (*unixptr != '/') {
7802 /* relative paths */
360732b5
JM
7803
7804 /* If allowing logical names on relative pathnames, then handle here */
7805 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7806 !decc_posix_compliant_pathnames) {
7807 char * nextslash;
7808 int seg_len;
7809 char * trn;
7810 int islnm;
7811
7812 /* Find the next slash */
7813 nextslash = strchr(unixptr,'/');
7814
7815 esa = PerlMem_malloc(vmspath_len);
7816 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7817
7818 trn = PerlMem_malloc(VMS_MAXRSS);
7819 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7820
7821 if (nextslash != NULL) {
7822
7823 seg_len = nextslash - unixptr;
7824 strncpy(esa, unixptr, seg_len);
7825 esa[seg_len] = 0;
7826 }
7827 else {
7828 strcpy(esa, unixptr);
7829 seg_len = strlen(unixptr);
7830 }
7831 /* trnlnm(section) */
7832 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7833
7834 if (islnm) {
7835 /* Now fix up the directory */
7836
7837 /* Split up the path to find the components */
7838 sts = vms_split_path
7839 (trn,
7840 &v_spec,
7841 &v_len,
7842 &r_spec,
7843 &r_len,
7844 &d_spec,
7845 &d_len,
7846 &n_spec,
7847 &n_len,
7848 &e_spec,
7849 &e_len,
7850 &vs_spec,
7851 &vs_len);
7852
7853 while (sts == 0) {
7854 char * strt;
7855 int cmp;
7856
7857 /* A logical name must be a directory or the full
7858 specification. It is only a full specification if
7859 it is the only component */
7860 if ((unixptr[seg_len] == '\0') ||
7861 (unixptr[seg_len+1] == '\0')) {
7862
7863 /* Is a directory being required? */
7864 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7865 /* Not a logical name */
7866 break;
7867 }
7868
7869
7870 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7871 /* This must be a directory */
7872 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7873 strcpy(vmsptr, esa);
7874 vmslen=strlen(vmsptr);
7875 vmsptr[vmslen] = ':';
7876 vmslen++;
7877 vmsptr[vmslen] = '\0';
7878 return SS$_NORMAL;
7879 }
7880 }
7881
7882 }
7883
7884
7885 /* must be dev/directory - ignore version */
7886 if ((n_len + e_len) != 0)
7887 break;
7888
7889 /* transfer the volume */
7890 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7891 strncpy(vmsptr, v_spec, v_len);
7892 vmsptr += v_len;
7893 vmsptr[0] = '\0';
7894 vmslen += v_len;
7895 }
7896
7897 /* unroot the rooted directory */
7898 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7899 r_spec[0] = '[';
7900 r_spec[r_len - 1] = ']';
7901
7902 /* This should not be there, but nothing is perfect */
7903 if (r_len > 9) {
7904 cmp = strcmp(&r_spec[1], "000000.");
7905 if (cmp == 0) {
7906 r_spec += 7;
7907 r_spec[7] = '[';
7908 r_len -= 7;
7909 if (r_len == 2)
7910 r_len = 0;
7911 }
7912 }
7913 if (r_len > 0) {
7914 strncpy(vmsptr, r_spec, r_len);
7915 vmsptr += r_len;
7916 vmslen += r_len;
7917 vmsptr[0] = '\0';
7918 }
7919 }
7920 /* Bring over the directory. */
7921 if ((d_len > 0) &&
7922 ((d_len + vmslen) < vmspath_len)) {
7923 d_spec[0] = '[';
7924 d_spec[d_len - 1] = ']';
7925 if (d_len > 9) {
7926 cmp = strcmp(&d_spec[1], "000000.");
7927 if (cmp == 0) {
7928 d_spec += 7;
7929 d_spec[7] = '[';
7930 d_len -= 7;
7931 if (d_len == 2)
7932 d_len = 0;
7933 }
7934 }
7935
7936 if (r_len > 0) {
7937 /* Remove the redundant root */
7938 if (r_len > 0) {
7939 /* remove the ][ */
7940 vmsptr--;
7941 vmslen--;
7942 d_spec++;
7943 d_len--;
7944 }
7945 strncpy(vmsptr, d_spec, d_len);
7946 vmsptr += d_len;
7947 vmslen += d_len;
7948 vmsptr[0] = '\0';
7949 }
7950 }
7951 break;
7952 }
7953 }
7954
7955 PerlMem_free(esa);
7956 PerlMem_free(trn);
7957 }
7958
2497a41f
JM
7959 if (lastslash > unixptr) {
7960 int dotdir_seen;
7961
7962 /* skip leading ./ */
7963 dotdir_seen = 0;
7964 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7965 dotdir_seen = 1;
7966 unixptr++;
7967 unixptr++;
7968 }
7969
7970 /* Are we still in a directory? */
7971 if (unixptr <= lastslash) {
7972 *vmsptr++ = '[';
7973 vmslen = 1;
7974 dir_start = 1;
7975
7976 /* if not backing up, then it is relative forward. */
7977 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
360732b5 7978 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
2497a41f
JM
7979 *vmsptr++ = '.';
7980 vmslen++;
7981 dir_dot = 1;
360732b5 7982 }
2497a41f
JM
7983 }
7984 else {
7985 if (dotdir_seen) {
7986 /* Perl wants an empty directory here to tell the difference
7987 * between a DCL commmand and a filename
7988 */
7989 *vmsptr++ = '[';
7990 *vmsptr++ = ']';
7991 vmslen = 2;
7992 }
7993 }
7994 }
7995 else {
7996 /* Handle two special files . and .. */
7997 if (unixptr[0] == '.') {
360732b5 7998 if (&unixptr[1] == unixend) {
2497a41f
JM
7999 *vmsptr++ = '[';
8000 *vmsptr++ = ']';
8001 vmslen += 2;
8002 *vmsptr++ = '\0';
8003 return SS$_NORMAL;
8004 }
360732b5 8005 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
2497a41f
JM
8006 *vmsptr++ = '[';
8007 *vmsptr++ = '-';
8008 *vmsptr++ = ']';
8009 vmslen += 3;
8010 *vmsptr++ = '\0';
8011 return SS$_NORMAL;
8012 }
8013 }
8014 }
8015 }
8016 else { /* Absolute PATH handling */
8017 int sts;
8018 char * nextslash;
8019 int seg_len;
8020 /* Need to find out where root is */
8021
8022 /* In theory, this procedure should never get an absolute POSIX pathname
8023 * that can not be found on the POSIX root.
8024 * In practice, that can not be relied on, and things will show up
8025 * here that are a VMS device name or concealed logical name instead.
8026 * So to make things work, this procedure must be tolerant.
8027 */
c5375c28
JM
8028 esa = PerlMem_malloc(vmspath_len);
8029 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2497a41f
JM
8030
8031 sts = SS$_NORMAL;
8032 nextslash = strchr(&unixptr[1],'/');
8033 seg_len = 0;
8034 if (nextslash != NULL) {
360732b5 8035 int cmp;
2497a41f
JM
8036 seg_len = nextslash - &unixptr[1];
8037 strncpy(vmspath, unixptr, seg_len + 1);
8038 vmspath[seg_len+1] = 0;
360732b5
JM
8039 cmp = 1;
8040 if (seg_len == 3) {
8041 cmp = strncmp(vmspath, "dev", 4);
8042 if (cmp == 0) {
8043 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8044 if (sts = SS$_NORMAL)
8045 return SS$_NORMAL;
8046 }
8047 }
8048 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
2497a41f
JM
8049 }
8050
360732b5 8051 if ($VMS_STATUS_SUCCESS(sts)) {
2497a41f
JM
8052 /* This is verified to be a real path */
8053
360732b5
JM
8054 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8055 if ($VMS_STATUS_SUCCESS(sts)) {
8056 strcpy(vmspath, esa);
8057 vmslen = strlen(vmspath);
8058 vmsptr = vmspath + vmslen;
8059 unixptr++;
8060 if (unixptr < lastslash) {
8061 char * rptr;
8062 vmsptr--;
8063 *vmsptr++ = '.';
8064 dir_start = 1;
8065 dir_dot = 1;
8066 if (vmslen > 7) {
8067 int cmp;
8068 rptr = vmsptr - 7;
8069 cmp = strcmp(rptr,"000000.");
8070 if (cmp == 0) {
8071 vmslen -= 7;
8072 vmsptr -= 7;
8073 vmsptr[1] = '\0';
8074 } /* removing 6 zeros */
8075 } /* vmslen < 7, no 6 zeros possible */
8076 } /* Not in a directory */
8077 } /* Posix root found */
8078 else {
8079 /* No posix root, fall back to default directory */
8080 strcpy(vmspath, "SYS$DISK:[");
8081 vmsptr = &vmspath[10];
8082 vmslen = 10;
8083 if (unixptr > lastslash) {
8084 *vmsptr = ']';
8085 vmsptr++;
8086 vmslen++;
8087 }
8088 else {
8089 dir_start = 1;
8090 }
8091 }
2497a41f
JM
8092 } /* end of verified real path handling */
8093 else {
8094 int add_6zero;
8095 int islnm;
8096
8097 /* Ok, we have a device or a concealed root that is not in POSIX
8098 * or we have garbage. Make the best of it.
8099 */
8100
8101 /* Posix to VMS destroyed this, so copy it again */
8102 strncpy(vmspath, &unixptr[1], seg_len);
8103 vmspath[seg_len] = 0;
8104 vmslen = seg_len;
8105 vmsptr = &vmsptr[vmslen];
8106 islnm = 0;
8107
8108 /* Now do we need to add the fake 6 zero directory to it? */
8109 add_6zero = 1;
8110 if ((*lastslash == '/') && (nextslash < lastslash)) {
8111 /* No there is another directory */
8112 add_6zero = 0;
8113 }
8114 else {
8115 int trnend;
360732b5 8116 int cmp;
2497a41f
JM
8117
8118 /* now we have foo:bar or foo:[000000]bar to decide from */
7ded3206 8119 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
360732b5
JM
8120
8121 if (!islnm && !decc_posix_compliant_pathnames) {
8122
8123 cmp = strncmp("bin", vmspath, 4);
8124 if (cmp == 0) {
8125 /* bin => SYS$SYSTEM: */
8126 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8127 }
8128 else {
8129 /* tmp => SYS$SCRATCH: */
8130 cmp = strncmp("tmp", vmspath, 4);
8131 if (cmp == 0) {
8132 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8133 }
8134 }
8135 }
8136
7ded3206 8137 trnend = islnm ? islnm - 1 : 0;
2497a41f
JM
8138
8139 /* if this was a logical name, ']' or '>' must be present */
8140 /* if not a logical name, then assume a device and hope. */
8141 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8142
8143 /* if log name and trailing '.' then rooted - treat as device */
8144 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8145
8146 /* Fix me, if not a logical name, a device lookup should be
8147 * done to see if the device is file structured. If the device
8148 * is not file structured, the 6 zeros should not be put on.
8149 *
8150 * As it is, perl is occasionally looking for dev:[000000]tty.
8151 * which looks a little strange.
360732b5
JM
8152 *
8153 * Not that easy to detect as "/dev" may be file structured with
8154 * special device files.
2497a41f
JM
8155 */
8156
30e68285 8157 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
360732b5 8158 (&nextslash[1] == unixend)) {
2497a41f
JM
8159 /* No real directory present */
8160 add_6zero = 1;
8161 }
8162 }
8163
8164 /* Put the device delimiter on */
8165 *vmsptr++ = ':';
8166 vmslen++;
8167 unixptr = nextslash;
8168 unixptr++;
8169
8170 /* Start directory if needed */
8171 if (!islnm || add_6zero) {
8172 *vmsptr++ = '[';
8173 vmslen++;
8174 dir_start = 1;
8175 }
8176
8177 /* add fake 000000] if needed */
8178 if (add_6zero) {
8179 *vmsptr++ = '0';
8180 *vmsptr++ = '0';
8181 *vmsptr++ = '0';
8182 *vmsptr++ = '0';
8183 *vmsptr++ = '0';
8184 *vmsptr++ = '0';
8185 *vmsptr++ = ']';
8186 vmslen += 7;
8187 dir_start = 0;
8188 }
8189
8190 } /* non-POSIX translation */
367e4b85 8191 PerlMem_free(esa);
2497a41f
JM
8192 } /* End of relative/absolute path handling */
8193
360732b5 8194 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
2497a41f 8195 int dash_flag;
360732b5
JM
8196 int in_cnt;
8197 int out_cnt;
2497a41f
JM
8198
8199 dash_flag = 0;
8200
8201 if (dir_start != 0) {
8202
8203 /* First characters in a directory are handled special */
8204 while ((*unixptr == '/') ||
8205 ((*unixptr == '.') &&
360732b5
JM
8206 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8207 (&unixptr[1]==unixend)))) {
2497a41f
JM
8208 int loop_flag;
8209
8210 loop_flag = 0;
8211
8212 /* Skip redundant / in specification */
8213 while ((*unixptr == '/') && (dir_start != 0)) {
8214 loop_flag = 1;
8215 unixptr++;
8216 if (unixptr == lastslash)
8217 break;
8218 }
8219 if (unixptr == lastslash)
8220 break;
8221
8222 /* Skip redundant ./ characters */
8223 while ((*unixptr == '.') &&
360732b5 8224 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
2497a41f
JM
8225 loop_flag = 1;
8226 unixptr++;
8227 if (unixptr == lastslash)
8228 break;
8229 if (*unixptr == '/')
8230 unixptr++;
8231 }
8232 if (unixptr == lastslash)
8233 break;
8234
8235 /* Skip redundant ../ characters */
8236 while ((*unixptr == '.') && (unixptr[1] == '.') &&
360732b5 8237 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
2497a41f
JM
8238 /* Set the backing up flag */
8239 loop_flag = 1;
8240 dir_dot = 0;
8241 dash_flag = 1;
8242 *vmsptr++ = '-';
8243 vmslen++;
8244 unixptr++; /* first . */
8245 unixptr++; /* second . */
8246 if (unixptr == lastslash)
8247 break;
8248 if (*unixptr == '/') /* The slash */
8249 unixptr++;
8250 }
8251 if (unixptr == lastslash)
8252 break;
8253
8254 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8255 /* Not needed when VMS is pretending to be UNIX. */
8256
8257 /* Is this loop stuck because of too many dots? */
8258 if (loop_flag == 0) {
8259 /* Exit the loop and pass the rest through */
8260 break;
8261 }
8262 }
8263
8264 /* Are we done with directories yet? */
8265 if (unixptr >= lastslash) {
8266
8267 /* Watch out for trailing dots */
8268 if (dir_dot != 0) {
8269 vmslen --;
8270 vmsptr--;
8271 }
8272 *vmsptr++ = ']';
8273 vmslen++;
8274 dash_flag = 0;
8275 dir_start = 0;
8276 if (*unixptr == '/')
8277 unixptr++;
8278 }
8279 else {
8280 /* Have we stopped backing up? */
8281 if (dash_flag) {
8282 *vmsptr++ = '.';
8283 vmslen++;
8284 dash_flag = 0;
8285 /* dir_start continues to be = 1 */
8286 }
8287 if (*unixptr == '-') {
8288 *vmsptr++ = '^';
8289 *vmsptr++ = *unixptr++;
8290 vmslen += 2;
8291 dir_start = 0;
8292
8293 /* Now are we done with directories yet? */
8294 if (unixptr >= lastslash) {
8295
8296 /* Watch out for trailing dots */
8297 if (dir_dot != 0) {
8298 vmslen --;
8299 vmsptr--;
8300 }
8301
8302 *vmsptr++ = ']';
8303 vmslen++;
8304 dash_flag = 0;
8305 dir_start = 0;
8306 }
8307 }
8308 }
8309 }
8310
8311 /* All done? */
360732b5 8312 if (unixptr >= unixend)
2497a41f
JM
8313 break;
8314
8315 /* Normal characters - More EFS work probably needed */
8316 dir_start = 0;
8317 dir_dot = 0;
8318
8319 switch(*unixptr) {
8320 case '/':
8321 /* remove multiple / */
8322 while (unixptr[1] == '/') {
8323 unixptr++;
8324 }
8325 if (unixptr == lastslash) {
8326 /* Watch out for trailing dots */
8327 if (dir_dot != 0) {
8328 vmslen --;
8329 vmsptr--;
8330 }
8331 *vmsptr++ = ']';
8332 }
8333 else {
8334 dir_start = 1;
8335 *vmsptr++ = '.';
8336 dir_dot = 1;
8337
8338 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8339 /* Not needed when VMS is pretending to be UNIX. */
8340
8341 }
8342 dash_flag = 0;
360732b5 8343 if (unixptr != unixend)
2497a41f
JM
8344 unixptr++;
8345 vmslen++;
8346 break;
2497a41f 8347 case '.':
360732b5
JM
8348 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8349 (&unixptr[1] == unixend)) {
2497a41f
JM
8350 *vmsptr++ = '^';
8351 *vmsptr++ = '.';
8352 vmslen += 2;
8353 unixptr++;
8354
8355 /* trailing dot ==> '^..' on VMS */
360732b5 8356 if (unixptr == unixend) {
2497a41f
JM
8357 *vmsptr++ = '.';
8358 vmslen++;
360732b5 8359 unixptr++;
2497a41f 8360 }
2497a41f
JM
8361 break;
8362 }
360732b5 8363
2497a41f 8364 *vmsptr++ = *unixptr++;
360732b5
JM
8365 vmslen ++;
8366 break;
8367 case '"':
8368 if (quoted && (&unixptr[1] == unixend)) {
8369 unixptr++;
8370 break;
8371 }
8372 in_cnt = copy_expand_unix_filename_escape
8373 (vmsptr, unixptr, &out_cnt, utf8_fl);
8374 vmsptr += out_cnt;
8375 unixptr += in_cnt;
2497a41f
JM
8376 break;
8377 case '~':
8378 case ';':
8379 case '\\':
360732b5
JM
8380 case '?':
8381 case ' ':
2497a41f 8382 default:
360732b5
JM
8383 in_cnt = copy_expand_unix_filename_escape
8384 (vmsptr, unixptr, &out_cnt, utf8_fl);
8385 vmsptr += out_cnt;
8386 unixptr += in_cnt;
2497a41f
JM
8387 break;
8388 }
8389 }
8390
8391 /* Make sure directory is closed */
8392 if (unixptr == lastslash) {
8393 char *vmsptr2;
8394 vmsptr2 = vmsptr - 1;
8395
8396 if (*vmsptr2 != ']') {
8397 *vmsptr2--;
8398
8399 /* directories do not end in a dot bracket */
8400 if (*vmsptr2 == '.') {
8401 vmsptr2--;
8402
8403 /* ^. is allowed */
8404 if (*vmsptr2 != '^') {
8405 vmsptr--; /* back up over the dot */
8406 }
8407 }
8408 *vmsptr++ = ']';
8409 }
8410 }
8411 else {
8412 char *vmsptr2;
8413 /* Add a trailing dot if a file with no extension */
8414 vmsptr2 = vmsptr - 1;
360732b5
JM
8415 if ((vmslen > 1) &&
8416 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
30e68285 8417 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
2497a41f
JM
8418 *vmsptr++ = '.';
8419 vmslen++;
8420 }
8421 }
8422
8423 *vmsptr = '\0';
8424 return SS$_NORMAL;
8425}
8426#endif
8427
360732b5
JM
8428 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8429static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8430{
8431char * result;
8432int utf8_flag;
8433
8434 /* If a UTF8 flag is being passed, honor it */
8435 utf8_flag = 0;
8436 if (utf8_fl != NULL) {
8437 utf8_flag = *utf8_fl;
8438 *utf8_fl = 0;
8439 }
8440
8441 if (utf8_flag) {
8442 /* If there is a possibility of UTF8, then if any UTF8 characters
8443 are present, then they must be converted to VTF-7
8444 */
8445 result = strcpy(rslt, path); /* FIX-ME */
8446 }
8447 else
8448 result = strcpy(rslt, path);
8449
8450 return result;
8451}
8452
8453
df278665 8454
360732b5 8455/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
df278665
JM
8456static char *int_tovmsspec
8457 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8458 char *dirend;
f7ddb74a
JM
8459 char *lastdot;
8460 char *vms_delim;
b8ffc8df
RGS
8461 register char *cp1;
8462 const char *cp2;
e518068a 8463 unsigned long int infront = 0, hasdir = 1;
f7ddb74a
JM
8464 int rslt_len;
8465 int no_type_seen;
360732b5
JM
8466 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8467 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
a0d0e21e 8468
df278665
JM
8469 if (vms_debug_fileify) {
8470 if (path == NULL)
8471 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8472 else
8473 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8474 }
8475
8476 if (path == NULL) {
8477 /* If we fail, we should be setting errno */
8478 set_errno(EINVAL);
8479 set_vaxc_errno(SS$_BADPARAM);
8480 return NULL;
8481 }
4d743a9b 8482 rslt_len = VMS_MAXRSS-1;
360732b5
JM
8483
8484 /* '.' and '..' are "[]" and "[-]" for a quick check */
8485 if (path[0] == '.') {
8486 if (path[1] == '\0') {
8487 strcpy(rslt,"[]");
8488 if (utf8_flag != NULL)
8489 *utf8_flag = 0;
8490 return rslt;
8491 }
8492 else {
8493 if (path[1] == '.' && path[2] == '\0') {
8494 strcpy(rslt,"[-]");
8495 if (utf8_flag != NULL)
8496 *utf8_flag = 0;
8497 return rslt;
8498 }
8499 }
a0d0e21e 8500 }
f7ddb74a 8501
2497a41f
JM
8502 /* Posix specifications are now a native VMS format */
8503 /*--------------------------------------------------*/
8504#if __CRTL_VER >= 80200000 && !defined(__VAX)
8505 if (decc_posix_compliant_pathnames) {
8506 if (strncmp(path,"\"^UP^",5) == 0) {
360732b5 8507 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
2497a41f
JM
8508 return rslt;
8509 }
8510 }
8511#endif
8512
360732b5
JM
8513 /* This is really the only way to see if this is already in VMS format */
8514 sts = vms_split_path
8515 (path,
8516 &v_spec,
8517 &v_len,
8518 &r_spec,
8519 &r_len,
8520 &d_spec,
8521 &d_len,
8522 &n_spec,
8523 &n_len,
8524 &e_spec,
8525 &e_len,
8526 &vs_spec,
8527 &vs_len);
8528 if (sts == 0) {
8529 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8530 replacement, because the above parse just took care of most of
8531 what is needed to do vmspath when the specification is already
8532 in VMS format.
8533
8534 And if it is not already, it is easier to do the conversion as
8535 part of this routine than to call this routine and then work on
8536 the result.
8537 */
2497a41f 8538
360732b5
JM
8539 /* If VMS punctuation was found, it is already VMS format */
8540 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8541 if (utf8_flag != NULL)
8542 *utf8_flag = 0;
8543 strcpy(rslt, path);
df278665
JM
8544 if (vms_debug_fileify) {
8545 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8546 }
360732b5
JM
8547 return rslt;
8548 }
8549 /* Now, what to do with trailing "." cases where there is no
8550 extension? If this is a UNIX specification, and EFS characters
8551 are enabled, then the trailing "." should be converted to a "^.".
8552 But if this was already a VMS specification, then it should be
8553 left alone.
2497a41f 8554
360732b5
JM
8555 So in the case of ambiguity, leave the specification alone.
8556 */
2497a41f 8557
2497a41f 8558
360732b5
JM
8559 /* If there is a possibility of UTF8, then if any UTF8 characters
8560 are present, then they must be converted to VTF-7
8561 */
8562 if (utf8_flag != NULL)
8563 *utf8_flag = 0;
8564 strcpy(rslt, path);
df278665
JM
8565 if (vms_debug_fileify) {
8566 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8567 }
2497a41f
JM
8568 return rslt;
8569 }
8570
360732b5
JM
8571 dirend = strrchr(path,'/');
8572
8573 if (dirend == NULL) {
df278665
JM
8574 char *macro_start;
8575 int has_macro;
8576
360732b5
JM
8577 /* If we get here with no UNIX directory delimiters, then this is
8578 not a complete file specification, either garbage a UNIX glob
8579 specification that can not be converted to a VMS wildcard, or
df278665
JM
8580 it a UNIX shell macro. MakeMaker wants shell macros passed
8581 through AS-IS,
360732b5
JM
8582
8583 utf8 flag setting needs to be preserved.
8584 */
df278665
JM
8585 hasdir = 0;
8586
8587 has_macro = 0;
8588 macro_start = strchr(path,'$');
8589 if (macro_start != NULL) {
8590 if (macro_start[1] == '(') {
8591 has_macro = 1;
8592 }
8593 }
8594 if ((decc_efs_charset == 0) || (has_macro)) {
8595 strcpy(rslt, path);
8596 if (vms_debug_fileify) {
8597 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8598 }
8599 return rslt;
8600 }
360732b5
JM
8601 }
8602
30e68285 8603/* If EFS charset mode active, handle the conversion */
2497a41f 8604#if __CRTL_VER >= 80200000 && !defined(__VAX)
360732b5
JM
8605 if (decc_efs_charset) {
8606 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
df278665
JM
8607 if (vms_debug_fileify) {
8608 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8609 }
2497a41f
JM
8610 return rslt;
8611 }
8612#endif
f7ddb74a 8613
f86702cc 8614 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
748a9306
LW
8615 if (!*(dirend+2)) dirend +=2;
8616 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
360732b5
JM
8617 if (decc_efs_charset == 0) {
8618 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8619 }
748a9306 8620 }
f7ddb74a 8621
a0d0e21e
LW
8622 cp1 = rslt;
8623 cp2 = path;
f7ddb74a 8624 lastdot = strrchr(cp2,'.');
a0d0e21e 8625 if (*cp2 == '/') {
a480973c 8626 char *trndev;
e518068a 8627 int islnm, rooted;
8628 STRLEN trnend;
8629
b7ae7a0d 8630 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
61bb5906 8631 if (!*(cp2+1)) {
f7ddb74a
JM
8632 if (decc_disable_posix_root) {
8633 strcpy(rslt,"sys$disk:[000000]");
8634 }
8635 else {
8636 strcpy(rslt,"sys$posix_root:[000000]");
8637 }
360732b5
JM
8638 if (utf8_flag != NULL)
8639 *utf8_flag = 0;
df278665
JM
8640 if (vms_debug_fileify) {
8641 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8642 }
61bb5906
CB
8643 return rslt;
8644 }
a0d0e21e 8645 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
e518068a 8646 *cp1 = '\0';
c5375c28 8647 trndev = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 8648 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
b8486b9d 8649 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8650
8651 /* DECC special handling */
8652 if (!islnm) {
8653 if (strcmp(rslt,"bin") == 0) {
8654 strcpy(rslt,"sys$system");
8655 cp1 = rslt + 10;
8656 *cp1 = 0;
b8486b9d 8657 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8658 }
8659 else if (strcmp(rslt,"tmp") == 0) {
8660 strcpy(rslt,"sys$scratch");
8661 cp1 = rslt + 11;
8662 *cp1 = 0;
b8486b9d 8663 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8664 }
8665 else if (!decc_disable_posix_root) {
8666 strcpy(rslt, "sys$posix_root");
b8486b9d 8667 cp1 = rslt + 14;
f7ddb74a
JM
8668 *cp1 = 0;
8669 cp2 = path;
8670 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
b8486b9d 8671 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8672 }
8673 else if (strcmp(rslt,"dev") == 0) {
8674 if (strncmp(cp2,"/null", 5) == 0) {
8675 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8676 strcpy(rslt,"NLA0");
8677 cp1 = rslt + 4;
8678 *cp1 = 0;
8679 cp2 = cp2 + 5;
b8486b9d 8680 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8681 }
8682 }
8683 }
8684 }
8685
e518068a 8686 trnend = islnm ? strlen(trndev) - 1 : 0;
8687 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8688 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8689 /* If the first element of the path is a logical name, determine
8690 * whether it has to be translated so we can add more directories. */
8691 if (!islnm || rooted) {
8692 *(cp1++) = ':';
8693 *(cp1++) = '[';
8694 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8695 else cp2++;
8696 }
8697 else {
8698 if (cp2 != dirend) {
e518068a 8699 strcpy(rslt,trndev);
8700 cp1 = rslt + trnend;
755b3d5d
JM
8701 if (*cp2 != 0) {
8702 *(cp1++) = '.';
8703 cp2++;
8704 }
e518068a 8705 }
8706 else {
f7ddb74a
JM
8707 if (decc_disable_posix_root) {
8708 *(cp1++) = ':';
8709 hasdir = 0;
8710 }
e518068a 8711 }
8712 }
367e4b85 8713 PerlMem_free(trndev);
748a9306 8714 }
a0d0e21e
LW
8715 else {
8716 *(cp1++) = '[';
748a9306
LW
8717 if (*cp2 == '.') {
8718 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8719 cp2 += 2; /* skip over "./" - it's redundant */
8720 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8721 }
8722 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8723 *(cp1++) = '-'; /* "../" --> "-" */
8724 cp2 += 3;
8725 }
f86702cc 8726 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8727 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8728 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8729 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8730 cp2 += 4;
8731 }
f7ddb74a
JM
8732 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8733 /* Escape the extra dots in EFS file specifications */
8734 *(cp1++) = '^';
8735 }
748a9306
LW
8736 if (cp2 > dirend) cp2 = dirend;
8737 }
8738 else *(cp1++) = '.';
8739 }
8740 for (; cp2 < dirend; cp2++) {
8741 if (*cp2 == '/') {
01b8edb6 8742 if (*(cp2-1) == '/') continue;
748a9306
LW
8743 if (*(cp1-1) != '.') *(cp1++) = '.';
8744 infront = 0;
8745 }
8746 else if (!infront && *cp2 == '.') {
01b8edb6 8747 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8748 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
fd7385b9
CB
8749 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8750 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
748a9306 8751 else if (*(cp1-2) == '[') *(cp1-1) = '-';
fd7385b9
CB
8752 else { /* back up over previous directory name */
8753 cp1--;
8754 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8755 if (*(cp1-1) == '[') {
8756 memcpy(cp1,"000000.",7);
8757 cp1 += 7;
8758 }
748a9306
LW
8759 }
8760 cp2 += 2;
01b8edb6 8761 if (cp2 == dirend) break;
748a9306 8762 }
f86702cc 8763 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8764 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8765 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8766 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8767 if (!*(cp2+3)) {
8768 *(cp1++) = '.'; /* Simulate trailing '/' */
8769 cp2 += 2; /* for loop will incr this to == dirend */
8770 }
8771 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8772 }
f7ddb74a
JM
8773 else {
8774 if (decc_efs_charset == 0)
8775 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8776 else {
8777 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8778 *(cp1++) = '.';
8779 }
8780 }
748a9306
LW
8781 }
8782 else {
e518068a 8783 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
f7ddb74a
JM
8784 if (*cp2 == '.') {
8785 if (decc_efs_charset == 0)
8786 *(cp1++) = '_';
8787 else {
8788 *(cp1++) = '^';
8789 *(cp1++) = '.';
8790 }
8791 }
748a9306
LW
8792 else *(cp1++) = *cp2;
8793 infront = 1;
8794 }
a0d0e21e 8795 }
748a9306 8796 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
e518068a 8797 if (hasdir) *(cp1++) = ']';
748a9306 8798 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
f7ddb74a
JM
8799 /* fixme for ODS5 */
8800 no_type_seen = 0;
8801 if (cp2 > lastdot)
8802 no_type_seen = 1;
8803 while (*cp2) {
8804 switch(*cp2) {
8805 case '?':
360732b5
JM
8806 if (decc_efs_charset == 0)
8807 *(cp1++) = '%';
8808 else
8809 *(cp1++) = '?';
f7ddb74a
JM
8810 cp2++;
8811 case ' ':
8812 *(cp1)++ = '^';
8813 *(cp1)++ = '_';
8814 cp2++;
8815 break;
8816 case '.':
8817 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8818 decc_readdir_dropdotnotype) {
8819 *(cp1)++ = '^';
8820 *(cp1)++ = '.';
8821 cp2++;
8822
8823 /* trailing dot ==> '^..' on VMS */
8824 if (*cp2 == '\0') {
8825 *(cp1++) = '.';
8826 no_type_seen = 0;
8827 }
8828 }
8829 else {
8830 *(cp1++) = *(cp2++);
8831 no_type_seen = 0;
8832 }
8833 break;
360732b5
JM
8834 case '$':
8835 /* This could be a macro to be passed through */
8836 *(cp1++) = *(cp2++);
8837 if (*cp2 == '(') {
8838 const char * save_cp2;
8839 char * save_cp1;
8840 int is_macro;
8841
8842 /* paranoid check */
8843 save_cp2 = cp2;
8844 save_cp1 = cp1;
8845 is_macro = 0;
8846
8847 /* Test through */
8848 *(cp1++) = *(cp2++);
8849 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8850 *(cp1++) = *(cp2++);
8851 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8852 *(cp1++) = *(cp2++);
8853 }
8854 if (*cp2 == ')') {
8855 *(cp1++) = *(cp2++);
8856 is_macro = 1;
8857 }
8858 }
8859 if (is_macro == 0) {
8860 /* Not really a macro - never mind */
8861 cp2 = save_cp2;
8862 cp1 = save_cp1;
8863 }
8864 }
8865 break;
f7ddb74a
JM
8866 case '\"':
8867 case '~':
8868 case '`':
8869 case '!':
8870 case '#':
8871 case '%':
8872 case '^':
adc11f0b
CB
8873 /* Don't escape again if following character is
8874 * already something we escape.
8875 */
8876 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8877 *(cp1++) = *(cp2++);
8878 break;
8879 }
8880 /* But otherwise fall through and escape it. */
f7ddb74a
JM
8881 case '&':
8882 case '(':
8883 case ')':
8884 case '=':
8885 case '+':
8886 case '\'':
8887 case '@':
8888 case '[':
8889 case ']':
8890 case '{':
8891 case '}':
8892 case ':':
8893 case '\\':
8894 case '|':
8895 case '<':
8896 case '>':
8897 *(cp1++) = '^';
8898 *(cp1++) = *(cp2++);
8899 break;
8900 case ';':
8901 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
657054d4 8902 * which is wrong. UNIX notation should be ".dir." unless
f7ddb74a
JM
8903 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8904 * changing this behavior could break more things at this time.
2497a41f
JM
8905 * efs character set effectively does not allow "." to be a version
8906 * delimiter as a further complication about changing this.
f7ddb74a
JM
8907 */
8908 if (decc_filename_unix_report != 0) {
8909 *(cp1++) = '^';
8910 }
8911 *(cp1++) = *(cp2++);
8912 break;
8913 default:
8914 *(cp1++) = *(cp2++);
8915 }
8916 }
8917 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8918 char *lcp1;
8919 lcp1 = cp1;
8920 lcp1--;
8921 /* Fix me for "^]", but that requires making sure that you do
8922 * not back up past the start of the filename
8923 */
8924 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8925 *cp1++ = '.';
8926 }
a0d0e21e
LW
8927 *cp1 = '\0';
8928
360732b5
JM
8929 if (utf8_flag != NULL)
8930 *utf8_flag = 0;
df278665
JM
8931 if (vms_debug_fileify) {
8932 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8933 }
a0d0e21e
LW
8934 return rslt;
8935
df278665
JM
8936} /* end of int_tovmsspec() */
8937
8938
8939/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8940static char *mp_do_tovmsspec
8941 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8942 static char __tovmsspec_retbuf[VMS_MAXRSS];
8943 char * vmsspec, *ret_spec, *ret_buf;
8944
8945 vmsspec = NULL;
8946 ret_buf = buf;
8947 if (ret_buf == NULL) {
8948 if (ts) {
8949 Newx(vmsspec, VMS_MAXRSS, char);
8950 if (vmsspec == NULL)
8951 _ckvmssts(SS$_INSFMEM);
8952 ret_buf = vmsspec;
8953 } else {
8954 ret_buf = __tovmsspec_retbuf;
8955 }
8956 }
8957
8958 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8959
8960 if (ret_spec == NULL) {
8961 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8962 if (vmsspec)
8963 Safefree(vmsspec);
8964 }
8965
8966 return ret_spec;
8967
8968} /* end of mp_do_tovmsspec() */
a0d0e21e
LW
8969/*}}}*/
8970/* External entry points */
360732b5
JM
8971char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8972 { return do_tovmsspec(path,buf,0,NULL); }
8973char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8974 { return do_tovmsspec(path,buf,1,NULL); }
8975char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8976 { return do_tovmsspec(path,buf,0,utf8_fl); }
8977char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8978 { return do_tovmsspec(path,buf,1,utf8_fl); }
8979
4846f1d7
JM
8980/*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8981/* Internal routine for use with out an explict context present */
8982static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8983
8984 char * ret_spec, *pathified;
8985
8986 if (path == NULL)
8987 return NULL;
8988
8989 pathified = PerlMem_malloc(VMS_MAXRSS);
8990 if (pathified == NULL)
8991 _ckvmssts_noperl(SS$_INSFMEM);
8992
8993 ret_spec = int_pathify_dirspec(path, pathified);
8994
8995 if (ret_spec == NULL) {
8996 PerlMem_free(pathified);
8997 return NULL;
8998 }
8999
9000 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
9001
9002 PerlMem_free(pathified);
9003 return ret_spec;
9004
9005}
9006
360732b5
JM
9007/*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
9008static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 9009 static char __tovmspath_retbuf[VMS_MAXRSS];
a0d0e21e 9010 int vmslen;
a480973c 9011 char *pathified, *vmsified, *cp;
a0d0e21e 9012
748a9306 9013 if (path == NULL) return NULL;
c5375c28
JM
9014 pathified = PerlMem_malloc(VMS_MAXRSS);
9015 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 9016 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 9017 PerlMem_free(pathified);
a480973c
JM
9018 return NULL;
9019 }
c5375c28
JM
9020
9021 vmsified = NULL;
9022 if (buf == NULL)
9023 Newx(vmsified, VMS_MAXRSS, char);
360732b5 9024 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
c5375c28
JM
9025 PerlMem_free(pathified);
9026 if (vmsified) Safefree(vmsified);
a480973c
JM
9027 return NULL;
9028 }
c5375c28 9029 PerlMem_free(pathified);
a480973c 9030 if (buf) {
a480973c
JM
9031 return buf;
9032 }
a0d0e21e
LW
9033 else if (ts) {
9034 vmslen = strlen(vmsified);
a02a5408 9035 Newx(cp,vmslen+1,char);
a0d0e21e
LW
9036 memcpy(cp,vmsified,vmslen);
9037 cp[vmslen] = '\0';
a480973c 9038 Safefree(vmsified);
a0d0e21e
LW
9039 return cp;
9040 }
9041 else {
9042 strcpy(__tovmspath_retbuf,vmsified);
a480973c 9043 Safefree(vmsified);
a0d0e21e
LW
9044 return __tovmspath_retbuf;
9045 }
9046
9047} /* end of do_tovmspath() */
9048/*}}}*/
9049/* External entry points */
360732b5
JM
9050char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9051 { return do_tovmspath(path,buf,0, NULL); }
9052char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9053 { return do_tovmspath(path,buf,1, NULL); }
9054char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9055 { return do_tovmspath(path,buf,0,utf8_fl); }
9056char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9057 { return do_tovmspath(path,buf,1,utf8_fl); }
9058
9059
9060/*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9061static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 9062 static char __tounixpath_retbuf[VMS_MAXRSS];
a0d0e21e 9063 int unixlen;
a480973c 9064 char *pathified, *unixified, *cp;
a0d0e21e 9065
748a9306 9066 if (path == NULL) return NULL;
c5375c28
JM
9067 pathified = PerlMem_malloc(VMS_MAXRSS);
9068 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 9069 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 9070 PerlMem_free(pathified);
a480973c
JM
9071 return NULL;
9072 }
c5375c28
JM
9073
9074 unixified = NULL;
9075 if (buf == NULL) {
9076 Newx(unixified, VMS_MAXRSS, char);
9077 }
360732b5 9078 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
c5375c28
JM
9079 PerlMem_free(pathified);
9080 if (unixified) Safefree(unixified);
a480973c
JM
9081 return NULL;
9082 }
c5375c28 9083 PerlMem_free(pathified);
a480973c 9084 if (buf) {
a480973c
JM
9085 return buf;
9086 }
a0d0e21e
LW
9087 else if (ts) {
9088 unixlen = strlen(unixified);
a02a5408 9089 Newx(cp,unixlen+1,char);
a0d0e21e
LW
9090 memcpy(cp,unixified,unixlen);
9091 cp[unixlen] = '\0';
a480973c 9092 Safefree(unixified);
a0d0e21e
LW
9093 return cp;
9094 }
9095 else {
9096 strcpy(__tounixpath_retbuf,unixified);
a480973c 9097 Safefree(unixified);
a0d0e21e
LW
9098 return __tounixpath_retbuf;
9099 }
9100
9101} /* end of do_tounixpath() */
9102/*}}}*/
9103/* External entry points */
360732b5
JM
9104char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9105 { return do_tounixpath(path,buf,0,NULL); }
9106char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9107 { return do_tounixpath(path,buf,1,NULL); }
9108char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9109 { return do_tounixpath(path,buf,0,utf8_fl); }
9110char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9111 { return do_tounixpath(path,buf,1,utf8_fl); }
a0d0e21e
LW
9112
9113/*
cbb8049c 9114 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
9115 *
9116 *****************************************************************************
9117 * *
cbb8049c 9118 * Copyright (C) 1989-1994, 2007 by *
a0d0e21e
LW
9119 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9120 * *
cbb8049c
MP
9121 * Permission is hereby granted for the reproduction of this software *
9122 * on condition that this copyright notice is included in source *
9123 * distributions of the software. The code may be modified and *
9124 * distributed under the same terms as Perl itself. *
a0d0e21e
LW
9125 * *
9126 * 27-Aug-1994 Modified for inclusion in perl5 *
cbb8049c 9127 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
a0d0e21e
LW
9128 *****************************************************************************
9129 */
9130
9131/*
9132 * getredirection() is intended to aid in porting C programs
9133 * to VMS (Vax-11 C). The native VMS environment does not support
9134 * '>' and '<' I/O redirection, or command line wild card expansion,
9135 * or a command line pipe mechanism using the '|' AND background
9136 * command execution '&'. All of these capabilities are provided to any
9137 * C program which calls this procedure as the first thing in the
9138 * main program.
9139 * The piping mechanism will probably work with almost any 'filter' type
9140 * of program. With suitable modification, it may useful for other
9141 * portability problems as well.
9142 *
cbb8049c 9143 * Author: Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
9144 */
9145struct list_item
9146 {
9147 struct list_item *next;
9148 char *value;
9149 };
9150
9151static void add_item(struct list_item **head,
9152 struct list_item **tail,
9153 char *value,
9154 int *count);
9155
4b19af01
CB
9156static void mp_expand_wild_cards(pTHX_ char *item,
9157 struct list_item **head,
9158 struct list_item **tail,
9159 int *count);
a0d0e21e 9160
8df869cb 9161static int background_process(pTHX_ int argc, char **argv);
a0d0e21e 9162
fd8cd3a3 9163static void pipe_and_fork(pTHX_ char **cmargv);
a0d0e21e
LW
9164
9165/*{{{ void getredirection(int *ac, char ***av)*/
84902520 9166static void
4b19af01 9167mp_getredirection(pTHX_ int *ac, char ***av)
a0d0e21e
LW
9168/*
9169 * Process vms redirection arg's. Exit if any error is seen.
9170 * If getredirection() processes an argument, it is erased
9171 * from the vector. getredirection() returns a new argc and argv value.
9172 * In the event that a background command is requested (by a trailing "&"),
9173 * this routine creates a background subprocess, and simply exits the program.
9174 *
9175 * Warning: do not try to simplify the code for vms. The code
9176 * presupposes that getredirection() is called before any data is
9177 * read from stdin or written to stdout.
9178 *
9179 * Normal usage is as follows:
9180 *
9181 * main(argc, argv)
9182 * int argc;
9183 * char *argv[];
9184 * {
9185 * getredirection(&argc, &argv);
9186 * }
9187 */
9188{
9189 int argc = *ac; /* Argument Count */
9190 char **argv = *av; /* Argument Vector */
9191 char *ap; /* Argument pointer */
9192 int j; /* argv[] index */
9193 int item_count = 0; /* Count of Items in List */
9194 struct list_item *list_head = 0; /* First Item in List */
9195 struct list_item *list_tail; /* Last Item in List */
9196 char *in = NULL; /* Input File Name */
9197 char *out = NULL; /* Output File Name */
9198 char *outmode = "w"; /* Mode to Open Output File */
9199 char *err = NULL; /* Error File Name */
9200 char *errmode = "w"; /* Mode to Open Error File */
9201 int cmargc = 0; /* Piped Command Arg Count */
9202 char **cmargv = NULL;/* Piped Command Arg Vector */
a0d0e21e
LW
9203
9204 /*
9205 * First handle the case where the last thing on the line ends with
9206 * a '&'. This indicates the desire for the command to be run in a
9207 * subprocess, so we satisfy that desire.
9208 */
9209 ap = argv[argc-1];
9210 if (0 == strcmp("&", ap))
8c3eed29 9211 exit(background_process(aTHX_ --argc, argv));
e518068a 9212 if (*ap && '&' == ap[strlen(ap)-1])
a0d0e21e
LW
9213 {
9214 ap[strlen(ap)-1] = '\0';
8c3eed29 9215 exit(background_process(aTHX_ argc, argv));
a0d0e21e
LW
9216 }
9217 /*
9218 * Now we handle the general redirection cases that involve '>', '>>',
9219 * '<', and pipes '|'.
9220 */
9221 for (j = 0; j < argc; ++j)
9222 {
9223 if (0 == strcmp("<", argv[j]))
9224 {
9225 if (j+1 >= argc)
9226 {
fd71b04b 9227 fprintf(stderr,"No input file after < on command line");
748a9306 9228 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9229 }
9230 in = argv[++j];
9231 continue;
9232 }
9233 if ('<' == *(ap = argv[j]))
9234 {
9235 in = 1 + ap;
9236 continue;
9237 }
9238 if (0 == strcmp(">", ap))
9239 {
9240 if (j+1 >= argc)
9241 {
fd71b04b 9242 fprintf(stderr,"No output file after > on command line");
748a9306 9243 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9244 }
9245 out = argv[++j];
9246 continue;
9247 }
9248 if ('>' == *ap)
9249 {
9250 if ('>' == ap[1])
9251 {
9252 outmode = "a";
9253 if ('\0' == ap[2])
9254 out = argv[++j];
9255 else
9256 out = 2 + ap;
9257 }
9258 else
9259 out = 1 + ap;
9260 if (j >= argc)
9261 {
fd71b04b 9262 fprintf(stderr,"No output file after > or >> on command line");
748a9306 9263 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9264 }
9265 continue;
9266 }
9267 if (('2' == *ap) && ('>' == ap[1]))
9268 {
9269 if ('>' == ap[2])
9270 {
9271 errmode = "a";
9272 if ('\0' == ap[3])
9273 err = argv[++j];
9274 else
9275 err = 3 + ap;
9276 }
9277 else
9278 if ('\0' == ap[2])
9279 err = argv[++j];
9280 else
748a9306 9281 err = 2 + ap;
a0d0e21e
LW
9282 if (j >= argc)
9283 {
fd71b04b 9284 fprintf(stderr,"No output file after 2> or 2>> on command line");
748a9306 9285 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9286 }
9287 continue;
9288 }
9289 if (0 == strcmp("|", argv[j]))
9290 {
9291 if (j+1 >= argc)
9292 {
fd71b04b 9293 fprintf(stderr,"No command into which to pipe on command line");
748a9306 9294 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9295 }
9296 cmargc = argc-(j+1);
9297 cmargv = &argv[j+1];
9298 argc = j;
9299 continue;
9300 }
9301 if ('|' == *(ap = argv[j]))
9302 {
9303 ++argv[j];
9304 cmargc = argc-j;
9305 cmargv = &argv[j];
9306 argc = j;
9307 continue;
9308 }
9309 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9310 }
9311 /*
9312 * Allocate and fill in the new argument vector, Some Unix's terminate
9313 * the list with an extra null pointer.
9314 */
e0ef6b43 9315 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
c5375c28 9316 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9317 *av = argv;
9318 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9319 argv[j] = list_head->value;
9320 *ac = item_count;
9321 if (cmargv != NULL)
9322 {
9323 if (out != NULL)
9324 {
fd71b04b 9325 fprintf(stderr,"'|' and '>' may not both be specified on command line");
748a9306 9326 exit(LIB$_INVARGORD);
a0d0e21e 9327 }
fd8cd3a3 9328 pipe_and_fork(aTHX_ cmargv);
a0d0e21e
LW
9329 }
9330
9331 /* Check for input from a pipe (mailbox) */
9332
a5f75d66 9333 if (in == NULL && 1 == isapipe(0))
a0d0e21e
LW
9334 {
9335 char mbxname[L_tmpnam];
9336 long int bufsize;
9337 long int dvi_item = DVI$_DEVBUFSIZ;
9338 $DESCRIPTOR(mbxnam, "");
9339 $DESCRIPTOR(mbxdevnam, "");
9340
9341 /* Input from a pipe, reopen it in binary mode to disable */
9342 /* carriage control processing. */
9343
bf8d1304 9344 fgetname(stdin, mbxname, 1);
a0d0e21e
LW
9345 mbxnam.dsc$a_pointer = mbxname;
9346 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9347 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9348 mbxdevnam.dsc$a_pointer = mbxname;
9349 mbxdevnam.dsc$w_length = sizeof(mbxname);
9350 dvi_item = DVI$_DEVNAM;
9351 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9352 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
748a9306
LW
9353 set_errno(0);
9354 set_vaxc_errno(1);
a0d0e21e
LW
9355 freopen(mbxname, "rb", stdin);
9356 if (errno != 0)
9357 {
fd71b04b 9358 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
748a9306 9359 exit(vaxc$errno);
a0d0e21e
LW
9360 }
9361 }
9362 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9363 {
fd71b04b 9364 fprintf(stderr,"Can't open input file %s as stdin",in);
748a9306 9365 exit(vaxc$errno);
a0d0e21e
LW
9366 }
9367 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9368 {
fd71b04b 9369 fprintf(stderr,"Can't open output file %s as stdout",out);
748a9306 9370 exit(vaxc$errno);
a0d0e21e 9371 }
fd8cd3a3 9372 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
0e06870b 9373
748a9306 9374 if (err != NULL) {
71d7ec5d 9375 if (strcmp(err,"&1") == 0) {
a15cef0c 9376 dup2(fileno(stdout), fileno(stderr));
fd8cd3a3 9377 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
71d7ec5d 9378 } else {
748a9306
LW
9379 FILE *tmperr;
9380 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9381 {
fd71b04b 9382 fprintf(stderr,"Can't open error file %s as stderr",err);
748a9306
LW
9383 exit(vaxc$errno);
9384 }
9385 fclose(tmperr);
a15cef0c 9386 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
748a9306
LW
9387 {
9388 exit(vaxc$errno);
9389 }
fd8cd3a3 9390 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
a0d0e21e 9391 }
71d7ec5d 9392 }
a0d0e21e 9393#ifdef ARGPROC_DEBUG
740ce14c 9394 PerlIO_printf(Perl_debug_log, "Arglist:\n");
a0d0e21e 9395 for (j = 0; j < *ac; ++j)
740ce14c 9396 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
a0d0e21e 9397#endif
b7ae7a0d 9398 /* Clear errors we may have hit expanding wildcards, so they don't
9399 show up in Perl's $! later */
9400 set_errno(0); set_vaxc_errno(1);
a0d0e21e
LW
9401} /* end of getredirection() */
9402/*}}}*/
9403
9404static void add_item(struct list_item **head,
9405 struct list_item **tail,
9406 char *value,
9407 int *count)
9408{
9409 if (*head == 0)
9410 {
e0ef6b43 9411 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9412 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9413 *tail = *head;
9414 }
9415 else {
e0ef6b43 9416 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9417 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9418 *tail = (*tail)->next;
9419 }
9420 (*tail)->value = value;
9421 ++(*count);
9422}
9423
4b19af01 9424static void mp_expand_wild_cards(pTHX_ char *item,
a0d0e21e
LW
9425 struct list_item **head,
9426 struct list_item **tail,
9427 int *count)
9428{
9429int expcount = 0;
748a9306 9430unsigned long int context = 0;
a0d0e21e 9431int isunix = 0;
773da73d 9432int item_len = 0;
a0d0e21e
LW
9433char *had_version;
9434char *had_device;
9435int had_directory;
f675dbe5 9436char *devdir,*cp;
a480973c 9437char *vmsspec;
a0d0e21e 9438$DESCRIPTOR(filespec, "");
748a9306 9439$DESCRIPTOR(defaultspec, "SYS$DISK:[]");
a0d0e21e 9440$DESCRIPTOR(resultspec, "");
a480973c
JM
9441unsigned long int lff_flags = 0;
9442int sts;
dca5a913 9443int rms_sts;
a480973c
JM
9444
9445#ifdef VMS_LONGNAME_SUPPORT
9446 lff_flags = LIB$M_FIL_LONG_NAMES;
9447#endif
a0d0e21e 9448
f675dbe5
CB
9449 for (cp = item; *cp; cp++) {
9450 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9451 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9452 }
9453 if (!*cp || isspace(*cp))
a0d0e21e
LW
9454 {
9455 add_item(head, tail, item, count);
9456 return;
9457 }
773da73d
JH
9458 else
9459 {
9460 /* "double quoted" wild card expressions pass as is */
9461 /* From DCL that means using e.g.: */
9462 /* perl program """perl.*""" */
9463 item_len = strlen(item);
9464 if ( '"' == *item && '"' == item[item_len-1] )
9465 {
9466 item++;
9467 item[item_len-2] = '\0';
9468 add_item(head, tail, item, count);
9469 return;
9470 }
9471 }
a0d0e21e
LW
9472 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9473 resultspec.dsc$b_class = DSC$K_CLASS_D;
9474 resultspec.dsc$a_pointer = NULL;
c5375c28
JM
9475 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9476 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
748a9306 9477 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
df278665 9478 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
a0d0e21e
LW
9479 if (!isunix || !filespec.dsc$a_pointer)
9480 filespec.dsc$a_pointer = item;
9481 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9482 /*
9483 * Only return version specs, if the caller specified a version
9484 */
9485 had_version = strchr(item, ';');
9486 /*
9487 * Only return device and directory specs, if the caller specifed either.
9488 */
9489 had_device = strchr(item, ':');
9490 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9491
a480973c
JM
9492 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9493 (&filespec, &resultspec, &context,
dca5a913 9494 &defaultspec, 0, &rms_sts, &lff_flags)))
a0d0e21e
LW
9495 {
9496 char *string;
9497 char *c;
9498
c5375c28
JM
9499 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9500 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9501 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9502 string[resultspec.dsc$w_length] = '\0';
9503 if (NULL == had_version)
f7ddb74a 9504 *(strrchr(string, ';')) = '\0';
a0d0e21e
LW
9505 if ((!had_directory) && (had_device == NULL))
9506 {
9507 if (NULL == (devdir = strrchr(string, ']')))
9508 devdir = strrchr(string, '>');
9509 strcpy(string, devdir + 1);
9510 }
9511 /*
9512 * Be consistent with what the C RTL has already done to the rest of
9513 * the argv items and lowercase all of these names.
9514 */
f7ddb74a
JM
9515 if (!decc_efs_case_preserve) {
9516 for (c = string; *c; ++c)
a0d0e21e
LW
9517 if (isupper(*c))
9518 *c = tolower(*c);
f7ddb74a 9519 }
f86702cc 9520 if (isunix) trim_unixpath(string,item,1);
a0d0e21e
LW
9521 add_item(head, tail, string, count);
9522 ++expcount;
a480973c 9523 }
367e4b85 9524 PerlMem_free(vmsspec);
c07a80fd 9525 if (sts != RMS$_NMF)
9526 {
9527 set_vaxc_errno(sts);
9528 switch (sts)
9529 {
f282b18d 9530 case RMS$_FNF: case RMS$_DNF:
c07a80fd 9531 set_errno(ENOENT); break;
f282b18d
CB
9532 case RMS$_DIR:
9533 set_errno(ENOTDIR); break;
c07a80fd 9534 case RMS$_DEV:
9535 set_errno(ENODEV); break;
f282b18d 9536 case RMS$_FNM: case RMS$_SYN:
c07a80fd 9537 set_errno(EINVAL); break;
9538 case RMS$_PRV:
9539 set_errno(EACCES); break;
9540 default:
b7ae7a0d 9541 _ckvmssts_noperl(sts);
c07a80fd 9542 }
9543 }
a0d0e21e
LW
9544 if (expcount == 0)
9545 add_item(head, tail, item, count);
b7ae7a0d 9546 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9547 _ckvmssts_noperl(lib$find_file_end(&context));
a0d0e21e
LW
9548}
9549
9550static int child_st[2];/* Event Flag set when child process completes */
9551
748a9306 9552static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
a0d0e21e 9553
748a9306 9554static unsigned long int exit_handler(int *status)
a0d0e21e
LW
9555{
9556short iosb[4];
9557
9558 if (0 == child_st[0])
9559 {
9560#ifdef ARGPROC_DEBUG
740ce14c 9561 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
a0d0e21e
LW
9562#endif
9563 fflush(stdout); /* Have to flush pipe for binary data to */
9564 /* terminate properly -- <tp@mccall.com> */
9565 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9566 sys$dassgn(child_chan);
9567 fclose(stdout);
9568 sys$synch(0, child_st);
9569 }
9570 return(1);
9571}
9572
9573static void sig_child(int chan)
9574{
9575#ifdef ARGPROC_DEBUG
740ce14c 9576 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
a0d0e21e
LW
9577#endif
9578 if (child_st[0] == 0)
9579 child_st[0] = 1;
9580}
9581
748a9306 9582static struct exit_control_block exit_block =
a0d0e21e
LW
9583 {
9584 0,
9585 exit_handler,
9586 1,
9587 &exit_block.exit_status,
9588 0
9589 };
9590
ff7adb52
CL
9591static void
9592pipe_and_fork(pTHX_ char **cmargv)
a0d0e21e 9593{
ff7adb52 9594 PerlIO *fp;
218fdd94 9595 struct dsc$descriptor_s *vmscmd;
ff7adb52
CL
9596 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9597 int sts, j, l, ismcr, quote, tquote = 0;
9598
218fdd94
CL
9599 sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9600 vms_execfree(vmscmd);
ff7adb52
CL
9601
9602 j = l = 0;
9603 p = subcmd;
9604 q = cmargv[0];
9605 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9606 && toupper(*(q+2)) == 'R' && !*(q+3);
9607
9608 while (q && l < MAX_DCL_LINE_LENGTH) {
9609 if (!*q) {
9610 if (j > 0 && quote) {
9611 *p++ = '"';
9612 l++;
9613 }
9614 q = cmargv[++j];
9615 if (q) {
9616 if (ismcr && j > 1) quote = 1;
9617 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9618 *p++ = ' ';
9619 l++;
9620 if (quote || tquote) {
9621 *p++ = '"';
9622 l++;
9623 }
988c775c 9624 }
ff7adb52
CL
9625 } else {
9626 if ((quote||tquote) && *q == '"') {
9627 *p++ = '"';
9628 l++;
988c775c 9629 }
ff7adb52
CL
9630 *p++ = *q++;
9631 l++;
9632 }
9633 }
9634 *p = '\0';
a0d0e21e 9635
218fdd94 9636 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
4e205ed6 9637 if (fp == NULL) {
ff7adb52 9638 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
988c775c 9639 }
a0d0e21e
LW
9640}
9641
8df869cb 9642static int background_process(pTHX_ int argc, char **argv)
a0d0e21e 9643{
a480973c 9644char command[MAX_DCL_SYMBOL + 1] = "$";
a0d0e21e
LW
9645$DESCRIPTOR(value, "");
9646static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9647static $DESCRIPTOR(null, "NLA0:");
9648static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9649char pidstring[80];
9650$DESCRIPTOR(pidstr, "");
9651int pid;
748a9306 9652unsigned long int flags = 17, one = 1, retsts;
a480973c 9653int len;
a0d0e21e
LW
9654
9655 strcat(command, argv[0]);
a480973c
JM
9656 len = strlen(command);
9657 while (--argc && (len < MAX_DCL_SYMBOL))
a0d0e21e
LW
9658 {
9659 strcat(command, " \"");
9660 strcat(command, *(++argv));
9661 strcat(command, "\"");
a480973c 9662 len = strlen(command);
a0d0e21e
LW
9663 }
9664 value.dsc$a_pointer = command;
9665 value.dsc$w_length = strlen(value.dsc$a_pointer);
b7ae7a0d 9666 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
748a9306
LW
9667 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9668 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
b7ae7a0d 9669 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
748a9306
LW
9670 }
9671 else {
b7ae7a0d 9672 _ckvmssts_noperl(retsts);
748a9306 9673 }
a0d0e21e 9674#ifdef ARGPROC_DEBUG
740ce14c 9675 PerlIO_printf(Perl_debug_log, "%s\n", command);
a0d0e21e
LW
9676#endif
9677 sprintf(pidstring, "%08X", pid);
740ce14c 9678 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
a0d0e21e
LW
9679 pidstr.dsc$a_pointer = pidstring;
9680 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9681 lib$set_symbol(&pidsymbol, &pidstr);
9682 return(SS$_NORMAL);
9683}
9684/*}}}*/
9685/***** End of code taken from Mark Pizzolato's argproc.c package *****/
9686
84902520
TB
9687
9688/* OS-specific initialization at image activation (not thread startup) */
61bb5906
CB
9689/* Older VAXC header files lack these constants */
9690#ifndef JPI$_RIGHTS_SIZE
9691# define JPI$_RIGHTS_SIZE 817
9692#endif
9693#ifndef KGB$M_SUBSYSTEM
9694# define KGB$M_SUBSYSTEM 0x8
9695#endif
a480973c 9696
e0ef6b43
CB
9697/* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9698
84902520
TB
9699/*{{{void vms_image_init(int *, char ***)*/
9700void
9701vms_image_init(int *argcp, char ***argvp)
9702{
b53f3677 9703 int status;
f675dbe5
CB
9704 char eqv[LNM$C_NAMLENGTH+1] = "";
9705 unsigned int len, tabct = 8, tabidx = 0;
9706 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
61bb5906
CB
9707 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9708 unsigned short int dummy, rlen;
f675dbe5 9709 struct dsc$descriptor_s **tabvec;
fd8cd3a3
DS
9710#if defined(PERL_IMPLICIT_CONTEXT)
9711 pTHX = NULL;
9712#endif
61bb5906
CB
9713 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9714 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9715 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9716 { 0, 0, 0, 0} };
84902520 9717
2e34cc90 9718#ifdef KILL_BY_SIGPRC
f7ddb74a 9719 Perl_csighandler_init();
2e34cc90
CL
9720#endif
9721
778e045f 9722#if __CRTL_VER >= 70300000 && !defined(__VAX)
b53f3677
JM
9723 /* This was moved from the pre-image init handler because on threaded */
9724 /* Perl it was always returning 0 for the default value. */
98c7875d 9725 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
b53f3677
JM
9726 if (status > 0) {
9727 int s;
9728 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9729 if (s > 0) {
9730 int initial;
9731 initial = decc$feature_get_value(s, 4);
98c7875d
CB
9732 if (initial > 0) {
9733 /* initial is: 0 if nothing has set the feature */
9734 /* -1 if initialized to default */
9735 /* 1 if set by logical name */
9736 /* 2 if set by decc$feature_set_value */
b53f3677
JM
9737 decc_disable_posix_root = decc$feature_get_value(s, 1);
9738
9739 /* If the value is not valid, force the feature off */
9740 if (decc_disable_posix_root < 0) {
9741 decc$feature_set_value(s, 1, 1);
9742 decc_disable_posix_root = 1;
9743 }
9744 }
9745 else {
98c7875d 9746 /* Nothing has asked for it explicitly, so use our own default. */
b53f3677
JM
9747 decc_disable_posix_root = 1;
9748 decc$feature_set_value(s, 1, 1);
9749 }
9750 }
9751 }
778e045f 9752#endif
b53f3677 9753
fd8cd3a3
DS
9754 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9755 _ckvmssts_noperl(iosb[0]);
61bb5906
CB
9756 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9757 if (iprv[i]) { /* Running image installed with privs? */
fd8cd3a3 9758 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
f675dbe5 9759 will_taint = TRUE;
84902520
TB
9760 break;
9761 }
9762 }
61bb5906 9763 /* Rights identifiers might trigger tainting as well. */
f675dbe5 9764 if (!will_taint && (rlen || rsz)) {
61bb5906
CB
9765 while (rlen < rsz) {
9766 /* We didn't get all the identifiers on the first pass. Allocate a
9767 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9768 * were needed to hold all identifiers at time of last call; we'll
9769 * allocate that many unsigned long ints), and go back and get 'em.
22d4bb9c
CB
9770 * If it gave us less than it wanted to despite ample buffer space,
9771 * something's broken. Is your system missing a system identifier?
61bb5906 9772 */
22d4bb9c
CB
9773 if (rsz <= jpilist[1].buflen) {
9774 /* Perl_croak accvios when used this early in startup. */
9775 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9776 rsz, (unsigned long) jpilist[1].buflen,
9777 "Check your rights database for corruption.\n");
9778 exit(SS$_ABORT);
9779 }
e0ef6b43
CB
9780 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9781 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
c5375c28 9782 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
61bb5906 9783 jpilist[1].buflen = rsz * sizeof(unsigned long int);
fd8cd3a3
DS
9784 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9785 _ckvmssts_noperl(iosb[0]);
61bb5906
CB
9786 }
9787 mask = jpilist[1].bufadr;
9788 /* Check attribute flags for each identifier (2nd longword); protected
9789 * subsystem identifiers trigger tainting.
9790 */
9791 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9792 if (mask[i] & KGB$M_SUBSYSTEM) {
f675dbe5 9793 will_taint = TRUE;
61bb5906
CB
9794 break;
9795 }
9796 }
367e4b85 9797 if (mask != rlst) PerlMem_free(mask);
61bb5906 9798 }
f7ddb74a
JM
9799
9800 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9801 * logical, some versions of the CRTL will add a phanthom /000000/
9802 * directory. This needs to be removed.
9803 */
9804 if (decc_filename_unix_report) {
9805 char * zeros;
9806 int ulen;
9807 ulen = strlen(argvp[0][0]);
9808 if (ulen > 7) {
9809 zeros = strstr(argvp[0][0], "/000000/");
9810 if (zeros != NULL) {
9811 int mlen;
9812 mlen = ulen - (zeros - argvp[0][0]) - 7;
9813 memmove(zeros, &zeros[7], mlen);
9814 ulen = ulen - 7;
9815 argvp[0][0][ulen] = '\0';
9816 }
9817 }
9818 /* It also may have a trailing dot that needs to be removed otherwise
9819 * it will be converted to VMS mode incorrectly.
9820 */
9821 ulen--;
9822 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9823 argvp[0][0][ulen] = '\0';
9824 }
9825
61bb5906 9826 /* We need to use this hack to tell Perl it should run with tainting,
6b88bc9c 9827 * since its tainting flag may be part of the PL_curinterp struct, which
61bb5906
CB
9828 * hasn't been allocated when vms_image_init() is called.
9829 */
f675dbe5 9830 if (will_taint) {
ec618cdf
CB
9831 char **newargv, **oldargv;
9832 oldargv = *argvp;
e0ef6b43 9833 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
c5375c28 9834 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf 9835 newargv[0] = oldargv[0];
c5375c28
JM
9836 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9837 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf
CB
9838 strcpy(newargv[1], "-T");
9839 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9840 (*argcp)++;
9841 newargv[*argcp] = NULL;
61bb5906
CB
9842 /* We orphan the old argv, since we don't know where it's come from,
9843 * so we don't know how to free it.
9844 */
ec618cdf 9845 *argvp = newargv;
61bb5906 9846 }
f675dbe5
CB
9847 else { /* Did user explicitly request tainting? */
9848 int i;
9849 char *cp, **av = *argvp;
9850 for (i = 1; i < *argcp; i++) {
9851 if (*av[i] != '-') break;
9852 for (cp = av[i]+1; *cp; cp++) {
9853 if (*cp == 'T') { will_taint = 1; break; }
9854 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9855 strchr("DFIiMmx",*cp)) break;
9856 }
9857 if (will_taint) break;
9858 }
9859 }
9860
9861 for (tabidx = 0;
9862 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9863 tabidx++) {
c5375c28
JM
9864 if (!tabidx) {
9865 tabvec = (struct dsc$descriptor_s **)
9866 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9867 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9868 }
f675dbe5
CB
9869 else if (tabidx >= tabct) {
9870 tabct += 8;
e0ef6b43 9871 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
c5375c28 9872 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5 9873 }
e0ef6b43 9874 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
c5375c28 9875 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5
CB
9876 tabvec[tabidx]->dsc$w_length = 0;
9877 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9878 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9879 tabvec[tabidx]->dsc$a_pointer = NULL;
fd8cd3a3 9880 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
f675dbe5
CB
9881 }
9882 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9883
84902520 9884 getredirection(argcp,argvp);
3bc25146
CB
9885#if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9886 {
9887# include <reentrancy.h>
f7ddb74a 9888 decc$set_reentrancy(C$C_MULTITHREAD);
3bc25146
CB
9889 }
9890#endif
84902520
TB
9891 return;
9892}
9893/*}}}*/
9894
9895
a0d0e21e
LW
9896/* trim_unixpath()
9897 * Trim Unix-style prefix off filespec, so it looks like what a shell
9898 * glob expansion would return (i.e. from specified prefix on, not
9899 * full path). Note that returned filespec is Unix-style, regardless
9900 * of whether input filespec was VMS-style or Unix-style.
9901 *
a3e9d8c9 9902 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
f86702cc 9903 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9904 * vector of options; at present, only bit 0 is used, and if set tells
9905 * trim unixpath to try the current default directory as a prefix when
9906 * presented with a possibly ambiguous ... wildcard.
a3e9d8c9 9907 *
9908 * Returns !=0 on success, with trimmed filespec replacing contents of
9909 * fspec, and 0 on failure, with contents of fpsec unchanged.
a0d0e21e 9910 */
f86702cc 9911/*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
a0d0e21e 9912int
2fbb330f 9913Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
a0d0e21e 9914{
a480973c 9915 char *unixified, *unixwild,
f86702cc 9916 *template, *base, *end, *cp1, *cp2;
9917 register int tmplen, reslen = 0, dirs = 0;
a0d0e21e 9918
a3e9d8c9 9919 if (!wildspec || !fspec) return 0;
ebd4d70b
JM
9920
9921 unixwild = PerlMem_malloc(VMS_MAXRSS);
9922 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f 9923 template = unixwild;
a3e9d8c9 9924 if (strpbrk(wildspec,"]>:") != NULL) {
0e5ce2c7 9925 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
367e4b85 9926 PerlMem_free(unixwild);
a480973c
JM
9927 return 0;
9928 }
a3e9d8c9 9929 }
2fbb330f 9930 else {
a480973c
JM
9931 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9932 unixwild[VMS_MAXRSS-1] = 0;
2fbb330f 9933 }
c5375c28 9934 unixified = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9935 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e 9936 if (strpbrk(fspec,"]>:") != NULL) {
0e5ce2c7 9937 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
367e4b85
JM
9938 PerlMem_free(unixwild);
9939 PerlMem_free(unixified);
a480973c
JM
9940 return 0;
9941 }
a0d0e21e 9942 else base = unixified;
a3e9d8c9 9943 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9944 * check to see that final result fits into (isn't longer than) fspec */
9945 reslen = strlen(fspec);
a0d0e21e
LW
9946 }
9947 else base = fspec;
a3e9d8c9 9948
9949 /* No prefix or absolute path on wildcard, so nothing to remove */
9950 if (!*template || *template == '/') {
367e4b85 9951 PerlMem_free(unixwild);
a480973c 9952 if (base == fspec) {
367e4b85 9953 PerlMem_free(unixified);
a480973c
JM
9954 return 1;
9955 }
a3e9d8c9 9956 tmplen = strlen(unixified);
a480973c 9957 if (tmplen > reslen) {
367e4b85 9958 PerlMem_free(unixified);
a480973c
JM
9959 return 0; /* not enough space */
9960 }
a3e9d8c9 9961 /* Copy unixified resultant, including trailing NUL */
9962 memmove(fspec,unixified,tmplen+1);
367e4b85 9963 PerlMem_free(unixified);
a3e9d8c9 9964 return 1;
9965 }
a0d0e21e 9966
f86702cc 9967 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9968 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9969 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9970 for (cp1 = end ;cp1 >= base; cp1--)
9971 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9972 { cp1++; break; }
9973 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
367e4b85
JM
9974 PerlMem_free(unixified);
9975 PerlMem_free(unixwild);
a3e9d8c9 9976 return 1;
9977 }
f86702cc 9978 else {
a480973c 9979 char *tpl, *lcres;
f86702cc 9980 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9981 int ells = 1, totells, segdirs, match;
a480973c 9982 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
f86702cc 9983 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9984
9985 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9986 totells = ells;
9987 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
367e4b85 9988 tpl = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9989 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f86702cc 9990 if (ellipsis == template && opts & 1) {
9991 /* Template begins with an ellipsis. Since we can't tell how many
9992 * directory names at the front of the resultant to keep for an
9993 * arbitrary starting point, we arbitrarily choose the current
9994 * default directory as a starting point. If it's there as a prefix,
9995 * clip it off. If not, fall through and act as if the leading
9996 * ellipsis weren't there (i.e. return shortest possible path that
9997 * could match template).
9998 */
a480973c 9999 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
367e4b85
JM
10000 PerlMem_free(tpl);
10001 PerlMem_free(unixified);
10002 PerlMem_free(unixwild);
a480973c
JM
10003 return 0;
10004 }
f7ddb74a
JM
10005 if (!decc_efs_case_preserve) {
10006 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10007 if (_tolower(*cp1) != _tolower(*cp2)) break;
10008 }
f86702cc 10009 segdirs = dirs - totells; /* Min # of dirs we must have left */
10010 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
10011 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
18a3d61e 10012 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
10013 PerlMem_free(tpl);
10014 PerlMem_free(unixified);
10015 PerlMem_free(unixwild);
f86702cc 10016 return 1;
a3e9d8c9 10017 }
a3e9d8c9 10018 }
f86702cc 10019 /* First off, back up over constant elements at end of path */
10020 if (dirs) {
10021 for (front = end ; front >= base; front--)
10022 if (*front == '/' && !dirs--) { front++; break; }
a3e9d8c9 10023 }
c5375c28 10024 lcres = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 10025 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c
JM
10026 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10027 cp1++,cp2++) {
10028 if (!decc_efs_case_preserve) {
10029 *cp2 = _tolower(*cp1); /* Make lc copy for match */
10030 }
10031 else {
10032 *cp2 = *cp1;
10033 }
10034 }
10035 if (cp1 != '\0') {
367e4b85
JM
10036 PerlMem_free(tpl);
10037 PerlMem_free(unixified);
10038 PerlMem_free(unixwild);
c5375c28 10039 PerlMem_free(lcres);
a480973c 10040 return 0; /* Path too long. */
f7ddb74a 10041 }
f86702cc 10042 lcend = cp2;
10043 *cp2 = '\0'; /* Pick up with memcpy later */
10044 lcfront = lcres + (front - base);
10045 /* Now skip over each ellipsis and try to match the path in front of it. */
10046 while (ells--) {
10047 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10048 if (*(cp1) == '.' && *(cp1+1) == '.' &&
10049 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
10050 if (cp1 < template) break; /* template started with an ellipsis */
10051 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10052 ellipsis = cp1; continue;
10053 }
a480973c 10054 wilddsc.dsc$a_pointer = tpl;
f86702cc 10055 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10056 nextell = cp1;
10057 for (segdirs = 0, cp2 = tpl;
a480973c 10058 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
f86702cc 10059 cp1++, cp2++) {
10060 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
f7ddb74a
JM
10061 else {
10062 if (!decc_efs_case_preserve) {
10063 *cp2 = _tolower(*cp1); /* else lowercase for match */
10064 }
10065 else {
10066 *cp2 = *cp1; /* else preserve case for match */
10067 }
10068 }
f86702cc 10069 if (*cp2 == '/') segdirs++;
10070 }
a480973c 10071 if (cp1 != ellipsis - 1) {
367e4b85
JM
10072 PerlMem_free(tpl);
10073 PerlMem_free(unixified);
10074 PerlMem_free(unixwild);
10075 PerlMem_free(lcres);
a480973c
JM
10076 return 0; /* Path too long */
10077 }
f86702cc 10078 /* Back up at least as many dirs as in template before matching */
10079 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10080 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10081 for (match = 0; cp1 > lcres;) {
10082 resdsc.dsc$a_pointer = cp1;
10083 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10084 match++;
10085 if (match == 1) lcfront = cp1;
10086 }
10087 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10088 }
a480973c 10089 if (!match) {
367e4b85
JM
10090 PerlMem_free(tpl);
10091 PerlMem_free(unixified);
10092 PerlMem_free(unixwild);
10093 PerlMem_free(lcres);
a480973c
JM
10094 return 0; /* Can't find prefix ??? */
10095 }
f86702cc 10096 if (match > 1 && opts & 1) {
10097 /* This ... wildcard could cover more than one set of dirs (i.e.
10098 * a set of similar dir names is repeated). If the template
10099 * contains more than 1 ..., upstream elements could resolve the
10100 * ambiguity, but it's not worth a full backtracking setup here.
10101 * As a quick heuristic, clip off the current default directory
10102 * if it's present to find the trimmed spec, else use the
10103 * shortest string that this ... could cover.
10104 */
10105 char def[NAM$C_MAXRSS+1], *st;
10106
a480973c 10107 if (getcwd(def, sizeof def,0) == NULL) {
827f156d
JM
10108 PerlMem_free(unixified);
10109 PerlMem_free(unixwild);
10110 PerlMem_free(lcres);
10111 PerlMem_free(tpl);
a480973c
JM
10112 return 0;
10113 }
f7ddb74a
JM
10114 if (!decc_efs_case_preserve) {
10115 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10116 if (_tolower(*cp1) != _tolower(*cp2)) break;
10117 }
f86702cc 10118 segdirs = dirs - totells; /* Min # of dirs we must have left */
10119 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10120 if (*cp1 == '\0' && *cp2 == '/') {
18a3d61e 10121 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
10122 PerlMem_free(tpl);
10123 PerlMem_free(unixified);
10124 PerlMem_free(unixwild);
10125 PerlMem_free(lcres);
f86702cc 10126 return 1;
10127 }
10128 /* Nope -- stick with lcfront from above and keep going. */
10129 }
10130 }
18a3d61e 10131 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
367e4b85
JM
10132 PerlMem_free(tpl);
10133 PerlMem_free(unixified);
10134 PerlMem_free(unixwild);
10135 PerlMem_free(lcres);
a3e9d8c9 10136 return 1;
f86702cc 10137 ellipsis = nextell;
a0d0e21e 10138 }
a0d0e21e
LW
10139
10140} /* end of trim_unixpath() */
10141/*}}}*/
10142
a0d0e21e
LW
10143
10144/*
10145 * VMS readdir() routines.
10146 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
a0d0e21e 10147 *
bd3fa61c 10148 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
a0d0e21e
LW
10149 * Minor modifications to original routines.
10150 */
10151
a9852f7c
CB
10152/* readdir may have been redefined by reentr.h, so make sure we get
10153 * the local version for what we do here.
10154 */
10155#ifdef readdir
10156# undef readdir
10157#endif
10158#if !defined(PERL_IMPLICIT_CONTEXT)
10159# define readdir Perl_readdir
10160#else
10161# define readdir(a) Perl_readdir(aTHX_ a)
10162#endif
10163
a0d0e21e
LW
10164 /* Number of elements in vms_versions array */
10165#define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10166
10167/*
10168 * Open a directory, return a handle for later use.
10169 */
10170/*{{{ DIR *opendir(char*name) */
ddcbaa1c 10171DIR *
b8ffc8df 10172Perl_opendir(pTHX_ const char *name)
a0d0e21e 10173{
ddcbaa1c 10174 DIR *dd;
657054d4 10175 char *dir;
61bb5906 10176 Stat_t sb;
657054d4
JM
10177
10178 Newx(dir, VMS_MAXRSS, char);
4846f1d7 10179 if (int_tovmspath(name, dir, NULL) == NULL) {
657054d4 10180 Safefree(dir);
61bb5906 10181 return NULL;
a0d0e21e 10182 }
ada67d10
CB
10183 /* Check access before stat; otherwise stat does not
10184 * accurately report whether it's a directory.
10185 */
a1887106 10186 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
fac786e7 10187 /* cando_by_name has already set errno */
657054d4 10188 Safefree(dir);
ada67d10
CB
10189 return NULL;
10190 }
61bb5906
CB
10191 if (flex_stat(dir,&sb) == -1) return NULL;
10192 if (!S_ISDIR(sb.st_mode)) {
657054d4 10193 Safefree(dir);
61bb5906
CB
10194 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10195 return NULL;
10196 }
61bb5906 10197 /* Get memory for the handle, and the pattern. */
ddcbaa1c 10198 Newx(dd,1,DIR);
a02a5408 10199 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
a0d0e21e
LW
10200
10201 /* Fill in the fields; mainly playing with the descriptor. */
f7ddb74a 10202 sprintf(dd->pattern, "%s*.*",dir);
657054d4 10203 Safefree(dir);
a0d0e21e
LW
10204 dd->context = 0;
10205 dd->count = 0;
657054d4 10206 dd->flags = 0;
a096370a
CB
10207 /* By saying we always want the result of readdir() in unix format, we
10208 * are really saying we want all the escapes removed. Otherwise the caller,
10209 * having no way to know whether it's already in VMS format, might send it
10210 * through tovmsspec again, thus double escaping.
10211 */
10212 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
a0d0e21e
LW
10213 dd->pat.dsc$a_pointer = dd->pattern;
10214 dd->pat.dsc$w_length = strlen(dd->pattern);
10215 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10216 dd->pat.dsc$b_class = DSC$K_CLASS_S;
3bc25146 10217#if defined(USE_ITHREADS)
a02a5408 10218 Newx(dd->mutex,1,perl_mutex);
a9852f7c
CB
10219 MUTEX_INIT( (perl_mutex *) dd->mutex );
10220#else
10221 dd->mutex = NULL;
10222#endif
a0d0e21e
LW
10223
10224 return dd;
10225} /* end of opendir() */
10226/*}}}*/
10227
10228/*
10229 * Set the flag to indicate we want versions or not.
10230 */
10231/*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10232void
ddcbaa1c 10233vmsreaddirversions(DIR *dd, int flag)
a0d0e21e 10234{
657054d4
JM
10235 if (flag)
10236 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10237 else
10238 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
a0d0e21e
LW
10239}
10240/*}}}*/
10241
10242/*
10243 * Free up an opened directory.
10244 */
10245/*{{{ void closedir(DIR *dd)*/
10246void
ddcbaa1c 10247Perl_closedir(DIR *dd)
a0d0e21e 10248{
f7ddb74a
JM
10249 int sts;
10250
10251 sts = lib$find_file_end(&dd->context);
a0d0e21e 10252 Safefree(dd->pattern);
3bc25146 10253#if defined(USE_ITHREADS)
a9852f7c
CB
10254 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10255 Safefree(dd->mutex);
10256#endif
f7ddb74a 10257 Safefree(dd);
a0d0e21e
LW
10258}
10259/*}}}*/
10260
10261/*
10262 * Collect all the version numbers for the current file.
10263 */
10264static void
ddcbaa1c 10265collectversions(pTHX_ DIR *dd)
a0d0e21e
LW
10266{
10267 struct dsc$descriptor_s pat;
10268 struct dsc$descriptor_s res;
ddcbaa1c 10269 struct dirent *e;
657054d4 10270 char *p, *text, *buff;
a0d0e21e
LW
10271 int i;
10272 unsigned long context, tmpsts;
10273
10274 /* Convenient shorthand. */
10275 e = &dd->entry;
10276
10277 /* Add the version wildcard, ignoring the "*.*" put on before */
10278 i = strlen(dd->pattern);
a02a5408 10279 Newx(text,i + e->d_namlen + 3,char);
f7ddb74a
JM
10280 strcpy(text, dd->pattern);
10281 sprintf(&text[i - 3], "%s;*", e->d_name);
a0d0e21e
LW
10282
10283 /* Set up the pattern descriptor. */
10284 pat.dsc$a_pointer = text;
10285 pat.dsc$w_length = i + e->d_namlen - 1;
10286 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10287 pat.dsc$b_class = DSC$K_CLASS_S;
10288
10289 /* Set up result descriptor. */
657054d4 10290 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10291 res.dsc$a_pointer = buff;
657054d4 10292 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
10293 res.dsc$b_dtype = DSC$K_DTYPE_T;
10294 res.dsc$b_class = DSC$K_CLASS_S;
10295
10296 /* Read files, collecting versions. */
10297 for (context = 0, e->vms_verscount = 0;
10298 e->vms_verscount < VERSIZE(e);
10299 e->vms_verscount++) {
657054d4
JM
10300 unsigned long rsts;
10301 unsigned long flags = 0;
10302
10303#ifdef VMS_LONGNAME_SUPPORT
988c775c 10304 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10305#endif
10306 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
a0d0e21e 10307 if (tmpsts == RMS$_NMF || context == 0) break;
748a9306 10308 _ckvmssts(tmpsts);
657054d4 10309 buff[VMS_MAXRSS - 1] = '\0';
748a9306 10310 if ((p = strchr(buff, ';')))
a0d0e21e
LW
10311 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10312 else
10313 e->vms_versions[e->vms_verscount] = -1;
10314 }
10315
748a9306 10316 _ckvmssts(lib$find_file_end(&context));
a0d0e21e 10317 Safefree(text);
657054d4 10318 Safefree(buff);
a0d0e21e
LW
10319
10320} /* end of collectversions() */
10321
10322/*
10323 * Read the next entry from the directory.
10324 */
10325/*{{{ struct dirent *readdir(DIR *dd)*/
ddcbaa1c
CB
10326struct dirent *
10327Perl_readdir(pTHX_ DIR *dd)
a0d0e21e
LW
10328{
10329 struct dsc$descriptor_s res;
657054d4 10330 char *p, *buff;
a0d0e21e 10331 unsigned long int tmpsts;
657054d4
JM
10332 unsigned long rsts;
10333 unsigned long flags = 0;
dca5a913 10334 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
657054d4 10335 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
a0d0e21e
LW
10336
10337 /* Set up result descriptor, and get next file. */
657054d4 10338 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10339 res.dsc$a_pointer = buff;
657054d4 10340 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
10341 res.dsc$b_dtype = DSC$K_DTYPE_T;
10342 res.dsc$b_class = DSC$K_CLASS_S;
657054d4
JM
10343
10344#ifdef VMS_LONGNAME_SUPPORT
988c775c 10345 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10346#endif
10347
10348 tmpsts = lib$find_file
10349 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
4633a7c4
LW
10350 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10351 if (!(tmpsts & 1)) {
10352 set_vaxc_errno(tmpsts);
10353 switch (tmpsts) {
10354 case RMS$_PRV:
c07a80fd 10355 set_errno(EACCES); break;
4633a7c4 10356 case RMS$_DEV:
c07a80fd 10357 set_errno(ENODEV); break;
4633a7c4 10358 case RMS$_DIR:
f282b18d
CB
10359 set_errno(ENOTDIR); break;
10360 case RMS$_FNF: case RMS$_DNF:
c07a80fd 10361 set_errno(ENOENT); break;
4633a7c4
LW
10362 default:
10363 set_errno(EVMSERR);
10364 }
657054d4 10365 Safefree(buff);
4633a7c4
LW
10366 return NULL;
10367 }
10368 dd->count++;
a0d0e21e 10369 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
c43a0d1c
CB
10370 buff[res.dsc$w_length] = '\0';
10371 p = buff + res.dsc$w_length;
10372 while (--p >= buff) if (!isspace(*p)) break;
10373 *p = '\0';
f7ddb74a 10374 if (!decc_efs_case_preserve) {
f7ddb74a 10375 for (p = buff; *p; p++) *p = _tolower(*p);
f7ddb74a 10376 }
a0d0e21e
LW
10377
10378 /* Skip any directory component and just copy the name. */
657054d4 10379 sts = vms_split_path
360732b5 10380 (buff,
657054d4
JM
10381 &v_spec,
10382 &v_len,
10383 &r_spec,
10384 &r_len,
10385 &d_spec,
10386 &d_len,
10387 &n_spec,
10388 &n_len,
10389 &e_spec,
10390 &e_len,
10391 &vs_spec,
10392 &vs_len);
10393
0dddfaca
JM
10394 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10395
10396 /* In Unix report mode, remove the ".dir;1" from the name */
10397 /* if it is a real directory. */
10398 if (decc_filename_unix_report || decc_efs_charset) {
f785e3a1
JM
10399 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10400 Stat_t statbuf;
10401 int ret_sts;
10402
10403 ret_sts = flex_lstat(buff, &statbuf);
10404 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10405 e_len = 0;
10406 e_spec[0] = 0;
0dddfaca
JM
10407 }
10408 }
10409 }
10410
10411 /* Drop NULL extensions on UNIX file specification */
10412 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10413 e_len = 0;
10414 e_spec[0] = '\0';
10415 }
dca5a913
JM
10416 }
10417
657054d4
JM
10418 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10419 dd->entry.d_name[n_len + e_len] = '\0';
10420 dd->entry.d_namlen = strlen(dd->entry.d_name);
a0d0e21e 10421
657054d4
JM
10422 /* Convert the filename to UNIX format if needed */
10423 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10424
10425 /* Translate the encoded characters. */
38a44b82 10426 /* Fixme: Unicode handling could result in embedded 0 characters */
657054d4
JM
10427 if (strchr(dd->entry.d_name, '^') != NULL) {
10428 char new_name[256];
10429 char * q;
657054d4
JM
10430 p = dd->entry.d_name;
10431 q = new_name;
10432 while (*p != 0) {
f617045b
CB
10433 int inchars_read, outchars_added;
10434 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10435 p += inchars_read;
10436 q += outchars_added;
dca5a913 10437 /* fix-me */
f617045b 10438 /* if outchars_added > 1, then this is a wide file specification */
dca5a913 10439 /* Wide file specifications need to be passed in Perl */
38a44b82 10440 /* counted strings apparently with a Unicode flag */
657054d4
JM
10441 }
10442 *q = 0;
10443 strcpy(dd->entry.d_name, new_name);
f617045b 10444 dd->entry.d_namlen = strlen(dd->entry.d_name);
657054d4 10445 }
657054d4 10446 }
a0d0e21e 10447
a0d0e21e 10448 dd->entry.vms_verscount = 0;
657054d4
JM
10449 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10450 Safefree(buff);
a0d0e21e
LW
10451 return &dd->entry;
10452
10453} /* end of readdir() */
10454/*}}}*/
10455
10456/*
a9852f7c
CB
10457 * Read the next entry from the directory -- thread-safe version.
10458 */
10459/*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10460int
ddcbaa1c 10461Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
a9852f7c
CB
10462{
10463 int retval;
10464
10465 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10466
7ded3206 10467 entry = readdir(dd);
a9852f7c
CB
10468 *result = entry;
10469 retval = ( *result == NULL ? errno : 0 );
10470
10471 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10472
10473 return retval;
10474
10475} /* end of readdir_r() */
10476/*}}}*/
10477
10478/*
a0d0e21e
LW
10479 * Return something that can be used in a seekdir later.
10480 */
10481/*{{{ long telldir(DIR *dd)*/
10482long
ddcbaa1c 10483Perl_telldir(DIR *dd)
a0d0e21e
LW
10484{
10485 return dd->count;
10486}
10487/*}}}*/
10488
10489/*
10490 * Return to a spot where we used to be. Brute force.
10491 */
10492/*{{{ void seekdir(DIR *dd,long count)*/
10493void
ddcbaa1c 10494Perl_seekdir(pTHX_ DIR *dd, long count)
a0d0e21e 10495{
657054d4 10496 int old_flags;
a0d0e21e
LW
10497
10498 /* If we haven't done anything yet... */
10499 if (dd->count == 0)
10500 return;
10501
10502 /* Remember some state, and clear it. */
657054d4
JM
10503 old_flags = dd->flags;
10504 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
748a9306 10505 _ckvmssts(lib$find_file_end(&dd->context));
a0d0e21e
LW
10506 dd->context = 0;
10507
10508 /* The increment is in readdir(). */
10509 for (dd->count = 0; dd->count < count; )
f7ddb74a 10510 readdir(dd);
a0d0e21e 10511
657054d4 10512 dd->flags = old_flags;
a0d0e21e
LW
10513
10514} /* end of seekdir() */
10515/*}}}*/
10516
10517/* VMS subprocess management
10518 *
10519 * my_vfork() - just a vfork(), after setting a flag to record that
10520 * the current script is trying a Unix-style fork/exec.
10521 *
10522 * vms_do_aexec() and vms_do_exec() are called in response to the
10523 * perl 'exec' function. If this follows a vfork call, then they
a6d05634 10524 * call out the regular perl routines in doio.c which do an
a0d0e21e
LW
10525 * execvp (for those who really want to try this under VMS).
10526 * Otherwise, they do exactly what the perl docs say exec should
10527 * do - terminate the current script and invoke a new command
10528 * (See below for notes on command syntax.)
10529 *
10530 * do_aspawn() and do_spawn() implement the VMS side of the perl
10531 * 'system' function.
10532 *
10533 * Note on command arguments to perl 'exec' and 'system': When handled
10534 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
eed5d6a1
CB
10535 * are concatenated to form a DCL command string. If the first non-numeric
10536 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
a6d05634 10537 * the command string is handed off to DCL directly. Otherwise,
a0d0e21e
LW
10538 * the first token of the command is taken as the filespec of an image
10539 * to run. The filespec is expanded using a default type of '.EXE' and
3eeba6fb 10540 * the process defaults for device, directory, etc., and if found, the resultant
a0d0e21e 10541 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
3eeba6fb 10542 * the command string as parameters. This is perhaps a bit complicated,
a0d0e21e
LW
10543 * but I hope it will form a happy medium between what VMS folks expect
10544 * from lib$spawn and what Unix folks expect from exec.
10545 */
10546
10547static int vfork_called;
10548
10549/*{{{int my_vfork()*/
10550int
10551my_vfork()
10552{
748a9306 10553 vfork_called++;
a0d0e21e
LW
10554 return vfork();
10555}
10556/*}}}*/
10557
4633a7c4 10558
a0d0e21e 10559static void
218fdd94
CL
10560vms_execfree(struct dsc$descriptor_s *vmscmd)
10561{
10562 if (vmscmd) {
10563 if (vmscmd->dsc$a_pointer) {
c5375c28 10564 PerlMem_free(vmscmd->dsc$a_pointer);
218fdd94 10565 }
c5375c28 10566 PerlMem_free(vmscmd);
4633a7c4
LW
10567 }
10568}
10569
10570static char *
fd8cd3a3 10571setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
a0d0e21e 10572{
4e205ed6 10573 char *junk, *tmps = NULL;
a0d0e21e
LW
10574 register size_t cmdlen = 0;
10575 size_t rlen;
10576 register SV **idx;
2d8e6c8d 10577 STRLEN n_a;
a0d0e21e
LW
10578
10579 idx = mark;
4633a7c4
LW
10580 if (really) {
10581 tmps = SvPV(really,rlen);
10582 if (*tmps) {
10583 cmdlen += rlen + 1;
10584 idx++;
10585 }
a0d0e21e
LW
10586 }
10587
10588 for (idx++; idx <= sp; idx++) {
10589 if (*idx) {
10590 junk = SvPVx(*idx,rlen);
10591 cmdlen += rlen ? rlen + 1 : 0;
10592 }
10593 }
c5375c28 10594 Newx(PL_Cmd, cmdlen+1, char);
a0d0e21e 10595
4633a7c4 10596 if (tmps && *tmps) {
6b88bc9c 10597 strcpy(PL_Cmd,tmps);
a0d0e21e
LW
10598 mark++;
10599 }
6b88bc9c 10600 else *PL_Cmd = '\0';
a0d0e21e
LW
10601 while (++mark <= sp) {
10602 if (*mark) {
3eeba6fb
CB
10603 char *s = SvPVx(*mark,n_a);
10604 if (!*s) continue;
10605 if (*PL_Cmd) strcat(PL_Cmd," ");
10606 strcat(PL_Cmd,s);
a0d0e21e
LW
10607 }
10608 }
6b88bc9c 10609 return PL_Cmd;
a0d0e21e
LW
10610
10611} /* end of setup_argstr() */
10612
4633a7c4 10613
a0d0e21e 10614static unsigned long int
2fbb330f 10615setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
218fdd94 10616 struct dsc$descriptor_s **pvmscmd)
a0d0e21e 10617{
e919cd19
JM
10618 char * vmsspec;
10619 char * resspec;
e886094b
JM
10620 char image_name[NAM$C_MAXRSS+1];
10621 char image_argv[NAM$C_MAXRSS+1];
a0d0e21e 10622 $DESCRIPTOR(defdsc,".EXE");
8012a33e 10623 $DESCRIPTOR(defdsc2,".");
e919cd19 10624 struct dsc$descriptor_s resdsc;
218fdd94 10625 struct dsc$descriptor_s *vmscmd;
a0d0e21e 10626 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3eeba6fb 10627 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
aa779de1 10628 register char *s, *rest, *cp, *wordbreak;
2fbb330f
JM
10629 char * cmd;
10630 int cmdlen;
aa779de1 10631 register int isdcl;
a0d0e21e 10632
c5375c28 10633 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
ebd4d70b 10634 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f 10635
e919cd19
JM
10636 /* vmsspec is a DCL command buffer, not just a filename */
10637 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10638 if (vmsspec == NULL)
10639 _ckvmssts_noperl(SS$_INSFMEM);
10640
10641 resspec = PerlMem_malloc(VMS_MAXRSS);
10642 if (resspec == NULL)
10643 _ckvmssts_noperl(SS$_INSFMEM);
10644
2fbb330f
JM
10645 /* Make a copy for modification */
10646 cmdlen = strlen(incmd);
c5375c28 10647 cmd = PerlMem_malloc(cmdlen+1);
ebd4d70b 10648 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f
JM
10649 strncpy(cmd, incmd, cmdlen);
10650 cmd[cmdlen] = 0;
e886094b
JM
10651 image_name[0] = 0;
10652 image_argv[0] = 0;
2fbb330f 10653
e919cd19
JM
10654 resdsc.dsc$a_pointer = resspec;
10655 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10656 resdsc.dsc$b_class = DSC$K_CLASS_S;
10657 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10658
218fdd94
CL
10659 vmscmd->dsc$a_pointer = NULL;
10660 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10661 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10662 vmscmd->dsc$w_length = 0;
10663 if (pvmscmd) *pvmscmd = vmscmd;
10664
ff7adb52
CL
10665 if (suggest_quote) *suggest_quote = 0;
10666
2fbb330f 10667 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
c5375c28 10668 PerlMem_free(cmd);
e919cd19
JM
10669 PerlMem_free(vmsspec);
10670 PerlMem_free(resspec);
a2669cfc 10671 return CLI$_BUFOVF; /* continuation lines currently unsupported */
2fbb330f
JM
10672 }
10673
a0d0e21e 10674 s = cmd;
2fbb330f 10675
a0d0e21e 10676 while (*s && isspace(*s)) s++;
aa779de1
CB
10677
10678 if (*s == '@' || *s == '$') {
10679 vmsspec[0] = *s; rest = s + 1;
10680 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10681 }
10682 else { cp = vmsspec; rest = s; }
10683 if (*rest == '.' || *rest == '/') {
10684 char *cp2;
10685 for (cp2 = resspec;
e919cd19 10686 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
aa779de1
CB
10687 rest++, cp2++) *cp2 = *rest;
10688 *cp2 = '\0';
df278665 10689 if (int_tovmsspec(resspec, cp, 0, NULL)) {
aa779de1 10690 s = vmsspec;
cfbf46cd
JM
10691
10692 /* When a UNIX spec with no file type is translated to VMS, */
10693 /* A trailing '.' is appended under ODS-5 rules. */
10694 /* Here we do not want that trailing "." as it prevents */
10695 /* Looking for a implied ".exe" type. */
10696 if (decc_efs_charset) {
10697 int i;
10698 i = strlen(vmsspec);
10699 if (vmsspec[i-1] == '.') {
10700 vmsspec[i-1] = '\0';
10701 }
10702 }
10703
aa779de1
CB
10704 if (*rest) {
10705 for (cp2 = vmsspec + strlen(vmsspec);
e919cd19 10706 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
aa779de1
CB
10707 rest++, cp2++) *cp2 = *rest;
10708 *cp2 = '\0';
a0d0e21e
LW
10709 }
10710 }
10711 }
aa779de1
CB
10712 /* Intuit whether verb (first word of cmd) is a DCL command:
10713 * - if first nonspace char is '@', it's a DCL indirection
10714 * otherwise
10715 * - if verb contains a filespec separator, it's not a DCL command
10716 * - if it doesn't, caller tells us whether to default to a DCL
10717 * command, or to a local image unless told it's DCL (by leading '$')
10718 */
ff7adb52
CL
10719 if (*s == '@') {
10720 isdcl = 1;
10721 if (suggest_quote) *suggest_quote = 1;
10722 } else {
aa779de1
CB
10723 register char *filespec = strpbrk(s,":<[.;");
10724 rest = wordbreak = strpbrk(s," \"\t/");
10725 if (!wordbreak) wordbreak = s + strlen(s);
10726 if (*s == '$') check_img = 0;
10727 if (filespec && (filespec < wordbreak)) isdcl = 0;
10728 else isdcl = !check_img;
10729 }
10730
3eeba6fb 10731 if (!isdcl) {
dca5a913 10732 int rsts;
aa779de1
CB
10733 imgdsc.dsc$a_pointer = s;
10734 imgdsc.dsc$w_length = wordbreak - s;
dca5a913 10735 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8012a33e 10736 if (!(retsts&1)) {
ebd4d70b 10737 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10738 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f 10739 if (!(retsts & 1) && *s == '$') {
ebd4d70b 10740 _ckvmssts_noperl(lib$find_file_end(&cxt));
2497a41f 10741 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
dca5a913 10742 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
2497a41f 10743 if (!(retsts&1)) {
ebd4d70b 10744 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10745 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f
JM
10746 }
10747 }
aa779de1 10748 }
ebd4d70b 10749 _ckvmssts_noperl(lib$find_file_end(&cxt));
8012a33e 10750
aa779de1 10751 if (retsts & 1) {
8012a33e 10752 FILE *fp;
a0d0e21e
LW
10753 s = resspec;
10754 while (*s && !isspace(*s)) s++;
10755 *s = '\0';
8012a33e
CB
10756
10757 /* check that it's really not DCL with no file extension */
e886094b 10758 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8012a33e 10759 if (fp) {
2497a41f
JM
10760 char b[256] = {0,0,0,0};
10761 read(fileno(fp), b, 256);
8012a33e 10762 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
2497a41f 10763 if (isdcl) {
e886094b
JM
10764 int shebang_len;
10765
2497a41f 10766 /* Check for script */
e886094b
JM
10767 shebang_len = 0;
10768 if ((b[0] == '#') && (b[1] == '!'))
10769 shebang_len = 2;
10770#ifdef ALTERNATE_SHEBANG
10771 else {
10772 shebang_len = strlen(ALTERNATE_SHEBANG);
10773 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10774 char * perlstr;
10775 perlstr = strstr("perl",b);
10776 if (perlstr == NULL)
10777 shebang_len = 0;
10778 }
10779 else
10780 shebang_len = 0;
10781 }
10782#endif
10783
10784 if (shebang_len > 0) {
10785 int i;
10786 int j;
10787 char tmpspec[NAM$C_MAXRSS + 1];
10788
10789 i = shebang_len;
10790 /* Image is following after white space */
10791 /*--------------------------------------*/
10792 while (isprint(b[i]) && isspace(b[i]))
10793 i++;
10794
10795 j = 0;
10796 while (isprint(b[i]) && !isspace(b[i])) {
10797 tmpspec[j++] = b[i++];
10798 if (j >= NAM$C_MAXRSS)
10799 break;
10800 }
10801 tmpspec[j] = '\0';
10802
10803 /* There may be some default parameters to the image */
10804 /*---------------------------------------------------*/
10805 j = 0;
10806 while (isprint(b[i])) {
10807 image_argv[j++] = b[i++];
10808 if (j >= NAM$C_MAXRSS)
10809 break;
10810 }
10811 while ((j > 0) && !isprint(image_argv[j-1]))
10812 j--;
10813 image_argv[j] = 0;
10814
2497a41f 10815 /* It will need to be converted to VMS format and validated */
e886094b
JM
10816 if (tmpspec[0] != '\0') {
10817 char * iname;
10818
10819 /* Try to find the exact program requested to be run */
10820 /*---------------------------------------------------*/
6fb6c614
JM
10821 iname = int_rmsexpand
10822 (tmpspec, image_name, ".exe",
360732b5 10823 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10824 if (iname != NULL) {
a1887106
JM
10825 if (cando_by_name_int
10826 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
10827 /* MCR prefix needed */
10828 isdcl = 0;
10829 }
10830 else {
10831 /* Try again with a null type */
10832 /*----------------------------*/
6fb6c614
JM
10833 iname = int_rmsexpand
10834 (tmpspec, image_name, ".",
360732b5 10835 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10836 if (iname != NULL) {
a1887106
JM
10837 if (cando_by_name_int
10838 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
10839 /* MCR prefix needed */
10840 isdcl = 0;
10841 }
10842 }
10843 }
10844
10845 /* Did we find the image to run the script? */
10846 /*------------------------------------------*/
10847 if (isdcl) {
10848 char *tchr;
10849
10850 /* Assume DCL or foreign command exists */
10851 /*--------------------------------------*/
10852 tchr = strrchr(tmpspec, '/');
10853 if (tchr != NULL) {
10854 tchr++;
10855 }
10856 else {
10857 tchr = tmpspec;
10858 }
10859 strcpy(image_name, tchr);
10860 }
10861 }
10862 }
2497a41f
JM
10863 }
10864 }
8012a33e
CB
10865 fclose(fp);
10866 }
e919cd19
JM
10867 if (check_img && isdcl) {
10868 PerlMem_free(cmd);
10869 PerlMem_free(resspec);
10870 PerlMem_free(vmsspec);
10871 return RMS$_FNF;
10872 }
8012a33e 10873
3eeba6fb 10874 if (cando_by_name(S_IXUSR,0,resspec)) {
c5375c28 10875 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
ebd4d70b 10876 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8012a33e 10877 if (!isdcl) {
218fdd94 10878 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
e886094b
JM
10879 if (image_name[0] != 0) {
10880 strcat(vmscmd->dsc$a_pointer, image_name);
10881 strcat(vmscmd->dsc$a_pointer, " ");
10882 }
10883 } else if (image_name[0] != 0) {
10884 strcpy(vmscmd->dsc$a_pointer, image_name);
10885 strcat(vmscmd->dsc$a_pointer, " ");
8012a33e 10886 } else {
218fdd94 10887 strcpy(vmscmd->dsc$a_pointer,"@");
8012a33e 10888 }
e886094b
JM
10889 if (suggest_quote) *suggest_quote = 1;
10890
10891 /* If there is an image name, use original command */
10892 if (image_name[0] == 0)
10893 strcat(vmscmd->dsc$a_pointer,resspec);
10894 else {
10895 rest = cmd;
10896 while (*rest && isspace(*rest)) rest++;
10897 }
10898
10899 if (image_argv[0] != 0) {
10900 strcat(vmscmd->dsc$a_pointer,image_argv);
10901 strcat(vmscmd->dsc$a_pointer, " ");
10902 }
10903 if (rest) {
10904 int rest_len;
10905 int vmscmd_len;
10906
10907 rest_len = strlen(rest);
10908 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10909 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10910 strcat(vmscmd->dsc$a_pointer,rest);
10911 else
10912 retsts = CLI$_BUFOVF;
10913 }
218fdd94 10914 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
c5375c28 10915 PerlMem_free(cmd);
e919cd19
JM
10916 PerlMem_free(vmsspec);
10917 PerlMem_free(resspec);
218fdd94 10918 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
3eeba6fb 10919 }
c5375c28
JM
10920 else
10921 retsts = RMS$_PRV;
a0d0e21e
LW
10922 }
10923 }
3eeba6fb 10924 /* It's either a DCL command or we couldn't find a suitable image */
218fdd94 10925 vmscmd->dsc$w_length = strlen(cmd);
ff7adb52 10926
b011c7bd 10927 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
c5375c28 10928 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
b011c7bd 10929 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
c5375c28
JM
10930
10931 PerlMem_free(cmd);
e919cd19
JM
10932 PerlMem_free(resspec);
10933 PerlMem_free(vmsspec);
2fbb330f 10934
ff7adb52
CL
10935 /* check if it's a symbol (for quoting purposes) */
10936 if (suggest_quote && !*suggest_quote) {
10937 int iss;
10938 char equiv[LNM$C_NAMLENGTH];
10939 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10940 eqvdsc.dsc$a_pointer = equiv;
10941
218fdd94 10942 iss = lib$get_symbol(vmscmd,&eqvdsc);
ff7adb52
CL
10943 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10944 }
3eeba6fb
CB
10945 if (!(retsts & 1)) {
10946 /* just hand off status values likely to be due to user error */
10947 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10948 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10949 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
ebd4d70b 10950 else { _ckvmssts_noperl(retsts); }
3eeba6fb 10951 }
a0d0e21e 10952
218fdd94 10953 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
a3e9d8c9 10954
a0d0e21e
LW
10955} /* end of setup_cmddsc() */
10956
a3e9d8c9 10957
a0d0e21e
LW
10958/* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10959bool
fd8cd3a3 10960Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
a0d0e21e 10961{
c5375c28
JM
10962bool exec_sts;
10963char * cmd;
10964
a0d0e21e
LW
10965 if (sp > mark) {
10966 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
10967 vfork_called--;
10968 if (vfork_called < 0) {
5c84aa53 10969 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
10970 vfork_called = 0;
10971 }
10972 else return do_aexec(really,mark,sp);
a0d0e21e 10973 }
4633a7c4 10974 /* no vfork - act VMSish */
c5375c28
JM
10975 cmd = setup_argstr(aTHX_ really,mark,sp);
10976 exec_sts = vms_do_exec(cmd);
10977 Safefree(cmd); /* Clean up from setup_argstr() */
10978 return exec_sts;
a0d0e21e
LW
10979 }
10980
10981 return FALSE;
10982} /* end of vms_do_aexec() */
10983/*}}}*/
10984
10985/* {{{bool vms_do_exec(char *cmd) */
10986bool
2fbb330f 10987Perl_vms_do_exec(pTHX_ const char *cmd)
a0d0e21e 10988{
218fdd94 10989 struct dsc$descriptor_s *vmscmd;
a0d0e21e
LW
10990
10991 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
10992 vfork_called--;
10993 if (vfork_called < 0) {
5c84aa53 10994 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
10995 vfork_called = 0;
10996 }
10997 else return do_exec(cmd);
a0d0e21e 10998 }
748a9306
LW
10999
11000 { /* no vfork - act VMSish */
748a9306 11001 unsigned long int retsts;
a0d0e21e 11002
1e422769 11003 TAINT_ENV();
11004 TAINT_PROPER("exec");
218fdd94
CL
11005 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
11006 retsts = lib$do_command(vmscmd);
a0d0e21e 11007
09b7f37c 11008 switch (retsts) {
f282b18d 11009 case RMS$_FNF: case RMS$_DNF:
09b7f37c 11010 set_errno(ENOENT); break;
f282b18d 11011 case RMS$_DIR:
09b7f37c 11012 set_errno(ENOTDIR); break;
f282b18d
CB
11013 case RMS$_DEV:
11014 set_errno(ENODEV); break;
09b7f37c
CB
11015 case RMS$_PRV:
11016 set_errno(EACCES); break;
11017 case RMS$_SYN:
11018 set_errno(EINVAL); break;
a2669cfc 11019 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
09b7f37c
CB
11020 set_errno(E2BIG); break;
11021 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 11022 _ckvmssts_noperl(retsts); /* fall through */
09b7f37c
CB
11023 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11024 set_errno(EVMSERR);
11025 }
748a9306 11026 set_vaxc_errno(retsts);
3eeba6fb 11027 if (ckWARN(WARN_EXEC)) {
f98bc0c6 11028 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
218fdd94 11029 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
3eeba6fb 11030 }
218fdd94 11031 vms_execfree(vmscmd);
a0d0e21e
LW
11032 }
11033
11034 return FALSE;
11035
11036} /* end of vms_do_exec() */
11037/*}}}*/
11038
9ec7171b 11039int do_spawn2(pTHX_ const char *, int);
a0d0e21e 11040
9ec7171b
CB
11041int
11042Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
a0d0e21e 11043{
c5375c28
JM
11044unsigned long int sts;
11045char * cmd;
eed5d6a1 11046int flags = 0;
a0d0e21e 11047
c5375c28 11048 if (sp > mark) {
eed5d6a1
CB
11049
11050 /* We'll copy the (undocumented?) Win32 behavior and allow a
11051 * numeric first argument. But the only value we'll support
11052 * through do_aspawn is a value of 1, which means spawn without
11053 * waiting for completion -- other values are ignored.
11054 */
9ec7171b 11055 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
eed5d6a1 11056 ++mark;
9ec7171b 11057 flags = SvIVx(*mark);
eed5d6a1
CB
11058 }
11059
11060 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11061 flags = CLI$M_NOWAIT;
11062 else
11063 flags = 0;
11064
9ec7171b 11065 cmd = setup_argstr(aTHX_ really, mark, sp);
eed5d6a1 11066 sts = do_spawn2(aTHX_ cmd, flags);
c5375c28
JM
11067 /* pp_sys will clean up cmd */
11068 return sts;
11069 }
a0d0e21e
LW
11070 return SS$_ABORT;
11071} /* end of do_aspawn() */
11072/*}}}*/
11073
eed5d6a1 11074
9ec7171b
CB
11075/* {{{int do_spawn(char* cmd) */
11076int
11077Perl_do_spawn(pTHX_ char* cmd)
a0d0e21e 11078{
7918f24d
NC
11079 PERL_ARGS_ASSERT_DO_SPAWN;
11080
eed5d6a1
CB
11081 return do_spawn2(aTHX_ cmd, 0);
11082}
11083/*}}}*/
11084
9ec7171b
CB
11085/* {{{int do_spawn_nowait(char* cmd) */
11086int
11087Perl_do_spawn_nowait(pTHX_ char* cmd)
11088{
11089 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11090
11091 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11092}
11093/*}}}*/
11094
11095/* {{{int do_spawn2(char *cmd) */
11096int
eed5d6a1
CB
11097do_spawn2(pTHX_ const char *cmd, int flags)
11098{
209030df 11099 unsigned long int sts, substs;
a0d0e21e 11100
c5375c28
JM
11101 /* The caller of this routine expects to Safefree(PL_Cmd) */
11102 Newx(PL_Cmd,10,char);
11103
1e422769 11104 TAINT_ENV();
11105 TAINT_PROPER("spawn");
748a9306 11106 if (!cmd || !*cmd) {
eed5d6a1 11107 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
c8795d8b
JH
11108 if (!(sts & 1)) {
11109 switch (sts) {
209030df
JH
11110 case RMS$_FNF: case RMS$_DNF:
11111 set_errno(ENOENT); break;
11112 case RMS$_DIR:
11113 set_errno(ENOTDIR); break;
11114 case RMS$_DEV:
11115 set_errno(ENODEV); break;
11116 case RMS$_PRV:
11117 set_errno(EACCES); break;
11118 case RMS$_SYN:
11119 set_errno(EINVAL); break;
11120 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11121 set_errno(E2BIG); break;
11122 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 11123 _ckvmssts_noperl(sts); /* fall through */
209030df
JH
11124 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11125 set_errno(EVMSERR);
c8795d8b
JH
11126 }
11127 set_vaxc_errno(sts);
11128 if (ckWARN(WARN_EXEC)) {
f98bc0c6 11129 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
c8795d8b
JH
11130 Strerror(errno));
11131 }
09b7f37c 11132 }
c8795d8b 11133 sts = substs;
48023aa8
CL
11134 }
11135 else {
eed5d6a1 11136 char mode[3];
2fbb330f 11137 PerlIO * fp;
eed5d6a1
CB
11138 if (flags & CLI$M_NOWAIT)
11139 strcpy(mode, "n");
11140 else
11141 strcpy(mode, "nW");
11142
11143 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
2fbb330f
JM
11144 if (fp != NULL)
11145 my_pclose(fp);
eed5d6a1 11146 /* sts will be the pid in the nowait case */
48023aa8 11147 }
48023aa8 11148 return sts;
eed5d6a1 11149} /* end of do_spawn2() */
a0d0e21e
LW
11150/*}}}*/
11151
bc10a425
CB
11152
11153static unsigned int *sockflags, sockflagsize;
11154
11155/*
11156 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11157 * routines found in some versions of the CRTL can't deal with sockets.
11158 * We don't shim the other file open routines since a socket isn't
11159 * likely to be opened by a name.
11160 */
275feba9
CB
11161/*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11162FILE *my_fdopen(int fd, const char *mode)
bc10a425 11163{
f7ddb74a 11164 FILE *fp = fdopen(fd, mode);
bc10a425
CB
11165
11166 if (fp) {
11167 unsigned int fdoff = fd / sizeof(unsigned int);
2497a41f 11168 Stat_t sbuf; /* native stat; we don't need flex_stat */
bc10a425
CB
11169 if (!sockflagsize || fdoff > sockflagsize) {
11170 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
a02a5408 11171 else Newx (sockflags,fdoff+2,unsigned int);
bc10a425
CB
11172 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11173 sockflagsize = fdoff + 2;
11174 }
312ac60b 11175 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
bc10a425
CB
11176 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11177 }
11178 return fp;
11179
11180}
11181/*}}}*/
11182
11183
11184/*
11185 * Clear the corresponding bit when the (possibly) socket stream is closed.
11186 * There still a small hole: we miss an implicit close which might occur
11187 * via freopen(). >> Todo
11188 */
11189/*{{{ int my_fclose(FILE *fp)*/
11190int my_fclose(FILE *fp) {
11191 if (fp) {
11192 unsigned int fd = fileno(fp);
11193 unsigned int fdoff = fd / sizeof(unsigned int);
11194
e0951028 11195 if (sockflagsize && fdoff < sockflagsize)
bc10a425
CB
11196 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11197 }
11198 return fclose(fp);
11199}
11200/*}}}*/
11201
11202
a0d0e21e
LW
11203/*
11204 * A simple fwrite replacement which outputs itmsz*nitm chars without
11205 * introducing record boundaries every itmsz chars.
22d4bb9c
CB
11206 * We are using fputs, which depends on a terminating null. We may
11207 * well be writing binary data, so we need to accommodate not only
11208 * data with nulls sprinkled in the middle but also data with no null
11209 * byte at the end.
a0d0e21e 11210 */
a15cef0c 11211/*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
a0d0e21e 11212int
a15cef0c 11213my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
a0d0e21e 11214{
2e05a54c
CB
11215 register char *cp, *end, *cpd;
11216 char *data;
bc10a425
CB
11217 register unsigned int fd = fileno(dest);
11218 register unsigned int fdoff = fd / sizeof(unsigned int);
22d4bb9c 11219 int retval;
bc10a425
CB
11220 int bufsize = itmsz * nitm + 1;
11221
11222 if (fdoff < sockflagsize &&
11223 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11224 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11225 return nitm;
11226 }
22d4bb9c 11227
bc10a425 11228 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
22d4bb9c
CB
11229 memcpy( data, src, itmsz*nitm );
11230 data[itmsz*nitm] = '\0';
a0d0e21e 11231
22d4bb9c
CB
11232 end = data + itmsz * nitm;
11233 retval = (int) nitm; /* on success return # items written */
a0d0e21e 11234
22d4bb9c
CB
11235 cpd = data;
11236 while (cpd <= end) {
11237 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11238 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
a0d0e21e 11239 if (cp < end)
22d4bb9c
CB
11240 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11241 cpd = cp + 1;
a0d0e21e
LW
11242 }
11243
bc10a425 11244 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
22d4bb9c 11245 return retval;
a0d0e21e
LW
11246
11247} /* end of my_fwrite() */
11248/*}}}*/
11249
d27fe803
JH
11250/*{{{ int my_flush(FILE *fp)*/
11251int
fd8cd3a3 11252Perl_my_flush(pTHX_ FILE *fp)
d27fe803
JH
11253{
11254 int res;
93948341 11255 if ((res = fflush(fp)) == 0 && fp) {
d27fe803 11256#ifdef VMS_DO_SOCKETS
61bb5906 11257 Stat_t s;
ed1b9de0 11258 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
d27fe803
JH
11259#endif
11260 res = fsync(fileno(fp));
11261 }
22d4bb9c
CB
11262/*
11263 * If the flush succeeded but set end-of-file, we need to clear
11264 * the error because our caller may check ferror(). BTW, this
11265 * probably means we just flushed an empty file.
11266 */
11267 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11268
d27fe803
JH
11269 return res;
11270}
11271/*}}}*/
11272
bf8d1304
JM
11273/* fgetname() is not returning the correct file specifications when
11274 * decc_filename_unix_report mode is active. So we have to have it
11275 * aways return filenames in VMS mode and convert it ourselves.
11276 */
11277
11278/*{{{ char * my_fgetname(FILE *fp, buf)*/
11279char *
11280Perl_my_fgetname(FILE *fp, char * buf) {
11281 char * retname;
11282 char * vms_name;
11283
11284 retname = fgetname(fp, buf, 1);
11285
11286 /* If we are in VMS mode, then we are done */
11287 if (!decc_filename_unix_report || (retname == NULL)) {
11288 return retname;
11289 }
11290
11291 /* Convert this to Unix format */
11292 vms_name = PerlMem_malloc(VMS_MAXRSS + 1);
11293 strcpy(vms_name, retname);
11294 retname = int_tounixspec(vms_name, buf, NULL);
11295 PerlMem_free(vms_name);
11296
11297 return retname;
11298}
11299/*}}}*/
11300
748a9306
LW
11301/*
11302 * Here are replacements for the following Unix routines in the VMS environment:
11303 * getpwuid Get information for a particular UIC or UID
11304 * getpwnam Get information for a named user
11305 * getpwent Get information for each user in the rights database
11306 * setpwent Reset search to the start of the rights database
11307 * endpwent Finish searching for users in the rights database
11308 *
11309 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11310 * (defined in pwd.h), which contains the following fields:-
11311 * struct passwd {
11312 * char *pw_name; Username (in lower case)
11313 * char *pw_passwd; Hashed password
11314 * unsigned int pw_uid; UIC
11315 * unsigned int pw_gid; UIC group number
11316 * char *pw_unixdir; Default device/directory (VMS-style)
11317 * char *pw_gecos; Owner name
11318 * char *pw_dir; Default device/directory (Unix-style)
11319 * char *pw_shell; Default CLI name (eg. DCL)
11320 * };
11321 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11322 *
11323 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11324 * not the UIC member number (eg. what's returned by getuid()),
11325 * getpwuid() can accept either as input (if uid is specified, the caller's
11326 * UIC group is used), though it won't recognise gid=0.
11327 *
11328 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11329 * information about other users in your group or in other groups, respectively.
11330 * If the required privilege is not available, then these routines fill only
11331 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11332 * string).
11333 *
11334 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11335 */
11336
11337/* sizes of various UAF record fields */
11338#define UAI$S_USERNAME 12
11339#define UAI$S_IDENT 31
11340#define UAI$S_OWNER 31
11341#define UAI$S_DEFDEV 31
11342#define UAI$S_DEFDIR 63
11343#define UAI$S_DEFCLI 31
11344#define UAI$S_PWD 8
11345
11346#define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11347 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11348 (uic).uic$v_group != UIC$K_WILD_GROUP)
11349
4633a7c4
LW
11350static char __empty[]= "";
11351static struct passwd __passwd_empty=
748a9306
LW
11352 {(char *) __empty, (char *) __empty, 0, 0,
11353 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11354static int contxt= 0;
11355static struct passwd __pwdcache;
11356static char __pw_namecache[UAI$S_IDENT+1];
11357
748a9306
LW
11358/*
11359 * This routine does most of the work extracting the user information.
11360 */
fd8cd3a3 11361static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
a0d0e21e 11362{
748a9306
LW
11363 static struct {
11364 unsigned char length;
11365 char pw_gecos[UAI$S_OWNER+1];
11366 } owner;
11367 static union uicdef uic;
11368 static struct {
11369 unsigned char length;
11370 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11371 } defdev;
11372 static struct {
11373 unsigned char length;
11374 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11375 } defdir;
11376 static struct {
11377 unsigned char length;
11378 char pw_shell[UAI$S_DEFCLI+1];
11379 } defcli;
11380 static char pw_passwd[UAI$S_PWD+1];
11381
11382 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11383 struct dsc$descriptor_s name_desc;
c07a80fd 11384 unsigned long int sts;
748a9306 11385
4633a7c4 11386 static struct itmlst_3 itmlst[]= {
748a9306
LW
11387 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11388 {sizeof(uic), UAI$_UIC, &uic, &luic},
11389 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11390 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11391 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11392 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11393 {0, 0, NULL, NULL}};
11394
11395 name_desc.dsc$w_length= strlen(name);
11396 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11397 name_desc.dsc$b_class= DSC$K_CLASS_S;
f7ddb74a 11398 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
748a9306
LW
11399
11400/* Note that sys$getuai returns many fields as counted strings. */
c07a80fd 11401 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11402 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11403 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11404 }
11405 else { _ckvmssts(sts); }
11406 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
748a9306
LW
11407
11408 if ((int) owner.length < lowner) lowner= (int) owner.length;
11409 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11410 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11411 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11412 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11413 owner.pw_gecos[lowner]= '\0';
11414 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11415 defcli.pw_shell[ldefcli]= '\0';
11416 if (valid_uic(uic)) {
11417 pwd->pw_uid= uic.uic$l_uic;
11418 pwd->pw_gid= uic.uic$v_group;
11419 }
11420 else
5c84aa53 11421 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
748a9306
LW
11422 pwd->pw_passwd= pw_passwd;
11423 pwd->pw_gecos= owner.pw_gecos;
11424 pwd->pw_dir= defdev.pw_dir;
360732b5 11425 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
748a9306
LW
11426 pwd->pw_shell= defcli.pw_shell;
11427 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11428 int ldir;
11429 ldir= strlen(pwd->pw_unixdir) - 1;
11430 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11431 }
11432 else
11433 strcpy(pwd->pw_unixdir, pwd->pw_dir);
f7ddb74a
JM
11434 if (!decc_efs_case_preserve)
11435 __mystrtolower(pwd->pw_unixdir);
c07a80fd 11436 return 1;
a0d0e21e 11437}
748a9306
LW
11438
11439/*
11440 * Get information for a named user.
11441*/
11442/*{{{struct passwd *getpwnam(char *name)*/
2fbb330f 11443struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
748a9306
LW
11444{
11445 struct dsc$descriptor_s name_desc;
11446 union uicdef uic;
aa689395 11447 unsigned long int status, sts;
748a9306
LW
11448
11449 __pwdcache = __passwd_empty;
fd8cd3a3 11450 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
748a9306
LW
11451 /* We still may be able to determine pw_uid and pw_gid */
11452 name_desc.dsc$w_length= strlen(name);
11453 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11454 name_desc.dsc$b_class= DSC$K_CLASS_S;
11455 name_desc.dsc$a_pointer= (char *) name;
aa689395 11456 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
748a9306
LW
11457 __pwdcache.pw_uid= uic.uic$l_uic;
11458 __pwdcache.pw_gid= uic.uic$v_group;
11459 }
c07a80fd 11460 else {
aa689395 11461 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11462 set_vaxc_errno(sts);
11463 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
c07a80fd 11464 return NULL;
11465 }
aa689395 11466 else { _ckvmssts(sts); }
c07a80fd 11467 }
748a9306 11468 }
748a9306
LW
11469 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11470 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11471 __pwdcache.pw_name= __pw_namecache;
11472 return &__pwdcache;
11473} /* end of my_getpwnam() */
a0d0e21e
LW
11474/*}}}*/
11475
748a9306
LW
11476/*
11477 * Get information for a particular UIC or UID.
11478 * Called by my_getpwent with uid=-1 to list all users.
11479*/
11480/*{{{struct passwd *my_getpwuid(Uid_t uid)*/
fd8cd3a3 11481struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
a0d0e21e 11482{
748a9306
LW
11483 const $DESCRIPTOR(name_desc,__pw_namecache);
11484 unsigned short lname;
11485 union uicdef uic;
11486 unsigned long int status;
11487
11488 if (uid == (unsigned int) -1) {
11489 do {
11490 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11491 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
c07a80fd 11492 set_vaxc_errno(status);
11493 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
748a9306
LW
11494 my_endpwent();
11495 return NULL;
11496 }
11497 else { _ckvmssts(status); }
11498 } while (!valid_uic (uic));
11499 }
11500 else {
11501 uic.uic$l_uic= uid;
c07a80fd 11502 if (!uic.uic$v_group)
76e3520e 11503 uic.uic$v_group= PerlProc_getgid();
748a9306
LW
11504 if (valid_uic(uic))
11505 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11506 else status = SS$_IVIDENT;
c07a80fd 11507 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11508 status == RMS$_PRV) {
11509 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11510 return NULL;
11511 }
11512 else { _ckvmssts(status); }
748a9306
LW
11513 }
11514 __pw_namecache[lname]= '\0';
01b8edb6 11515 __mystrtolower(__pw_namecache);
748a9306
LW
11516
11517 __pwdcache = __passwd_empty;
11518 __pwdcache.pw_name = __pw_namecache;
11519
11520/* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11521 The identifier's value is usually the UIC, but it doesn't have to be,
11522 so if we can, we let fillpasswd update this. */
11523 __pwdcache.pw_uid = uic.uic$l_uic;
11524 __pwdcache.pw_gid = uic.uic$v_group;
11525
fd8cd3a3 11526 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
748a9306 11527 return &__pwdcache;
a0d0e21e 11528
748a9306
LW
11529} /* end of my_getpwuid() */
11530/*}}}*/
11531
11532/*
11533 * Get information for next user.
11534*/
11535/*{{{struct passwd *my_getpwent()*/
fd8cd3a3 11536struct passwd *Perl_my_getpwent(pTHX)
748a9306
LW
11537{
11538 return (my_getpwuid((unsigned int) -1));
11539}
11540/*}}}*/
a0d0e21e 11541
748a9306
LW
11542/*
11543 * Finish searching rights database for users.
11544*/
11545/*{{{void my_endpwent()*/
fd8cd3a3 11546void Perl_my_endpwent(pTHX)
748a9306
LW
11547{
11548 if (contxt) {
11549 _ckvmssts(sys$finish_rdb(&contxt));
11550 contxt= 0;
11551 }
a0d0e21e
LW
11552}
11553/*}}}*/
748a9306 11554
61bb5906
CB
11555#ifdef HOMEGROWN_POSIX_SIGNALS
11556 /* Signal handling routines, pulled into the core from POSIX.xs.
11557 *
11558 * We need these for threads, so they've been rolled into the core,
11559 * rather than left in POSIX.xs.
11560 *
11561 * (DRS, Oct 23, 1997)
11562 */
5b411029 11563
61bb5906
CB
11564 /* sigset_t is atomic under VMS, so these routines are easy */
11565/*{{{int my_sigemptyset(sigset_t *) */
5b411029 11566int my_sigemptyset(sigset_t *set) {
61bb5906
CB
11567 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11568 *set = 0; return 0;
5b411029 11569}
61bb5906
CB
11570/*}}}*/
11571
11572
11573/*{{{int my_sigfillset(sigset_t *)*/
5b411029 11574int my_sigfillset(sigset_t *set) {
61bb5906
CB
11575 int i;
11576 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11577 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11578 return 0;
5b411029 11579}
61bb5906
CB
11580/*}}}*/
11581
11582
11583/*{{{int my_sigaddset(sigset_t *set, int sig)*/
5b411029 11584int my_sigaddset(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; }
11587 *set |= (1 << (sig - 1));
11588 return 0;
5b411029 11589}
61bb5906
CB
11590/*}}}*/
11591
11592
11593/*{{{int my_sigdelset(sigset_t *set, int sig)*/
5b411029 11594int my_sigdelset(sigset_t *set, int sig) {
61bb5906
CB
11595 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11596 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11597 *set &= ~(1 << (sig - 1));
11598 return 0;
5b411029 11599}
61bb5906
CB
11600/*}}}*/
11601
11602
11603/*{{{int my_sigismember(sigset_t *set, int sig)*/
5b411029 11604int my_sigismember(sigset_t *set, int sig) {
61bb5906
CB
11605 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11606 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
73e350d9 11607 return *set & (1 << (sig - 1));
5b411029 11608}
61bb5906 11609/*}}}*/
5b411029 11610
5b411029 11611
61bb5906
CB
11612/*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11613int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11614 sigset_t tempmask;
11615
11616 /* If set and oset are both null, then things are badly wrong. Bail out. */
11617 if ((oset == NULL) && (set == NULL)) {
11618 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5b411029
MB
11619 return -1;
11620 }
5b411029 11621
61bb5906
CB
11622 /* If set's null, then we're just handling a fetch. */
11623 if (set == NULL) {
11624 tempmask = sigblock(0);
11625 }
11626 else {
11627 switch (how) {
11628 case SIG_SETMASK:
11629 tempmask = sigsetmask(*set);
11630 break;
11631 case SIG_BLOCK:
11632 tempmask = sigblock(*set);
11633 break;
11634 case SIG_UNBLOCK:
11635 tempmask = sigblock(0);
11636 sigsetmask(*oset & ~tempmask);
11637 break;
11638 default:
11639 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11640 return -1;
11641 }
11642 }
11643
11644 /* Did they pass us an oset? If so, stick our holding mask into it */
11645 if (oset)
11646 *oset = tempmask;
5b411029 11647
61bb5906 11648 return 0;
5b411029 11649}
61bb5906
CB
11650/*}}}*/
11651#endif /* HOMEGROWN_POSIX_SIGNALS */
11652
5b411029 11653
ff0cee69 11654/* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11655 * my_utime(), and flex_stat(), all of which operate on UTC unless
11656 * VMSISH_TIMES is true.
11657 */
11658/* method used to handle UTC conversions:
11659 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
e518068a 11660 */
ff0cee69 11661static int gmtime_emulation_type;
11662/* number of secs to add to UTC POSIX-style time to get local time */
11663static long int utc_offset_secs;
e518068a 11664
ff0cee69 11665/* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11666 * in vmsish.h. #undef them here so we can call the CRTL routines
11667 * directly.
e518068a 11668 */
11669#undef gmtime
ff0cee69 11670#undef localtime
11671#undef time
11672
61bb5906 11673
a44ceb8e
CB
11674/*
11675 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11676 * qualifier with the extern prefix pragma. This provisional
11677 * hack circumvents this prefix pragma problem in previous
11678 * precompilers.
11679 */
11680#if defined(__VMS_VER) && __VMS_VER >= 70000000
11681# if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11682# pragma __extern_prefix save
11683# pragma __extern_prefix "" /* set to empty to prevent prefixing */
11684# define gmtime decc$__utctz_gmtime
11685# define localtime decc$__utctz_localtime
11686# define time decc$__utc_time
11687# pragma __extern_prefix restore
11688
11689 struct tm *gmtime(), *localtime();
11690
11691# endif
11692#endif
11693
11694
61bb5906
CB
11695static time_t toutc_dst(time_t loc) {
11696 struct tm *rsltmp;
11697
11698 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11699 loc -= utc_offset_secs;
11700 if (rsltmp->tm_isdst) loc -= 3600;
11701 return loc;
11702}
32da55ab 11703#define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11704 ((gmtime_emulation_type || my_time(NULL)), \
11705 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11706 ((secs) - utc_offset_secs))))
11707
11708static time_t toloc_dst(time_t utc) {
11709 struct tm *rsltmp;
11710
11711 utc += utc_offset_secs;
11712 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11713 if (rsltmp->tm_isdst) utc += 3600;
11714 return utc;
11715}
32da55ab 11716#define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11717 ((gmtime_emulation_type || my_time(NULL)), \
11718 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11719 ((secs) + utc_offset_secs))))
11720
22d4bb9c
CB
11721#ifndef RTL_USES_UTC
11722/*
11723
11724 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11725 DST starts on 1st sun of april at 02:00 std time
11726 ends on last sun of october at 02:00 dst time
11727 see the UCX management command reference, SET CONFIG TIMEZONE
11728 for formatting info.
11729
11730 No, it's not as general as it should be, but then again, NOTHING
11731 will handle UK times in a sensible way.
11732*/
11733
11734
11735/*
11736 parse the DST start/end info:
11737 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11738*/
11739
11740static char *
11741tz_parse_startend(char *s, struct tm *w, int *past)
11742{
11743 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11744 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11745 time_t g;
11746
11747 if (!s) return 0;
11748 if (!w) return 0;
11749 if (!past) return 0;
11750
11751 ly = 0;
11752 if (w->tm_year % 4 == 0) ly = 1;
11753 if (w->tm_year % 100 == 0) ly = 0;
11754 if (w->tm_year+1900 % 400 == 0) ly = 1;
11755 if (ly) dinm[1]++;
11756
11757 dozjd = isdigit(*s);
11758 if (*s == 'J' || *s == 'j' || dozjd) {
11759 if (!dozjd && !isdigit(*++s)) return 0;
11760 d = *s++ - '0';
11761 if (isdigit(*s)) {
11762 d = d*10 + *s++ - '0';
11763 if (isdigit(*s)) {
11764 d = d*10 + *s++ - '0';
11765 }
11766 }
11767 if (d == 0) return 0;
11768 if (d > 366) return 0;
11769 d--;
11770 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11771 g = d * 86400;
11772 dozjd = 1;
11773 } else if (*s == 'M' || *s == 'm') {
11774 if (!isdigit(*++s)) return 0;
11775 m = *s++ - '0';
11776 if (isdigit(*s)) m = 10*m + *s++ - '0';
11777 if (*s != '.') return 0;
11778 if (!isdigit(*++s)) return 0;
11779 n = *s++ - '0';
11780 if (n < 1 || n > 5) return 0;
11781 if (*s != '.') return 0;
11782 if (!isdigit(*++s)) return 0;
11783 d = *s++ - '0';
11784 if (d > 6) return 0;
11785 }
11786
11787 if (*s == '/') {
11788 if (!isdigit(*++s)) return 0;
11789 hour = *s++ - '0';
11790 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11791 if (*s == ':') {
11792 if (!isdigit(*++s)) return 0;
11793 min = *s++ - '0';
11794 if (isdigit(*s)) min = 10*min + *s++ - '0';
11795 if (*s == ':') {
11796 if (!isdigit(*++s)) return 0;
11797 sec = *s++ - '0';
11798 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11799 }
11800 }
11801 } else {
11802 hour = 2;
11803 min = 0;
11804 sec = 0;
11805 }
11806
11807 if (dozjd) {
11808 if (w->tm_yday < d) goto before;
11809 if (w->tm_yday > d) goto after;
11810 } else {
11811 if (w->tm_mon+1 < m) goto before;
11812 if (w->tm_mon+1 > m) goto after;
11813
11814 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11815 k = d - j; /* mday of first d */
11816 if (k <= 0) k += 7;
11817 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11818 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11819 if (w->tm_mday < k) goto before;
11820 if (w->tm_mday > k) goto after;
11821 }
11822
11823 if (w->tm_hour < hour) goto before;
11824 if (w->tm_hour > hour) goto after;
11825 if (w->tm_min < min) goto before;
11826 if (w->tm_min > min) goto after;
11827 if (w->tm_sec < sec) goto before;
11828 goto after;
11829
11830before:
11831 *past = 0;
11832 return s;
11833after:
11834 *past = 1;
11835 return s;
11836}
11837
11838
11839
11840
11841/* parse the offset: (+|-)hh[:mm[:ss]] */
11842
11843static char *
11844tz_parse_offset(char *s, int *offset)
11845{
11846 int hour = 0, min = 0, sec = 0;
11847 int neg = 0;
11848 if (!s) return 0;
11849 if (!offset) return 0;
11850
11851 if (*s == '-') {neg++; s++;}
11852 if (*s == '+') s++;
11853 if (!isdigit(*s)) return 0;
11854 hour = *s++ - '0';
11855 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11856 if (hour > 24) return 0;
11857 if (*s == ':') {
11858 if (!isdigit(*++s)) return 0;
11859 min = *s++ - '0';
11860 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11861 if (min > 59) return 0;
11862 if (*s == ':') {
11863 if (!isdigit(*++s)) return 0;
11864 sec = *s++ - '0';
11865 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11866 if (sec > 59) return 0;
11867 }
11868 }
11869
11870 *offset = (hour*60+min)*60 + sec;
11871 if (neg) *offset = -*offset;
11872 return s;
11873}
11874
11875/*
11876 input time is w, whatever type of time the CRTL localtime() uses.
11877 sets dst, the zone, and the gmtoff (seconds)
11878
11879 caches the value of TZ and UCX$TZ env variables; note that
11880 my_setenv looks for these and sets a flag if they're changed
11881 for efficiency.
11882
11883 We have to watch out for the "australian" case (dst starts in
11884 october, ends in april)...flagged by "reverse" and checked by
11885 scanning through the months of the previous year.
11886
11887*/
11888
11889static int
fd8cd3a3 11890tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
22d4bb9c
CB
11891{
11892 time_t when;
11893 struct tm *w2;
11894 char *s,*s2;
11895 char *dstzone, *tz, *s_start, *s_end;
11896 int std_off, dst_off, isdst;
11897 int y, dststart, dstend;
11898 static char envtz[1025]; /* longer than any logical, symbol, ... */
11899 static char ucxtz[1025];
11900 static char reversed = 0;
11901
11902 if (!w) return 0;
11903
11904 if (tz_updated) {
11905 tz_updated = 0;
11906 reversed = -1; /* flag need to check */
11907 envtz[0] = ucxtz[0] = '\0';
11908 tz = my_getenv("TZ",0);
11909 if (tz) strcpy(envtz, tz);
11910 tz = my_getenv("UCX$TZ",0);
11911 if (tz) strcpy(ucxtz, tz);
11912 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11913 }
11914 tz = envtz;
11915 if (!*tz) tz = ucxtz;
11916
11917 s = tz;
11918 while (isalpha(*s)) s++;
11919 s = tz_parse_offset(s, &std_off);
11920 if (!s) return 0;
11921 if (!*s) { /* no DST, hurray we're done! */
11922 isdst = 0;
11923 goto done;
11924 }
11925
11926 dstzone = s;
11927 while (isalpha(*s)) s++;
11928 s2 = tz_parse_offset(s, &dst_off);
11929 if (s2) {
11930 s = s2;
11931 } else {
11932 dst_off = std_off - 3600;
11933 }
11934
11935 if (!*s) { /* default dst start/end?? */
11936 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11937 s = strchr(ucxtz,',');
11938 }
11939 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11940 }
11941 if (*s != ',') return 0;
11942
11943 when = *w;
11944 when = _toutc(when); /* convert to utc */
11945 when = when - std_off; /* convert to pseudolocal time*/
11946
11947 w2 = localtime(&when);
11948 y = w2->tm_year;
11949 s_start = s+1;
11950 s = tz_parse_startend(s_start,w2,&dststart);
11951 if (!s) return 0;
11952 if (*s != ',') return 0;
11953
11954 when = *w;
11955 when = _toutc(when); /* convert to utc */
11956 when = when - dst_off; /* convert to pseudolocal time*/
11957 w2 = localtime(&when);
11958 if (w2->tm_year != y) { /* spans a year, just check one time */
11959 when += dst_off - std_off;
11960 w2 = localtime(&when);
11961 }
11962 s_end = s+1;
11963 s = tz_parse_startend(s_end,w2,&dstend);
11964 if (!s) return 0;
11965
11966 if (reversed == -1) { /* need to check if start later than end */
11967 int j, ds, de;
11968
11969 when = *w;
11970 if (when < 2*365*86400) {
11971 when += 2*365*86400;
11972 } else {
11973 when -= 365*86400;
11974 }
11975 w2 =localtime(&when);
11976 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11977
11978 for (j = 0; j < 12; j++) {
11979 w2 =localtime(&when);
f7ddb74a
JM
11980 tz_parse_startend(s_start,w2,&ds);
11981 tz_parse_startend(s_end,w2,&de);
22d4bb9c
CB
11982 if (ds != de) break;
11983 when += 30*86400;
11984 }
11985 reversed = 0;
11986 if (de && !ds) reversed = 1;
11987 }
11988
11989 isdst = dststart && !dstend;
11990 if (reversed) isdst = dststart || !dstend;
11991
11992done:
11993 if (dst) *dst = isdst;
11994 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11995 if (isdst) tz = dstzone;
11996 if (zone) {
11997 while(isalpha(*tz)) *zone++ = *tz++;
11998 *zone = '\0';
11999 }
12000 return 1;
12001}
12002
12003#endif /* !RTL_USES_UTC */
61bb5906 12004
ff0cee69 12005/* my_time(), my_localtime(), my_gmtime()
61bb5906 12006 * By default traffic in UTC time values, using CRTL gmtime() or
ff0cee69 12007 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
61bb5906
CB
12008 * Note: We need to use these functions even when the CRTL has working
12009 * UTC support, since they also handle C<use vmsish qw(times);>
12010 *
ff0cee69 12011 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
bd3fa61c 12012 * Modified by Charles Bailey <bailey@newman.upenn.edu>
ff0cee69 12013 */
12014
12015/*{{{time_t my_time(time_t *timep)*/
fd8cd3a3 12016time_t Perl_my_time(pTHX_ time_t *timep)
e518068a 12017{
e518068a 12018 time_t when;
61bb5906 12019 struct tm *tm_p;
e518068a 12020
12021 if (gmtime_emulation_type == 0) {
61bb5906
CB
12022 int dstnow;
12023 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
12024 /* results of calls to gmtime() and localtime() */
12025 /* for same &base */
ff0cee69 12026
e518068a 12027 gmtime_emulation_type++;
ff0cee69 12028 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
f675dbe5 12029 char off[LNM$C_NAMLENGTH+1];;
ff0cee69 12030
e518068a 12031 gmtime_emulation_type++;
f675dbe5 12032 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
e518068a 12033 gmtime_emulation_type++;
22d4bb9c 12034 utc_offset_secs = 0;
5c84aa53 12035 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
ff0cee69 12036 }
12037 else { utc_offset_secs = atol(off); }
e518068a 12038 }
ff0cee69 12039 else { /* We've got a working gmtime() */
12040 struct tm gmt, local;
e518068a 12041
ff0cee69 12042 gmt = *tm_p;
12043 tm_p = localtime(&base);
12044 local = *tm_p;
12045 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
12046 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12047 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
12048 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
12049 }
e518068a 12050 }
ff0cee69 12051
12052 when = time(NULL);
61bb5906
CB
12053# ifdef VMSISH_TIME
12054# ifdef RTL_USES_UTC
12055 if (VMSISH_TIME) when = _toloc(when);
12056# else
12057 if (!VMSISH_TIME) when = _toutc(when);
12058# endif
12059# endif
ff0cee69 12060 if (timep != NULL) *timep = when;
12061 return when;
12062
12063} /* end of my_time() */
12064/*}}}*/
12065
12066
12067/*{{{struct tm *my_gmtime(const time_t *timep)*/
12068struct tm *
fd8cd3a3 12069Perl_my_gmtime(pTHX_ const time_t *timep)
ff0cee69 12070{
12071 char *p;
12072 time_t when;
61bb5906 12073 struct tm *rsltmp;
ff0cee69 12074
68dc0745 12075 if (timep == NULL) {
12076 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12077 return NULL;
12078 }
12079 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
ff0cee69 12080
12081 when = *timep;
12082# ifdef VMSISH_TIME
61bb5906
CB
12083 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12084# endif
12085# ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
12086 return gmtime(&when);
12087# else
ff0cee69 12088 /* CRTL localtime() wants local time as input, so does no tz correction */
61bb5906
CB
12089 rsltmp = localtime(&when);
12090 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
12091 return rsltmp;
12092#endif
e518068a 12093} /* end of my_gmtime() */
e518068a 12094/*}}}*/
12095
12096
ff0cee69 12097/*{{{struct tm *my_localtime(const time_t *timep)*/
12098struct tm *
fd8cd3a3 12099Perl_my_localtime(pTHX_ const time_t *timep)
ff0cee69 12100{
22d4bb9c 12101 time_t when, whenutc;
61bb5906 12102 struct tm *rsltmp;
22d4bb9c 12103 int dst, offset;
ff0cee69 12104
68dc0745 12105 if (timep == NULL) {
12106 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12107 return NULL;
12108 }
12109 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
f7ddb74a 12110 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
ff0cee69 12111
12112 when = *timep;
61bb5906 12113# ifdef RTL_USES_UTC
ff0cee69 12114# ifdef VMSISH_TIME
61bb5906 12115 if (VMSISH_TIME) when = _toutc(when);
ff0cee69 12116# endif
61bb5906 12117 /* CRTL localtime() wants UTC as input, does tz correction itself */
ff0cee69 12118 return localtime(&when);
22d4bb9c
CB
12119
12120# else /* !RTL_USES_UTC */
12121 whenutc = when;
61bb5906 12122# ifdef VMSISH_TIME
22d4bb9c
CB
12123 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
12124 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
61bb5906 12125# endif
22d4bb9c
CB
12126 dst = -1;
12127#ifndef RTL_USES_UTC
32af7c23 12128 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
22d4bb9c
CB
12129 when = whenutc - offset; /* pseudolocal time*/
12130 }
61bb5906
CB
12131# endif
12132 /* CRTL localtime() wants local time as input, so does no tz correction */
12133 rsltmp = localtime(&when);
22d4bb9c 12134 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
61bb5906 12135 return rsltmp;
22d4bb9c 12136# endif
ff0cee69 12137
12138} /* end of my_localtime() */
12139/*}}}*/
12140
12141/* Reset definitions for later calls */
12142#define gmtime(t) my_gmtime(t)
12143#define localtime(t) my_localtime(t)
12144#define time(t) my_time(t)
12145
12146
941b3de1
CB
12147/* my_utime - update modification/access time of a file
12148 *
12149 * VMS 7.3 and later implementation
12150 * Only the UTC translation is home-grown. The rest is handled by the
12151 * CRTL utime(), which will take into account the relevant feature
12152 * logicals and ODS-5 volume characteristics for true access times.
12153 *
12154 * pre VMS 7.3 implementation:
12155 * The calling sequence is identical to POSIX utime(), but under
12156 * VMS with ODS-2, only the modification time is changed; ODS-2 does
12157 * not maintain access times. Restrictions differ from the POSIX
ff0cee69 12158 * definition in that the time can be changed as long as the
12159 * caller has permission to execute the necessary IO$_MODIFY $QIO;
12160 * no separate checks are made to insure that the caller is the
12161 * owner of the file or has special privs enabled.
12162 * Code here is based on Joe Meadows' FILE utility.
941b3de1 12163 *
ff0cee69 12164 */
12165
12166/* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12167 * to VMS epoch (01-JAN-1858 00:00:00.00)
12168 * in 100 ns intervals.
12169 */
12170static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12171
94a11853
CB
12172/*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12173int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
ff0cee69 12174{
941b3de1
CB
12175#if __CRTL_VER >= 70300000
12176 struct utimbuf utc_utimes, *utc_utimesp;
12177
12178 if (utimes != NULL) {
12179 utc_utimes.actime = utimes->actime;
12180 utc_utimes.modtime = utimes->modtime;
12181# ifdef VMSISH_TIME
12182 /* If input was local; convert to UTC for sys svc */
12183 if (VMSISH_TIME) {
12184 utc_utimes.actime = _toutc(utimes->actime);
12185 utc_utimes.modtime = _toutc(utimes->modtime);
12186 }
12187# endif
12188 utc_utimesp = &utc_utimes;
12189 }
12190 else {
12191 utc_utimesp = NULL;
12192 }
12193
12194 return utime(file, utc_utimesp);
12195
12196#else /* __CRTL_VER < 70300000 */
12197
ff0cee69 12198 register int i;
f7ddb74a 12199 int sts;
ff0cee69 12200 long int bintime[2], len = 2, lowbit, unixtime,
12201 secscale = 10000000; /* seconds --> 100 ns intervals */
12202 unsigned long int chan, iosb[2], retsts;
12203 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12204 struct FAB myfab = cc$rms_fab;
12205 struct NAM mynam = cc$rms_nam;
12206#if defined (__DECC) && defined (__VAX)
12207 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12208 * at least through VMS V6.1, which causes a type-conversion warning.
12209 */
12210# pragma message save
12211# pragma message disable cvtdiftypes
12212#endif
12213 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12214 struct fibdef myfib;
12215#if defined (__DECC) && defined (__VAX)
12216 /* This should be right after the declaration of myatr, but due
12217 * to a bug in VAX DEC C, this takes effect a statement early.
12218 */
12219# pragma message restore
12220#endif
f7ddb74a 12221 /* cast ok for read only parameter */
ff0cee69 12222 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12223 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12224 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
704c2eb3 12225
ff0cee69 12226 if (file == NULL || *file == '\0') {
941b3de1 12227 SETERRNO(ENOENT, LIB$_INVARG);
ff0cee69 12228 return -1;
12229 }
704c2eb3
JM
12230
12231 /* Convert to VMS format ensuring that it will fit in 255 characters */
6fb6c614 12232 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
941b3de1
CB
12233 SETERRNO(ENOENT, LIB$_INVARG);
12234 return -1;
12235 }
ff0cee69 12236 if (utimes != NULL) {
12237 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
12238 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12239 * Since time_t is unsigned long int, and lib$emul takes a signed long int
12240 * as input, we force the sign bit to be clear by shifting unixtime right
12241 * one bit, then multiplying by an extra factor of 2 in lib$emul().
12242 */
12243 lowbit = (utimes->modtime & 1) ? secscale : 0;
12244 unixtime = (long int) utimes->modtime;
61bb5906
CB
12245# ifdef VMSISH_TIME
12246 /* If input was UTC; convert to local for sys svc */
12247 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
ff0cee69 12248# endif
1a6334fb 12249 unixtime >>= 1; secscale <<= 1;
ff0cee69 12250 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12251 if (!(retsts & 1)) {
941b3de1 12252 SETERRNO(EVMSERR, retsts);
ff0cee69 12253 return -1;
12254 }
12255 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12256 if (!(retsts & 1)) {
941b3de1 12257 SETERRNO(EVMSERR, retsts);
ff0cee69 12258 return -1;
12259 }
12260 }
12261 else {
12262 /* Just get the current time in VMS format directly */
12263 retsts = sys$gettim(bintime);
12264 if (!(retsts & 1)) {
941b3de1 12265 SETERRNO(EVMSERR, retsts);
ff0cee69 12266 return -1;
12267 }
12268 }
12269
12270 myfab.fab$l_fna = vmsspec;
12271 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12272 myfab.fab$l_nam = &mynam;
12273 mynam.nam$l_esa = esa;
12274 mynam.nam$b_ess = (unsigned char) sizeof esa;
12275 mynam.nam$l_rsa = rsa;
12276 mynam.nam$b_rss = (unsigned char) sizeof rsa;
f7ddb74a
JM
12277 if (decc_efs_case_preserve)
12278 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
ff0cee69 12279
12280 /* Look for the file to be affected, letting RMS parse the file
12281 * specification for us as well. I have set errno using only
12282 * values documented in the utime() man page for VMS POSIX.
12283 */
12284 retsts = sys$parse(&myfab,0,0);
12285 if (!(retsts & 1)) {
12286 set_vaxc_errno(retsts);
12287 if (retsts == RMS$_PRV) set_errno(EACCES);
12288 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12289 else set_errno(EVMSERR);
12290 return -1;
12291 }
12292 retsts = sys$search(&myfab,0,0);
12293 if (!(retsts & 1)) {
752635ea 12294 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12295 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 12296 set_vaxc_errno(retsts);
12297 if (retsts == RMS$_PRV) set_errno(EACCES);
12298 else if (retsts == RMS$_FNF) set_errno(ENOENT);
12299 else set_errno(EVMSERR);
12300 return -1;
12301 }
12302
12303 devdsc.dsc$w_length = mynam.nam$b_dev;
f7ddb74a 12304 /* cast ok for read only parameter */
ff0cee69 12305 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12306
12307 retsts = sys$assign(&devdsc,&chan,0,0);
12308 if (!(retsts & 1)) {
752635ea 12309 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12310 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 12311 set_vaxc_errno(retsts);
12312 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
12313 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
12314 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
12315 else set_errno(EVMSERR);
12316 return -1;
12317 }
12318
12319 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12320 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12321
12322 memset((void *) &myfib, 0, sizeof myfib);
22d4bb9c 12323#if defined(__DECC) || defined(__DECCXX)
ff0cee69 12324 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12325 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12326 /* This prevents the revision time of the file being reset to the current
12327 * time as a result of our IO$_MODIFY $QIO. */
12328 myfib.fib$l_acctl = FIB$M_NORECORD;
12329#else
12330 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12331 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12332 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12333#endif
12334 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
752635ea 12335 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12336 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 12337 _ckvmssts(sys$dassgn(chan));
12338 if (retsts & 1) retsts = iosb[0];
12339 if (!(retsts & 1)) {
12340 set_vaxc_errno(retsts);
12341 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12342 else set_errno(EVMSERR);
12343 return -1;
12344 }
12345
12346 return 0;
941b3de1
CB
12347
12348#endif /* #if __CRTL_VER >= 70300000 */
12349
ff0cee69 12350} /* end of my_utime() */
12351/*}}}*/
12352
748a9306 12353/*
2497a41f 12354 * flex_stat, flex_lstat, flex_fstat
748a9306
LW
12355 * basic stat, but gets it right when asked to stat
12356 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12357 */
12358
2497a41f 12359#ifndef _USE_STD_STAT
748a9306
LW
12360/* encode_dev packs a VMS device name string into an integer to allow
12361 * simple comparisons. This can be used, for example, to check whether two
12362 * files are located on the same device, by comparing their encoded device
12363 * names. Even a string comparison would not do, because stat() reuses the
12364 * device name buffer for each call; so without encode_dev, it would be
12365 * necessary to save the buffer and use strcmp (this would mean a number of
12366 * changes to the standard Perl code, to say nothing of what a Perl script
12367 * would have to do.
12368 *
12369 * The device lock id, if it exists, should be unique (unless perhaps compared
12370 * with lock ids transferred from other nodes). We have a lock id if the disk is
12371 * mounted cluster-wide, which is when we tend to get long (host-qualified)
12372 * device names. Thus we use the lock id in preference, and only if that isn't
12373 * available, do we try to pack the device name into an integer (flagged by
12374 * the sign bit (LOCKID_MASK) being set).
12375 *
e518068a 12376 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
748a9306
LW
12377 * name and its encoded form, but it seems very unlikely that we will find
12378 * two files on different disks that share the same encoded device names,
12379 * and even more remote that they will share the same file id (if the test
12380 * is to check for the same file).
12381 *
12382 * A better method might be to use sys$device_scan on the first call, and to
12383 * search for the device, returning an index into the cached array.
cb9e088c 12384 * The number returned would be more intelligible.
748a9306
LW
12385 * This is probably not worth it, and anyway would take quite a bit longer
12386 * on the first call.
12387 */
12388#define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
fd8cd3a3 12389static mydev_t encode_dev (pTHX_ const char *dev)
748a9306
LW
12390{
12391 int i;
12392 unsigned long int f;
aa689395 12393 mydev_t enc;
748a9306
LW
12394 char c;
12395 const char *q;
12396
12397 if (!dev || !dev[0]) return 0;
12398
12399#if LOCKID_MASK
12400 {
12401 struct dsc$descriptor_s dev_desc;
cb9e088c 12402 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
748a9306
LW
12403
12404 /* For cluster-mounted disks, the disk lock identifier is unique, so we
12405 can try that first. */
12406 dev_desc.dsc$w_length = strlen (dev);
12407 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
12408 dev_desc.dsc$b_class = DSC$K_CLASS_S;
f7ddb74a 12409 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
cb9e088c 12410 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
360732b5 12411 if (!$VMS_STATUS_SUCCESS(status)) {
cb9e088c
CB
12412 switch (status) {
12413 case SS$_NOSUCHDEV:
12414 SETERRNO(ENODEV, status);
12415 return 0;
12416 default:
12417 _ckvmssts(status);
12418 }
12419 }
748a9306
LW
12420 if (lockid) return (lockid & ~LOCKID_MASK);
12421 }
a0d0e21e 12422#endif
748a9306
LW
12423
12424 /* Otherwise we try to encode the device name */
12425 enc = 0;
12426 f = 1;
12427 i = 0;
12428 for (q = dev + strlen(dev); q--; q >= dev) {
988c775c
JM
12429 if (*q == ':')
12430 break;
748a9306
LW
12431 if (isdigit (*q))
12432 c= (*q) - '0';
12433 else if (isalpha (toupper (*q)))
12434 c= toupper (*q) - 'A' + (char)10;
12435 else
12436 continue; /* Skip '$'s */
12437 i++;
12438 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
12439 if (i>1) f *= 36;
12440 enc += f * (unsigned long int) c;
12441 }
12442 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
12443
12444} /* end of encode_dev() */
cfcfe586
JM
12445#define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12446 device_no = encode_dev(aTHX_ devname)
12447#else
12448#define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12449 device_no = new_dev_no
2497a41f 12450#endif
748a9306 12451
748a9306
LW
12452static int
12453is_null_device(name)
12454 const char *name;
12455{
2497a41f 12456 if (decc_bug_devnull != 0) {
682e4b71 12457 if (strncmp("/dev/null", name, 9) == 0)
2497a41f
JM
12458 return 1;
12459 }
748a9306
LW
12460 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12461 The underscore prefix, controller letter, and unit number are
12462 independently optional; for our purposes, the colon punctuation
12463 is not. The colon can be trailed by optional directory and/or
12464 filename, but two consecutive colons indicates a nodename rather
12465 than a device. [pr] */
12466 if (*name == '_') ++name;
12467 if (tolower(*name++) != 'n') return 0;
12468 if (tolower(*name++) != 'l') return 0;
12469 if (tolower(*name) == 'a') ++name;
12470 if (*name == '0') ++name;
12471 return (*name++ == ':') && (*name != ':');
12472}
12473
312ac60b
JM
12474static int
12475Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
c07a80fd 12476
46c05374
CB
12477#define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12478
a1887106
JM
12479static I32
12480Perl_cando_by_name_int
12481 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
748a9306 12482{
e538e23f
CB
12483 char usrname[L_cuserid];
12484 struct dsc$descriptor_s usrdsc =
748a9306 12485 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
e538e23f 12486 char *vmsname = NULL, *fileified = NULL;
597c27e2 12487 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
2d9f3838 12488 unsigned short int retlen, trnlnm_iter_count;
748a9306
LW
12489 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12490 union prvdef curprv;
597c27e2
CB
12491 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12492 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12493 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
ada67d10
CB
12494 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12495 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12496 {0,0,0,0}};
12497 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
748a9306 12498 {0,0,0,0}};
ada67d10 12499 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
858aded6 12500 Stat_t st;
6151c65c 12501 static int profile_context = -1;
748a9306
LW
12502
12503 if (!fname || !*fname) return FALSE;
a1887106 12504
e538e23f
CB
12505 /* Make sure we expand logical names, since sys$check_access doesn't */
12506 fileified = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12507 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f 12508 if (!strpbrk(fname,"/]>:")) {
a1887106
JM
12509 strcpy(fileified,fname);
12510 trnlnm_iter_count = 0;
e538e23f 12511 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
2d9f3838
CB
12512 trnlnm_iter_count++;
12513 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
a1887106
JM
12514 }
12515 fname = fileified;
e538e23f
CB
12516 }
12517
12518 vmsname = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12519 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f
CB
12520 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12521 /* Don't know if already in VMS format, so make sure */
360732b5 12522 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
a1887106 12523 PerlMem_free(fileified);
e538e23f 12524 PerlMem_free(vmsname);
a1887106
JM
12525 return FALSE;
12526 }
a1887106
JM
12527 }
12528 else {
e538e23f 12529 strcpy(vmsname,fname);
a5f75d66
AD
12530 }
12531
858aded6 12532 /* sys$check_access needs a file spec, not a directory spec.
312ac60b 12533 * flex_stat now will handle a null thread context during startup.
858aded6 12534 */
e538e23f
CB
12535
12536 retlen = namdsc.dsc$w_length = strlen(vmsname);
12537 if (vmsname[retlen-1] == ']'
12538 || vmsname[retlen-1] == '>'
858aded6 12539 || vmsname[retlen-1] == ':'
46c05374 12540 || (!flex_stat_int(vmsname, &st, 1) &&
312ac60b 12541 S_ISDIR(st.st_mode))) {
e538e23f 12542
a979ce91 12543 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
e538e23f
CB
12544 PerlMem_free(fileified);
12545 PerlMem_free(vmsname);
12546 return FALSE;
12547 }
12548 fname = fileified;
12549 }
858aded6
CB
12550 else {
12551 fname = vmsname;
12552 }
e538e23f
CB
12553
12554 retlen = namdsc.dsc$w_length = strlen(fname);
12555 namdsc.dsc$a_pointer = (char *)fname;
12556
748a9306 12557 switch (bit) {
f282b18d 12558 case S_IXUSR: case S_IXGRP: case S_IXOTH:
360732b5 12559 access = ARM$M_EXECUTE;
597c27e2
CB
12560 flags = CHP$M_READ;
12561 break;
f282b18d 12562 case S_IRUSR: case S_IRGRP: case S_IROTH:
360732b5 12563 access = ARM$M_READ;
597c27e2
CB
12564 flags = CHP$M_READ | CHP$M_USEREADALL;
12565 break;
f282b18d 12566 case S_IWUSR: case S_IWGRP: case S_IWOTH:
360732b5 12567 access = ARM$M_WRITE;
597c27e2
CB
12568 flags = CHP$M_READ | CHP$M_WRITE;
12569 break;
f282b18d 12570 case S_IDUSR: case S_IDGRP: case S_IDOTH:
360732b5 12571 access = ARM$M_DELETE;
597c27e2
CB
12572 flags = CHP$M_READ | CHP$M_WRITE;
12573 break;
748a9306 12574 default:
a1887106
JM
12575 if (fileified != NULL)
12576 PerlMem_free(fileified);
e538e23f
CB
12577 if (vmsname != NULL)
12578 PerlMem_free(vmsname);
748a9306
LW
12579 return FALSE;
12580 }
12581
ada67d10
CB
12582 /* Before we call $check_access, create a user profile with the current
12583 * process privs since otherwise it just uses the default privs from the
baf3cf9c
CB
12584 * UAF and might give false positives or negatives. This only works on
12585 * VMS versions v6.0 and later since that's when sys$create_user_profile
12586 * became available.
ada67d10
CB
12587 */
12588
12589 /* get current process privs and username */
ebd4d70b
JM
12590 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12591 _ckvmssts_noperl(iosb[0]);
ada67d10 12592
baf3cf9c
CB
12593#if defined(__VMS_VER) && __VMS_VER >= 60000000
12594
ada67d10 12595 /* find out the space required for the profile */
ebd4d70b 12596 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
6151c65c 12597 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12598
12599 /* allocate space for the profile and get it filled in */
c5375c28 12600 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
ebd4d70b
JM
12601 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12602 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
6151c65c 12603 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12604
12605 /* use the profile to check access to the file; free profile & analyze results */
6151c65c 12606 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
c5375c28 12607 PerlMem_free(usrprodsc.dsc$a_pointer);
ada67d10 12608 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
baf3cf9c
CB
12609
12610#else
12611
12612 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12613
12614#endif
12615
bbce6d69 12616 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
61bb5906 12617 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
22d4bb9c 12618 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
bbce6d69 12619 set_vaxc_errno(retsts);
12620 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12621 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12622 else set_errno(ENOENT);
a1887106
JM
12623 if (fileified != NULL)
12624 PerlMem_free(fileified);
e538e23f
CB
12625 if (vmsname != NULL)
12626 PerlMem_free(vmsname);
a3e9d8c9 12627 return FALSE;
12628 }
ada67d10 12629 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
a1887106
JM
12630 if (fileified != NULL)
12631 PerlMem_free(fileified);
e538e23f
CB
12632 if (vmsname != NULL)
12633 PerlMem_free(vmsname);
3a385817
GS
12634 return TRUE;
12635 }
ebd4d70b 12636 _ckvmssts_noperl(retsts);
748a9306 12637
a1887106
JM
12638 if (fileified != NULL)
12639 PerlMem_free(fileified);
e538e23f
CB
12640 if (vmsname != NULL)
12641 PerlMem_free(vmsname);
748a9306
LW
12642 return FALSE; /* Should never get here */
12643
a1887106
JM
12644}
12645
12646/* Do the permissions allow some operation? Assumes PL_statcache already set. */
12647/* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12648 * subset of the applicable information.
12649 */
12650bool
12651Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12652{
12653 return cando_by_name_int
12654 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12655} /* end of cando() */
12656/*}}}*/
12657
12658
12659/*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12660I32
12661Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12662{
12663 return cando_by_name_int(bit, effective, fname, 0);
12664
748a9306
LW
12665} /* end of cando_by_name() */
12666/*}}}*/
12667
12668
61bb5906 12669/*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
748a9306 12670int
fd8cd3a3 12671Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
748a9306 12672{
312ac60b 12673 if (!fstat(fd, &statbufp->crtl_stat)) {
75796008 12674 char *cptr;
988c775c
JM
12675 char *vms_filename;
12676 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12677 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
75796008 12678
988c775c
JM
12679 /* Save name for cando by name in VMS format */
12680 cptr = getname(fd, vms_filename, 1);
75796008 12681
988c775c
JM
12682 /* This should not happen, but just in case */
12683 if (cptr == NULL) {
12684 statbufp->st_devnam[0] = 0;
12685 }
12686 else {
12687 /* Make sure that the saved name fits in 255 characters */
6fb6c614 12688 cptr = int_rmsexpand_vms
988c775c
JM
12689 (vms_filename,
12690 statbufp->st_devnam,
6fb6c614 12691 0);
75796008 12692 if (cptr == NULL)
988c775c 12693 statbufp->st_devnam[0] = 0;
75796008 12694 }
988c775c 12695 PerlMem_free(vms_filename);
682e4b71
JM
12696
12697 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12698 VMS_DEVICE_ENCODE
12699 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
2497a41f 12700
61bb5906
CB
12701# ifdef RTL_USES_UTC
12702# ifdef VMSISH_TIME
12703 if (VMSISH_TIME) {
12704 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12705 statbufp->st_atime = _toloc(statbufp->st_atime);
12706 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12707 }
12708# endif
12709# else
ff0cee69 12710# ifdef VMSISH_TIME
12711 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12712# else
12713 if (1) {
12714# endif
61bb5906
CB
12715 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12716 statbufp->st_atime = _toutc(statbufp->st_atime);
12717 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 12718 }
61bb5906 12719#endif
b7ae7a0d 12720 return 0;
12721 }
12722 return -1;
748a9306
LW
12723
12724} /* end of flex_fstat() */
12725/*}}}*/
12726
2497a41f
JM
12727static int
12728Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
748a9306 12729{
312ac60b
JM
12730 char *fileified;
12731 char *temp_fspec;
12732 const char *save_spec;
12733 char *ret_spec;
bbce6d69 12734 int retval = -1;
312ac60b 12735 int efs_hack = 0;
4ee39169 12736 dSAVEDERRNO;
748a9306 12737
312ac60b
JM
12738 if (!fspec) {
12739 errno = EINVAL;
12740 return retval;
12741 }
988c775c 12742
2497a41f 12743 if (decc_bug_devnull != 0) {
312ac60b 12744 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
2497a41f 12745 memset(statbufp,0,sizeof *statbufp);
cfcfe586 12746 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
2497a41f
JM
12747 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12748 statbufp->st_uid = 0x00010001;
12749 statbufp->st_gid = 0x0001;
12750 time((time_t *)&statbufp->st_mtime);
12751 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12752 return 0;
12753 }
748a9306
LW
12754 }
12755
bbce6d69 12756 /* Try for a directory name first. If fspec contains a filename without
61bb5906 12757 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
bbce6d69 12758 * and sea:[wine.dark]water. exist, we prefer the directory here.
12759 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12760 * not sea:[wine.dark]., if the latter exists. If the intended target is
12761 * the file with null type, specify this by calling flex_stat() with
12762 * a '.' at the end of fspec.
2497a41f
JM
12763 *
12764 * If we are in Posix filespec mode, accept the filename as is.
bbce6d69 12765 */
f36b279d
CB
12766
12767
312ac60b
JM
12768 fileified = PerlMem_malloc(VMS_MAXRSS);
12769 if (fileified == NULL)
12770 _ckvmssts_noperl(SS$_INSFMEM);
12771
12772 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12773 if (temp_fspec == NULL)
12774 _ckvmssts_noperl(SS$_INSFMEM);
12775
12776 strcpy(temp_fspec, fspec);
12777
12778 SAVE_ERRNO;
f36b279d 12779
2497a41f
JM
12780#if __CRTL_VER >= 80200000 && !defined(__VAX)
12781 if (decc_posix_compliant_pathnames == 0) {
12782#endif
312ac60b
JM
12783
12784 /* We may be able to optimize this, but in order for fileify_dirspec to
12785 * always return a usuable answer, we have to call vmspath first to
12786 * make sure that it is in VMS directory format, as stat/lstat on 8.3
12787 * can not handle directories in unix format that it does not have read
12788 * access to. Vmspath handles the case where a bare name which could be
12789 * a logical name gets passed.
12790 */
12791 ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12792 if (ret_spec != NULL) {
d94c5a78 12793 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
312ac60b
JM
12794 if (ret_spec != NULL) {
12795 if (lstat_flag == 0)
12796 retval = stat(fileified, &statbufp->crtl_stat);
12797 else
12798 retval = lstat(fileified, &statbufp->crtl_stat);
12799 save_spec = fileified;
12800 }
748a9306 12801 }
312ac60b
JM
12802
12803 if (retval && vms_bug_stat_filename) {
12804
12805 /* We should try again as a vmsified file specification */
12806 /* However Perl traditionally has not done this, which */
12807 /* causes problems with existing tests */
12808
12809 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12810 if (ret_spec != NULL) {
12811 if (lstat_flag == 0)
12812 retval = stat(temp_fspec, &statbufp->crtl_stat);
12813 else
12814 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12815 save_spec = temp_fspec;
12816 }
2497a41f 12817 }
312ac60b 12818
f1db9cda 12819 if (retval) {
312ac60b
JM
12820 /* Last chance - allow multiple dots with out EFS CHARSET */
12821 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12822 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12823 * enable it if it isn't already.
12824 */
12825#if __CRTL_VER >= 70300000 && !defined(__VAX)
12826 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12827 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12828#endif
12829 if (lstat_flag == 0)
12830 retval = stat(fspec, &statbufp->crtl_stat);
12831 else
12832 retval = lstat(fspec, &statbufp->crtl_stat);
12833 save_spec = fspec;
12834#if __CRTL_VER >= 70300000 && !defined(__VAX)
12835 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12836 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12837 efs_hack = 1;
12838 }
12839#endif
f1db9cda 12840 }
312ac60b 12841
2497a41f
JM
12842#if __CRTL_VER >= 80200000 && !defined(__VAX)
12843 } else {
12844 if (lstat_flag == 0)
312ac60b 12845 retval = stat(temp_fspec, &statbufp->crtl_stat);
2497a41f 12846 else
312ac60b 12847 retval = lstat(temp_fspec, &statbufp->crtl_stat);
988c775c 12848 save_spec = temp_fspec;
2497a41f
JM
12849 }
12850#endif
f36b279d
CB
12851
12852#if __CRTL_VER >= 70300000 && !defined(__VAX)
12853 /* As you were... */
12854 if (!decc_efs_charset)
12855 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12856#endif
12857
ff0cee69 12858 if (!retval) {
988c775c 12859 char * cptr;
d584a1c6
JM
12860 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12861
12862 /* If this is an lstat, do not follow the link */
12863 if (lstat_flag)
12864 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12865
312ac60b
JM
12866#if __CRTL_VER >= 70300000 && !defined(__VAX)
12867 /* If we used the efs_hack above, we must also use it here for */
12868 /* perl_cando to work */
12869 if (efs_hack && (decc_efs_charset_index > 0)) {
12870 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12871 }
12872#endif
6fb6c614 12873 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
312ac60b
JM
12874#if __CRTL_VER >= 70300000 && !defined(__VAX)
12875 if (efs_hack && (decc_efs_charset_index > 0)) {
12876 decc$feature_set_value(decc_efs_charset, 1, 0);
12877 }
12878#endif
12879
12880 /* Fix me: If this is NULL then stat found a file, and we could */
12881 /* not convert the specification to VMS - Should never happen */
988c775c
JM
12882 if (cptr == NULL)
12883 statbufp->st_devnam[0] = 0;
12884
682e4b71 12885 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12886 VMS_DEVICE_ENCODE
12887 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
61bb5906
CB
12888# ifdef RTL_USES_UTC
12889# ifdef VMSISH_TIME
12890 if (VMSISH_TIME) {
12891 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12892 statbufp->st_atime = _toloc(statbufp->st_atime);
12893 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12894 }
12895# endif
12896# else
ff0cee69 12897# ifdef VMSISH_TIME
12898 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12899# else
12900 if (1) {
12901# endif
61bb5906
CB
12902 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12903 statbufp->st_atime = _toutc(statbufp->st_atime);
12904 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 12905 }
61bb5906 12906# endif
ff0cee69 12907 }
9543c6b6 12908 /* If we were successful, leave errno where we found it */
4ee39169 12909 if (retval == 0) RESTORE_ERRNO;
acb491c0
CB
12910 PerlMem_free(temp_fspec);
12911 PerlMem_free(fileified);
748a9306
LW
12912 return retval;
12913
2497a41f
JM
12914} /* end of flex_stat_int() */
12915
12916
12917/*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12918int
12919Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12920{
7ded3206 12921 return flex_stat_int(fspec, statbufp, 0);
2497a41f
JM
12922}
12923/*}}}*/
12924
12925/*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12926int
12927Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12928{
7ded3206 12929 return flex_stat_int(fspec, statbufp, 1);
2497a41f 12930}
748a9306
LW
12931/*}}}*/
12932
b7ae7a0d 12933
c07a80fd 12934/*{{{char *my_getlogin()*/
12935/* VMS cuserid == Unix getlogin, except calling sequence */
12936char *
2fbb330f 12937my_getlogin(void)
c07a80fd 12938{
12939 static char user[L_cuserid];
12940 return cuserid(user);
12941}
12942/*}}}*/
12943
12944
a5f75d66
AD
12945/* rmscopy - copy a file using VMS RMS routines
12946 *
12947 * Copies contents and attributes of spec_in to spec_out, except owner
12948 * and protection information. Name and type of spec_in are used as
a3e9d8c9 12949 * defaults for spec_out. The third parameter specifies whether rmscopy()
12950 * should try to propagate timestamps from the input file to the output file.
12951 * If it is less than 0, no timestamps are preserved. If it is 0, then
12952 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12953 * propagated to the output file at creation iff the output file specification
12954 * did not contain an explicit name or type, and the revision date is always
12955 * updated at the end of the copy operation. If it is greater than 0, then
12956 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12957 * other than the revision date should be propagated, and bit 1 indicates
12958 * that the revision date should be propagated.
12959 *
12960 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
a5f75d66 12961 *
bd3fa61c 12962 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
a5f75d66 12963 * Incorporates, with permission, some code from EZCOPY by Tim Adye
01b8edb6 12964 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12965 * as part of the Perl standard distribution under the terms of the
12966 * GNU General Public License or the Perl Artistic License. Copies
12967 * of each may be found in the Perl standard distribution.
a480973c 12968 */ /* FIXME */
a3e9d8c9 12969/*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
a480973c
JM
12970int
12971Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12972{
d584a1c6
JM
12973 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12974 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
a480973c 12975 unsigned long int i, sts, sts2;
a1887106 12976 int dna_len;
a480973c
JM
12977 struct FAB fab_in, fab_out;
12978 struct RAB rab_in, rab_out;
a1887106
JM
12979 rms_setup_nam(nam);
12980 rms_setup_nam(nam_out);
a480973c
JM
12981 struct XABDAT xabdat;
12982 struct XABFHC xabfhc;
12983 struct XABRDT xabrdt;
12984 struct XABSUM xabsum;
12985
c5375c28 12986 vmsin = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12987 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 12988 vmsout = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12989 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
df278665
JM
12990 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12991 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
c5375c28
JM
12992 PerlMem_free(vmsin);
12993 PerlMem_free(vmsout);
a480973c
JM
12994 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12995 return 0;
12996 }
12997
b1a8dcd7 12998 esa = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12999 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13000 esal = NULL;
13001#if !defined(__VAX) && defined(NAML$C_MAXRSS)
13002 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13003 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13004#endif
a480973c 13005 fab_in = cc$rms_fab;
a1887106 13006 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
a480973c
JM
13007 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
13008 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
13009 fab_in.fab$l_fop = FAB$M_SQO;
a1887106 13010 rms_bind_fab_nam(fab_in, nam);
a480973c
JM
13011 fab_in.fab$l_xab = (void *) &xabdat;
13012
b1a8dcd7 13013 rsa = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13014 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13015 rsal = NULL;
13016#if !defined(__VAX) && defined(NAML$C_MAXRSS)
13017 rsal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13018 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13019#endif
13020 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
13021 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
a1887106
JM
13022 rms_nam_esl(nam) = 0;
13023 rms_nam_rsl(nam) = 0;
13024 rms_nam_esll(nam) = 0;
13025 rms_nam_rsll(nam) = 0;
a480973c
JM
13026#ifdef NAM$M_NO_SHORT_UPCASE
13027 if (decc_efs_case_preserve)
a1887106 13028 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
a480973c
JM
13029#endif
13030
13031 xabdat = cc$rms_xabdat; /* To get creation date */
13032 xabdat.xab$l_nxt = (void *) &xabfhc;
13033
13034 xabfhc = cc$rms_xabfhc; /* To get record length */
13035 xabfhc.xab$l_nxt = (void *) &xabsum;
13036
13037 xabsum = cc$rms_xabsum; /* To get key and area information */
13038
13039 if (!((sts = sys$open(&fab_in)) & 1)) {
c5375c28
JM
13040 PerlMem_free(vmsin);
13041 PerlMem_free(vmsout);
13042 PerlMem_free(esa);
d584a1c6
JM
13043 if (esal != NULL)
13044 PerlMem_free(esal);
c5375c28 13045 PerlMem_free(rsa);
d584a1c6
JM
13046 if (rsal != NULL)
13047 PerlMem_free(rsal);
a480973c
JM
13048 set_vaxc_errno(sts);
13049 switch (sts) {
13050 case RMS$_FNF: case RMS$_DNF:
13051 set_errno(ENOENT); break;
13052 case RMS$_DIR:
13053 set_errno(ENOTDIR); break;
13054 case RMS$_DEV:
13055 set_errno(ENODEV); break;
13056 case RMS$_SYN:
13057 set_errno(EINVAL); break;
13058 case RMS$_PRV:
13059 set_errno(EACCES); break;
13060 default:
13061 set_errno(EVMSERR);
13062 }
13063 return 0;
13064 }
13065
13066 nam_out = nam;
13067 fab_out = fab_in;
13068 fab_out.fab$w_ifi = 0;
13069 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13070 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13071 fab_out.fab$l_fop = FAB$M_SQO;
a1887106
JM
13072 rms_bind_fab_nam(fab_out, nam_out);
13073 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13074 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13075 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
d584a1c6 13076 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 13077 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13078 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 13079 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13080 esal_out = NULL;
13081 rsal_out = NULL;
13082#if !defined(__VAX) && defined(NAML$C_MAXRSS)
13083 esal_out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13084 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13085 rsal_out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13086 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13087#endif
13088 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13089 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
a480973c
JM
13090
13091 if (preserve_dates == 0) { /* Act like DCL COPY */
a1887106 13092 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
a480973c 13093 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
a1887106 13094 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
13095 PerlMem_free(vmsin);
13096 PerlMem_free(vmsout);
13097 PerlMem_free(esa);
d584a1c6
JM
13098 if (esal != NULL)
13099 PerlMem_free(esal);
c5375c28 13100 PerlMem_free(rsa);
d584a1c6
JM
13101 if (rsal != NULL)
13102 PerlMem_free(rsal);
c5375c28 13103 PerlMem_free(esa_out);
d584a1c6
JM
13104 if (esal_out != NULL)
13105 PerlMem_free(esal_out);
13106 PerlMem_free(rsa_out);
13107 if (rsal_out != NULL)
13108 PerlMem_free(rsal_out);
a480973c
JM
13109 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13110 set_vaxc_errno(sts);
13111 return 0;
13112 }
13113 fab_out.fab$l_xab = (void *) &xabdat;
a1887106
JM
13114 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13115 preserve_dates = 1;
a480973c
JM
13116 }
13117 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
13118 preserve_dates =0; /* bitmask from this point forward */
13119
13120 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
a1887106 13121 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
13122 PerlMem_free(vmsin);
13123 PerlMem_free(vmsout);
13124 PerlMem_free(esa);
d584a1c6
JM
13125 if (esal != NULL)
13126 PerlMem_free(esal);
c5375c28 13127 PerlMem_free(rsa);
d584a1c6
JM
13128 if (rsal != NULL)
13129 PerlMem_free(rsal);
c5375c28 13130 PerlMem_free(esa_out);
d584a1c6
JM
13131 if (esal_out != NULL)
13132 PerlMem_free(esal_out);
13133 PerlMem_free(rsa_out);
13134 if (rsal_out != NULL)
13135 PerlMem_free(rsal_out);
a480973c
JM
13136 set_vaxc_errno(sts);
13137 switch (sts) {
13138 case RMS$_DNF:
13139 set_errno(ENOENT); break;
13140 case RMS$_DIR:
13141 set_errno(ENOTDIR); break;
13142 case RMS$_DEV:
13143 set_errno(ENODEV); break;
13144 case RMS$_SYN:
13145 set_errno(EINVAL); break;
13146 case RMS$_PRV:
13147 set_errno(EACCES); break;
13148 default:
13149 set_errno(EVMSERR);
13150 }
13151 return 0;
13152 }
13153 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
13154 if (preserve_dates & 2) {
13155 /* sys$close() will process xabrdt, not xabdat */
13156 xabrdt = cc$rms_xabrdt;
13157#ifndef __GNUC__
13158 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13159#else
13160 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13161 * is unsigned long[2], while DECC & VAXC use a struct */
13162 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13163#endif
13164 fab_out.fab$l_xab = (void *) &xabrdt;
13165 }
13166
c5375c28 13167 ubf = PerlMem_malloc(32256);
ebd4d70b 13168 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c
JM
13169 rab_in = cc$rms_rab;
13170 rab_in.rab$l_fab = &fab_in;
13171 rab_in.rab$l_rop = RAB$M_BIO;
13172 rab_in.rab$l_ubf = ubf;
13173 rab_in.rab$w_usz = 32256;
13174 if (!((sts = sys$connect(&rab_in)) & 1)) {
13175 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
13176 PerlMem_free(vmsin);
13177 PerlMem_free(vmsout);
c5375c28 13178 PerlMem_free(ubf);
d584a1c6
JM
13179 PerlMem_free(esa);
13180 if (esal != NULL)
13181 PerlMem_free(esal);
c5375c28 13182 PerlMem_free(rsa);
d584a1c6
JM
13183 if (rsal != NULL)
13184 PerlMem_free(rsal);
c5375c28 13185 PerlMem_free(esa_out);
d584a1c6
JM
13186 if (esal_out != NULL)
13187 PerlMem_free(esal_out);
13188 PerlMem_free(rsa_out);
13189 if (rsal_out != NULL)
13190 PerlMem_free(rsal_out);
a480973c
JM
13191 set_errno(EVMSERR); set_vaxc_errno(sts);
13192 return 0;
13193 }
13194
13195 rab_out = cc$rms_rab;
13196 rab_out.rab$l_fab = &fab_out;
13197 rab_out.rab$l_rbf = ubf;
13198 if (!((sts = sys$connect(&rab_out)) & 1)) {
13199 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
13200 PerlMem_free(vmsin);
13201 PerlMem_free(vmsout);
c5375c28 13202 PerlMem_free(ubf);
d584a1c6
JM
13203 PerlMem_free(esa);
13204 if (esal != NULL)
13205 PerlMem_free(esal);
c5375c28 13206 PerlMem_free(rsa);
d584a1c6
JM
13207 if (rsal != NULL)
13208 PerlMem_free(rsal);
c5375c28 13209 PerlMem_free(esa_out);
d584a1c6
JM
13210 if (esal_out != NULL)
13211 PerlMem_free(esal_out);
13212 PerlMem_free(rsa_out);
13213 if (rsal_out != NULL)
13214 PerlMem_free(rsal_out);
a480973c
JM
13215 set_errno(EVMSERR); set_vaxc_errno(sts);
13216 return 0;
13217 }
13218
13219 while ((sts = sys$read(&rab_in))) { /* always true */
13220 if (sts == RMS$_EOF) break;
13221 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13222 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13223 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
13224 PerlMem_free(vmsin);
13225 PerlMem_free(vmsout);
c5375c28 13226 PerlMem_free(ubf);
d584a1c6
JM
13227 PerlMem_free(esa);
13228 if (esal != NULL)
13229 PerlMem_free(esal);
c5375c28 13230 PerlMem_free(rsa);
d584a1c6
JM
13231 if (rsal != NULL)
13232 PerlMem_free(rsal);
c5375c28 13233 PerlMem_free(esa_out);
d584a1c6
JM
13234 if (esal_out != NULL)
13235 PerlMem_free(esal_out);
13236 PerlMem_free(rsa_out);
13237 if (rsal_out != NULL)
13238 PerlMem_free(rsal_out);
a480973c
JM
13239 set_errno(EVMSERR); set_vaxc_errno(sts);
13240 return 0;
13241 }
13242 }
13243
13244
13245 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
13246 sys$close(&fab_in); sys$close(&fab_out);
13247 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
a480973c 13248
c5375c28
JM
13249 PerlMem_free(vmsin);
13250 PerlMem_free(vmsout);
c5375c28 13251 PerlMem_free(ubf);
d584a1c6
JM
13252 PerlMem_free(esa);
13253 if (esal != NULL)
13254 PerlMem_free(esal);
c5375c28 13255 PerlMem_free(rsa);
d584a1c6
JM
13256 if (rsal != NULL)
13257 PerlMem_free(rsal);
c5375c28 13258 PerlMem_free(esa_out);
d584a1c6
JM
13259 if (esal_out != NULL)
13260 PerlMem_free(esal_out);
13261 PerlMem_free(rsa_out);
13262 if (rsal_out != NULL)
13263 PerlMem_free(rsal_out);
13264
13265 if (!(sts & 1)) {
13266 set_errno(EVMSERR); set_vaxc_errno(sts);
13267 return 0;
13268 }
13269
a480973c
JM
13270 return 1;
13271
13272} /* end of rmscopy() */
a5f75d66
AD
13273/*}}}*/
13274
13275
748a9306
LW
13276/*** The following glue provides 'hooks' to make some of the routines
13277 * from this file available from Perl. These routines are sufficiently
13278 * basic, and are required sufficiently early in the build process,
13279 * that's it's nice to have them available to miniperl as well as the
13280 * full Perl, so they're set up here instead of in an extension. The
13281 * Perl code which handles importation of these names into a given
13282 * package lives in [.VMS]Filespec.pm in @INC.
13283 */
13284
13285void
5c84aa53 13286rmsexpand_fromperl(pTHX_ CV *cv)
01b8edb6 13287{
13288 dXSARGS;
bbce6d69 13289 char *fspec, *defspec = NULL, *rslt;
2d8e6c8d 13290 STRLEN n_a;
360732b5 13291 int fs_utf8, dfs_utf8;
01b8edb6 13292
360732b5
JM
13293 fs_utf8 = 0;
13294 dfs_utf8 = 0;
bbce6d69 13295 if (!items || items > 2)
5c84aa53 13296 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
2d8e6c8d 13297 fspec = SvPV(ST(0),n_a);
360732b5 13298 fs_utf8 = SvUTF8(ST(0));
bbce6d69 13299 if (!fspec || !*fspec) XSRETURN_UNDEF;
360732b5
JM
13300 if (items == 2) {
13301 defspec = SvPV(ST(1),n_a);
13302 dfs_utf8 = SvUTF8(ST(1));
13303 }
13304 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
bbce6d69 13305 ST(0) = sv_newmortal();
360732b5
JM
13306 if (rslt != NULL) {
13307 sv_usepvn(ST(0),rslt,strlen(rslt));
13308 if (fs_utf8) {
13309 SvUTF8_on(ST(0));
13310 }
13311 }
740ce14c 13312 XSRETURN(1);
01b8edb6 13313}
13314
13315void
5c84aa53 13316vmsify_fromperl(pTHX_ CV *cv)
748a9306
LW
13317{
13318 dXSARGS;
13319 char *vmsified;
2d8e6c8d 13320 STRLEN n_a;
360732b5 13321 int utf8_fl;
748a9306 13322
5c84aa53 13323 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
360732b5
JM
13324 utf8_fl = SvUTF8(ST(0));
13325 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13326 ST(0) = sv_newmortal();
360732b5
JM
13327 if (vmsified != NULL) {
13328 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13329 if (utf8_fl) {
13330 SvUTF8_on(ST(0));
13331 }
13332 }
748a9306
LW
13333 XSRETURN(1);
13334}
13335
13336void
5c84aa53 13337unixify_fromperl(pTHX_ CV *cv)
748a9306
LW
13338{
13339 dXSARGS;
13340 char *unixified;
2d8e6c8d 13341 STRLEN n_a;
360732b5 13342 int utf8_fl;
748a9306 13343
5c84aa53 13344 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
360732b5
JM
13345 utf8_fl = SvUTF8(ST(0));
13346 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13347 ST(0) = sv_newmortal();
360732b5
JM
13348 if (unixified != NULL) {
13349 sv_usepvn(ST(0),unixified,strlen(unixified));
13350 if (utf8_fl) {
13351 SvUTF8_on(ST(0));
13352 }
13353 }
748a9306
LW
13354 XSRETURN(1);
13355}
13356
13357void
5c84aa53 13358fileify_fromperl(pTHX_ CV *cv)
748a9306
LW
13359{
13360 dXSARGS;
13361 char *fileified;
2d8e6c8d 13362 STRLEN n_a;
360732b5 13363 int utf8_fl;
748a9306 13364
5c84aa53 13365 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
360732b5
JM
13366 utf8_fl = SvUTF8(ST(0));
13367 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13368 ST(0) = sv_newmortal();
360732b5
JM
13369 if (fileified != NULL) {
13370 sv_usepvn(ST(0),fileified,strlen(fileified));
13371 if (utf8_fl) {
13372 SvUTF8_on(ST(0));
13373 }
13374 }
748a9306
LW
13375 XSRETURN(1);
13376}
13377
13378void
5c84aa53 13379pathify_fromperl(pTHX_ CV *cv)
748a9306
LW
13380{
13381 dXSARGS;
13382 char *pathified;
2d8e6c8d 13383 STRLEN n_a;
360732b5 13384 int utf8_fl;
748a9306 13385
5c84aa53 13386 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
360732b5
JM
13387 utf8_fl = SvUTF8(ST(0));
13388 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13389 ST(0) = sv_newmortal();
360732b5
JM
13390 if (pathified != NULL) {
13391 sv_usepvn(ST(0),pathified,strlen(pathified));
13392 if (utf8_fl) {
13393 SvUTF8_on(ST(0));
13394 }
13395 }
748a9306
LW
13396 XSRETURN(1);
13397}
13398
13399void
5c84aa53 13400vmspath_fromperl(pTHX_ CV *cv)
748a9306
LW
13401{
13402 dXSARGS;
13403 char *vmspath;
2d8e6c8d 13404 STRLEN n_a;
360732b5 13405 int utf8_fl;
748a9306 13406
5c84aa53 13407 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
360732b5
JM
13408 utf8_fl = SvUTF8(ST(0));
13409 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13410 ST(0) = sv_newmortal();
360732b5
JM
13411 if (vmspath != NULL) {
13412 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13413 if (utf8_fl) {
13414 SvUTF8_on(ST(0));
13415 }
13416 }
748a9306
LW
13417 XSRETURN(1);
13418}
13419
13420void
5c84aa53 13421unixpath_fromperl(pTHX_ CV *cv)
748a9306
LW
13422{
13423 dXSARGS;
13424 char *unixpath;
2d8e6c8d 13425 STRLEN n_a;
360732b5 13426 int utf8_fl;
748a9306 13427
5c84aa53 13428 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
360732b5
JM
13429 utf8_fl = SvUTF8(ST(0));
13430 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13431 ST(0) = sv_newmortal();
360732b5
JM
13432 if (unixpath != NULL) {
13433 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13434 if (utf8_fl) {
13435 SvUTF8_on(ST(0));
13436 }
13437 }
748a9306
LW
13438 XSRETURN(1);
13439}
13440
13441void
5c84aa53 13442candelete_fromperl(pTHX_ CV *cv)
748a9306
LW
13443{
13444 dXSARGS;
988c775c 13445 char *fspec, *fsp;
a5f75d66
AD
13446 SV *mysv;
13447 IO *io;
2d8e6c8d 13448 STRLEN n_a;
748a9306 13449
5c84aa53 13450 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
a5f75d66
AD
13451
13452 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
988c775c
JM
13453 Newx(fspec, VMS_MAXRSS, char);
13454 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
6d24fbd1 13455 if (isGV_with_GP(mysv)) {
a15cef0c 13456 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
a5f75d66 13457 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13458 ST(0) = &PL_sv_no;
988c775c 13459 Safefree(fspec);
a5f75d66
AD
13460 XSRETURN(1);
13461 }
13462 fsp = fspec;
13463 }
13464 else {
2d8e6c8d 13465 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
a5f75d66 13466 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13467 ST(0) = &PL_sv_no;
988c775c 13468 Safefree(fspec);
a5f75d66
AD
13469 XSRETURN(1);
13470 }
13471 }
13472
54310121 13473 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
988c775c 13474 Safefree(fspec);
a5f75d66
AD
13475 XSRETURN(1);
13476}
13477
13478void
5c84aa53 13479rmscopy_fromperl(pTHX_ CV *cv)
a5f75d66
AD
13480{
13481 dXSARGS;
a480973c 13482 char *inspec, *outspec, *inp, *outp;
a3e9d8c9 13483 int date_flag;
a5f75d66
AD
13484 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13485 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13486 unsigned long int sts;
13487 SV *mysv;
13488 IO *io;
2d8e6c8d 13489 STRLEN n_a;
a5f75d66 13490
a3e9d8c9 13491 if (items < 2 || items > 3)
5c84aa53 13492 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
a5f75d66
AD
13493
13494 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
a480973c 13495 Newx(inspec, VMS_MAXRSS, char);
6d24fbd1 13496 if (isGV_with_GP(mysv)) {
a15cef0c 13497 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
a5f75d66 13498 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13499 ST(0) = sv_2mortal(newSViv(0));
a480973c 13500 Safefree(inspec);
a5f75d66
AD
13501 XSRETURN(1);
13502 }
13503 inp = inspec;
13504 }
13505 else {
2d8e6c8d 13506 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
a5f75d66 13507 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13508 ST(0) = sv_2mortal(newSViv(0));
a480973c 13509 Safefree(inspec);
a5f75d66
AD
13510 XSRETURN(1);
13511 }
13512 }
13513 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
a480973c 13514 Newx(outspec, VMS_MAXRSS, char);
6d24fbd1 13515 if (isGV_with_GP(mysv)) {
a15cef0c 13516 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
a5f75d66 13517 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13518 ST(0) = sv_2mortal(newSViv(0));
a480973c
JM
13519 Safefree(inspec);
13520 Safefree(outspec);
a5f75d66
AD
13521 XSRETURN(1);
13522 }
13523 outp = outspec;
13524 }
13525 else {
2d8e6c8d 13526 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
a5f75d66 13527 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 13528 ST(0) = sv_2mortal(newSViv(0));
a480973c
JM
13529 Safefree(inspec);
13530 Safefree(outspec);
a5f75d66
AD
13531 XSRETURN(1);
13532 }
13533 }
a3e9d8c9 13534 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
a5f75d66 13535
fd188159 13536 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
a480973c
JM
13537 Safefree(inspec);
13538 Safefree(outspec);
748a9306
LW
13539 XSRETURN(1);
13540}
13541
a480973c
JM
13542/* The mod2fname is limited to shorter filenames by design, so it should
13543 * not be modified to support longer EFS pathnames
13544 */
4b19af01 13545void
fd8cd3a3 13546mod2fname(pTHX_ CV *cv)
4b19af01
CB
13547{
13548 dXSARGS;
13549 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13550 workbuff[NAM$C_MAXRSS*1 + 1];
13551 int total_namelen = 3, counter, num_entries;
13552 /* ODS-5 ups this, but we want to be consistent, so... */
13553 int max_name_len = 39;
13554 AV *in_array = (AV *)SvRV(ST(0));
13555
13556 num_entries = av_len(in_array);
13557
13558 /* All the names start with PL_. */
13559 strcpy(ultimate_name, "PL_");
13560
13561 /* Clean up our working buffer */
13562 Zero(work_name, sizeof(work_name), char);
13563
13564 /* Run through the entries and build up a working name */
13565 for(counter = 0; counter <= num_entries; counter++) {
13566 /* If it's not the first name then tack on a __ */
13567 if (counter) {
13568 strcat(work_name, "__");
13569 }
bfd025d9 13570 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
4b19af01
CB
13571 }
13572
13573 /* Check to see if we actually have to bother...*/
13574 if (strlen(work_name) + 3 <= max_name_len) {
13575 strcat(ultimate_name, work_name);
13576 } else {
13577 /* It's too darned big, so we need to go strip. We use the same */
13578 /* algorithm as xsubpp does. First, strip out doubled __ */
13579 char *source, *dest, last;
13580 dest = workbuff;
13581 last = 0;
13582 for (source = work_name; *source; source++) {
13583 if (last == *source && last == '_') {
13584 continue;
13585 }
13586 *dest++ = *source;
13587 last = *source;
13588 }
13589 /* Go put it back */
13590 strcpy(work_name, workbuff);
13591 /* Is it still too big? */
13592 if (strlen(work_name) + 3 > max_name_len) {
13593 /* Strip duplicate letters */
13594 last = 0;
13595 dest = workbuff;
13596 for (source = work_name; *source; source++) {
13597 if (last == toupper(*source)) {
13598 continue;
13599 }
13600 *dest++ = *source;
13601 last = toupper(*source);
13602 }
13603 strcpy(work_name, workbuff);
13604 }
13605
13606 /* Is it *still* too big? */
13607 if (strlen(work_name) + 3 > max_name_len) {
13608 /* Too bad, we truncate */
13609 work_name[max_name_len - 2] = 0;
13610 }
13611 strcat(ultimate_name, work_name);
13612 }
13613
13614 /* Okay, return it */
13615 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13616 XSRETURN(1);
13617}
13618
748a9306 13619void
96e176bf
CL
13620hushexit_fromperl(pTHX_ CV *cv)
13621{
13622 dXSARGS;
13623
13624 if (items > 0) {
13625 VMSISH_HUSHED = SvTRUE(ST(0));
13626 }
13627 ST(0) = boolSV(VMSISH_HUSHED);
13628 XSRETURN(1);
13629}
13630
dca5a913
JM
13631
13632PerlIO *
13633Perl_vms_start_glob
13634 (pTHX_ SV *tmpglob,
13635 IO *io)
13636{
13637 PerlIO *fp;
13638 struct vs_str_st *rslt;
13639 char *vmsspec;
13640 char *rstr;
13641 char *begin, *cp;
13642 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13643 PerlIO *tmpfp;
13644 STRLEN i;
13645 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13646 struct dsc$descriptor_vs rsdsc;
13647 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13648 unsigned long hasver = 0, isunix = 0;
13649 unsigned long int lff_flags = 0;
13650 int rms_sts;
85e7c9de 13651 int vms_old_glob = 1;
dca5a913 13652
83b907a4
CB
13653 if (!SvOK(tmpglob)) {
13654 SETERRNO(ENOENT,RMS$_FNF);
13655 return NULL;
13656 }
13657
85e7c9de
JM
13658 vms_old_glob = !decc_filename_unix_report;
13659
dca5a913
JM
13660#ifdef VMS_LONGNAME_SUPPORT
13661 lff_flags = LIB$M_FIL_LONG_NAMES;
13662#endif
13663 /* The Newx macro will not allow me to assign a smaller array
13664 * to the rslt pointer, so we will assign it to the begin char pointer
13665 * and then copy the value into the rslt pointer.
13666 */
13667 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13668 rslt = (struct vs_str_st *)begin;
13669 rslt->length = 0;
13670 rstr = &rslt->str[0];
13671 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13672 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13673 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13674 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13675
13676 Newx(vmsspec, VMS_MAXRSS, char);
13677
13678 /* We could find out if there's an explicit dev/dir or version
13679 by peeking into lib$find_file's internal context at
13680 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13681 but that's unsupported, so I don't want to do it now and
13682 have it bite someone in the future. */
13683 /* Fix-me: vms_split_path() is the only way to do this, the
13684 existing method will fail with many legal EFS or UNIX specifications
13685 */
13686
13687 cp = SvPV(tmpglob,i);
13688
13689 for (; i; i--) {
13690 if (cp[i] == ';') hasver = 1;
13691 if (cp[i] == '.') {
13692 if (sts) hasver = 1;
13693 else sts = 1;
13694 }
13695 if (cp[i] == '/') {
13696 hasdir = isunix = 1;
13697 break;
13698 }
13699 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13700 hasdir = 1;
13701 break;
13702 }
13703 }
85e7c9de
JM
13704
13705 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13706 if ((hasdir == 0) && decc_filename_unix_report) {
13707 isunix = 1;
13708 }
13709
dca5a913 13710 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
85e7c9de
JM
13711 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13712 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13713 int wildstar = 0;
13714 int wildquery = 0;
990cad08 13715 int found = 0;
dca5a913
JM
13716 Stat_t st;
13717 int stat_sts;
13718 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13719 if (!stat_sts && S_ISDIR(st.st_mode)) {
85e7c9de
JM
13720 char * vms_dir;
13721 const char * fname;
13722 STRLEN fname_len;
13723
13724 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13725 /* path delimiter of ':>]', if so, then the old behavior has */
13726 /* obviously been specificially requested */
13727
13728 fname = SvPVX_const(tmpglob);
13729 fname_len = strlen(fname);
13730 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13731 if (vms_old_glob || (vms_dir != NULL)) {
13732 wilddsc.dsc$a_pointer = tovmspath_utf8(
13733 SvPVX(tmpglob),vmsspec,NULL);
13734 ok = (wilddsc.dsc$a_pointer != NULL);
13735 /* maybe passed 'foo' rather than '[.foo]', thus not
13736 detected above */
13737 hasdir = 1;
13738 } else {
13739 /* Operate just on the directory, the special stat/fstat for */
13740 /* leaves the fileified specification in the st_devnam */
13741 /* member. */
13742 wilddsc.dsc$a_pointer = st.st_devnam;
13743 ok = 1;
13744 }
dca5a913
JM
13745 }
13746 else {
360732b5 13747 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
dca5a913
JM
13748 ok = (wilddsc.dsc$a_pointer != NULL);
13749 }
13750 if (ok)
13751 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13752
13753 /* If not extended character set, replace ? with % */
13754 /* With extended character set, ? is a wildcard single character */
85e7c9de
JM
13755 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13756 if (*cp == '?') {
13757 wildquery = 1;
13758 if (!decc_efs_case_preserve)
13759 *cp = '%';
13760 } else if (*cp == '%') {
13761 wildquery = 1;
13762 } else if (*cp == '*') {
13763 wildstar = 1;
13764 }
dca5a913 13765 }
85e7c9de
JM
13766
13767 if (ok) {
13768 wv_sts = vms_split_path(
13769 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13770 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13771 &wvs_spec, &wvs_len);
13772 } else {
13773 wn_spec = NULL;
13774 wn_len = 0;
13775 we_spec = NULL;
13776 we_len = 0;
13777 }
13778
dca5a913
JM
13779 sts = SS$_NORMAL;
13780 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13781 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13782 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
85e7c9de 13783 int valid_find;
dca5a913 13784
85e7c9de 13785 valid_find = 0;
dca5a913
JM
13786 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13787 &dfltdsc,NULL,&rms_sts,&lff_flags);
13788 if (!$VMS_STATUS_SUCCESS(sts))
13789 break;
13790
13791 /* with varying string, 1st word of buffer contains result length */
13792 rstr[rslt->length] = '\0';
13793
13794 /* Find where all the components are */
13795 v_sts = vms_split_path
360732b5 13796 (rstr,
dca5a913
JM
13797 &v_spec,
13798 &v_len,
13799 &r_spec,
13800 &r_len,
13801 &d_spec,
13802 &d_len,
13803 &n_spec,
13804 &n_len,
13805 &e_spec,
13806 &e_len,
13807 &vs_spec,
13808 &vs_len);
13809
13810 /* If no version on input, truncate the version on output */
13811 if (!hasver && (vs_len > 0)) {
13812 *vs_spec = '\0';
13813 vs_len = 0;
85e7c9de
JM
13814 }
13815
13816 if (isunix) {
13817
13818 /* In Unix report mode, remove the ".dir;1" from the name */
13819 /* if it is a real directory */
13820 if (decc_filename_unix_report || decc_efs_charset) {
13821 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13822 Stat_t statbuf;
13823 int ret_sts;
13824
13825 ret_sts = flex_lstat(rstr, &statbuf);
13826 if ((ret_sts == 0) &&
13827 S_ISDIR(statbuf.st_mode)) {
13828 e_len = 0;
13829 e_spec[0] = 0;
13830 }
13831 }
13832 }
dca5a913
JM
13833
13834 /* No version & a null extension on UNIX handling */
85e7c9de 13835 if ((e_len == 1) && decc_readdir_dropdotnotype) {
dca5a913
JM
13836 e_len = 0;
13837 *e_spec = '\0';
13838 }
13839 }
13840
13841 if (!decc_efs_case_preserve) {
13842 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13843 }
13844
85e7c9de
JM
13845 /* Find File treats a Null extension as return all extensions */
13846 /* This is contrary to Perl expectations */
13847
13848 if (wildstar || wildquery || vms_old_glob) {
13849 /* really need to see if the returned file name matched */
13850 /* but for now will assume that it matches */
13851 valid_find = 1;
13852 } else {
13853 /* Exact Match requested */
13854 /* How are directories handled? - like a file */
13855 if ((e_len == we_len) && (n_len == wn_len)) {
13856 int t1;
13857 t1 = e_len;
13858 if (t1 > 0)
13859 t1 = strncmp(e_spec, we_spec, e_len);
13860 if (t1 == 0) {
13861 t1 = n_len;
13862 if (t1 > 0)
13863 t1 = strncmp(n_spec, we_spec, n_len);
13864 if (t1 == 0)
13865 valid_find = 1;
13866 }
13867 }
13868 }
13869
13870 if (valid_find) {
13871 found++;
13872
13873 if (hasdir) {
13874 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13875 begin = rstr;
13876 }
13877 else {
13878 /* Start with the name */
13879 begin = n_spec;
13880 }
13881 strcat(begin,"\n");
13882 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13883 }
dca5a913
JM
13884 }
13885 if (cxt) (void)lib$find_file_end(&cxt);
990cad08
CB
13886
13887 if (!found) {
13888 /* Be POSIXish: return the input pattern when no matches */
2da7a6b5
CB
13889 strcpy(rstr,SvPVX(tmpglob));
13890 strcat(rstr,"\n");
13891 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
990cad08
CB
13892 }
13893
dca5a913
JM
13894 if (ok && sts != RMS$_NMF &&
13895 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13896 if (!ok) {
13897 if (!(sts & 1)) {
13898 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13899 }
13900 PerlIO_close(tmpfp);
13901 fp = NULL;
13902 }
13903 else {
13904 PerlIO_rewind(tmpfp);
13905 IoTYPE(io) = IoTYPE_RDONLY;
13906 IoIFP(io) = fp = tmpfp;
13907 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13908 }
13909 }
13910 Safefree(vmsspec);
13911 Safefree(rslt);
13912 return fp;
13913}
13914
cd1191f1 13915
2497a41f 13916static char *
5c4d031a 13917mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
d584a1c6 13918 int *utf8_fl);
2497a41f
JM
13919
13920void
4d8d3a9c 13921unixrealpath_fromperl(pTHX_ CV *cv)
2497a41f 13922{
d584a1c6
JM
13923 dXSARGS;
13924 char *fspec, *rslt_spec, *rslt;
13925 STRLEN n_a;
2497a41f 13926
d584a1c6 13927 if (!items || items != 1)
4d8d3a9c 13928 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
2497a41f 13929
d584a1c6
JM
13930 fspec = SvPV(ST(0),n_a);
13931 if (!fspec || !*fspec) XSRETURN_UNDEF;
2497a41f 13932
d584a1c6
JM
13933 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13934 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13935
13936 ST(0) = sv_newmortal();
13937 if (rslt != NULL)
13938 sv_usepvn(ST(0),rslt,strlen(rslt));
13939 else
13940 Safefree(rslt_spec);
13941 XSRETURN(1);
2497a41f 13942}
2ee6e19d 13943
b1a8dcd7
JM
13944static char *
13945mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13946 int *utf8_fl);
13947
13948void
4d8d3a9c 13949vmsrealpath_fromperl(pTHX_ CV *cv)
b1a8dcd7
JM
13950{
13951 dXSARGS;
13952 char *fspec, *rslt_spec, *rslt;
13953 STRLEN n_a;
13954
13955 if (!items || items != 1)
4d8d3a9c 13956 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
b1a8dcd7
JM
13957
13958 fspec = SvPV(ST(0),n_a);
13959 if (!fspec || !*fspec) XSRETURN_UNDEF;
13960
13961 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13962 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13963
13964 ST(0) = sv_newmortal();
13965 if (rslt != NULL)
13966 sv_usepvn(ST(0),rslt,strlen(rslt));
13967 else
13968 Safefree(rslt_spec);
13969 XSRETURN(1);
13970}
13971
13972#ifdef HAS_SYMLINK
2ee6e19d
CB
13973/*
13974 * A thin wrapper around decc$symlink to make sure we follow the
13975 * standard and do not create a symlink with a zero-length name.
4148925f
JM
13976 *
13977 * Also in ODS-2 mode, existing tests assume that the link target
13978 * will be converted to UNIX format.
2ee6e19d 13979 */
4148925f
JM
13980/*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13981int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13982 if (!link_name || !*link_name) {
2ee6e19d
CB
13983 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13984 return -1;
13985 }
4148925f
JM
13986
13987 if (decc_efs_charset) {
13988 return symlink(contents, link_name);
13989 } else {
13990 int sts;
13991 char * utarget;
13992
13993 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13994 /* because in order to work, the symlink target must be in UNIX format */
13995
13996 /* As symbolic links can hold things other than files, we will only do */
13997 /* the conversion in in ODS-2 mode */
13998
4d9538c1 13999 utarget = PerlMem_malloc(VMS_MAXRSS + 1);
0e5ce2c7 14000 if (int_tounixspec(contents, utarget, NULL) == NULL) {
4148925f
JM
14001
14002 /* This should not fail, as an untranslatable filename */
14003 /* should be passed through */
14004 utarget = (char *)contents;
14005 }
14006 sts = symlink(utarget, link_name);
4d9538c1 14007 PerlMem_free(utarget);
4148925f
JM
14008 return sts;
14009 }
14010
2ee6e19d
CB
14011}
14012/*}}}*/
14013
14014#endif /* HAS_SYMLINK */
2497a41f 14015
2497a41f
JM
14016int do_vms_case_tolerant(void);
14017
14018void
4d8d3a9c 14019case_tolerant_process_fromperl(pTHX_ CV *cv)
2497a41f
JM
14020{
14021 dXSARGS;
14022 ST(0) = boolSV(do_vms_case_tolerant());
14023 XSRETURN(1);
14024}
2497a41f 14025
9ec7171b
CB
14026#ifdef USE_ITHREADS
14027
96e176bf
CL
14028void
14029Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14030 struct interp_intern *dst)
14031{
7918f24d
NC
14032 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14033
96e176bf
CL
14034 memcpy(dst,src,sizeof(struct interp_intern));
14035}
14036
9ec7171b
CB
14037#endif
14038
96e176bf
CL
14039void
14040Perl_sys_intern_clear(pTHX)
14041{
14042}
14043
14044void
14045Perl_sys_intern_init(pTHX)
14046{
3ff49832
CL
14047 unsigned int ix = RAND_MAX;
14048 double x;
96e176bf
CL
14049
14050 VMSISH_HUSHED = 0;
14051
1a3aec58 14052 MY_POSIX_EXIT = vms_posix_exit;
7a7fd8e0 14053
96e176bf
CL
14054 x = (float)ix;
14055 MY_INV_RAND_MAX = 1./x;
ff7adb52 14056}
96e176bf
CL
14057
14058void
f7ddb74a 14059init_os_extras(void)
748a9306 14060{
a69a6dba 14061 dTHX;
748a9306 14062 char* file = __FILE__;
988c775c 14063 if (decc_disable_to_vms_logname_translation) {
93948341
CB
14064 no_translate_barewords = TRUE;
14065 } else {
14066 no_translate_barewords = FALSE;
14067 }
748a9306 14068
740ce14c 14069 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
a5f75d66
AD
14070 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14071 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14072 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14073 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14074 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14075 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14076 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
4b19af01 14077 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
a5f75d66 14078 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
96e176bf 14079 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
4d8d3a9c
CB
14080 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14081 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14082 newXSproto("VMS::Filespec::case_tolerant_process",
14083 case_tolerant_process_fromperl,file,"");
17f28c40 14084
afd8f436 14085 store_pipelocs(aTHX); /* will redo any earlier attempts */
22d4bb9c 14086
748a9306
LW
14087 return;
14088}
14089
f7ddb74a
JM
14090#if __CRTL_VER == 80200000
14091/* This missed getting in to the DECC SDK for 8.2 */
14092char *realpath(const char *file_name, char * resolved_name, ...);
14093#endif
14094
14095/*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14096/* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14097 * The perl fallback routine to provide realpath() is not as efficient
14098 * on OpenVMS.
14099 */
d584a1c6
JM
14100
14101/* Hack, use old stat() as fastest way of getting ino_t and device */
14102int decc$stat(const char *name, void * statbuf);
312ac60b
JM
14103#if !defined(__VAX) && __CRTL_VER >= 80200000
14104int decc$lstat(const char *name, void * statbuf);
14105#else
14106#define decc$lstat decc$stat
14107#endif
d584a1c6
JM
14108
14109
14110/* Realpath is fragile. In 8.3 it does not work if the feature
14111 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14112 * links are implemented in RMS, not the CRTL. It also can fail if the
14113 * user does not have read/execute access to some of the directories.
14114 * So in order for Do What I Mean mode to work, if realpath() fails,
14115 * fall back to looking up the filename by the device name and FID.
14116 */
14117
312ac60b
JM
14118int vms_fid_to_name(char * outname, int outlen,
14119 const char * name, int lstat_flag, mode_t * mode)
d584a1c6 14120{
312ac60b
JM
14121#pragma message save
14122#pragma message disable MISALGNDSTRCT
14123#pragma message disable MISALGNDMEM
14124#pragma member_alignment save
14125#pragma nomember_alignment
d584a1c6
JM
14126struct statbuf_t {
14127 char * st_dev;
b1a8dcd7 14128 unsigned short st_ino[3];
312ac60b 14129 unsigned short old_st_mode;
d584a1c6
JM
14130 unsigned long padl[30]; /* plenty of room */
14131} statbuf;
312ac60b
JM
14132#pragma message restore
14133#pragma member_alignment restore
14134
14135 int sts;
14136 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14137 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14138 char *fileified;
14139 char *temp_fspec;
14140 char *ret_spec;
14141
14142 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14143 * unexpected answers
14144 */
14145
14146 fileified = PerlMem_malloc(VMS_MAXRSS);
14147 if (fileified == NULL)
14148 _ckvmssts_noperl(SS$_INSFMEM);
14149
14150 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14151 if (temp_fspec == NULL)
14152 _ckvmssts_noperl(SS$_INSFMEM);
14153
14154 sts = -1;
14155 /* First need to try as a directory */
14156 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14157 if (ret_spec != NULL) {
14158 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14159 if (ret_spec != NULL) {
14160 if (lstat_flag == 0)
14161 sts = decc$stat(fileified, &statbuf);
14162 else
14163 sts = decc$lstat(fileified, &statbuf);
14164 }
14165 }
14166
14167 /* Then as a VMS file spec */
14168 if (sts != 0) {
14169 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14170 if (ret_spec != NULL) {
14171 if (lstat_flag == 0) {
14172 sts = decc$stat(temp_fspec, &statbuf);
14173 } else {
14174 sts = decc$lstat(temp_fspec, &statbuf);
14175 }
14176 }
14177 }
14178
14179 if (sts) {
14180 /* Next try - allow multiple dots with out EFS CHARSET */
14181 /* The CRTL stat() falls down hard on multi-dot filenames in unix
14182 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14183 * enable it if it isn't already.
14184 */
14185#if __CRTL_VER >= 70300000 && !defined(__VAX)
14186 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14187 decc$feature_set_value(decc_efs_charset_index, 1, 1);
14188#endif
14189 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14190 if (lstat_flag == 0) {
14191 sts = decc$stat(name, &statbuf);
14192 } else {
14193 sts = decc$lstat(name, &statbuf);
14194 }
14195#if __CRTL_VER >= 70300000 && !defined(__VAX)
14196 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14197 decc$feature_set_value(decc_efs_charset_index, 1, 0);
14198#endif
14199 }
14200
14201
14202 /* and then because the Perl Unix to VMS conversion is not perfect */
14203 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14204 /* characters from filenames so we need to try it as-is */
14205 if (sts) {
14206 if (lstat_flag == 0) {
14207 sts = decc$stat(name, &statbuf);
14208 } else {
14209 sts = decc$lstat(name, &statbuf);
14210 }
14211 }
d584a1c6 14212
d584a1c6 14213 if (sts == 0) {
312ac60b 14214 int vms_sts;
d584a1c6
JM
14215
14216 dvidsc.dsc$a_pointer=statbuf.st_dev;
d94c5a78 14217 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
d584a1c6
JM
14218
14219 specdsc.dsc$a_pointer = outname;
14220 specdsc.dsc$w_length = outlen-1;
14221
d94c5a78 14222 vms_sts = lib$fid_to_name
d584a1c6 14223 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
d94c5a78 14224 if ($VMS_STATUS_SUCCESS(vms_sts)) {
d584a1c6 14225 outname[specdsc.dsc$w_length] = 0;
312ac60b
JM
14226
14227 /* Return the mode */
14228 if (mode) {
14229 *mode = statbuf.old_st_mode;
14230 }
d584a1c6
JM
14231 }
14232 }
9e2bec02
CB
14233 PerlMem_free(temp_fspec);
14234 PerlMem_free(fileified);
d584a1c6
JM
14235 return sts;
14236}
14237
14238
14239
f7ddb74a 14240static char *
5c4d031a 14241mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
d584a1c6 14242 int *utf8_fl)
f7ddb74a 14243{
d584a1c6
JM
14244 char * rslt = NULL;
14245
b1a8dcd7
JM
14246#ifdef HAS_SYMLINK
14247 if (decc_posix_compliant_pathnames > 0 ) {
14248 /* realpath currently only works if posix compliant pathnames are
14249 * enabled. It may start working when they are not, but in that
14250 * case we still want the fallback behavior for backwards compatibility
14251 */
d584a1c6 14252 rslt = realpath(filespec, outbuf);
b1a8dcd7
JM
14253 }
14254#endif
d584a1c6
JM
14255
14256 if (rslt == NULL) {
14257 char * vms_spec;
14258 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14259 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14260 int file_len;
312ac60b 14261 mode_t my_mode;
d584a1c6
JM
14262
14263 /* Fall back to fid_to_name */
14264
14265 Newx(vms_spec, VMS_MAXRSS + 1, char);
14266
312ac60b 14267 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
4d8d3a9c 14268 if (sts == 0) {
d584a1c6
JM
14269
14270
14271 /* Now need to trim the version off */
14272 sts = vms_split_path
14273 (vms_spec,
14274 &v_spec,
14275 &v_len,
14276 &r_spec,
14277 &r_len,
14278 &d_spec,
14279 &d_len,
14280 &n_spec,
14281 &n_len,
14282 &e_spec,
14283 &e_len,
14284 &vs_spec,
14285 &vs_len);
14286
14287
4d8d3a9c
CB
14288 if (sts == 0) {
14289 int haslower = 0;
14290 const char *cp;
d584a1c6 14291
4d8d3a9c
CB
14292 /* Trim off the version */
14293 int file_len = v_len + r_len + d_len + n_len + e_len;
14294 vms_spec[file_len] = 0;
d584a1c6 14295
f785e3a1
JM
14296 /* Trim off the .DIR if this is a directory */
14297 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
14298 if (S_ISDIR(my_mode)) {
14299 e_len = 0;
14300 e_spec[0] = 0;
14301 }
14302 }
14303
14304 /* Drop NULL extensions on UNIX file specification */
14305 if ((e_len == 1) && decc_readdir_dropdotnotype) {
14306 e_len = 0;
14307 e_spec[0] = '\0';
14308 }
14309
4d8d3a9c 14310 /* The result is expected to be in UNIX format */
0e5ce2c7 14311 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
4d8d3a9c
CB
14312
14313 /* Downcase if input had any lower case letters and
14314 * case preservation is not in effect.
14315 */
14316 if (!decc_efs_case_preserve) {
14317 for (cp = filespec; *cp; cp++)
14318 if (islower(*cp)) { haslower = 1; break; }
14319
14320 if (haslower) __mystrtolower(rslt);
14321 }
14322 }
643f470b
CB
14323 } else {
14324
14325 /* Now for some hacks to deal with backwards and forward */
14326 /* compatibilty */
14327 if (!decc_efs_charset) {
14328
14329 /* 1. ODS-2 mode wants to do a syntax only translation */
6fb6c614
JM
14330 rslt = int_rmsexpand(filespec, outbuf,
14331 NULL, 0, NULL, utf8_fl);
643f470b
CB
14332
14333 } else {
14334 if (decc_filename_unix_report) {
14335 char * dir_name;
14336 char * vms_dir_name;
14337 char * file_name;
14338
14339 /* 2. ODS-5 / UNIX report mode should return a failure */
14340 /* if the parent directory also does not exist */
14341 /* Otherwise, get the real path for the parent */
14342 /* and add the child to it.
14343
14344 /* basename / dirname only available for VMS 7.0+ */
14345 /* So we may need to implement them as common routines */
14346
14347 Newx(dir_name, VMS_MAXRSS + 1, char);
14348 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14349 dir_name[0] = '\0';
14350 file_name = NULL;
14351
14352 /* First try a VMS parse */
14353 sts = vms_split_path
14354 (filespec,
14355 &v_spec,
14356 &v_len,
14357 &r_spec,
14358 &r_len,
14359 &d_spec,
14360 &d_len,
14361 &n_spec,
14362 &n_len,
14363 &e_spec,
14364 &e_len,
14365 &vs_spec,
14366 &vs_len);
14367
14368 if (sts == 0) {
14369 /* This is VMS */
14370
14371 int dir_len = v_len + r_len + d_len + n_len;
14372 if (dir_len > 0) {
14373 strncpy(dir_name, filespec, dir_len);
14374 dir_name[dir_len] = '\0';
14375 file_name = (char *)&filespec[dir_len + 1];
14376 }
14377 } else {
14378 /* This must be UNIX */
14379 char * tchar;
14380
14381 tchar = strrchr(filespec, '/');
14382
4148925f
JM
14383 if (tchar != NULL) {
14384 int dir_len = tchar - filespec;
14385 strncpy(dir_name, filespec, dir_len);
14386 dir_name[dir_len] = '\0';
14387 file_name = (char *) &filespec[dir_len + 1];
14388 }
14389 }
14390
14391 /* Dir name is defaulted */
14392 if (dir_name[0] == 0) {
14393 dir_name[0] = '.';
14394 dir_name[1] = '\0';
14395 }
14396
14397 /* Need realpath for the directory */
14398 sts = vms_fid_to_name(vms_dir_name,
14399 VMS_MAXRSS + 1,
312ac60b 14400 dir_name, 0, NULL);
4148925f
JM
14401
14402 if (sts == 0) {
14403 /* Now need to pathify it.
1fe570cc
JM
14404 char *tdir = int_pathify_dirspec(vms_dir_name,
14405 outbuf);
4148925f
JM
14406
14407 /* And now add the original filespec to it */
14408 if (file_name != NULL) {
14409 strcat(outbuf, file_name);
14410 }
14411 return outbuf;
14412 }
14413 Safefree(vms_dir_name);
14414 Safefree(dir_name);
14415 }
14416 }
643f470b 14417 }
d584a1c6
JM
14418 Safefree(vms_spec);
14419 }
14420 return rslt;
f7ddb74a
JM
14421}
14422
b1a8dcd7
JM
14423static char *
14424mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14425 int *utf8_fl)
14426{
14427 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14428 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14429 int file_len;
14430
14431 /* Fall back to fid_to_name */
14432
312ac60b 14433 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
cd43acd7
CB
14434 if (sts != 0) {
14435 return NULL;
14436 }
14437 else {
b1a8dcd7
JM
14438
14439
14440 /* Now need to trim the version off */
14441 sts = vms_split_path
14442 (outbuf,
14443 &v_spec,
14444 &v_len,
14445 &r_spec,
14446 &r_len,
14447 &d_spec,
14448 &d_len,
14449 &n_spec,
14450 &n_len,
14451 &e_spec,
14452 &e_len,
14453 &vs_spec,
14454 &vs_len);
14455
14456
14457 if (sts == 0) {
4d8d3a9c
CB
14458 int haslower = 0;
14459 const char *cp;
14460
14461 /* Trim off the version */
14462 int file_len = v_len + r_len + d_len + n_len + e_len;
14463 outbuf[file_len] = 0;
b1a8dcd7 14464
4d8d3a9c
CB
14465 /* Downcase if input had any lower case letters and
14466 * case preservation is not in effect.
14467 */
14468 if (!decc_efs_case_preserve) {
14469 for (cp = filespec; *cp; cp++)
14470 if (islower(*cp)) { haslower = 1; break; }
14471
14472 if (haslower) __mystrtolower(outbuf);
14473 }
b1a8dcd7
JM
14474 }
14475 }
14476 return outbuf;
14477}
14478
14479
f7ddb74a
JM
14480/*}}}*/
14481/* External entry points */
360732b5
JM
14482char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14483{ return do_vms_realpath(filespec, outbuf, utf8_fl); }
f7ddb74a 14484
b1a8dcd7
JM
14485char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14486{ return do_vms_realname(filespec, outbuf, utf8_fl); }
f7ddb74a 14487
f7ddb74a
JM
14488/* case_tolerant */
14489
14490/*{{{int do_vms_case_tolerant(void)*/
14491/* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14492 * controlled by a process setting.
14493 */
14494int do_vms_case_tolerant(void)
14495{
14496 return vms_process_case_tolerant;
14497}
14498/*}}}*/
14499/* External entry points */
b1a8dcd7 14500#if __CRTL_VER >= 70301000 && !defined(__VAX)
f7ddb74a
JM
14501int Perl_vms_case_tolerant(void)
14502{ return do_vms_case_tolerant(); }
14503#else
14504int Perl_vms_case_tolerant(void)
14505{ return vms_process_case_tolerant; }
14506#endif
14507
14508
14509 /* Start of DECC RTL Feature handling */
14510
14511static int sys_trnlnm
14512 (const char * logname,
14513 char * value,
14514 int value_len)
14515{
14516 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14517 const unsigned long attr = LNM$M_CASE_BLIND;
14518 struct dsc$descriptor_s name_dsc;
14519 int status;
14520 unsigned short result;
14521 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14522 {0, 0, 0, 0}};
14523
14524 name_dsc.dsc$w_length = strlen(logname);
14525 name_dsc.dsc$a_pointer = (char *)logname;
14526 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14527 name_dsc.dsc$b_class = DSC$K_CLASS_S;
14528
14529 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14530
14531 if ($VMS_STATUS_SUCCESS(status)) {
14532
14533 /* Null terminate and return the string */
14534 /*--------------------------------------*/
14535 value[result] = 0;
14536 }
14537
14538 return status;
14539}
14540
14541static int sys_crelnm
14542 (const char * logname,
14543 const char * value)
14544{
14545 int ret_val;
14546 const char * proc_table = "LNM$PROCESS_TABLE";
14547 struct dsc$descriptor_s proc_table_dsc;
14548 struct dsc$descriptor_s logname_dsc;
14549 struct itmlst_3 item_list[2];
14550
14551 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14552 proc_table_dsc.dsc$w_length = strlen(proc_table);
14553 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14554 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14555
14556 logname_dsc.dsc$a_pointer = (char *) logname;
14557 logname_dsc.dsc$w_length = strlen(logname);
14558 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14559 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14560
14561 item_list[0].buflen = strlen(value);
14562 item_list[0].itmcode = LNM$_STRING;
14563 item_list[0].bufadr = (char *)value;
14564 item_list[0].retlen = NULL;
14565
14566 item_list[1].buflen = 0;
14567 item_list[1].itmcode = 0;
14568
14569 ret_val = sys$crelnm
14570 (NULL,
14571 (const struct dsc$descriptor_s *)&proc_table_dsc,
14572 (const struct dsc$descriptor_s *)&logname_dsc,
14573 NULL,
14574 (const struct item_list_3 *) item_list);
14575
14576 return ret_val;
14577}
14578
f7ddb74a
JM
14579/* C RTL Feature settings */
14580
14581static int set_features
14582 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
14583 int (* cli_routine)(void), /* Not documented */
14584 void *image_info) /* Not documented */
14585{
14586 int status;
14587 int s;
f7ddb74a
JM
14588 char* str;
14589 char val_str[10];
3c841f20 14590#if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
f7ddb74a
JM
14591 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14592 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14593 unsigned long case_perm;
14594 unsigned long case_image;
3c841f20 14595#endif
f7ddb74a 14596
9c1171d1
JM
14597 /* Allow an exception to bring Perl into the VMS debugger */
14598 vms_debug_on_exception = 0;
14599 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14600 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14601 val_str[0] = _toupper(val_str[0]);
9c1171d1
JM
14602 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14603 vms_debug_on_exception = 1;
14604 else
14605 vms_debug_on_exception = 0;
14606 }
14607
b53f3677
JM
14608 /* Debug unix/vms file translation routines */
14609 vms_debug_fileify = 0;
14610 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14611 if ($VMS_STATUS_SUCCESS(status)) {
14612 val_str[0] = _toupper(val_str[0]);
14613 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14614 vms_debug_fileify = 1;
14615 else
14616 vms_debug_fileify = 0;
14617 }
14618
14619
14620 /* Historically PERL has been doing vmsify / stat differently than */
14621 /* the CRTL. In particular, under some conditions the CRTL will */
14622 /* remove some illegal characters like spaces from filenames */
14623 /* resulting in some differences. The stat()/lstat() wrapper has */
14624 /* been reporting such file names as invalid and fails to stat them */
14625 /* fixing this bug so that stat()/lstat() accept these like the */
14626 /* CRTL does will result in several tests failing. */
14627 /* This should really be fixed, but for now, set up a feature to */
14628 /* enable it so that the impact can be studied. */
14629 vms_bug_stat_filename = 0;
14630 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14631 if ($VMS_STATUS_SUCCESS(status)) {
14632 val_str[0] = _toupper(val_str[0]);
14633 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14634 vms_bug_stat_filename = 1;
14635 else
14636 vms_bug_stat_filename = 0;
14637 }
14638
14639
38a44b82 14640 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
360732b5
JM
14641 vms_vtf7_filenames = 0;
14642 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14643 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14644 val_str[0] = _toupper(val_str[0]);
360732b5
JM
14645 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14646 vms_vtf7_filenames = 1;
14647 else
14648 vms_vtf7_filenames = 0;
14649 }
14650
e0e5e8d6 14651 /* unlink all versions on unlink() or rename() */
d584a1c6 14652 vms_unlink_all_versions = 0;
e0e5e8d6
JM
14653 status = sys_trnlnm
14654 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14655 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14656 val_str[0] = _toupper(val_str[0]);
e0e5e8d6
JM
14657 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14658 vms_unlink_all_versions = 1;
14659 else
14660 vms_unlink_all_versions = 0;
14661 }
14662
360732b5
JM
14663 /* Dectect running under GNV Bash or other UNIX like shell */
14664#if __CRTL_VER >= 70300000 && !defined(__VAX)
14665 gnv_unix_shell = 0;
14666 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14667 if ($VMS_STATUS_SUCCESS(status)) {
360732b5
JM
14668 gnv_unix_shell = 1;
14669 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14670 set_feature_default("DECC$EFS_CHARSET", 1);
14671 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14672 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14673 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14674 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
e0e5e8d6 14675 vms_unlink_all_versions = 1;
1a3aec58 14676 vms_posix_exit = 1;
360732b5
JM
14677 }
14678#endif
9c1171d1 14679
2497a41f
JM
14680 /* hacks to see if known bugs are still present for testing */
14681
2497a41f 14682 /* PCP mode requires creating /dev/null special device file */
2623a4a6 14683 decc_bug_devnull = 0;
2497a41f
JM
14684 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14685 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14686 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
14687 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14688 decc_bug_devnull = 1;
682e4b71
JM
14689 else
14690 decc_bug_devnull = 0;
2497a41f
JM
14691 }
14692
2497a41f
JM
14693 /* UNIX directory names with no paths are broken in a lot of places */
14694 decc_dir_barename = 1;
14695 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14696 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14697 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
14698 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14699 decc_dir_barename = 1;
14700 else
14701 decc_dir_barename = 0;
14702 }
14703
f7ddb74a
JM
14704#if __CRTL_VER >= 70300000 && !defined(__VAX)
14705 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14706 if (s >= 0) {
14707 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14708 if (decc_disable_to_vms_logname_translation < 0)
14709 decc_disable_to_vms_logname_translation = 0;
14710 }
14711
14712 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14713 if (s >= 0) {
14714 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14715 if (decc_efs_case_preserve < 0)
14716 decc_efs_case_preserve = 0;
14717 }
14718
14719 s = decc$feature_get_index("DECC$EFS_CHARSET");
b53f3677 14720 decc_efs_charset_index = s;
f7ddb74a
JM
14721 if (s >= 0) {
14722 decc_efs_charset = decc$feature_get_value(s, 1);
14723 if (decc_efs_charset < 0)
14724 decc_efs_charset = 0;
14725 }
14726
14727 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14728 if (s >= 0) {
14729 decc_filename_unix_report = decc$feature_get_value(s, 1);
1a3aec58 14730 if (decc_filename_unix_report > 0) {
f7ddb74a 14731 decc_filename_unix_report = 1;
1a3aec58
JM
14732 vms_posix_exit = 1;
14733 }
f7ddb74a
JM
14734 else
14735 decc_filename_unix_report = 0;
14736 }
14737
14738 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14739 if (s >= 0) {
14740 decc_filename_unix_only = decc$feature_get_value(s, 1);
14741 if (decc_filename_unix_only > 0) {
14742 decc_filename_unix_only = 1;
14743 }
14744 else {
14745 decc_filename_unix_only = 0;
14746 }
14747 }
14748
14749 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14750 if (s >= 0) {
14751 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14752 if (decc_filename_unix_no_version < 0)
14753 decc_filename_unix_no_version = 0;
14754 }
14755
14756 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14757 if (s >= 0) {
14758 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14759 if (decc_readdir_dropdotnotype < 0)
14760 decc_readdir_dropdotnotype = 0;
14761 }
14762
f7ddb74a
JM
14763#if __CRTL_VER >= 80200000
14764 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14765 if (s >= 0) {
14766 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14767 if (decc_posix_compliant_pathnames < 0)
14768 decc_posix_compliant_pathnames = 0;
14769 if (decc_posix_compliant_pathnames > 4)
14770 decc_posix_compliant_pathnames = 0;
14771 }
14772
14773#endif
14774#else
14775 status = sys_trnlnm
14776 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14777 if ($VMS_STATUS_SUCCESS(status)) {
14778 val_str[0] = _toupper(val_str[0]);
14779 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14780 decc_disable_to_vms_logname_translation = 1;
14781 }
14782 }
14783
14784#ifndef __VAX
14785 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14786 if ($VMS_STATUS_SUCCESS(status)) {
14787 val_str[0] = _toupper(val_str[0]);
14788 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14789 decc_efs_case_preserve = 1;
14790 }
14791 }
14792#endif
14793
14794 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14795 if ($VMS_STATUS_SUCCESS(status)) {
14796 val_str[0] = _toupper(val_str[0]);
14797 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14798 decc_filename_unix_report = 1;
14799 }
14800 }
14801 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14802 if ($VMS_STATUS_SUCCESS(status)) {
14803 val_str[0] = _toupper(val_str[0]);
14804 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14805 decc_filename_unix_only = 1;
14806 decc_filename_unix_report = 1;
14807 }
14808 }
14809 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14810 if ($VMS_STATUS_SUCCESS(status)) {
14811 val_str[0] = _toupper(val_str[0]);
14812 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14813 decc_filename_unix_no_version = 1;
14814 }
14815 }
14816 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14817 if ($VMS_STATUS_SUCCESS(status)) {
14818 val_str[0] = _toupper(val_str[0]);
14819 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14820 decc_readdir_dropdotnotype = 1;
14821 }
14822 }
14823#endif
14824
28ff9735 14825#if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
f7ddb74a
JM
14826
14827 /* Report true case tolerance */
14828 /*----------------------------*/
14829 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14830 if (!$VMS_STATUS_SUCCESS(status))
14831 case_perm = PPROP$K_CASE_BLIND;
14832 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14833 if (!$VMS_STATUS_SUCCESS(status))
14834 case_image = PPROP$K_CASE_BLIND;
14835 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14836 (case_image == PPROP$K_CASE_SENSITIVE))
14837 vms_process_case_tolerant = 0;
14838
14839#endif
14840
1a3aec58
JM
14841 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14842 /* for strict backward compatibilty */
14843 status = sys_trnlnm
14844 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14845 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14846 val_str[0] = _toupper(val_str[0]);
1a3aec58
JM
14847 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14848 vms_posix_exit = 1;
14849 else
14850 vms_posix_exit = 0;
14851 }
14852
f7ddb74a
JM
14853
14854 /* CRTL can be initialized past this point, but not before. */
14855/* DECC$CRTL_INIT(); */
14856
14857 return SS$_NORMAL;
14858}
14859
14860#ifdef __DECC
f7ddb74a
JM
14861#pragma nostandard
14862#pragma extern_model save
14863#pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
f7ddb74a 14864 const __align (LONGWORD) int spare[8] = {0};
dfffea70
CB
14865
14866/* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14867#if __DECC_VER >= 60560002
14868#pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14869#else
14870#pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
f7ddb74a 14871#endif
dfffea70
CB
14872#endif /* __DECC */
14873
f7ddb74a
JM
14874const long vms_cc_features = (const long)set_features;
14875
14876/*
14877** Force a reference to LIB$INITIALIZE to ensure it
14878** exists in the image.
14879*/
17072196 14880#define lib$initialize LIB$INITIALIZE
f7ddb74a
JM
14881int lib$initialize(void);
14882#ifdef __DECC
14883#pragma extern_model strict_refdef
14884#endif
14885 int lib_init_ref = (int) lib$initialize;
14886
14887#ifdef __DECC
14888#pragma extern_model restore
14889#pragma standard
14890#endif
14891
748a9306 14892/* End of vms.c */