diff --git a/config.h-vms.template b/config.h-vms.template index 2126483..cb21cae 100644 --- a/config.h-vms.template +++ b/config.h-vms.template @@ -416,3 +416,13 @@ this program. If not, see . */ /* Build host information. */ #define MAKE_HOST "VMS" + +/* Define if argv[0] should be adjusted to the "command" */ +/* #define VMS_USE_MAIN_WRAPPER 1 */ + +/* Define if there is an environment passed to main. + needs to be defined with VMS_USE_MAIN_WRAPPER */ +/* #define HAVE_VMS_MAIN_ENVP 1 */ + +/* Define if the scratch directory should be the unix style /tmp */ +/* #define VMS_SCRATCH_IS_TMP 1 */ diff --git a/default.c b/default.c index d6c08a5..c7f03b8 100644 --- a/default.c +++ b/default.c @@ -332,7 +332,6 @@ static const char *default_variables[] = "CC", "cc", #endif "CD", "builtin_cd", - "MAKE", "make", "ECHO", "write sys$$output \"", #ifdef GCC_IS_NATIVE "C++", "gcc/plus", diff --git a/main.c b/main.c index 3a3d016..c23402a 100644 --- a/main.c +++ b/main.c @@ -1017,6 +1017,10 @@ msdos_return_to_initial_directory (void) } #endif /* __MSDOS__ */ +#if defined (VMS) && defined (VMS_USE_MAIN_WRAPPER) +# define main original_main +#endif + #ifdef _AMIGA int main (int argc, char **argv) @@ -1161,11 +1165,7 @@ main (int argc, char **argv, char **envp) program = "make"; else { -#ifdef VMS - program = strrchr (argv[0], ']'); -#else program = strrchr (argv[0], '/'); -#endif #if defined(__MSDOS__) || defined(__EMX__) if (program == 0) program = strrchr (argv[0], '\\'); @@ -1195,7 +1195,11 @@ main (int argc, char **argv, char **envp) } #endif if (program == 0) +#if defined(VMS) && !defined(VMS_USE_MAIN_WRAPPER) + program = "make"; +#else program = argv[0]; +#endif else ++program; } @@ -1581,7 +1585,14 @@ main (int argc, char **argv, char **envp) /* The extra indirection through $(MAKE_COMMAND) is done for hysterical raisins. */ +#if defined(VMS) && !defined(VMS_USE_MAIN_WRAPPER) + { + extern char *vms_command(const char *); + define_variable_cname ("MAKE_COMMAND", vms_command(argv[0]), o_default, 0); + } +#else define_variable_cname ("MAKE_COMMAND", argv[0], o_default, 0); +#endif define_variable_cname ("MAKE", "$(MAKE_COMMAND)", o_default, 1); if (command_variables != 0) @@ -1725,7 +1736,11 @@ main (int argc, char **argv, char **envp) _("Makefile from standard input specified twice.")); #ifdef VMS -# define DEFAULT_TMPDIR "sys$scratch:" +# ifdef VMS_SCRATCH_IS_TMP +# define DEFAULT_TMPDIR "/tmp" +# else +# define DEFAULT_TMPDIR "sys$scratch:" +# endif #else # ifdef P_tmpdir # define DEFAULT_TMPDIR P_tmpdir @@ -2525,6 +2540,9 @@ main (int argc, char **argv, char **envp) /* NOTREACHED */ exit (0); +#if defined (VMS) && defined (VMS_USE_MAIN_WRAPPER) + return 0; /* Silence compiler warning */ +#endif } /* Parsing of arguments, decoding of switches. */ diff --git a/makeint.h b/makeint.h index 7c695f5..16e427d 100644 --- a/makeint.h +++ b/makeint.h @@ -626,14 +626,14 @@ extern int handling_fatal_signal; #define MAX(_a,_b) ((_a)>(_b)?(_a):(_b)) #endif +#define MAKE_SUCCESS 0 +#define MAKE_TROUBLE 1 +#define MAKE_FAILURE 2 + #ifdef VMS -# define MAKE_SUCCESS 1 -# define MAKE_TROUBLE 2 -# define MAKE_FAILURE 3 -#else -# define MAKE_SUCCESS 0 -# define MAKE_TROUBLE 1 -# define MAKE_FAILURE 2 +void vms_exit(int); +# define _exit(foo) vms_exit(foo) +# define exit(foo) vms_exit(foo) #endif /* Set up heap debugging library dmalloc. */ diff --git a/vmsfunctions.c b/vmsfunctions.c index 1907e3a..4f032de 100644 --- a/vmsfunctions.c +++ b/vmsfunctions.c @@ -19,7 +19,12 @@ this program. If not, see . */ #include "job.h" #ifdef __DECC +/* Make sure there will be better prototype checking for ... */ +#define SYS$GETDVIW SKIP_PROTO_SYS$GETDVIW +#define SYS$FILESCAN SKIP_PROTO_SYS$FILESCAN #include +#undef SYS$FILESCAN +#undef SYS$GETDVIW #endif #include #include @@ -259,3 +264,425 @@ cvt_time (unsigned long tval) return (str); } + +#include +void decc$exit(int status); +#ifndef C_FACILITY_NO +#define C_FACILITY_NO 0x350000 +#endif + +/* Build a Posix Exit with VMS severity */ +void vms_exit(int status) { + int vms_status; + /* Fake the __posix_exit with severity added */ + /* Undocumented correct way to do this. */ + vms_status = 0; + if (status != 0) { + vms_status = C_FACILITY_NO | 0xA000 | STS$M_INHIB_MSG; + vms_status |= (status << 3); + } + if (status == MAKE_FAILURE) { + vms_status |= STS$K_ERROR; + } else if (status == MAKE_TROUBLE) { + vms_status |= STS$K_ERROR; + } else { + vms_status |= STS$K_SUCCESS; + } + decc$exit(vms_status); +} + +#ifdef VMS_USE_MAIN_WRAPPER +/* File: vms_main_wrapper.c + * + * $Id: vms_main_wrapper.c,v 1.3 2013/06/29 17:14:47 wb8tyw Exp $ + * + * This module provides a wrapper around the main() function of a ported + * program for two functions: + * + * 1. Make sure that the argv[0] string is set as close as possible to + * what the original command was given. + * + * 2. Make sure that the posix exit is called. + * + * Copyright 2012, John Malmberg + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#pragma member_alignment save +#pragma nomember_alignment longword +struct item_list_3 { + unsigned short len; + unsigned short code; + void * bufadr; + unsigned short * retlen; +}; + +struct filescan_itmlst_2 { + unsigned short length; + unsigned short itmcode; + char * component; +}; + +#pragma member_alignment restore + +int SYS$GETDVIW + (unsigned long efn, + unsigned short chan, + const struct dsc$descriptor_s * devnam, + const struct item_list_3 * itmlst, + void * iosb, + void (* astadr)(unsigned long), + unsigned long astprm, + void * nullarg); + +int SYS$FILESCAN + (const struct dsc$descriptor_s * srcstr, + struct filescan_itmlst_2 * valuelist, + unsigned long * fldflags, + struct dsc$descriptor_s *auxout, + unsigned short * retlen); + +#ifdef HAVE_VMS_MAIN_ENVP +#define VMS_ENVP ,char **env +#define VMS_ENV , env +#else +#define VMS_ENVP +#define VMS_ENV +#endif +int original_main(int argc, char ** argv VMS_ENVP); + +int main(int argc, char ** argv VMS_ENVP) { +int status; +int result; +char arg_nam[256]; +char **new_argv; +int need_vms_translate; + + new_argv = argv; + result = 0; + + /* If the path name starts with a /, then it is an absolute path */ + /* that may have been generated by the CRTL instead of the command name */ + /* If it is the device name between the slashes, then this was likely */ + /* from the run command and needs to be fixed up. */ + /* If the DECC$POSIX_COMPLIANT_PATHNAMES is set to 2, then it is the */ + /* DISK$VOLUME that will be present, and it will still need to be fixed. */ + if (argv[0][0] == '/') { + char * nextslash; + int length; + struct item_list_3 itemlist[3]; + unsigned short dvi_iosb[4]; + char alldevnam[64]; + unsigned short alldevnam_len; + struct dsc$descriptor_s devname_dsc; + char diskvolnam[256]; + unsigned short diskvolnam_len; + + /* Get some information about the disk */ + /*--------------------------------------*/ + itemlist[0].len = (sizeof alldevnam) - 1; + itemlist[0].code = DVI$_ALLDEVNAM; + itemlist[0].bufadr = alldevnam; + itemlist[0].retlen = &alldevnam_len; + itemlist[1].len = (sizeof diskvolnam) - 1 - 5; + itemlist[1].code = DVI$_VOLNAM; + itemlist[1].bufadr = &diskvolnam[5]; + itemlist[1].retlen = &diskvolnam_len; + itemlist[2].len = 0; + itemlist[2].code = 0; + + /* Add the prefix for the volume name. */ + /* SYS$GETDVI will append the volume name to this */ + strcpy(diskvolnam,"DISK$"); + + nextslash = strchr(&argv[0][1], '/'); + if (nextslash != NULL) { + length = nextslash - argv[0] - 1; + + /* Cast needed for HP C compiler diagnostic */ + devname_dsc.dsc$a_pointer = (char *)&argv[0][1]; + devname_dsc.dsc$w_length = length; + devname_dsc.dsc$b_dtype = DSC$K_DTYPE_T; + devname_dsc.dsc$b_class = DSC$K_CLASS_S; + + status = SYS$GETDVIW + (EFN$C_ENF, + 0, + &devname_dsc, + itemlist, + dvi_iosb, + NULL, 0, 0); + if (!$VMS_STATUS_SUCCESS(status)) { + /* If the sys$getdviw fails, then this path was passed by */ + /* An exec() program and not from DCL, so do nothing */ + /* An example is "/tmp/program" where tmp: does not exist */ + result = 0; + } else if (!$VMS_STATUS_SUCCESS(dvi_iosb[0])) { + + result = 0; + } else { + char * devnam; + int devnam_len; + char argv_dev[64]; + + /* Null terminate the returned alldevnam */ + alldevnam[alldevnam_len] = 0; + devnam = alldevnam; + devnam_len = alldevnam_len; + + /* Need to skip past any leading underscore */ + if (devnam[0] == '_') { + devnam++; + devnam_len--; + } + + /* And remove the trailing colon */ + if (devnam[devnam_len - 1] == ':') { + devnam_len--; + devnam[devnam_len] = 0; + } + + /* Null terminate the returned volnam */ + diskvolnam_len += 5; + diskvolnam[diskvolnam_len] = 0; + + /* Check first for normal CRTL behavior */ + if (devnam_len == length) { + strncpy(arg_nam, &argv[0][1], length); + arg_nam[length] = 0; + result = (strcasecmp(devnam, arg_nam) == 0); + } + + /* If we have not got a match check for POSIX Compliant */ + /* behavior. To be more accurate, we could also check */ + /* to see if that feature is active. */ + if ((result == 0) && (diskvolnam_len == length)) { + strncpy(arg_nam, &argv[0][1], length); + arg_nam[length] = 0; + result = (strcasecmp(diskvolnam, arg_nam) == 0); + } + } + } + } else { + /* The path did not start with a slash, so it could be VMS format */ + /* If it is vms format, it has a volume/device in it as it must */ + /* be an absolute path */ + struct dsc$descriptor_s path_desc; + int status; + unsigned long field_flags; + struct filescan_itmlst_2 item_list[5]; + char * volume; + char * name; + int name_len; + char * ext; + + path_desc.dsc$a_pointer = (char *)argv[0]; /* cast ok */ + path_desc.dsc$w_length = strlen(argv[0]); + path_desc.dsc$b_dtype = DSC$K_DTYPE_T; + path_desc.dsc$b_class = DSC$K_CLASS_S; + + /* Don't actually need to initialize anything buf itmcode */ + /* I just do not like uninitialized input values */ + + /* Sanity check, this must be the same length as input */ + item_list[0].itmcode = FSCN$_FILESPEC; + item_list[0].length = 0; + item_list[0].component = NULL; + + /* If the device is present, then it if a VMS spec */ + item_list[1].itmcode = FSCN$_DEVICE; + item_list[1].length = 0; + item_list[1].component = NULL; + + /* we need the program name and type */ + item_list[2].itmcode = FSCN$_NAME; + item_list[2].length = 0; + item_list[2].component = NULL; + + item_list[3].itmcode = FSCN$_TYPE; + item_list[3].length = 0; + item_list[3].component = NULL; + + /* End the list */ + item_list[4].itmcode = 0; + item_list[4].length = 0; + item_list[4].component = NULL; + + status = SYS$FILESCAN( + (const struct dsc$descriptor_s *)&path_desc, + item_list, &field_flags, NULL, NULL); + + if ($VMS_STATUS_SUCCESS(status) && + (item_list[0].length == path_desc.dsc$w_length) && + (item_list[1].length != 0)) { + + char * dollar; + int keep_ext; + int i; + + /* We need the filescan to be successful, */ + /* same length as input, and a volume to be present */ + + /* Need a new argv array */ + new_argv = malloc((argc + 1) * (sizeof(char *))); + new_argv[0] = arg_nam; + i = 1; + while (i < argc) { + new_argv[i] = argv[i]; + i++; + } + + /* We will assume that we only get to this path on a version */ + /* of VMS that does not support the EFS character set */ + + /* There may be a xxx$ prefix on the image name. Linux */ + /* programs do not handle that well, so strip the prefix */ + name = item_list[2].component; + name_len = item_list[2].length; + dollar = strrchr(name, '$'); + if (dollar != NULL) { + dollar++; + name_len = name_len - (dollar - name); + name = dollar; + } + + strncpy(arg_nam, name, name_len); + arg_nam[name_len] = 0; + + /* We only keep the extension if it is not ".exe" */ + keep_ext = 0; + ext = item_list[3].component; + + if (item_list[3].length != 1) { + if (item_list[3].length != 4) { + keep_ext = 1; + } else { + int x; + x = strncmp(ext, ".exe", 4); + if (x != 0) { + keep_ext = 1; + } + } + } + + if (keep_ext == 1) { + strncpy(&arg_nam[name_len], ext, item_list[3].length); + } + } + } + + if (result) { + char * lastslash; + char * dollar; + char * dotexe; + char * lastdot; + char * extension; + + /* This means it is probably the name from a DCL command */ + /* Find the last slash which separates the file from the */ + /* path. */ + lastslash = strrchr(argv[0], '/'); + + if (lastslash != NULL) { + int i; + + lastslash++; + + /* There may be a xxx$ prefix on the image name. Linux */ + /* programs do not handle that well, so strip the prefix */ + dollar = strrchr(lastslash, '$'); + + if (dollar != NULL) { + dollar++; + lastslash = dollar; + } + + strcpy(arg_nam, lastslash); + + /* In UNIX mode + EFS character set, there should not be a */ + /* version present, as it is not possible when parsing to */ + /* tell if it is a version or part of the UNIX filename as */ + /* UNIX programs use numeric extensions for many reasons. */ + + lastdot = strrchr(arg_nam, '.'); + if (lastdot != NULL) { + int i; + + i = 1; + while (isdigit(lastdot[i])) { + i++; + } + if (lastdot[i] == 0) { + *lastdot = 0; + } + } + + /* Find the .exe on the name (case insenstive) and toss it */ + dotexe = strrchr(arg_nam, '.'); + if (dotexe != NULL) { + if ((dotexe[1] == 'e' || dotexe[1] == 'E') && + (dotexe[2] == 'x' || dotexe[2] == 'X') && + (dotexe[3] == 'e' || dotexe[3] == 'E') && + (dotexe[4] == 0)) { + + *dotexe = 0; + } else { + /* Also need to handle a null extension because of a */ + /* CRTL bug. */ + if (dotexe[1] == 0) { + *dotexe = 0; + } + } + } + + /* Need a new argv array */ + new_argv = malloc((argc + 1) * (sizeof(char *))); + new_argv[0] = arg_nam; + i = 1; + while (i < argc) { + new_argv[i] = argv[i]; + i++; + } + new_argv[i] = 0; + + } else { + /* There is no way that the code should ever get here */ + /* As we already verified that the '/' was present */ + fprintf(stderr, "Sanity failure somewhere we lost a '/'\n"); + } + + } + exit(original_main(argc, new_argv VMS_ENV)); +} +#endif +char* vms_command(const char* argv0) +{ + size_t l = strlen(argv0) + 1; + char* s = malloc(l + 4); + memcpy (s, "mcr ", 4); + memcpy (s+4, argv0, l); + return s; +}