1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 *  Back-end functions for spec to mapfile converter
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <ctype.h>
36#include <string.h>
37#include <errno.h>
38#include <sys/utsname.h>
39#include "xlator.h"
40#include "util.h"
41#include "bucket.h"
42
43/* Globals */
44enum {
45	/* These first four (commented out) are defined in parser.h */
46	/* XLATOR_KW_NOTFOUND = 0, */
47	/* XLATOR_KW_FUNC, */
48	/* XLATOR_KW_DATA, */
49	/* XLATOR_KW_END, */
50	XLATOR_KW_VERSION = 4,
51	XLATOR_KW_ARCH,
52	XLATOR_KW_BINDING,
53	XLATOR_KW_FILTER,
54	XLATOR_KW_AUXILIARY
55};
56#define	FIRST_TOKEN 4	/* Must match the first token in the enum above */
57
58static xlator_keyword_t Keywords[] = {
59	{ "version", XLATOR_KW_VERSION },
60	{ "arch", XLATOR_KW_ARCH },
61	{ "binding", XLATOR_KW_BINDING },
62	{ "filter", XLATOR_KW_FILTER },
63	{ "auxiliary", XLATOR_KW_AUXILIARY },
64	{ NULL, XLATOR_KW_NOTFOUND }
65};
66
67static char	const *OutputFile;
68static char	const *Curfile;
69static char	*Curfun;
70static int	Curline;
71static Interface Iface;
72
73static int  Verbosity;
74static int  TargetArchToken;		/* set from -a option to front-end */
75char *TargetArchStr = NULL;		/* from -a option to front-end */
76int IsFilterLib = 0;			/* set from -F option to front-end */
77static int  Supported_Arch = XLATOR_ALLARCH;	/* from "Arch" SPEC keyword */
78static int	Flags;
79
80/*
81 * WHAT!?
82 * from Version line
83 * 0 means architecture is not specified in the
84 * version line so it applies to all versions
85 */
86static int  Version_Arch;
87int  Num_versfiles = 0;
88static int  Has_Version;
89
90static char *Versfile;
91
92static char *getversion(const char *);
93static int version_sanity(const char *value, char **subv);
94static int arch_version_sanity(char *av);
95static char *getfilter(const char *);
96static void writemapfile(FILE *);
97static int set_version_arch(const char *);
98static int set_supported_arch(const char *);
99
100/*
101 * xlator_init()
102 *    back-end initialization
103 *    returns pointer to Keywords on success
104 *    returns NULL pointer on failure
105 */
106xlator_keyword_t *
107xlator_init(const Translator_info *t_info)
108{
109	/*
110	 * initially so we don't lose error messages from version_check
111	 * we'll set this again later based on ti_info.ti_verbosity
112	 */
113	seterrseverity(WARNING);
114
115	/* set verbosity */
116	Verbosity = t_info->ti_verbosity;
117	seterrseverity(t_info->ti_verbosity);
118
119	/* Obtain translator flags */
120	Flags = t_info->ti_flags;
121
122	/*
123	 * set Library Type
124	 * 1 if filter lib, 0 otherwise
125	 */
126	IsFilterLib = t_info->ti_libtype;
127
128	/* set target architecture */
129	TargetArchStr = t_info->ti_arch;
130	TargetArchToken = t_info->ti_archtoken;
131
132	errlog(STATUS, "Architecture set to \"%s\"", TargetArchStr);
133
134	/* set output file */
135	OutputFile = t_info->ti_output_file;
136	if (OutputFile) {
137		errlog(STATUS, "Output will go into %s",
138		    OutputFile);
139	} else {
140		OutputFile = "mapfile";
141		errlog(STATUS, "Using default output filename: %s",
142		    OutputFile);
143	}
144
145	/* obtain name of version file */
146	Versfile = t_info->ti_versfile;
147
148	/* call create_lists() to setup for parse_versions() */
149	create_lists();
150
151	/* Process Vers Files */
152	if (parse_versions(Versfile)) {
153		return (NULL);
154	}
155
156	return (Keywords);
157}
158
159/*
160 * xlator_startlib()
161 *    start of library
162 *    returns:  XLATOR_SUCCESS	on success
163 *              XLATOR_SKIP		if library is to be skipped
164 *              XLATOR_NONFATAL	on error
165 */
166/*ARGSUSED*/
167int
168xlator_startlib(char const *libname)
169{
170	errlog(TRACING, "xlator_startlib");
171	return (XLATOR_SUCCESS);
172}
173
174/*
175 * xlator_startfile()
176 *    start of spec file
177 *    returns:  XLATOR_SUCCESS	on success
178 *              XLATOR_SKIP		if file is to be skipped
179 *              XLATOR_NONFATAL	on error
180 */
181int
182xlator_startfile(char const *filename)
183{
184	errlog(TRACING, "xlator_startfile");
185
186	Curfile = filename;
187
188	return (XLATOR_SUCCESS);
189}
190
191/*
192 * xlator_start_if ()
193 *    start of interface specification
194 *    returns:  XLATOR_SUCCESS	on success
195 *              XLATOR_SKIP		if interface is to be skipped
196 *              XLATOR_NONFATAL	on error
197 *              XLATOR_FATAL	on fatal error
198 */
199int
200xlator_start_if(const Meta_info meta_info, const int token, char *value)
201{
202	char rhs[BUFSIZ];
203	char *kw;
204	int err;
205
206	errlog(TRACING, "xlator_start_if %s", value);
207
208	switch (token) {
209	case XLATOR_KW_FUNC:
210		kw = "Function";
211		break;
212	case XLATOR_KW_DATA:
213		kw = "Data";
214		break;
215	default:
216		/* This should never happen */
217		errlog(ERROR,
218		    "\"%s\", line %d: Implementation error! "
219		    "Please file a bug\n", __FILE__, __LINE__);
220		return (XLATOR_FATAL);
221	}
222
223	Curline = meta_info.mi_line_number;
224	seterrline(Curline, meta_info.mi_filename, kw, value);
225
226	if (Curfun != NULL) {
227		errlog(INPUT|ERROR,
228		    "Error: Interface spec is missing the "
229		    "End keyword: %s", Curfun);
230		return (XLATOR_NONFATAL);
231	}
232
233	err = sscanf(value, "%s", rhs);
234	if (err == 0 || err == EOF) {
235		errlog(INPUT|ERROR,
236		    "Error: Missing argument in \"%s\" line", kw);
237		return (XLATOR_NONFATAL);
238	}
239
240	Curfun = strdup(rhs);
241
242	if (Curfun == NULL) {
243		errlog(ERROR | FATAL,
244		    "Internal Error: strdup() failure in xlator_startif()");
245	}
246
247	Iface.IF_name = Curfun;
248	Iface.IF_type = token;		/* FUNCTION or DATA */
249
250	Iface.IF_version = NULL;
251	Iface.IF_class = NULL;
252	Has_Version = 0;
253	Supported_Arch = XLATOR_ALLARCH;
254	Version_Arch = 0;
255
256	Iface.IF_binding = DEFAULT;
257
258	Iface.IF_filter = NULL;
259	Iface.IF_auxiliary = NULL;
260
261	return (XLATOR_SUCCESS);
262}
263
264/*
265 * xlator_take_kvpair()
266 *    processes spec keyword-value pairs
267 *    returns:  XLATOR_SUCCESS	on success
268 *              XLATOR_NONFATAL	on error
269 */
270int
271xlator_take_kvpair(const Meta_info meta_info, const int token,
272	char *value)
273{
274	char *p;
275	char *subv = NULL;
276	char *key = Keywords[token-FIRST_TOKEN].key;
277
278	Curline = meta_info.mi_line_number;
279	seterrline(Curline, meta_info.mi_filename, key, value);
280
281	errlog(TRACING,
282	    "take_kvpair called. ext_cnt=%d token=%d key=%s value=%s",
283	    meta_info.mi_ext_cnt, token, key, value);
284
285	if (Curfun == NULL) {
286		errlog(INPUT|ERROR, "Error: Keyword found outside "
287		    "an interface specification block, line %d", Curline);
288		return (XLATOR_NONFATAL);
289	}
290
291	switch (token) {
292	case XLATOR_KW_VERSION:
293		if (meta_info.mi_ext_cnt  !=  0)
294			return (XLATOR_SUCCESS);
295
296		errlog(TRACING, "Version found. Setting Version to %s", value);
297
298		/* Version line found ; used for auditing the SPEC */
299		Has_Version = 1;
300
301		/* remove trailing white space */
302		p = strrchr(value, '\n');
303		if (p) {
304			while (p >= value && isspace(*p)) {
305				*p = '\0';
306				--p;
307			}
308		}
309
310		/* is the version line valid */
311		switch (version_sanity(value, &subv)) {
312		case VS_OK:		/* OK, subv not set */
313			break;
314
315		case VS_INVARCH:	/* Invalid Arch */
316			errlog(INPUT|ERROR, "Error: Invalid architecture "
317			    "string found in spec or version file: %s", subv);
318			free(subv);
319			return (XLATOR_NONFATAL);
320
321		case VS_INVVERS:	/* Invalid Version String */
322			errlog(INPUT|ERROR, "Error: Invalid version string "
323			    "in spec or version file: %s", subv);
324			free(subv);
325			return (XLATOR_NONFATAL);
326
327		case VS_INVALID:	/* Both Version and Arch are invalid */
328			errlog(INPUT|ERROR, "Error: Invalid version and "
329			    "architecture string in spec or version file"
330				": %s", subv);
331			free(subv);
332			return (XLATOR_NONFATAL);
333
334		default:	/* BAD IMPLEMENTATION OF version_sanity */
335			errlog(FATAL, "Error: bad return value from "
336			    "version_sanity()! This should never happen!");
337		}
338
339		errlog(TRACING, "Version_Arch=%d", Version_Arch);
340
341		Iface.IF_version = getversion(value);
342		break;
343
344	case XLATOR_KW_ARCH:
345		if (meta_info.mi_ext_cnt  !=  0)
346			return (XLATOR_SUCCESS);
347
348		if (value[0] != '\0') {
349			Supported_Arch = 0;
350			if (set_supported_arch(value)) {
351				errlog(INPUT|ERROR,
352				    "Error: Unable to parse Arch line");
353				return (XLATOR_NONFATAL);
354			}
355		} else {
356			errlog(INPUT | ERROR, "Error: Empty Arch line.");
357		}
358
359		if (Supported_Arch == 0) {
360			errlog(INPUT | ERROR,
361			    "Error: Unknown architecture defined in Arch line");
362		}
363
364		errlog(TRACING,
365		    "Interface %s supports the following architectures: "
366		    "%s\tSupported_Arch=%d", Curfun, value, Supported_Arch);
367		break;
368
369	case XLATOR_KW_BINDING:
370
371		/*
372		 * Note that we allow extends for the binding keyword by
373		 * not checking that meta_info.mi_ext_cnt == 0 here.
374		 */
375
376		/* remove trailing white space */
377		p = strrchr(value, '\n');
378		if (p) {
379			while (p >= value && isspace(*p)) {
380				*p = '\0';
381				--p;
382			}
383		}
384
385		if (value[0] != '\0') {
386			if (strcmp(value, "direct") == 0) {
387				Iface.IF_binding = DIRECT;
388			} else if (strcmp(value, "nodirect") == 0) {
389				Iface.IF_binding = NODIRECT;
390			} else if (strcmp(value, "protected") == 0) {
391				Iface.IF_binding = PROTECTED;
392			} else {
393				errlog(INPUT|ERROR,
394				    "Error: Invalid binding value: %s", value);
395			}
396		} else {
397			errlog(INPUT | ERROR, "Error: Empty Binding line.");
398		}
399
400		errlog(TRACING,
401		    "Interface %s has binding value: "
402		    "%s", Curfun, value);
403		break;
404
405	case XLATOR_KW_FILTER:
406	case XLATOR_KW_AUXILIARY:
407		/*
408		 * The following is for the "extends" clause.  As with
409		 * XLATOR_KW_VERSION, we do not want to follow an "extends"
410		 * chain to get the filter or auxiliary values: we want
411		 * the first/most-tightly-bound one (mi_ext_cnt = 0).
412		 */
413		if (meta_info.mi_ext_cnt  !=  0)
414			return (XLATOR_SUCCESS);
415
416		errlog(TRACING, "Filter[token=%d] found. Setting Filter to %s",
417		    token, value);
418
419		/* remove trailing white space */
420		p = strrchr(value, '\n');
421		if (p) {
422			while (p >= value && isspace(*p)) {
423				*p = '\0';
424				--p;
425			}
426		}
427
428		errlog(TRACING, "Version_Arch=%d", Version_Arch);
429
430		if (token == XLATOR_KW_FILTER) {
431			Iface.IF_filter = getfilter(value);
432		} else if (token == XLATOR_KW_AUXILIARY) {
433			Iface.IF_auxiliary = getfilter(value);
434		}
435
436		break;
437	default:
438		errlog(INPUT|ERROR, "Error: Unrecognized keyword snuck in!"
439		    "\tThis is a programmer error: %s", key);
440		return (XLATOR_NONFATAL);
441	}
442
443	return (XLATOR_SUCCESS);
444}
445
446/*
447 * xlator_end_if ()
448 *  signal end of spec interface spec
449 *     returns: XLATOR_SUCCESS on success
450 *		XLATOR_NONFATAL	on error
451 */
452/*ARGSUSED*/
453int
454xlator_end_if(const Meta_info M, const char *value)
455{
456	int retval = XLATOR_NONFATAL;
457	int picky = Flags & XLATOR_PICKY_FLAG;
458
459	seterrline(M.mi_line_number, M.mi_filename, "End", "");
460	errlog(TRACING, "xlator_end_if");
461
462	if (Curfun == NULL) {
463		errlog(INPUT | ERROR, "Error: End without "
464		    "matching Function or Data in file \"%s\"", Curfile);
465		goto cleanup;
466	}
467
468	errlog(TRACING, "Interface=%s", Iface.IF_name);
469
470	if (!Has_Version) {
471		if (picky) {
472			errlog(INPUT | ERROR, "Error: Interface has no "
473			    "Version!\n\tInterface=%s\n\tSPEC File=%s",
474			    Iface.IF_name, Curfile);
475		} else {
476			errlog(INPUT | WARNING, "Warning: Interface has "
477			    "no Version!\n\tInterface=%s\n\tSPEC File=%s",
478			    Iface.IF_name, Curfile);
479			retval = XLATOR_SUCCESS;
480		}
481		goto cleanup;
482	}
483
484	if (Version_Arch & (~Supported_Arch)) {
485		errlog(INPUT | ERROR, "Error: Architectures in Version "
486		    "line must be a subset of Architectures in Arch line\n"
487		    "\tInterface=%s\n\tSPEC File=%s", Iface.IF_name, Curfile);
488		goto cleanup;
489	}
490
491	if ((TargetArchToken & Supported_Arch) == 0) {
492		/*
493		 * This interface is not for the architecture
494		 * we are currently processing, so we skip it.
495		 */
496		retval = XLATOR_SUCCESS;
497		goto cleanup;
498	}
499
500	if (Iface.IF_version == NULL) {
501		if (picky) {
502			errlog(ERROR|INPUT,
503			    "Error:  Version was not found for "
504			    "\"%s\" architecture\n\tInterface=%s",
505			    TargetArchStr, Iface.IF_name);
506		} else {
507			errlog(WARNING | INPUT,
508			    "Warning:  Version was not found for "
509			    "\"%s\" architecture\n\tInterface=%s",
510			    TargetArchStr, Iface.IF_name);
511			retval = XLATOR_SUCCESS;
512		}
513		goto cleanup;
514	}
515
516	/* check Iface.IF_type */
517	switch (Iface.IF_type) {
518	case FUNCTION:
519		errlog(VERBOSE, "Interface type = FUNCTION");
520		break;
521	case DATA:
522		errlog(VERBOSE, "Interface type = DATA");
523		break;
524	case NOTYPE:
525		errlog(WARNING,
526		    "Warning: Interface is neither "
527		    "DATA nor FUNCTION!!\n\t"
528		    "Interface=%s\n\tSPEC File=%s",
529		    Iface.IF_name, Curfile);
530		break;
531	default:
532		errlog(ERROR, "Error: Bad spec2map implementation!\n"
533		    "\tInterface type is invalid\n"
534		    "\tThis should never happen.\n"
535		    "\tInterface=%s\tSPEC File=%s", Iface.IF_name, Curfile);
536		goto cleanup;
537	}
538
539	(void) add_by_name(Iface.IF_version, &Iface);
540
541	retval = XLATOR_SUCCESS;
542
543cleanup:
544
545	/* cleanup */
546	Iface.IF_name = NULL;
547
548	free(Iface.IF_version);
549	Iface.IF_version = NULL;
550
551	free(Iface.IF_class);
552	Iface.IF_class = NULL;
553
554	free(Curfun);
555	Curfun = NULL;
556
557	Supported_Arch = XLATOR_ALLARCH;
558	return (retval);
559}
560
561/*
562 * xlator_endfile()
563 *   signal end of spec file
564 *    returns:  XLATOR_SUCCESS	on success
565 *              XLATOR_NONFATAL	on error
566 */
567int
568xlator_endfile(void)
569{
570
571	errlog(TRACING, "xlator_endfile");
572
573	Curfile = NULL;
574
575	return (XLATOR_SUCCESS);
576}
577
578/*
579 * xlator_endlib()
580 *   signal end of library
581 *    returns:  XLATOR_SUCCESS	on success
582 *              XLATOR_NONFATAL	on error
583 */
584int
585xlator_endlib(void)
586{
587	FILE *mapfp;
588	int retval = XLATOR_SUCCESS;
589
590	errlog(TRACING, "xlator_endlib");
591
592	/* Pretend to print mapfile */
593	if (Verbosity >= TRACING) {
594		print_all_buckets();
595	}
596
597	/* Everything read, now organize it! */
598	sort_buckets();
599	add_local();
600
601	/* Create Output */
602	mapfp = fopen(OutputFile, "w");
603	if (mapfp == NULL) {
604		errlog(ERROR,
605		    "Error: Unable to open output file \"%s\"\n\t%s",
606		    OutputFile, strerror(errno));
607		retval = XLATOR_NONFATAL;
608	} else {
609		writemapfile(mapfp);
610		(void) fclose(mapfp);
611	}
612
613	return (retval);
614}
615
616/*
617 * xlator_end()
618 *   signal end of translation
619 *    returns:  XLATOR_SUCCESS	on success
620 *              XLATOR_NONFATAL	on error
621 */
622int
623xlator_end(void)
624{
625	errlog(TRACING, "xlator_end");
626
627	/* Destroy the list created by create_lists */
628	delete_lists();
629
630	return (XLATOR_SUCCESS);
631}
632
633/*
634 * getversion()
635 * called by xlator_take_kvpair when Version keyword is found
636 * parses the Version string and returns the one that matches
637 * the current target architecture
638 *
639 * the pointer returned by this function must be freed later.
640 */
641static char *
642getversion(const char *value)
643{
644	char *v, *p;
645	char arch[ARCHBUFLEN];
646	int archlen;
647
648	/* up to ARCHBUFLEN-1 */
649	(void) strncpy(arch, TargetArchStr, ARCHBUFLEN-1);
650	arch[ARCHBUFLEN-2] = '\0';
651	(void) strcat(arch, "=");		/* append an '=' */
652	archlen = strlen(arch);
653
654	errlog(VERBOSE, "getversion: value=%s", value);
655
656	if (strchr(value, '=') != NULL) {
657		if ((v = strstr(value, arch)) != NULL) {
658			p = strdup(v + archlen);
659			if (p == NULL) {
660				errlog(ERROR | FATAL,
661				    "Internal Error: strdup() failure "
662				    "in getversion()");
663			}
664			v = p;
665			while (!isspace(*v) && *v != '\0')
666				++v;
667			*v = '\0';
668		} else {
669			errlog(VERBOSE, "getversion returns: NULL");
670			return (NULL);
671		}
672	} else {
673		p = strdup(value);
674		if (p == NULL) {
675			errlog(ERROR | FATAL, "Internal Error: strdup() "
676			    "failure in getversion()");
677		}
678	}
679
680	if (p != NULL)
681		errlog(VERBOSE, "getversion returns: %s", p);
682	else
683		errlog(VERBOSE, "getversion returns: NULL");
684
685	return (p);
686}
687
688/*
689 * getfilter()
690 * Called by xlator_take_kvpair when "filter" or "auxiliary" keyword is
691 * found.  Parses the Filter/Auxiliary string and returns the one that
692 * matches the current target architecture
693 *
694 * The pointer returned by this function must be freed later.
695 *
696 * Note that returning NULL here indicates there was no desired
697 * arch=path item in value, i.e. for TargetArchStr the interface is
698 * not a filter.
699 */
700static char *
701getfilter(const char *value)
702{
703	char *v, *p;
704	char arch[ARCHBUFLEN];
705	int archlen;
706
707	/* up to ARCHBUFLEN-1 */
708	(void) strncpy(arch, TargetArchStr, ARCHBUFLEN-1);
709	arch[ARCHBUFLEN-2] = '\0';
710	(void) strcat(arch, "=");		/* append an '=' */
711	archlen = strlen(arch);
712
713	errlog(VERBOSE, "getfilter: value=%s", value);
714
715	if (strchr(value, '=') != NULL) {
716		if ((v = strstr(value, arch)) != NULL) {
717			p = strdup(v + archlen);
718			if (p == NULL) {
719				errlog(ERROR | FATAL,
720				    "Internal Error: strdup() failure "
721				    "in getfilter()");
722			}
723			v = p;
724			while (!isspace(*v) && *v != '\0')
725				++v;
726			*v = '\0';
727		} else {
728			errlog(VERBOSE, "getfilter returns: NULL");
729			return (NULL);
730		}
731	} else {
732		p = strdup(value);
733		if (p == NULL) {
734			errlog(ERROR | FATAL, "Internal Error: strdup() "
735			    "failure in getfilter()");
736		}
737	}
738
739	if (p != NULL)
740		errlog(VERBOSE, "getfilter returns: %s", p);
741	else
742		errlog(VERBOSE, "getfilter returns: NULL");
743
744	return (p);
745}
746
747/*
748 * version_sanity()
749 *    for each version info in the Version line
750 *    check for its validity.
751 *    Set Version_arch to reflect all supported architectures if successful.
752 *    Upon return on failure, subv will contain the last version string
753 *    processed
754 *    returns: VS_OK	OK
755 *             VS_INVARCH    Invalid Architecture
756 *             VS_INVVERS    Invalid Version String
757 *             VS_INVALID    Both Version and Architecture are invalid;
758 */
759static int
760version_sanity(const char *value, char **subv)
761{
762	char *p, *v, *a;
763	int retval = VS_INVALID;
764
765	if (strchr(value, '=')) {
766		/* Form 1:   Version	arch=Version_string */
767		v = strdup(value);
768		if (v == NULL) {
769			errlog(ERROR | FATAL,
770			    "Internal Error: strdup() failure in "
771			    "version_sanity()");
772		}
773
774		/* process each arch=version string */
775		p = v;
776		while ((a = strtok(p, " \t\n"))) {
777			if ((retval = arch_version_sanity(a)) != VS_OK) {
778				*subv = strdup(a);
779				if (subv == NULL) {
780					errlog(ERROR | FATAL,
781					    "Internal Error: strdup() failure "
782					    "in version_sanity()");
783				}
784				break;
785			}
786			if ((retval = set_version_arch(a)) != VS_OK) {
787				/* set the global Version_arch */
788				*subv = strdup(a);
789				if (subv == NULL) {
790					errlog(ERROR | FATAL,
791					    "Internal Error: strdup() failure "
792					    "in version_sanity()");
793				}
794				break;
795			}
796			p = NULL;
797		}
798		free(v);
799	} else {
800		/* Form 2: Version		Version_string */
801		if (valid_version(value)) {
802			retval = VS_OK;
803		} else {
804			*subv = strdup(value);
805			if (subv == NULL) {
806				errlog(ERROR | FATAL,
807				    "Internal Error: strdup() failure "
808				    "in version_sanity()");
809			}
810		}
811	}
812	return (retval);
813}
814
815/*
816 * arch_version_sanity()
817 *    checks version lines of the form "arch=version"
818 *    av MUST be a string of the form "arch=version" (no spaces)
819 *    returns: VS_OK	OK
820 *             VS_INVARCH    Invalid Architecture
821 *             VS_INVVERS    Invalid Version String
822 *             VS_INVALID    Both Versions are invalid;
823 */
824static int
825arch_version_sanity(char *av)
826{
827	char *p, *v;
828	int retval = VS_OK;
829
830	p = strchr(av, '=');
831	if (p == NULL) {
832		errlog(INPUT|ERROR, "Error: Incorrect format of Version line");
833		return (VS_INVALID);
834	}
835
836	*p = '\0';	/* stick a '\0' where the '=' was */
837	v = p + 1;
838
839	if (valid_arch(av) == 0)
840		retval = VS_INVARCH;
841
842	if (valid_version(v) == 0)
843		retval += VS_INVVERS;
844
845	*p = '=';	/* restore the '=' */
846
847	return (retval);
848}
849
850/*
851 * writemapfile()
852 *    called by xlator_endlib();
853 *    writes out the map file
854 */
855static void
856writemapfile(FILE *mapfp)
857{
858	bucket_t *l;	/* List of buckets. */
859	bucket_t *b;	/* Bucket within list. */
860	struct bucketlist *bl;
861	table_t *t;
862	int i = 0, n = 0;
863	char **p;
864
865	errlog(BEGIN, "writemapfile() {");
866	for (l = first_list(); l != NULL; l = next_list()) {
867
868		for (b = first_from_list(l); b != NULL; b = next_from_list()) {
869			errlog(TRACING, "b_name = %s", b->b_name);
870			print_bucket(b); /* Debugging routine. */
871
872			if (!b->b_was_printed) {
873				/* Ok, we can print it. */
874				b->b_was_printed = 1;
875				(void) fprintf(mapfp, "%s {\n", b->b_name);
876
877				if (b->b_weak != 1) {
878					char *strtab;
879
880					(void) fprintf(mapfp, "    global:\n");
881
882					strtab = get_stringtable(
883						b->b_global_table, 0);
884
885					if (strtab == NULL) {
886						/*
887						 * There were no interfaces
888						 * in the bucket.
889						 * Insert a dummy entry
890						 * to avoid a "weak version"
891						 */
892						(void) fprintf(mapfp,
893						    "\t%s;\n", b->b_name);
894					}
895				} else {
896					(void) fprintf(mapfp,
897					    "    # Weak version\n");
898				}
899				/* Print all the interfaces in the bucket. */
900				t = b->b_global_table;
901				n = t->used;
902
903				for (i = 0; i <= n; ++i) {
904					(void) fprintf(mapfp, "\t%s;\n",
905					    get_stringtable(t, i));
906				}
907
908				if (b->b_has_protecteds) {
909					t = b->b_protected_table;
910					n = t->used;
911
912					(void) fprintf(mapfp,
913					    "    protected:\n");
914
915					for (i = 0; i <= n; ++i) {
916						(void) fprintf(mapfp, "\t%s;\n",
917						    get_stringtable(t, i));
918					}
919				}
920
921				/* Conditionally add ``local: *;''. */
922				if (b->b_has_locals) {
923					(void) fprintf(mapfp,
924					    "    local:\n\t*;\n}");
925				} else {
926					(void) fprintf(mapfp, "}");
927				}
928				/* Print name of all parents. */
929				for (p = parents_of(b);
930				    p !=  NULL && *p != '\0'; ++p) {
931					(void) fprintf(mapfp, " %s", *p);
932				}
933				bl = b->b_uncles;
934				while (bl != NULL) {
935					(void) fprintf(mapfp, " %s",
936					    bl->bl_bucket->b_name);
937					bl = bl->bl_next;
938				}
939
940				(void) fprintf(mapfp, ";\n\n");
941			} else {
942				/*
943				 * We've printed this one before,
944				 * so don't do it again.
945				 */
946				/*EMPTY*/;
947			}
948		}
949	}
950	errlog(END, "}");
951}
952
953/*
954 * set_version_arch ()
955 * input must be a string of the form "arch=version"
956 * turns on bits of global Version_Arch that correspond to the "arch"
957 * return VS_OK upon success
958 *  VS_INVARCH if architecture is invalid
959 *  EINVAL on other failure
960 */
961static int
962set_version_arch(const char *arch)
963{
964	char	*a, *p;
965	int	x;
966	int	retval = EINVAL;
967
968	if (arch == NULL)
969		return (retval);
970
971	a = strdup(arch);
972	if (a == NULL) {
973		errlog(ERROR | FATAL,
974		    "Internal Error: strdup() failure in "
975		    "set_version_arch()");
976	}
977
978	p = strchr(a, '=');
979	if (p) {
980		*p = '\0';
981		x = arch_strtoi(a);
982		if (x == 0) {
983			errlog(INPUT|ERROR,
984			    "Error: Invalid architecture: %s", a);
985			retval = VS_INVARCH;
986		} else {
987			Version_Arch |= x;
988			retval = 0;
989		}
990	}
991
992	free(a);
993	return (retval);
994}
995
996/*
997 * set_supported_arch ()
998 * input must be a string listing the architectures to be supported
999 * turns on bits of global Supported_Arch that correspond to the architecture
1000 * return 0 upon success, EINVAL on failure
1001 */
1002static int
1003set_supported_arch(const char *arch)
1004{
1005	char	*a, *p, *tmp;
1006	int	retval = EINVAL;
1007
1008	if (arch == NULL || *arch == '\0')
1009		return (EINVAL);
1010
1011	tmp = strdup(arch);
1012	if (tmp == NULL) {
1013		errlog(ERROR | FATAL, "Internal Error: strdup() failure in "
1014		    "set_supported_arch()");
1015	}
1016
1017	p = tmp;
1018	while ((a = strtok(p, " ,\t\n"))) {
1019		int x;
1020		x = arch_strtoi(a);
1021		if (x == 0) {
1022			errlog(INPUT|ERROR,
1023			    "Error: Invalid architecture: %s", a);
1024			free(tmp);
1025			return (EINVAL);
1026		}
1027		Supported_Arch |= x;
1028		retval = 0;
1029		p = NULL;
1030	}
1031
1032	free(tmp);
1033	return (retval);
1034}
1035