view usr/src/cmd/abi/spectrans/spec2map/xlator.c @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * 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	"%Z%%M%	%I%	%E% 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);
}