On 03/08/2014 02:47 PM, John E. Malmberg wrote:
> Since there will only be one build procedure and one resulting binary,
> there would not be two templates.
> 
> One of the main reasons for merging this fork back in is to get to one
> make binary.
> 
> The one binary where needed will check if it is running under a UNIX
> shell or DCL and adjust its behavior as needed.
> 
> This is the way that Curl and Perl now work.
> 
> That way users do not have to track two implementations of the same
> version of product.

It's not yet determined, whether there will be different binaries for
the same platform (and same VMS version). Again, I prefer to keep it
simple, whatever simple will be in this context.

> I am already expecting that a [.vms] directory will be needed to contain
> at the minimum, the files needed for building the PCSI kit.

Someone else needs to decide if these files should be in the GNU make
repository. I didn't look, but I also didn't expect to see files to
create a Debian or Red Hat package. But what do I know. Also I don't
know if the copyright for the wrapper code is OK, here.

Anyway, appended is what I came up with. I don't expect this to be the
final patch for this. So far it is only tested on Alpha/VMS 8.3.

I'm not happy with hijacking main. What this code seems to do is
replacing argv[0]. There seems to be no other change which affects the
main entry. It seems to be enough to call a vms_mangle_argv0(&argv[0]);
function, in main, just prior to when argv[0] is really used. This would
be similar to calls like initialize_main() which is "Needed for OS/2".

Anyway, these changes all depend on what is configured in
config.h[-vms[.template]]. (This also indicates that at the moment I
expect different binaries depending on what a user "configures".) As
default I used more or less the current behavior: no hihacking and no
/tmp. It also seems that HAVE_VMS_MAIN_ENVP must be defined. If that's
the case then there is no need to have it at all.

I did not test with DECC$POSIX_COMPLIANT_PATHNAMES or whatever affects
the argv[0] passed by the CRTL. It seems worth to have some comments in
these 300+ lines of code, which show some examples what is expected and
what the result will be.

In my opinion, silently defining or overwriting a symbol shoulnd't be
implemented. If the user wants/needs the argv[0] changed, she/he should
define a symbol. That means, this needs to be documented, in the readme
or in the config.h-vms.template.

On the other hand, moving the wrapper into an existing source file,
which includes starlet.h, exhibited incompatible prototypes for system
services, even without __NEW_STARLET defined. That is not really
surprising, but there should be a better way than the hack I introduced.

On MAKE_TROUBLE, I agree, as this is a failure status on Unix - it will
stop make as well - this should be in the same failure range as
MAKE_FAILURE, so I used STS$K_ERROR.

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 <http://www.gnu.org/licenses/>.  */
 
 /* 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 <http://www.gnu.org/licenses/>.  */
 #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 <starlet.h>
+#undef SYS$FILESCAN
+#undef SYS$GETDVIW
 #endif
 #include <descrip.h>
 #include <rms.h>
@@ -259,3 +264,425 @@ cvt_time (unsigned long tval)
 
   return (str);
 }
+
+#include <stsdef.h>
+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 <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <descrip.h>
+#include <dvidef.h>
+#include <efndef.h>
+#include <fscndef.h>
+
+#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;
+}
_______________________________________________
Bug-make mailing list
Bug-make@gnu.org
https://lists.gnu.org/mailman/listinfo/bug-make

Reply via email to