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 */
44 enum {
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 
58 static 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 
67 static char	const *OutputFile;
68 static char	const *Curfile;
69 static char	*Curfun;
70 static int	Curline;
71 static Interface Iface;
72 
73 static int  Verbosity;
74 static int  TargetArchToken;		/* set from -a option to front-end */
75 char *TargetArchStr = NULL;		/* from -a option to front-end */
76 int IsFilterLib = 0;			/* set from -F option to front-end */
77 static int  Supported_Arch = XLATOR_ALLARCH;	/* from "Arch" SPEC keyword */
78 static 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  */
86 static int  Version_Arch;
87 int  Num_versfiles = 0;
88 static int  Has_Version;
89 
90 static char *Versfile;
91 
92 static char *getversion(const char *);
93 static int version_sanity(const char *value, char **subv);
94 static int arch_version_sanity(char *av);
95 static char *getfilter(const char *);
96 static void writemapfile(FILE *);
97 static int set_version_arch(const char *);
98 static 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  */
106 xlator_keyword_t *
107 xlator_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*/
167 int
168 xlator_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  */
181 int
182 xlator_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  */
199 int
200 xlator_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  */
270 int
271 xlator_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*/
453 int
454 xlator_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 
543 cleanup:
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  */
567 int
568 xlator_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  */
584 int
585 xlator_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  */
622 int
623 xlator_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  */
641 static char *
642 getversion(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  */
700 static char *
701 getfilter(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  */
759 static int
760 version_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  */
824 static int
825 arch_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  */
855 static void
856 writemapfile(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  */
961 static int
962 set_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  */
1002 static int
1003 set_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