/*
* 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);
}