diff usr/src/cmd/abi/spectrans/spec2map/xlator.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/abi/spectrans/spec2map/xlator.c	Tue Jun 02 18:56:50 2009 +0900
@@ -0,0 +1,1034 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"@(#)xlator.c	1.12	05/06/08 SMI"
+
+/*
+ *  Back-end functions for spec to mapfile converter
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include "xlator.h"
+#include "util.h"
+#include "bucket.h"
+
+/* Globals */
+enum {
+	/* These first four (commented out) are defined in parser.h */
+	/* XLATOR_KW_NOTFOUND = 0, */
+	/* XLATOR_KW_FUNC, */
+	/* XLATOR_KW_DATA, */
+	/* XLATOR_KW_END, */
+	XLATOR_KW_VERSION = 4,
+	XLATOR_KW_ARCH,
+	XLATOR_KW_BINDING,
+	XLATOR_KW_FILTER,
+	XLATOR_KW_AUXILIARY
+};
+#define	FIRST_TOKEN 4	/* Must match the first token in the enum above */
+
+static xlator_keyword_t Keywords[] = {
+	{ "version", XLATOR_KW_VERSION },
+	{ "arch", XLATOR_KW_ARCH },
+	{ "binding", XLATOR_KW_BINDING },
+	{ "filter", XLATOR_KW_FILTER },
+	{ "auxiliary", XLATOR_KW_AUXILIARY },
+	{ NULL, XLATOR_KW_NOTFOUND }
+};
+
+static char	const *OutputFile;
+static char	const *Curfile;
+static char	*Curfun;
+static int	Curline;
+static Interface Iface;
+
+static int  Verbosity;
+static int  TargetArchToken;		/* set from -a option to front-end */
+char *TargetArchStr = NULL;		/* from -a option to front-end */
+int IsFilterLib = 0;			/* set from -F option to front-end */
+static int  Supported_Arch = XLATOR_ALLARCH;	/* from "Arch" SPEC keyword */
+static int	Flags;
+
+/*
+ * WHAT!?
+ * from Version line
+ * 0 means architecture is not specified in the
+ * version line so it applies to all versions
+ */
+static int  Version_Arch;
+int  Num_versfiles = 0;
+static int  Has_Version;
+
+static char *Versfile;
+
+static char *getversion(const char *);
+static int version_sanity(const char *value, char **subv);
+static int arch_version_sanity(char *av);
+static char *getfilter(const char *);
+static void writemapfile(FILE *);
+static int set_version_arch(const char *);
+static int set_supported_arch(const char *);
+
+/*
+ * xlator_init()
+ *    back-end initialization
+ *    returns pointer to Keywords on success
+ *    returns NULL pointer on failure
+ */
+xlator_keyword_t *
+xlator_init(const Translator_info *t_info)
+{
+	/*
+	 * initially so we don't lose error messages from version_check
+	 * we'll set this again later based on ti_info.ti_verbosity
+	 */
+	seterrseverity(WARNING);
+
+	/* set verbosity */
+	Verbosity = t_info->ti_verbosity;
+	seterrseverity(t_info->ti_verbosity);
+
+	/* Obtain translator flags */
+	Flags = t_info->ti_flags;
+
+	/*
+	 * set Library Type
+	 * 1 if filter lib, 0 otherwise
+	 */
+	IsFilterLib = t_info->ti_libtype;
+
+	/* set target architecture */
+	TargetArchStr = t_info->ti_arch;
+	TargetArchToken = t_info->ti_archtoken;
+
+	errlog(STATUS, "Architecture set to \"%s\"", TargetArchStr);
+
+	/* set output file */
+	OutputFile = t_info->ti_output_file;
+	if (OutputFile) {
+		errlog(STATUS, "Output will go into %s",
+		    OutputFile);
+	} else {
+		OutputFile = "mapfile";
+		errlog(STATUS, "Using default output filename: %s",
+		    OutputFile);
+	}
+
+	/* obtain name of version file */
+	Versfile = t_info->ti_versfile;
+
+	/* call create_lists() to setup for parse_versions() */
+	create_lists();
+
+	/* Process Vers Files */
+	if (parse_versions(Versfile)) {
+		return (NULL);
+	}
+
+	return (Keywords);
+}
+
+/*
+ * xlator_startlib()
+ *    start of library
+ *    returns:  XLATOR_SUCCESS	on success
+ *              XLATOR_SKIP		if library is to be skipped
+ *              XLATOR_NONFATAL	on error
+ */
+/*ARGSUSED*/
+int
+xlator_startlib(char const *libname)
+{
+	errlog(TRACING, "xlator_startlib");
+	return (XLATOR_SUCCESS);
+}
+
+/*
+ * xlator_startfile()
+ *    start of spec file
+ *    returns:  XLATOR_SUCCESS	on success
+ *              XLATOR_SKIP		if file is to be skipped
+ *              XLATOR_NONFATAL	on error
+ */
+int
+xlator_startfile(char const *filename)
+{
+	errlog(TRACING, "xlator_startfile");
+
+	Curfile = filename;
+
+	return (XLATOR_SUCCESS);
+}
+
+/*
+ * xlator_start_if ()
+ *    start of interface specification
+ *    returns:  XLATOR_SUCCESS	on success
+ *              XLATOR_SKIP		if interface is to be skipped
+ *              XLATOR_NONFATAL	on error
+ *              XLATOR_FATAL	on fatal error
+ */
+int
+xlator_start_if(const Meta_info meta_info, const int token, char *value)
+{
+	char rhs[BUFSIZ];
+	char *kw;
+	int err;
+
+	errlog(TRACING, "xlator_start_if %s", value);
+
+	switch (token) {
+	case XLATOR_KW_FUNC:
+		kw = "Function";
+		break;
+	case XLATOR_KW_DATA:
+		kw = "Data";
+		break;
+	default:
+		/* This should never happen */
+		errlog(ERROR,
+		    "\"%s\", line %d: Implementation error! "
+		    "Please file a bug\n", __FILE__, __LINE__);
+		return (XLATOR_FATAL);
+	}
+
+	Curline = meta_info.mi_line_number;
+	seterrline(Curline, meta_info.mi_filename, kw, value);
+
+	if (Curfun != NULL) {
+		errlog(INPUT|ERROR,
+		    "Error: Interface spec is missing the "
+		    "End keyword: %s", Curfun);
+		return (XLATOR_NONFATAL);
+	}
+
+	err = sscanf(value, "%s", rhs);
+	if (err == 0 || err == EOF) {
+		errlog(INPUT|ERROR,
+		    "Error: Missing argument in \"%s\" line", kw);
+		return (XLATOR_NONFATAL);
+	}
+
+	Curfun = strdup(rhs);
+
+	if (Curfun == NULL) {
+		errlog(ERROR | FATAL,
+		    "Internal Error: strdup() failure in xlator_startif()");
+	}
+
+	Iface.IF_name = Curfun;
+	Iface.IF_type = token;		/* FUNCTION or DATA */
+
+	Iface.IF_version = NULL;
+	Iface.IF_class = NULL;
+	Has_Version = 0;
+	Supported_Arch = XLATOR_ALLARCH;
+	Version_Arch = 0;
+
+	Iface.IF_binding = DEFAULT;
+
+	Iface.IF_filter = NULL;
+	Iface.IF_auxiliary = NULL;
+
+	return (XLATOR_SUCCESS);
+}
+
+/*
+ * xlator_take_kvpair()
+ *    processes spec keyword-value pairs
+ *    returns:  XLATOR_SUCCESS	on success
+ *              XLATOR_NONFATAL	on error
+ */
+int
+xlator_take_kvpair(const Meta_info meta_info, const int token,
+	char *value)
+{
+	char *p;
+	char *subv = NULL;
+	char *key = Keywords[token-FIRST_TOKEN].key;
+
+	Curline = meta_info.mi_line_number;
+	seterrline(Curline, meta_info.mi_filename, key, value);
+
+	errlog(TRACING,
+	    "take_kvpair called. ext_cnt=%d token=%d key=%s value=%s",
+	    meta_info.mi_ext_cnt, token, key, value);
+
+	if (Curfun == NULL) {
+		errlog(INPUT|ERROR, "Error: Keyword found outside "
+		    "an interface specification block, line %d", Curline);
+		return (XLATOR_NONFATAL);
+	}
+
+	switch (token) {
+	case XLATOR_KW_VERSION:
+		if (meta_info.mi_ext_cnt  !=  0)
+			return (XLATOR_SUCCESS);
+
+		errlog(TRACING, "Version found. Setting Version to %s", value);
+
+		/* Version line found ; used for auditing the SPEC */
+		Has_Version = 1;
+
+		/* remove trailing white space */
+		p = strrchr(value, '\n');
+		if (p) {
+			while (p >= value && isspace(*p)) {
+				*p = '\0';
+				--p;
+			}
+		}
+
+		/* is the version line valid */
+		switch (version_sanity(value, &subv)) {
+		case VS_OK:		/* OK, subv not set */
+			break;
+
+		case VS_INVARCH:	/* Invalid Arch */
+			errlog(INPUT|ERROR, "Error: Invalid architecture "
+			    "string found in spec or version file: %s", subv);
+			free(subv);
+			return (XLATOR_NONFATAL);
+
+		case VS_INVVERS:	/* Invalid Version String */
+			errlog(INPUT|ERROR, "Error: Invalid version string "
+			    "in spec or version file: %s", subv);
+			free(subv);
+			return (XLATOR_NONFATAL);
+
+		case VS_INVALID:	/* Both Version and Arch are invalid */
+			errlog(INPUT|ERROR, "Error: Invalid version and "
+			    "architecture string in spec or version file"
+				": %s", subv);
+			free(subv);
+			return (XLATOR_NONFATAL);
+
+		default:	/* BAD IMPLEMENTATION OF version_sanity */
+			errlog(FATAL, "Error: bad return value from "
+			    "version_sanity()! This should never happen!");
+		}
+
+		errlog(TRACING, "Version_Arch=%d", Version_Arch);
+
+		Iface.IF_version = getversion(value);
+		break;
+
+	case XLATOR_KW_ARCH:
+		if (meta_info.mi_ext_cnt  !=  0)
+			return (XLATOR_SUCCESS);
+
+		if (value[0] != '\0') {
+			Supported_Arch = 0;
+			if (set_supported_arch(value)) {
+				errlog(INPUT|ERROR,
+				    "Error: Unable to parse Arch line");
+				return (XLATOR_NONFATAL);
+			}
+		} else {
+			errlog(INPUT | ERROR, "Error: Empty Arch line.");
+		}
+
+		if (Supported_Arch == 0) {
+			errlog(INPUT | ERROR,
+			    "Error: Unknown architecture defined in Arch line");
+		}
+
+		errlog(TRACING,
+		    "Interface %s supports the following architectures: "
+		    "%s\tSupported_Arch=%d", Curfun, value, Supported_Arch);
+		break;
+
+	case XLATOR_KW_BINDING:
+
+		/*
+		 * Note that we allow extends for the binding keyword by
+		 * not checking that meta_info.mi_ext_cnt == 0 here.
+		 */
+
+		/* remove trailing white space */
+		p = strrchr(value, '\n');
+		if (p) {
+			while (p >= value && isspace(*p)) {
+				*p = '\0';
+				--p;
+			}
+		}
+
+		if (value[0] != '\0') {
+			if (strcmp(value, "direct") == 0) {
+				Iface.IF_binding = DIRECT;
+			} else if (strcmp(value, "nodirect") == 0) {
+				Iface.IF_binding = NODIRECT;
+			} else if (strcmp(value, "protected") == 0) {
+				Iface.IF_binding = PROTECTED;
+			} else {
+				errlog(INPUT|ERROR,
+				    "Error: Invalid binding value: %s", value);
+			}
+		} else {
+			errlog(INPUT | ERROR, "Error: Empty Binding line.");
+		}
+
+		errlog(TRACING,
+		    "Interface %s has binding value: "
+		    "%s", Curfun, value);
+		break;
+
+	case XLATOR_KW_FILTER:
+	case XLATOR_KW_AUXILIARY:
+		/*
+		 * The following is for the "extends" clause.  As with
+		 * XLATOR_KW_VERSION, we do not want to follow an "extends"
+		 * chain to get the filter or auxiliary values: we want
+		 * the first/most-tightly-bound one (mi_ext_cnt = 0).
+		 */
+		if (meta_info.mi_ext_cnt  !=  0)
+			return (XLATOR_SUCCESS);
+
+		errlog(TRACING, "Filter[token=%d] found. Setting Filter to %s",
+		    token, value);
+
+		/* remove trailing white space */
+		p = strrchr(value, '\n');
+		if (p) {
+			while (p >= value && isspace(*p)) {
+				*p = '\0';
+				--p;
+			}
+		}
+
+		errlog(TRACING, "Version_Arch=%d", Version_Arch);
+
+		if (token == XLATOR_KW_FILTER) {
+			Iface.IF_filter = getfilter(value);
+		} else if (token == XLATOR_KW_AUXILIARY) {
+			Iface.IF_auxiliary = getfilter(value);
+		}
+
+		break;
+	default:
+		errlog(INPUT|ERROR, "Error: Unrecognized keyword snuck in!"
+		    "\tThis is a programmer error: %s", key);
+		return (XLATOR_NONFATAL);
+	}
+
+	return (XLATOR_SUCCESS);
+}
+
+/*
+ * xlator_end_if ()
+ *  signal end of spec interface spec
+ *     returns: XLATOR_SUCCESS on success
+ *		XLATOR_NONFATAL	on error
+ */
+/*ARGSUSED*/
+int
+xlator_end_if(const Meta_info M, const char *value)
+{
+	int retval = XLATOR_NONFATAL;
+	int picky = Flags & XLATOR_PICKY_FLAG;
+
+	seterrline(M.mi_line_number, M.mi_filename, "End", "");
+	errlog(TRACING, "xlator_end_if");
+
+	if (Curfun == NULL) {
+		errlog(INPUT | ERROR, "Error: End without "
+		    "matching Function or Data in file \"%s\"", Curfile);
+		goto cleanup;
+	}
+
+	errlog(TRACING, "Interface=%s", Iface.IF_name);
+
+	if (!Has_Version) {
+		if (picky) {
+			errlog(INPUT | ERROR, "Error: Interface has no "
+			    "Version!\n\tInterface=%s\n\tSPEC File=%s",
+			    Iface.IF_name, Curfile);
+		} else {
+			errlog(INPUT | WARNING, "Warning: Interface has "
+			    "no Version!\n\tInterface=%s\n\tSPEC File=%s",
+			    Iface.IF_name, Curfile);
+			retval = XLATOR_SUCCESS;
+		}
+		goto cleanup;
+	}
+
+	if (Version_Arch & (~Supported_Arch)) {
+		errlog(INPUT | ERROR, "Error: Architectures in Version "
+		    "line must be a subset of Architectures in Arch line\n"
+		    "\tInterface=%s\n\tSPEC File=%s", Iface.IF_name, Curfile);
+		goto cleanup;
+	}
+
+	if ((TargetArchToken & Supported_Arch) == 0) {
+		/*
+		 * This interface is not for the architecture
+		 * we are currently processing, so we skip it.
+		 */
+		retval = XLATOR_SUCCESS;
+		goto cleanup;
+	}
+
+	if (Iface.IF_version == NULL) {
+		if (picky) {
+			errlog(ERROR|INPUT,
+			    "Error:  Version was not found for "
+			    "\"%s\" architecture\n\tInterface=%s",
+			    TargetArchStr, Iface.IF_name);
+		} else {
+			errlog(WARNING | INPUT,
+			    "Warning:  Version was not found for "
+			    "\"%s\" architecture\n\tInterface=%s",
+			    TargetArchStr, Iface.IF_name);
+			retval = XLATOR_SUCCESS;
+		}
+		goto cleanup;
+	}
+
+	/* check Iface.IF_type */
+	switch (Iface.IF_type) {
+	case FUNCTION:
+		errlog(VERBOSE, "Interface type = FUNCTION");
+		break;
+	case DATA:
+		errlog(VERBOSE, "Interface type = DATA");
+		break;
+	case NOTYPE:
+		errlog(WARNING,
+		    "Warning: Interface is neither "
+		    "DATA nor FUNCTION!!\n\t"
+		    "Interface=%s\n\tSPEC File=%s",
+		    Iface.IF_name, Curfile);
+		break;
+	default:
+		errlog(ERROR, "Error: Bad spec2map implementation!\n"
+		    "\tInterface type is invalid\n"
+		    "\tThis should never happen.\n"
+		    "\tInterface=%s\tSPEC File=%s", Iface.IF_name, Curfile);
+		goto cleanup;
+	}
+
+	(void) add_by_name(Iface.IF_version, &Iface);
+
+	retval = XLATOR_SUCCESS;
+
+cleanup:
+
+	/* cleanup */
+	Iface.IF_name = NULL;
+
+	free(Iface.IF_version);
+	Iface.IF_version = NULL;
+
+	free(Iface.IF_class);
+	Iface.IF_class = NULL;
+
+	free(Curfun);
+	Curfun = NULL;
+
+	Supported_Arch = XLATOR_ALLARCH;
+	return (retval);
+}
+
+/*
+ * xlator_endfile()
+ *   signal end of spec file
+ *    returns:  XLATOR_SUCCESS	on success
+ *              XLATOR_NONFATAL	on error
+ */
+int
+xlator_endfile(void)
+{
+
+	errlog(TRACING, "xlator_endfile");
+
+	Curfile = NULL;
+
+	return (XLATOR_SUCCESS);
+}
+
+/*
+ * xlator_endlib()
+ *   signal end of library
+ *    returns:  XLATOR_SUCCESS	on success
+ *              XLATOR_NONFATAL	on error
+ */
+int
+xlator_endlib(void)
+{
+	FILE *mapfp;
+	int retval = XLATOR_SUCCESS;
+
+	errlog(TRACING, "xlator_endlib");
+
+	/* Pretend to print mapfile */
+	if (Verbosity >= TRACING) {
+		print_all_buckets();
+	}
+
+	/* Everything read, now organize it! */
+	sort_buckets();
+	add_local();
+
+	/* Create Output */
+	mapfp = fopen(OutputFile, "w");
+	if (mapfp == NULL) {
+		errlog(ERROR,
+		    "Error: Unable to open output file \"%s\"\n\t%s",
+		    OutputFile, strerror(errno));
+		retval = XLATOR_NONFATAL;
+	} else {
+		writemapfile(mapfp);
+		(void) fclose(mapfp);
+	}
+
+	return (retval);
+}
+
+/*
+ * xlator_end()
+ *   signal end of translation
+ *    returns:  XLATOR_SUCCESS	on success
+ *              XLATOR_NONFATAL	on error
+ */
+int
+xlator_end(void)
+{
+	errlog(TRACING, "xlator_end");
+
+	/* Destroy the list created by create_lists */
+	delete_lists();
+
+	return (XLATOR_SUCCESS);
+}
+
+/*
+ * getversion()
+ * called by xlator_take_kvpair when Version keyword is found
+ * parses the Version string and returns the one that matches
+ * the current target architecture
+ *
+ * the pointer returned by this function must be freed later.
+ */
+static char *
+getversion(const char *value)
+{
+	char *v, *p;
+	char arch[ARCHBUFLEN];
+	int archlen;
+
+	/* up to ARCHBUFLEN-1 */
+	(void) strncpy(arch, TargetArchStr, ARCHBUFLEN-1);
+	arch[ARCHBUFLEN-2] = '\0';
+	(void) strcat(arch, "=");		/* append an '=' */
+	archlen = strlen(arch);
+
+	errlog(VERBOSE, "getversion: value=%s", value);
+
+	if (strchr(value, '=') != NULL) {
+		if ((v = strstr(value, arch)) != NULL) {
+			p = strdup(v + archlen);
+			if (p == NULL) {
+				errlog(ERROR | FATAL,
+				    "Internal Error: strdup() failure "
+				    "in getversion()");
+			}
+			v = p;
+			while (!isspace(*v) && *v != '\0')
+				++v;
+			*v = '\0';
+		} else {
+			errlog(VERBOSE, "getversion returns: NULL");
+			return (NULL);
+		}
+	} else {
+		p = strdup(value);
+		if (p == NULL) {
+			errlog(ERROR | FATAL, "Internal Error: strdup() "
+			    "failure in getversion()");
+		}
+	}
+
+	if (p != NULL)
+		errlog(VERBOSE, "getversion returns: %s", p);
+	else
+		errlog(VERBOSE, "getversion returns: NULL");
+
+	return (p);
+}
+
+/*
+ * getfilter()
+ * Called by xlator_take_kvpair when "filter" or "auxiliary" keyword is
+ * found.  Parses the Filter/Auxiliary string and returns the one that
+ * matches the current target architecture
+ *
+ * The pointer returned by this function must be freed later.
+ *
+ * Note that returning NULL here indicates there was no desired
+ * arch=path item in value, i.e. for TargetArchStr the interface is
+ * not a filter.
+ */
+static char *
+getfilter(const char *value)
+{
+	char *v, *p;
+	char arch[ARCHBUFLEN];
+	int archlen;
+
+	/* up to ARCHBUFLEN-1 */
+	(void) strncpy(arch, TargetArchStr, ARCHBUFLEN-1);
+	arch[ARCHBUFLEN-2] = '\0';
+	(void) strcat(arch, "=");		/* append an '=' */
+	archlen = strlen(arch);
+
+	errlog(VERBOSE, "getfilter: value=%s", value);
+
+	if (strchr(value, '=') != NULL) {
+		if ((v = strstr(value, arch)) != NULL) {
+			p = strdup(v + archlen);
+			if (p == NULL) {
+				errlog(ERROR | FATAL,
+				    "Internal Error: strdup() failure "
+				    "in getfilter()");
+			}
+			v = p;
+			while (!isspace(*v) && *v != '\0')
+				++v;
+			*v = '\0';
+		} else {
+			errlog(VERBOSE, "getfilter returns: NULL");
+			return (NULL);
+		}
+	} else {
+		p = strdup(value);
+		if (p == NULL) {
+			errlog(ERROR | FATAL, "Internal Error: strdup() "
+			    "failure in getfilter()");
+		}
+	}
+
+	if (p != NULL)
+		errlog(VERBOSE, "getfilter returns: %s", p);
+	else
+		errlog(VERBOSE, "getfilter returns: NULL");
+
+	return (p);
+}
+
+/*
+ * version_sanity()
+ *    for each version info in the Version line
+ *    check for its validity.
+ *    Set Version_arch to reflect all supported architectures if successful.
+ *    Upon return on failure, subv will contain the last version string
+ *    processed
+ *    returns: VS_OK	OK
+ *             VS_INVARCH    Invalid Architecture
+ *             VS_INVVERS    Invalid Version String
+ *             VS_INVALID    Both Version and Architecture are invalid;
+ */
+static int
+version_sanity(const char *value, char **subv)
+{
+	char *p, *v, *a;
+	int retval = VS_INVALID;
+
+	if (strchr(value, '=')) {
+		/* Form 1:   Version	arch=Version_string */
+		v = strdup(value);
+		if (v == NULL) {
+			errlog(ERROR | FATAL,
+			    "Internal Error: strdup() failure in "
+			    "version_sanity()");
+		}
+
+		/* process each arch=version string */
+		p = v;
+		while ((a = strtok(p, " \t\n"))) {
+			if ((retval = arch_version_sanity(a)) != VS_OK) {
+				*subv = strdup(a);
+				if (subv == NULL) {
+					errlog(ERROR | FATAL,
+					    "Internal Error: strdup() failure "
+					    "in version_sanity()");
+				}
+				break;
+			}
+			if ((retval = set_version_arch(a)) != VS_OK) {
+				/* set the global Version_arch */
+				*subv = strdup(a);
+				if (subv == NULL) {
+					errlog(ERROR | FATAL,
+					    "Internal Error: strdup() failure "
+					    "in version_sanity()");
+				}
+				break;
+			}
+			p = NULL;
+		}
+		free(v);
+	} else {
+		/* Form 2: Version		Version_string */
+		if (valid_version(value)) {
+			retval = VS_OK;
+		} else {
+			*subv = strdup(value);
+			if (subv == NULL) {
+				errlog(ERROR | FATAL,
+				    "Internal Error: strdup() failure "
+				    "in version_sanity()");
+			}
+		}
+	}
+	return (retval);
+}
+
+/*
+ * arch_version_sanity()
+ *    checks version lines of the form "arch=version"
+ *    av MUST be a string of the form "arch=version" (no spaces)
+ *    returns: VS_OK	OK
+ *             VS_INVARCH    Invalid Architecture
+ *             VS_INVVERS    Invalid Version String
+ *             VS_INVALID    Both Versions are invalid;
+ */
+static int
+arch_version_sanity(char *av)
+{
+	char *p, *v;
+	int retval = VS_OK;
+
+	p = strchr(av, '=');
+	if (p == NULL) {
+		errlog(INPUT|ERROR, "Error: Incorrect format of Version line");
+		return (VS_INVALID);
+	}
+
+	*p = '\0';	/* stick a '\0' where the '=' was */
+	v = p + 1;
+
+	if (valid_arch(av) == 0)
+		retval = VS_INVARCH;
+
+	if (valid_version(v) == 0)
+		retval += VS_INVVERS;
+
+	*p = '=';	/* restore the '=' */
+
+	return (retval);
+}
+
+/*
+ * writemapfile()
+ *    called by xlator_endlib();
+ *    writes out the map file
+ */
+static void
+writemapfile(FILE *mapfp)
+{
+	bucket_t *l;	/* List of buckets. */
+	bucket_t *b;	/* Bucket within list. */
+	struct bucketlist *bl;
+	table_t *t;
+	int i = 0, n = 0;
+	char **p;
+
+	errlog(BEGIN, "writemapfile() {");
+	for (l = first_list(); l != NULL; l = next_list()) {
+
+		for (b = first_from_list(l); b != NULL; b = next_from_list()) {
+			errlog(TRACING, "b_name = %s", b->b_name);
+			print_bucket(b); /* Debugging routine. */
+
+			if (!b->b_was_printed) {
+				/* Ok, we can print it. */
+				b->b_was_printed = 1;
+				(void) fprintf(mapfp, "%s {\n", b->b_name);
+
+				if (b->b_weak != 1) {
+					char *strtab;
+
+					(void) fprintf(mapfp, "    global:\n");
+
+					strtab = get_stringtable(
+						b->b_global_table, 0);
+
+					if (strtab == NULL) {
+						/*
+						 * There were no interfaces
+						 * in the bucket.
+						 * Insert a dummy entry
+						 * to avoid a "weak version"
+						 */
+						(void) fprintf(mapfp,
+						    "\t%s;\n", b->b_name);
+					}
+				} else {
+					(void) fprintf(mapfp,
+					    "    # Weak version\n");
+				}
+				/* Print all the interfaces in the bucket. */
+				t = b->b_global_table;
+				n = t->used;
+
+				for (i = 0; i <= n; ++i) {
+					(void) fprintf(mapfp, "\t%s;\n",
+					    get_stringtable(t, i));
+				}
+
+				if (b->b_has_protecteds) {
+					t = b->b_protected_table;
+					n = t->used;
+
+					(void) fprintf(mapfp,
+					    "    protected:\n");
+
+					for (i = 0; i <= n; ++i) {
+						(void) fprintf(mapfp, "\t%s;\n",
+						    get_stringtable(t, i));
+					}
+				}
+
+				/* Conditionally add ``local: *;''. */
+				if (b->b_has_locals) {
+					(void) fprintf(mapfp,
+					    "    local:\n\t*;\n}");
+				} else {
+					(void) fprintf(mapfp, "}");
+				}
+				/* Print name of all parents. */
+				for (p = parents_of(b);
+				    p !=  NULL && *p != '\0'; ++p) {
+					(void) fprintf(mapfp, " %s", *p);
+				}
+				bl = b->b_uncles;
+				while (bl != NULL) {
+					(void) fprintf(mapfp, " %s",
+					    bl->bl_bucket->b_name);
+					bl = bl->bl_next;
+				}
+
+				(void) fprintf(mapfp, ";\n\n");
+			} else {
+				/*
+				 * We've printed this one before,
+				 * so don't do it again.
+				 */
+				/*EMPTY*/;
+			}
+		}
+	}
+	errlog(END, "}");
+}
+
+/*
+ * set_version_arch ()
+ * input must be a string of the form "arch=version"
+ * turns on bits of global Version_Arch that correspond to the "arch"
+ * return VS_OK upon success
+ *  VS_INVARCH if architecture is invalid
+ *  EINVAL on other failure
+ */
+static int
+set_version_arch(const char *arch)
+{
+	char	*a, *p;
+	int	x;
+	int	retval = EINVAL;
+
+	if (arch == NULL)
+		return (retval);
+
+	a = strdup(arch);
+	if (a == NULL) {
+		errlog(ERROR | FATAL,
+		    "Internal Error: strdup() failure in "
+		    "set_version_arch()");
+	}
+
+	p = strchr(a, '=');
+	if (p) {
+		*p = '\0';
+		x = arch_strtoi(a);
+		if (x == 0) {
+			errlog(INPUT|ERROR,
+			    "Error: Invalid architecture: %s", a);
+			retval = VS_INVARCH;
+		} else {
+			Version_Arch |= x;
+			retval = 0;
+		}
+	}
+
+	free(a);
+	return (retval);
+}
+
+/*
+ * set_supported_arch ()
+ * input must be a string listing the architectures to be supported
+ * turns on bits of global Supported_Arch that correspond to the architecture
+ * return 0 upon success, EINVAL on failure
+ */
+static int
+set_supported_arch(const char *arch)
+{
+	char	*a, *p, *tmp;
+	int	retval = EINVAL;
+
+	if (arch == NULL || *arch == '\0')
+		return (EINVAL);
+
+	tmp = strdup(arch);
+	if (tmp == NULL) {
+		errlog(ERROR | FATAL, "Internal Error: strdup() failure in "
+		    "set_supported_arch()");
+	}
+
+	p = tmp;
+	while ((a = strtok(p, " ,\t\n"))) {
+		int x;
+		x = arch_strtoi(a);
+		if (x == 0) {
+			errlog(INPUT|ERROR,
+			    "Error: Invalid architecture: %s", a);
+			free(tmp);
+			return (EINVAL);
+		}
+		Supported_Arch |= x;
+		retval = 0;
+		p = NULL;
+	}
+
+	free(tmp);
+	return (retval);
+}