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