>From 23f015ac3abe14844e5fc1c067d98fa23b36ce3f Mon Sep 17 00:00:00 2001
From: Hartmut Becker
Date: Thu, 4 Sep 2014 21:04:52 +0200
Subject: [PATCH] Fix and enhance VMS library support.
* ar.c: fix VMS library search for members, which do
not have suffixes, aka filename extensions.
* arscan.c: fix time conversion and library callback routines.
* default.c: more suffixes and automatically create the VMS library
if it doesn't exists.
---
ar.c | 49 ++++++++++-
arscan.c | 275 +++++++++++++++++++++++++++++++++++++++++--------------------
default.c | 61 +++++++++++---
3 files changed, 281 insertions(+), 104 deletions(-)
diff --git a/ar.c b/ar.c
index 5d2f000..675572a 100644
--- a/ar.c
+++ b/ar.c
@@ -1,5 +1,5 @@
/* Interface to 'ar' archives for GNU Make.
-Copyright (C) 1988-2013 Free Software Foundation, Inc.
+Copyright (C) 1988-2014 Free Software Foundation, Inc.
This file is part of GNU Make.
@@ -172,10 +172,20 @@ ar_touch (const char *name)
/* State of an 'ar_glob' run, passed to 'ar_glob_match'. */
+/* On VMS, (object) modules in libraries do not have suffixes. That is, to
+ find a match for a pattern, the pattern must not have any suffix. So the
+ suffix of the pattern is saved and the pattern is stripped (ar_glob).
+ If there is a match and the match, which is a module name, is added to
+ the chain, the saved suffix is added back to construct a source filename
+ (ar_glob_match). */
+
struct ar_glob_state
{
const char *arname;
const char *pattern;
+#ifdef VMS
+ char *suffix;
+#endif
unsigned int size;
struct nameseq *chain;
unsigned int n;
@@ -196,7 +206,13 @@ ar_glob_match (int desc UNUSED, const char *mem, int truncated UNUSED,
{
/* We have a match. Add it to the chain. */
struct nameseq *new = xcalloc (state->size);
- new->name = strcache_add (concat (4, state->arname, "(", mem, ")"));
+#ifdef VMS
+ if (state->suffix)
+ new->name = strcache_add(
+ concat(5, state->arname, "(", mem, state->suffix, ")"));
+ else
+#endif
+ new->name = strcache_add(concat(4, state->arname, "(", mem, ")"));
new->next = state->chain;
state->chain = new;
++state->n;
@@ -248,7 +264,9 @@ ar_glob (const char *arname, const char *member_pattern, unsigned int size)
struct nameseq *n;
const char **names;
unsigned int i;
-
+#ifdef VMS
+ char *vms_member_pattern;
+#endif
if (! glob_pattern_p (member_pattern, 1))
return 0;
@@ -256,11 +274,36 @@ ar_glob (const char *arname, const char *member_pattern, unsigned int size)
ar_glob_match will accumulate them in STATE.chain. */
state.arname = arname;
state.pattern = member_pattern;
+#ifdef VMS
+ {
+ /* In a copy of the pattern, find the suffix, save it and remove it from
+ the pattern */
+ char *lastdot;
+ vms_member_pattern = xstrdup(member_pattern);
+ lastdot = strrchr(vms_member_pattern, '.');
+ state.suffix = lastdot;
+ if (lastdot)
+ {
+ state.suffix = xstrdup(lastdot);
+ *lastdot = 0;
+ }
+ state.pattern = vms_member_pattern;
+ }
+#endif
state.size = size;
state.chain = 0;
state.n = 0;
ar_scan (arname, ar_glob_match, &state);
+#ifdef VMS
+ /* Deallocate any duplicated string */
+ free(vms_member_pattern);
+ if (state.suffix)
+ {
+ free(state.suffix);
+ }
+#endif
+
if (state.chain == 0)
return 0;
diff --git a/arscan.c b/arscan.c
index 50d8495..24286fd 100644
--- a/arscan.c
+++ b/arscan.c
@@ -1,5 +1,5 @@
/* Library function for scanning an archive file.
-Copyright (C) 1987-2013 Free Software Foundation, Inc.
+Copyright (C) 1987-2014 Free Software Foundation, Inc.
This file is part of GNU Make.
GNU Make is free software; you can redistribute it and/or modify it under the
@@ -16,6 +16,11 @@ this program. If not, see . */
#include "makeint.h"
+#ifdef TEST
+/* Hack, the real error() routine eventually pulls in die from main.c */
+#define error(a, b, c, d)
+#endif
+
#ifdef HAVE_FCNTL_H
#include
#else
@@ -30,100 +35,139 @@ this program. If not, see . */
#include
#include
#include
+#include
+#include
+#include
+globalvalue unsigned int LBR$_HDRTRUNC;
+
#if __DECC
#include
#include
#endif
+const char *
+vmsify (const char *name, int type);
+
+/* Time conversion from VMS to Unix
+ Conversion from local time (stored in library) to GMT (needed for gmake)
+ Note: The tm_gmtoff element is a VMS extension to the ANSI standard. */
+static time_t
+vms_time_to_unix(void *vms_time)
+{
+ struct tm *tmp;
+ time_t unix_time;
-static void *VMS_lib_idx;
+ unix_time = decc$fix_time(vms_time);
+ tmp = localtime(&unix_time);
+ unix_time -= tmp->tm_gmtoff;
-static const char *VMS_saved_memname;
+ return unix_time;
+}
+
+
+/* VMS library routines need static variables for callback */
+static void *VMS_lib_idx;
-static time_t VMS_member_date;
+static const void *VMS_saved_arg;
static long int (*VMS_function) ();
+static long int VMS_function_ret;
+
+
+/* This is a callback procedure for lib$get_index */
static int
-VMS_get_member_info (struct dsc$descriptor_s *module, unsigned long *rfa)
+VMS_get_member_info(struct dsc$descriptor_s *module, unsigned long *rfa)
{
int status, i;
- long int fnval;
-
- time_t val;
+ const int truncated = 0; /* Member name may be truncated */
+ time_t member_date; /* Member date */
+ char *filename;
+ unsigned int buffer_length; /* Actual buffer length */
+
+ /* Unused constants - Make does not actually use most of these */
+ const int file_desc = -1; /* archive file descriptor for reading the data */
+ const int header_position = 0; /* Header position */
+ const int data_position = 0; /* Data position in file */
+ const int data_size = 0; /* Data size */
+ const int uid = 0; /* member gid */
+ const int gid = 0; /* member gid */
+ const int mode = 0; /* member protection mode */
+ /* End of unused constants */
static struct dsc$descriptor_s bufdesc =
{ 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
+ /* Only need the module definition */
struct mhddef *mhd;
- char filename[128];
- bufdesc.dsc$a_pointer = filename;
- bufdesc.dsc$w_length = sizeof (filename);
+ /* If a previous callback is non-zero, just return that status */
+ if (VMS_function_ret)
+ {
+ return SS$_NORMAL;
+ }
+
+ /* lbr_set_module returns more than just the module header. So allocate
+ a buffer which is big enough: the maximum LBR$C_MAXHDRSIZ. That's at
+ least bigger than the size of struct mhddef.
+ If the request is too small, a buffer truncated warning is issued so
+ it can be reissued with a larger buffer.
+ We do not care if the buffer is truncated, so that is still a success. */
+ mhd = xmalloc(LBR$C_MAXHDRSIZ);
+ bufdesc.dsc$a_pointer = (char *) mhd;
+ bufdesc.dsc$w_length = LBR$C_MAXHDRSIZ;
+
+ status = lbr$set_module(&VMS_lib_idx, rfa, &bufdesc, &buffer_length, 0);
- status = lbr$set_module (&VMS_lib_idx, rfa, &bufdesc,
- &bufdesc.dsc$w_length, 0);
- if (! (status & 1))
+ if ((status != LBR$_HDRTRUNC) && !$VMS_STATUS_SUCCESS(status))
{
- ON (error, NILF,
+ ON(error, NILF,
_("lbr$set_module() failed to extract module info, status = %d"),
status);
- lbr$close (&VMS_lib_idx);
+ lbr$close(&VMS_lib_idx);
- return 0;
+ return status;
}
- mhd = (struct mhddef *) filename;
-
-#ifdef __DECC
- /* John Fowler writes this is needed in his environment,
- * but that decc$fix_time() isn't documented to work this way. Let me
- * know if this causes problems in other VMS environments.
- */
- {
- /* Modified by M. Gehre at 11-JAN-2008 because old formula is wrong:
- * val = decc$fix_time (&mhd->mhd$l_datim) + timezone - daylight*3600;
- * a) daylight specifies, if the timezone has daylight saving enabled, not
- * if it is active
- * b) what we need is the information, if daylight saving was active, if
- * the library module was replaced. This information we get using the
- * localtime function
- */
-
- struct tm *tmp;
-
- /* Conversion from VMS time to C time */
- val = decc$fix_time (&mhd->mhd$l_datim);
-
- /*
- * Conversion from local time (stored in library) to GMT (needed for gmake)
- * Note: The tm_gmtoff element is a VMS extension to the ANSI standard.
- */
- tmp = localtime (&val);
- val -= tmp->tm_gmtoff;
- }
+#ifdef TEST
+ /* When testing this code, it is useful to know the length returned */
+ printf("Input length = %d, actual = %d\n",
+ bufdesc.dsc$w_length, buffer_length);
#endif
+ /* Conversion from VMS time to C time.
+ VMS defectlet - mhddef is sub-optimal, for the time, it has a 32 bit
+ longword, mhd$l_datim, and a 32 bit fill instead of two longwords, or
+ equivalent. */
+ member_date = vms_time_to_unix(&mhd->mhd$l_datim);
+ free(mhd);
+
+ /* Here we have a problem. The module name on VMS does not have
+ a file type, but the filename pattern in the "VMS_saved_arg"
+ may have one.
+ But only the method being called knows how to interpret the
+ filename pattern.
+ There are currently two different formats being used.
+ This means that we need a VMS specific code in those methods
+ to handle it. */
+ filename = xmalloc(module->dsc$w_length + 1);
+
+ /* TODO: We may need an option to preserve the case of the module
+ For now force the module name to lower case */
for (i = 0; i < module->dsc$w_length; i++)
- filename[i] = _tolower ((unsigned char)module->dsc$a_pointer[i]);
+ filename[i] = _tolower((unsigned char )module->dsc$a_pointer[i]);
filename[i] = '\0';
- VMS_member_date = (time_t) -1;
+ VMS_function_ret = (*VMS_function)(file_desc, filename, truncated,
+ header_position, data_position, data_size, member_date, uid, gid, mode,
+ VMS_saved_arg);
- fnval =
- (*VMS_function) (-1, filename, 0, 0, 0, 0, val, 0, 0, 0,
- VMS_saved_memname);
-
- if (fnval)
- {
- VMS_member_date = fnval;
- return 0;
- }
- else
- return 1;
+ free(filename);
+ return SS$_NORMAL;
}
+
/* Takes three arguments ARCHIVE, FUNCTION and ARG.
Open the archive named ARCHIVE, find its members one by one,
@@ -156,59 +200,87 @@ VMS_get_member_info (struct dsc$descriptor_s *module, unsigned long *rfa)
long int
ar_scan (const char *archive, ar_member_func_t function, const void *varg)
{
- char *p;
- const char *arg = varg;
+ char *vms_archive;
static struct dsc$descriptor_s libdesc =
{ 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
- unsigned long func = LBR$C_READ;
- unsigned long type = LBR$C_TYP_UNK;
- unsigned long index = 1;
-
+ const unsigned long func = LBR$C_READ;
+ const unsigned long type = LBR$C_TYP_UNK;
+ const unsigned long index = 1;
+ unsigned long lib_idx;
int status;
- status = lbr$ini_control (&VMS_lib_idx, &func, &type, 0);
+ VMS_saved_arg = varg;
- if (! (status & 1))
+ /* Null archive string can show up in test and cause an access violation */
+ if (archive == NULL)
{
- ON (error, NILF, _("lbr$ini_control() failed with status = %d"), status);
- return -2;
+ /* Null filenames do not exist */
+ return -1;
}
- /* there is no such descriptor with "const char *dsc$a_pointer" */
- libdesc.dsc$a_pointer = (char *)archive;
- libdesc.dsc$w_length = strlen (archive);
+ /* archive path name must be in VMS format */
+ vms_archive = (char *) vmsify(archive, 0);
- status = lbr$open (&VMS_lib_idx, &libdesc, 0, 0, 0, 0, 0);
+ status = lbr$ini_control(&VMS_lib_idx, &func, &type, 0);
- if (! (status & 1))
+ if (!$VMS_STATUS_SUCCESS(status))
{
- OSS (error, NILF, _("unable to open library '%s' to lookup member '%s'"),
- archive, arg);
- return -1;
+ ON(error, NILF, _("lbr$ini_control() failed with status = %d"), status);
+ return -2;
}
- VMS_saved_memname = arg;
+ libdesc.dsc$a_pointer = vms_archive;
+ libdesc.dsc$w_length = strlen(vms_archive);
+
+ status = lbr$open(&VMS_lib_idx, &libdesc, 0, NULL, 0, NULL, 0);
+
+ if (!$VMS_STATUS_SUCCESS(status))
+ {
- /* For comparison, delete .obj from arg name. */
+ /* TODO: A library format failure could mean that this is a file
+ generated by the GNU AR utility and in that case, we need to
+ take the UNIX codepath. This will also take a change to the
+ GNV AR wrapper program. */
- p = strrchr (VMS_saved_memname, '.');
- if (p)
- *p = '\0';
+ switch (status)
+ {
+ case RMS$_FNF:
+ /* Archive does not exist */
+ return -1;
+ default:
+#ifndef TEST
+ OSN(error, NILF,
+ _("unable to open library '%s' to lookup member status %d"),
+ archive, status);
+#endif
+ /* For library format errors, specification says to return -2 */
+ return -2;
+ }
+ }
VMS_function = function;
- VMS_member_date = (time_t) -1;
- lbr$get_index (&VMS_lib_idx, &index, VMS_get_member_info, 0);
+ /* Clear the return status, as we are supposed to stop calling the
+ callback function if it becomes non-zero, and this is a static
+ variable. */
+ VMS_function_ret = 0;
- /* Undo the damage. */
- if (p)
- *p = '.';
+ status = lbr$get_index(&VMS_lib_idx, &index, VMS_get_member_info, NULL, 0);
- lbr$close (&VMS_lib_idx);
+ lbr$close(&VMS_lib_idx);
+
+ /* Unless a failure occurred in the lbr$ routines, return the
+ the status from the 'function' routine. */
+ if ($VMS_STATUS_SUCCESS(status))
+ {
+ return VMS_function_ret;
+ }
- return VMS_member_date > 0 ? VMS_member_date : 0;
+ /* This must be something wrong with the library and an error
+ message should already have been printed. */
+ return -2;
}
#else /* !VMS */
@@ -753,9 +825,32 @@ ar_name_equal (const char *name, const char *mem, int truncated)
#endif /* !__hpux && !cray */
#endif /* !AIAMAG */
}
-#endif /* !VMS */
return !strcmp (name, mem);
+#else
+ /* VMS members do not have suffixes, but the filenames usually
+ have.
+ Do we need to strip VMS disk/directory format paths?
+
+ Most VMS compilers etc. by default are case insensitive
+ but produce uppercase external names, incl. module names.
+ However the VMS librarian (ar) and the linker by default
+ are case sensitive: they take what they get, usually
+ uppercase names. So for the non-default settings of the
+ compilers etc. there is a need to have a case sensitive
+ mode. */
+ {
+ int len;
+ len = strlen(mem);
+ int match;
+ char *dot;
+ if ((dot=strrchr(name,'.')))
+ match = (len == dot - name) && !strncasecmp(name, mem, len);
+ else
+ match = !strcasecmp (name, mem);
+ return match;
+ }
+#endif /* !VMS */
}
#ifndef VMS
diff --git a/default.c b/default.c
index b805c04..3b6f7ae 100644
--- a/default.c
+++ b/default.c
@@ -38,9 +38,11 @@ this program. If not, see . */
static char default_suffixes[]
#ifdef VMS
- = ".exe .olb .ln .obj .c .cxx .cc .pas .p .for .f .r .y .l .mar \
-.s .ss .i .ii .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
-.w .ch .cweb .web .com .sh .elc .el";
+ /* VMS should include all UNIX/POSIX + some VMS extensions */
+ = ".out .exe .a .olb .hlb .tlb .mlb .ln .o .obj .c .cxx .cc .cpp .pas .p \
+.for .f .r .y .l .ym .yl .mar .s .ss .i .ii .mod .sym .def .h .info .dvi \
+.tex .texinfo .texi .txinfo .mem .hlp .brn .rnh .rno .rnt .rnx .w .ch .cweb \
+.web .com .sh .elc .el";
#elif defined(__EMX__)
= ".out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym .yl .s .S \
.mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
@@ -53,19 +55,35 @@ static char default_suffixes[]
static struct pspec default_pattern_rules[] =
{
+#ifdef VMS
{ "(%)", "%",
+ "@if f$$search(\"address@hidden") .eqs. \"\" then $(LIBRARY)/CREATE/"
+ "$(or "
+ "$(patsubst %,TEXT,$(filter %.tlb %.TLB,$@)),"
+ "$(patsubst %,HELP,$(filter %.hlb %.HLB,$@)),"
+ "$(patsubst %,MACRO,$(filter %.mlb %.MLB,$@)),"
+ "$(and "
+ "$(patsubst %,SHARE,$(filter %.olb %.OLB,$@)),"
+ "$(patsubst %,SHARE,$(filter %.exe %.EXE,$<))),"
+ "OBJECT)"
+ " address@hidden"
"$(AR) $(ARFLAGS) $@ $<" },
+#else
+ { "(%)", "%",
+ "$(AR) $(ARFLAGS) $@ $<" },
+#endif
/* The X.out rules are only in BSD's default set because
BSD Make has no null-suffix rules, so 'foo.out' and
'foo' are the same thing. */
#ifdef VMS
{ "%.exe", "%",
- "copy $< $@" },
-#else
+ "$(CP) $< $@" },
+
+#endif
{ "%.out", "%",
"@rm -f $@ \n cp $< $@" },
-#endif
+
/* Syntax is "ctangle foo.w foo.ch foo.c". */
{ "%.c", "%.w %.ch",
"$(CTANGLE) $^ $@" },
@@ -78,18 +96,20 @@ static struct pspec default_pattern_rules[] =
static struct pspec default_terminal_rules[] =
{
#ifdef VMS
+
/* RCS. */
{ "%", "%$$5lv", /* Multinet style */
- "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },
+ "if f$$search(\"address@hidden") .nes. \"\" then +$(CHECKOUT,v)" },
{ "%", "[.$$rcs]%$$5lv", /* Multinet style */
- "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },
+ "if f$$search(\"address@hidden") .nes. \"\" then +$(CHECKOUT,v)" },
{ "%", "%_v", /* Normal style */
- "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },
+ "if f$$search(\"address@hidden") .nes. \"\" then +$(CHECKOUT,v)" },
{ "%", "[.rcs]%_v", /* Normal style */
- "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },
+ "if f$$search(\"address@hidden") .nes. \"\" then +$(CHECKOUT,v)" },
/* SCCS. */
/* ain't no SCCS on vms */
+
#else
/* RCS. */
{ "%", "%,v",
@@ -149,6 +169,8 @@ static const char *default_suffix_rules[] =
"$(COMPILE.c)/noprep/noobj/machine /list=$@ $<",
".c.obj",
"$(COMPILE.c) /obj=$@ $<",
+ ".c.o",
+ "$(COMPILE.c) /obj=$@ $<",
".cc.ii",
"$(COMPILE.cc)/prep /list=$@ $<",
".cc.ss",
@@ -157,12 +179,20 @@ static const char *default_suffix_rules[] =
"$(COMPILE.cc)/noprep/noobj/machine /list=$@ $<",
".cc.obj",
"$(COMPILE.cc) /obj=$@ $<",
+ ".cc.o",
+ "$(COMPILE.cc) /obj=$@ $<",
".cxx.obj",
"$(COMPILE.cxx) /obj=$@ $<",
+ ".cxx.o",
+ "$(COMPILE.cxx) /obj=$@ $<",
".for.obj",
"$(COMPILE.for) /obj=$@ $<",
+ ".for.o",
+ "$(COMPILE.for) /obj=$@ $<",
".pas.obj",
"$(COMPILE.pas) /obj=$@ $<",
+ ".pas.o",
+ "$(COMPILE.pas) /obj=$@ $<",
".y.c",
"$(YACC.y) $< \n rename y_tab.c $@",
@@ -322,7 +352,8 @@ static const char *default_variables[] =
#ifdef __VAX
"ARCH", "VAX",
#endif
- "AR", "library/obj",
+ "AR", "library",
+ "LIBRARY", "library",
"ARFLAGS", "/replace",
"AS", "macro",
"MACRO", "macro",
@@ -339,7 +370,14 @@ static const char *default_variables[] =
#else
"C++", "cxx",
"CXX", "cxx",
+#ifndef __ia64
"CXXLD", "cxxlink",
+ "CXXLINK", "cxxlink",
+#else
+ /* CXXLINK is not used on VMS/IA64 */
+ "CXXLD", "link",
+ "CXXLINK", "link",
+#endif
#endif
"CO", "co",
"CPP", "$(CC) /preprocess_only",
@@ -392,6 +430,7 @@ static const char *default_variables[] =
"MV", "rename/new_version",
"CP", "copy",
+ ".LIBPATTERNS", "%.olb lib%.a",
#else /* !VMS */
--
1.7.10.4