162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Generate kernel symbol version hashes.
362306a36Sopenharmony_ci   Copyright 1996, 1997 Linux International.
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci   New implementation contributed by Richard Henderson <rth@tamu.edu>
662306a36Sopenharmony_ci   Based on original work by Bjorn Ekwall <bj0rn@blox.se>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci   This file was part of the Linux modutils 2.4.22: moved back into the
962306a36Sopenharmony_ci   kernel sources by Rusty Russell/Kai Germaschewski.
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <stdio.h>
1462306a36Sopenharmony_ci#include <string.h>
1562306a36Sopenharmony_ci#include <stdlib.h>
1662306a36Sopenharmony_ci#include <unistd.h>
1762306a36Sopenharmony_ci#include <assert.h>
1862306a36Sopenharmony_ci#include <stdarg.h>
1962306a36Sopenharmony_ci#ifdef __GNU_LIBRARY__
2062306a36Sopenharmony_ci#include <getopt.h>
2162306a36Sopenharmony_ci#endif				/* __GNU_LIBRARY__ */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "genksyms.h"
2462306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define HASH_BUCKETS  4096
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic struct symbol *symtab[HASH_BUCKETS];
2962306a36Sopenharmony_cistatic FILE *debugfile;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint cur_line = 1;
3262306a36Sopenharmony_cichar *cur_filename;
3362306a36Sopenharmony_ciint in_source_file;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
3662306a36Sopenharmony_ci	   flag_preserve, flag_warnings;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int errors;
3962306a36Sopenharmony_cistatic int nsyms;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic struct symbol *expansion_trail;
4262306a36Sopenharmony_cistatic struct symbol *visited_symbols;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const struct {
4562306a36Sopenharmony_ci	int n;
4662306a36Sopenharmony_ci	const char *name;
4762306a36Sopenharmony_ci} symbol_types[] = {
4862306a36Sopenharmony_ci	[SYM_NORMAL]     = { 0, NULL},
4962306a36Sopenharmony_ci	[SYM_TYPEDEF]    = {'t', "typedef"},
5062306a36Sopenharmony_ci	[SYM_ENUM]       = {'e', "enum"},
5162306a36Sopenharmony_ci	[SYM_STRUCT]     = {'s', "struct"},
5262306a36Sopenharmony_ci	[SYM_UNION]      = {'u', "union"},
5362306a36Sopenharmony_ci	[SYM_ENUM_CONST] = {'E', "enum constant"},
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int equal_list(struct string_list *a, struct string_list *b);
5762306a36Sopenharmony_cistatic void print_list(FILE * f, struct string_list *list);
5862306a36Sopenharmony_cistatic struct string_list *concat_list(struct string_list *start, ...);
5962306a36Sopenharmony_cistatic struct string_list *mk_node(const char *string);
6062306a36Sopenharmony_cistatic void print_location(void);
6162306a36Sopenharmony_cistatic void print_type_name(enum symbol_type type, const char *name);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const unsigned int crctab32[] = {
6662306a36Sopenharmony_ci	0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
6762306a36Sopenharmony_ci	0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
6862306a36Sopenharmony_ci	0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
6962306a36Sopenharmony_ci	0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
7062306a36Sopenharmony_ci	0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
7162306a36Sopenharmony_ci	0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
7262306a36Sopenharmony_ci	0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
7362306a36Sopenharmony_ci	0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
7462306a36Sopenharmony_ci	0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
7562306a36Sopenharmony_ci	0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
7662306a36Sopenharmony_ci	0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
7762306a36Sopenharmony_ci	0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
7862306a36Sopenharmony_ci	0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
7962306a36Sopenharmony_ci	0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
8062306a36Sopenharmony_ci	0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
8162306a36Sopenharmony_ci	0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
8262306a36Sopenharmony_ci	0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
8362306a36Sopenharmony_ci	0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
8462306a36Sopenharmony_ci	0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
8562306a36Sopenharmony_ci	0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
8662306a36Sopenharmony_ci	0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
8762306a36Sopenharmony_ci	0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
8862306a36Sopenharmony_ci	0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
8962306a36Sopenharmony_ci	0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
9062306a36Sopenharmony_ci	0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
9162306a36Sopenharmony_ci	0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
9262306a36Sopenharmony_ci	0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
9362306a36Sopenharmony_ci	0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
9462306a36Sopenharmony_ci	0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
9562306a36Sopenharmony_ci	0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
9662306a36Sopenharmony_ci	0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
9762306a36Sopenharmony_ci	0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
9862306a36Sopenharmony_ci	0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
9962306a36Sopenharmony_ci	0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
10062306a36Sopenharmony_ci	0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
10162306a36Sopenharmony_ci	0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
10262306a36Sopenharmony_ci	0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
10362306a36Sopenharmony_ci	0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
10462306a36Sopenharmony_ci	0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
10562306a36Sopenharmony_ci	0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
10662306a36Sopenharmony_ci	0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
10762306a36Sopenharmony_ci	0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
10862306a36Sopenharmony_ci	0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
10962306a36Sopenharmony_ci	0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
11062306a36Sopenharmony_ci	0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
11162306a36Sopenharmony_ci	0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
11262306a36Sopenharmony_ci	0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
11362306a36Sopenharmony_ci	0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
11462306a36Sopenharmony_ci	0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
11562306a36Sopenharmony_ci	0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
11662306a36Sopenharmony_ci	0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
11762306a36Sopenharmony_ci	0x2d02ef8dU
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic unsigned long partial_crc32(const char *s, unsigned long crc)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	while (*s)
12862306a36Sopenharmony_ci		crc = partial_crc32_one(*s++, crc);
12962306a36Sopenharmony_ci	return crc;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic unsigned long crc32(const char *s)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic enum symbol_type map_to_ns(enum symbol_type t)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	switch (t) {
14262306a36Sopenharmony_ci	case SYM_ENUM_CONST:
14362306a36Sopenharmony_ci	case SYM_NORMAL:
14462306a36Sopenharmony_ci	case SYM_TYPEDEF:
14562306a36Sopenharmony_ci		return SYM_NORMAL;
14662306a36Sopenharmony_ci	case SYM_ENUM:
14762306a36Sopenharmony_ci	case SYM_STRUCT:
14862306a36Sopenharmony_ci	case SYM_UNION:
14962306a36Sopenharmony_ci		return SYM_STRUCT;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	return t;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistruct symbol *find_symbol(const char *name, enum symbol_type ns, int exact)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	unsigned long h = crc32(name) % HASH_BUCKETS;
15762306a36Sopenharmony_ci	struct symbol *sym;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	for (sym = symtab[h]; sym; sym = sym->hash_next)
16062306a36Sopenharmony_ci		if (map_to_ns(sym->type) == map_to_ns(ns) &&
16162306a36Sopenharmony_ci		    strcmp(name, sym->name) == 0 &&
16262306a36Sopenharmony_ci		    sym->is_declared)
16362306a36Sopenharmony_ci			break;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (exact && sym && sym->type != ns)
16662306a36Sopenharmony_ci		return NULL;
16762306a36Sopenharmony_ci	return sym;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int is_unknown_symbol(struct symbol *sym)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct string_list *defn;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return ((sym->type == SYM_STRUCT ||
17562306a36Sopenharmony_ci		 sym->type == SYM_UNION ||
17662306a36Sopenharmony_ci		 sym->type == SYM_ENUM) &&
17762306a36Sopenharmony_ci		(defn = sym->defn)  && defn->tag == SYM_NORMAL &&
17862306a36Sopenharmony_ci			strcmp(defn->string, "}") == 0 &&
17962306a36Sopenharmony_ci		(defn = defn->next) && defn->tag == SYM_NORMAL &&
18062306a36Sopenharmony_ci			strcmp(defn->string, "UNKNOWN") == 0 &&
18162306a36Sopenharmony_ci		(defn = defn->next) && defn->tag == SYM_NORMAL &&
18262306a36Sopenharmony_ci			strcmp(defn->string, "{") == 0);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic struct symbol *__add_symbol(const char *name, enum symbol_type type,
18662306a36Sopenharmony_ci			    struct string_list *defn, int is_extern,
18762306a36Sopenharmony_ci			    int is_reference)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	unsigned long h;
19062306a36Sopenharmony_ci	struct symbol *sym;
19162306a36Sopenharmony_ci	enum symbol_status status = STATUS_UNCHANGED;
19262306a36Sopenharmony_ci	/* The parser adds symbols in the order their declaration completes,
19362306a36Sopenharmony_ci	 * so it is safe to store the value of the previous enum constant in
19462306a36Sopenharmony_ci	 * a static variable.
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	static int enum_counter;
19762306a36Sopenharmony_ci	static struct string_list *last_enum_expr;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (type == SYM_ENUM_CONST) {
20062306a36Sopenharmony_ci		if (defn) {
20162306a36Sopenharmony_ci			free_list(last_enum_expr, NULL);
20262306a36Sopenharmony_ci			last_enum_expr = copy_list_range(defn, NULL);
20362306a36Sopenharmony_ci			enum_counter = 1;
20462306a36Sopenharmony_ci		} else {
20562306a36Sopenharmony_ci			struct string_list *expr;
20662306a36Sopenharmony_ci			char buf[20];
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci			snprintf(buf, sizeof(buf), "%d", enum_counter++);
20962306a36Sopenharmony_ci			if (last_enum_expr) {
21062306a36Sopenharmony_ci				expr = copy_list_range(last_enum_expr, NULL);
21162306a36Sopenharmony_ci				defn = concat_list(mk_node("("),
21262306a36Sopenharmony_ci						   expr,
21362306a36Sopenharmony_ci						   mk_node(")"),
21462306a36Sopenharmony_ci						   mk_node("+"),
21562306a36Sopenharmony_ci						   mk_node(buf), NULL);
21662306a36Sopenharmony_ci			} else {
21762306a36Sopenharmony_ci				defn = mk_node(buf);
21862306a36Sopenharmony_ci			}
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci	} else if (type == SYM_ENUM) {
22162306a36Sopenharmony_ci		free_list(last_enum_expr, NULL);
22262306a36Sopenharmony_ci		last_enum_expr = NULL;
22362306a36Sopenharmony_ci		enum_counter = 0;
22462306a36Sopenharmony_ci		if (!name)
22562306a36Sopenharmony_ci			/* Anonymous enum definition, nothing more to do */
22662306a36Sopenharmony_ci			return NULL;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	h = crc32(name) % HASH_BUCKETS;
23062306a36Sopenharmony_ci	for (sym = symtab[h]; sym; sym = sym->hash_next) {
23162306a36Sopenharmony_ci		if (map_to_ns(sym->type) == map_to_ns(type) &&
23262306a36Sopenharmony_ci		    strcmp(name, sym->name) == 0) {
23362306a36Sopenharmony_ci			if (is_reference)
23462306a36Sopenharmony_ci				/* fall through */ ;
23562306a36Sopenharmony_ci			else if (sym->type == type &&
23662306a36Sopenharmony_ci				 equal_list(sym->defn, defn)) {
23762306a36Sopenharmony_ci				if (!sym->is_declared && sym->is_override) {
23862306a36Sopenharmony_ci					print_location();
23962306a36Sopenharmony_ci					print_type_name(type, name);
24062306a36Sopenharmony_ci					fprintf(stderr, " modversion is "
24162306a36Sopenharmony_ci						"unchanged\n");
24262306a36Sopenharmony_ci				}
24362306a36Sopenharmony_ci				sym->is_declared = 1;
24462306a36Sopenharmony_ci				return sym;
24562306a36Sopenharmony_ci			} else if (!sym->is_declared) {
24662306a36Sopenharmony_ci				if (sym->is_override && flag_preserve) {
24762306a36Sopenharmony_ci					print_location();
24862306a36Sopenharmony_ci					fprintf(stderr, "ignoring ");
24962306a36Sopenharmony_ci					print_type_name(type, name);
25062306a36Sopenharmony_ci					fprintf(stderr, " modversion change\n");
25162306a36Sopenharmony_ci					sym->is_declared = 1;
25262306a36Sopenharmony_ci					return sym;
25362306a36Sopenharmony_ci				} else {
25462306a36Sopenharmony_ci					status = is_unknown_symbol(sym) ?
25562306a36Sopenharmony_ci						STATUS_DEFINED : STATUS_MODIFIED;
25662306a36Sopenharmony_ci				}
25762306a36Sopenharmony_ci			} else {
25862306a36Sopenharmony_ci				error_with_pos("redefinition of %s", name);
25962306a36Sopenharmony_ci				return sym;
26062306a36Sopenharmony_ci			}
26162306a36Sopenharmony_ci			break;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (sym) {
26662306a36Sopenharmony_ci		struct symbol **psym;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
26962306a36Sopenharmony_ci			if (*psym == sym) {
27062306a36Sopenharmony_ci				*psym = sym->hash_next;
27162306a36Sopenharmony_ci				break;
27262306a36Sopenharmony_ci			}
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci		--nsyms;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	sym = xmalloc(sizeof(*sym));
27862306a36Sopenharmony_ci	sym->name = name;
27962306a36Sopenharmony_ci	sym->type = type;
28062306a36Sopenharmony_ci	sym->defn = defn;
28162306a36Sopenharmony_ci	sym->expansion_trail = NULL;
28262306a36Sopenharmony_ci	sym->visited = NULL;
28362306a36Sopenharmony_ci	sym->is_extern = is_extern;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	sym->hash_next = symtab[h];
28662306a36Sopenharmony_ci	symtab[h] = sym;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	sym->is_declared = !is_reference;
28962306a36Sopenharmony_ci	sym->status = status;
29062306a36Sopenharmony_ci	sym->is_override = 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (flag_debug) {
29362306a36Sopenharmony_ci		if (symbol_types[type].name)
29462306a36Sopenharmony_ci			fprintf(debugfile, "Defn for %s %s == <",
29562306a36Sopenharmony_ci				symbol_types[type].name, name);
29662306a36Sopenharmony_ci		else
29762306a36Sopenharmony_ci			fprintf(debugfile, "Defn for type%d %s == <",
29862306a36Sopenharmony_ci				type, name);
29962306a36Sopenharmony_ci		if (is_extern)
30062306a36Sopenharmony_ci			fputs("extern ", debugfile);
30162306a36Sopenharmony_ci		print_list(debugfile, defn);
30262306a36Sopenharmony_ci		fputs(">\n", debugfile);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	++nsyms;
30662306a36Sopenharmony_ci	return sym;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistruct symbol *add_symbol(const char *name, enum symbol_type type,
31062306a36Sopenharmony_ci			  struct string_list *defn, int is_extern)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	return __add_symbol(name, type, defn, is_extern, 0);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
31662306a36Sopenharmony_ci				    struct string_list *defn, int is_extern)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	return __add_symbol(name, type, defn, is_extern, 1);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_civoid free_node(struct string_list *node)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	free(node->string);
32662306a36Sopenharmony_ci	free(node);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_civoid free_list(struct string_list *s, struct string_list *e)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	while (s != e) {
33262306a36Sopenharmony_ci		struct string_list *next = s->next;
33362306a36Sopenharmony_ci		free_node(s);
33462306a36Sopenharmony_ci		s = next;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic struct string_list *mk_node(const char *string)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct string_list *newnode;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	newnode = xmalloc(sizeof(*newnode));
34362306a36Sopenharmony_ci	newnode->string = xstrdup(string);
34462306a36Sopenharmony_ci	newnode->tag = SYM_NORMAL;
34562306a36Sopenharmony_ci	newnode->next = NULL;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return newnode;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic struct string_list *concat_list(struct string_list *start, ...)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	va_list ap;
35362306a36Sopenharmony_ci	struct string_list *n, *n2;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!start)
35662306a36Sopenharmony_ci		return NULL;
35762306a36Sopenharmony_ci	for (va_start(ap, start); (n = va_arg(ap, struct string_list *));) {
35862306a36Sopenharmony_ci		for (n2 = n; n2->next; n2 = n2->next)
35962306a36Sopenharmony_ci			;
36062306a36Sopenharmony_ci		n2->next = start;
36162306a36Sopenharmony_ci		start = n;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	va_end(ap);
36462306a36Sopenharmony_ci	return start;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistruct string_list *copy_node(struct string_list *node)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct string_list *newnode;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	newnode = xmalloc(sizeof(*newnode));
37262306a36Sopenharmony_ci	newnode->string = xstrdup(node->string);
37362306a36Sopenharmony_ci	newnode->tag = node->tag;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	return newnode;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistruct string_list *copy_list_range(struct string_list *start,
37962306a36Sopenharmony_ci				    struct string_list *end)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct string_list *res, *n;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (start == end)
38462306a36Sopenharmony_ci		return NULL;
38562306a36Sopenharmony_ci	n = res = copy_node(start);
38662306a36Sopenharmony_ci	for (start = start->next; start != end; start = start->next) {
38762306a36Sopenharmony_ci		n->next = copy_node(start);
38862306a36Sopenharmony_ci		n = n->next;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci	n->next = NULL;
39162306a36Sopenharmony_ci	return res;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic int equal_list(struct string_list *a, struct string_list *b)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	while (a && b) {
39762306a36Sopenharmony_ci		if (a->tag != b->tag || strcmp(a->string, b->string))
39862306a36Sopenharmony_ci			return 0;
39962306a36Sopenharmony_ci		a = a->next;
40062306a36Sopenharmony_ci		b = b->next;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return !a && !b;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic struct string_list *read_node(FILE *f)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	char buffer[256];
41162306a36Sopenharmony_ci	struct string_list node = {
41262306a36Sopenharmony_ci		.string = buffer,
41362306a36Sopenharmony_ci		.tag = SYM_NORMAL };
41462306a36Sopenharmony_ci	int c, in_string = 0;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	while ((c = fgetc(f)) != EOF) {
41762306a36Sopenharmony_ci		if (!in_string && c == ' ') {
41862306a36Sopenharmony_ci			if (node.string == buffer)
41962306a36Sopenharmony_ci				continue;
42062306a36Sopenharmony_ci			break;
42162306a36Sopenharmony_ci		} else if (c == '"') {
42262306a36Sopenharmony_ci			in_string = !in_string;
42362306a36Sopenharmony_ci		} else if (c == '\n') {
42462306a36Sopenharmony_ci			if (node.string == buffer)
42562306a36Sopenharmony_ci				return NULL;
42662306a36Sopenharmony_ci			ungetc(c, f);
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci		if (node.string >= buffer + sizeof(buffer) - 1) {
43062306a36Sopenharmony_ci			fprintf(stderr, "Token too long\n");
43162306a36Sopenharmony_ci			exit(1);
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci		*node.string++ = c;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci	if (node.string == buffer)
43662306a36Sopenharmony_ci		return NULL;
43762306a36Sopenharmony_ci	*node.string = 0;
43862306a36Sopenharmony_ci	node.string = buffer;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (node.string[1] == '#') {
44162306a36Sopenharmony_ci		size_t n;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		for (n = 0; n < ARRAY_SIZE(symbol_types); n++) {
44462306a36Sopenharmony_ci			if (node.string[0] == symbol_types[n].n) {
44562306a36Sopenharmony_ci				node.tag = n;
44662306a36Sopenharmony_ci				node.string += 2;
44762306a36Sopenharmony_ci				return copy_node(&node);
44862306a36Sopenharmony_ci			}
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci		fprintf(stderr, "Unknown type %c\n", node.string[0]);
45162306a36Sopenharmony_ci		exit(1);
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci	return copy_node(&node);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void read_reference(FILE *f)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	while (!feof(f)) {
45962306a36Sopenharmony_ci		struct string_list *defn = NULL;
46062306a36Sopenharmony_ci		struct string_list *sym, *def;
46162306a36Sopenharmony_ci		int is_extern = 0, is_override = 0;
46262306a36Sopenharmony_ci		struct symbol *subsym;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		sym = read_node(f);
46562306a36Sopenharmony_ci		if (sym && sym->tag == SYM_NORMAL &&
46662306a36Sopenharmony_ci		    !strcmp(sym->string, "override")) {
46762306a36Sopenharmony_ci			is_override = 1;
46862306a36Sopenharmony_ci			free_node(sym);
46962306a36Sopenharmony_ci			sym = read_node(f);
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci		if (!sym)
47262306a36Sopenharmony_ci			continue;
47362306a36Sopenharmony_ci		def = read_node(f);
47462306a36Sopenharmony_ci		if (def && def->tag == SYM_NORMAL &&
47562306a36Sopenharmony_ci		    !strcmp(def->string, "extern")) {
47662306a36Sopenharmony_ci			is_extern = 1;
47762306a36Sopenharmony_ci			free_node(def);
47862306a36Sopenharmony_ci			def = read_node(f);
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci		while (def) {
48162306a36Sopenharmony_ci			def->next = defn;
48262306a36Sopenharmony_ci			defn = def;
48362306a36Sopenharmony_ci			def = read_node(f);
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci		subsym = add_reference_symbol(xstrdup(sym->string), sym->tag,
48662306a36Sopenharmony_ci					      defn, is_extern);
48762306a36Sopenharmony_ci		subsym->is_override = is_override;
48862306a36Sopenharmony_ci		free_node(sym);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic void print_node(FILE * f, struct string_list *list)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	if (symbol_types[list->tag].n) {
49562306a36Sopenharmony_ci		putc(symbol_types[list->tag].n, f);
49662306a36Sopenharmony_ci		putc('#', f);
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	fputs(list->string, f);
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic void print_list(FILE * f, struct string_list *list)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct string_list **e, **b;
50462306a36Sopenharmony_ci	struct string_list *tmp, **tmp2;
50562306a36Sopenharmony_ci	int elem = 1;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (list == NULL) {
50862306a36Sopenharmony_ci		fputs("(nil)", f);
50962306a36Sopenharmony_ci		return;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	tmp = list;
51362306a36Sopenharmony_ci	while ((tmp = tmp->next) != NULL)
51462306a36Sopenharmony_ci		elem++;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	b = alloca(elem * sizeof(*e));
51762306a36Sopenharmony_ci	e = b + elem;
51862306a36Sopenharmony_ci	tmp2 = e - 1;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	(*tmp2--) = list;
52162306a36Sopenharmony_ci	while ((list = list->next) != NULL)
52262306a36Sopenharmony_ci		*(tmp2--) = list;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	while (b != e) {
52562306a36Sopenharmony_ci		print_node(f, *b++);
52662306a36Sopenharmony_ci		putc(' ', f);
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct string_list *list = sym->defn;
53362306a36Sopenharmony_ci	struct string_list **e, **b;
53462306a36Sopenharmony_ci	struct string_list *tmp, **tmp2;
53562306a36Sopenharmony_ci	int elem = 1;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (!list)
53862306a36Sopenharmony_ci		return crc;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	tmp = list;
54162306a36Sopenharmony_ci	while ((tmp = tmp->next) != NULL)
54262306a36Sopenharmony_ci		elem++;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	b = alloca(elem * sizeof(*e));
54562306a36Sopenharmony_ci	e = b + elem;
54662306a36Sopenharmony_ci	tmp2 = e - 1;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	*(tmp2--) = list;
54962306a36Sopenharmony_ci	while ((list = list->next) != NULL)
55062306a36Sopenharmony_ci		*(tmp2--) = list;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	while (b != e) {
55362306a36Sopenharmony_ci		struct string_list *cur;
55462306a36Sopenharmony_ci		struct symbol *subsym;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		cur = *(b++);
55762306a36Sopenharmony_ci		switch (cur->tag) {
55862306a36Sopenharmony_ci		case SYM_NORMAL:
55962306a36Sopenharmony_ci			if (flag_dump_defs)
56062306a36Sopenharmony_ci				fprintf(debugfile, "%s ", cur->string);
56162306a36Sopenharmony_ci			crc = partial_crc32(cur->string, crc);
56262306a36Sopenharmony_ci			crc = partial_crc32_one(' ', crc);
56362306a36Sopenharmony_ci			break;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci		case SYM_ENUM_CONST:
56662306a36Sopenharmony_ci		case SYM_TYPEDEF:
56762306a36Sopenharmony_ci			subsym = find_symbol(cur->string, cur->tag, 0);
56862306a36Sopenharmony_ci			/* FIXME: Bad reference files can segfault here. */
56962306a36Sopenharmony_ci			if (subsym->expansion_trail) {
57062306a36Sopenharmony_ci				if (flag_dump_defs)
57162306a36Sopenharmony_ci					fprintf(debugfile, "%s ", cur->string);
57262306a36Sopenharmony_ci				crc = partial_crc32(cur->string, crc);
57362306a36Sopenharmony_ci				crc = partial_crc32_one(' ', crc);
57462306a36Sopenharmony_ci			} else {
57562306a36Sopenharmony_ci				subsym->expansion_trail = expansion_trail;
57662306a36Sopenharmony_ci				expansion_trail = subsym;
57762306a36Sopenharmony_ci				crc = expand_and_crc_sym(subsym, crc);
57862306a36Sopenharmony_ci			}
57962306a36Sopenharmony_ci			break;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		case SYM_STRUCT:
58262306a36Sopenharmony_ci		case SYM_UNION:
58362306a36Sopenharmony_ci		case SYM_ENUM:
58462306a36Sopenharmony_ci			subsym = find_symbol(cur->string, cur->tag, 0);
58562306a36Sopenharmony_ci			if (!subsym) {
58662306a36Sopenharmony_ci				struct string_list *n;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci				error_with_pos("expand undefined %s %s",
58962306a36Sopenharmony_ci					       symbol_types[cur->tag].name,
59062306a36Sopenharmony_ci					       cur->string);
59162306a36Sopenharmony_ci				n = concat_list(mk_node
59262306a36Sopenharmony_ci						(symbol_types[cur->tag].name),
59362306a36Sopenharmony_ci						mk_node(cur->string),
59462306a36Sopenharmony_ci						mk_node("{"),
59562306a36Sopenharmony_ci						mk_node("UNKNOWN"),
59662306a36Sopenharmony_ci						mk_node("}"), NULL);
59762306a36Sopenharmony_ci				subsym =
59862306a36Sopenharmony_ci				    add_symbol(cur->string, cur->tag, n, 0);
59962306a36Sopenharmony_ci			}
60062306a36Sopenharmony_ci			if (subsym->expansion_trail) {
60162306a36Sopenharmony_ci				if (flag_dump_defs) {
60262306a36Sopenharmony_ci					fprintf(debugfile, "%s %s ",
60362306a36Sopenharmony_ci						symbol_types[cur->tag].name,
60462306a36Sopenharmony_ci						cur->string);
60562306a36Sopenharmony_ci				}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci				crc = partial_crc32(symbol_types[cur->tag].name,
60862306a36Sopenharmony_ci						    crc);
60962306a36Sopenharmony_ci				crc = partial_crc32_one(' ', crc);
61062306a36Sopenharmony_ci				crc = partial_crc32(cur->string, crc);
61162306a36Sopenharmony_ci				crc = partial_crc32_one(' ', crc);
61262306a36Sopenharmony_ci			} else {
61362306a36Sopenharmony_ci				subsym->expansion_trail = expansion_trail;
61462306a36Sopenharmony_ci				expansion_trail = subsym;
61562306a36Sopenharmony_ci				crc = expand_and_crc_sym(subsym, crc);
61662306a36Sopenharmony_ci			}
61762306a36Sopenharmony_ci			break;
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	{
62262306a36Sopenharmony_ci		static struct symbol **end = &visited_symbols;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		if (!sym->visited) {
62562306a36Sopenharmony_ci			*end = sym;
62662306a36Sopenharmony_ci			end = &sym->visited;
62762306a36Sopenharmony_ci			sym->visited = (struct symbol *)-1L;
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return crc;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_civoid export_symbol(const char *name)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct symbol *sym;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	sym = find_symbol(name, SYM_NORMAL, 0);
63962306a36Sopenharmony_ci	if (!sym)
64062306a36Sopenharmony_ci		error_with_pos("export undefined symbol %s", name);
64162306a36Sopenharmony_ci	else {
64262306a36Sopenharmony_ci		unsigned long crc;
64362306a36Sopenharmony_ci		int has_changed = 0;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		if (flag_dump_defs)
64662306a36Sopenharmony_ci			fprintf(debugfile, "Export %s == <", name);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		expansion_trail = (struct symbol *)-1L;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		sym->expansion_trail = expansion_trail;
65162306a36Sopenharmony_ci		expansion_trail = sym;
65262306a36Sopenharmony_ci		crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		sym = expansion_trail;
65562306a36Sopenharmony_ci		while (sym != (struct symbol *)-1L) {
65662306a36Sopenharmony_ci			struct symbol *n = sym->expansion_trail;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci			if (sym->status != STATUS_UNCHANGED) {
65962306a36Sopenharmony_ci				if (!has_changed) {
66062306a36Sopenharmony_ci					print_location();
66162306a36Sopenharmony_ci					fprintf(stderr, "%s: %s: modversion "
66262306a36Sopenharmony_ci						"changed because of changes "
66362306a36Sopenharmony_ci						"in ", flag_preserve ? "error" :
66462306a36Sopenharmony_ci						       "warning", name);
66562306a36Sopenharmony_ci				} else
66662306a36Sopenharmony_ci					fprintf(stderr, ", ");
66762306a36Sopenharmony_ci				print_type_name(sym->type, sym->name);
66862306a36Sopenharmony_ci				if (sym->status == STATUS_DEFINED)
66962306a36Sopenharmony_ci					fprintf(stderr, " (became defined)");
67062306a36Sopenharmony_ci				has_changed = 1;
67162306a36Sopenharmony_ci				if (flag_preserve)
67262306a36Sopenharmony_ci					errors++;
67362306a36Sopenharmony_ci			}
67462306a36Sopenharmony_ci			sym->expansion_trail = 0;
67562306a36Sopenharmony_ci			sym = n;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci		if (has_changed)
67862306a36Sopenharmony_ci			fprintf(stderr, "\n");
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		if (flag_dump_defs)
68162306a36Sopenharmony_ci			fputs(">\n", debugfile);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		printf("#SYMVER %s 0x%08lx\n", name, crc);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic void print_location(void)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic void print_type_name(enum symbol_type type, const char *name)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	if (symbol_types[type].name)
69762306a36Sopenharmony_ci		fprintf(stderr, "%s %s", symbol_types[type].name, name);
69862306a36Sopenharmony_ci	else
69962306a36Sopenharmony_ci		fprintf(stderr, "%s", name);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_civoid error_with_pos(const char *fmt, ...)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	va_list args;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (flag_warnings) {
70762306a36Sopenharmony_ci		print_location();
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		va_start(args, fmt);
71062306a36Sopenharmony_ci		vfprintf(stderr, fmt, args);
71162306a36Sopenharmony_ci		va_end(args);
71262306a36Sopenharmony_ci		putc('\n', stderr);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		errors++;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic void genksyms_usage(void)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	fputs("Usage:\n" "genksyms [-adDTwqhVR] > /path/to/.tmp_obj.ver\n" "\n"
72162306a36Sopenharmony_ci#ifdef __GNU_LIBRARY__
72262306a36Sopenharmony_ci	      "  -s, --symbol-prefix   Select symbol prefix\n"
72362306a36Sopenharmony_ci	      "  -d, --debug           Increment the debug level (repeatable)\n"
72462306a36Sopenharmony_ci	      "  -D, --dump            Dump expanded symbol defs (for debugging only)\n"
72562306a36Sopenharmony_ci	      "  -r, --reference file  Read reference symbols from a file\n"
72662306a36Sopenharmony_ci	      "  -T, --dump-types file Dump expanded types into file\n"
72762306a36Sopenharmony_ci	      "  -p, --preserve        Preserve reference modversions or fail\n"
72862306a36Sopenharmony_ci	      "  -w, --warnings        Enable warnings\n"
72962306a36Sopenharmony_ci	      "  -q, --quiet           Disable warnings (default)\n"
73062306a36Sopenharmony_ci	      "  -h, --help            Print this message\n"
73162306a36Sopenharmony_ci	      "  -V, --version         Print the release version\n"
73262306a36Sopenharmony_ci#else				/* __GNU_LIBRARY__ */
73362306a36Sopenharmony_ci	      "  -s                    Select symbol prefix\n"
73462306a36Sopenharmony_ci	      "  -d                    Increment the debug level (repeatable)\n"
73562306a36Sopenharmony_ci	      "  -D                    Dump expanded symbol defs (for debugging only)\n"
73662306a36Sopenharmony_ci	      "  -r file               Read reference symbols from a file\n"
73762306a36Sopenharmony_ci	      "  -T file               Dump expanded types into file\n"
73862306a36Sopenharmony_ci	      "  -p                    Preserve reference modversions or fail\n"
73962306a36Sopenharmony_ci	      "  -w                    Enable warnings\n"
74062306a36Sopenharmony_ci	      "  -q                    Disable warnings (default)\n"
74162306a36Sopenharmony_ci	      "  -h                    Print this message\n"
74262306a36Sopenharmony_ci	      "  -V                    Print the release version\n"
74362306a36Sopenharmony_ci#endif				/* __GNU_LIBRARY__ */
74462306a36Sopenharmony_ci	      , stderr);
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ciint main(int argc, char **argv)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	FILE *dumpfile = NULL, *ref_file = NULL;
75062306a36Sopenharmony_ci	int o;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci#ifdef __GNU_LIBRARY__
75362306a36Sopenharmony_ci	struct option long_opts[] = {
75462306a36Sopenharmony_ci		{"debug", 0, 0, 'd'},
75562306a36Sopenharmony_ci		{"warnings", 0, 0, 'w'},
75662306a36Sopenharmony_ci		{"quiet", 0, 0, 'q'},
75762306a36Sopenharmony_ci		{"dump", 0, 0, 'D'},
75862306a36Sopenharmony_ci		{"reference", 1, 0, 'r'},
75962306a36Sopenharmony_ci		{"dump-types", 1, 0, 'T'},
76062306a36Sopenharmony_ci		{"preserve", 0, 0, 'p'},
76162306a36Sopenharmony_ci		{"version", 0, 0, 'V'},
76262306a36Sopenharmony_ci		{"help", 0, 0, 'h'},
76362306a36Sopenharmony_ci		{0, 0, 0, 0}
76462306a36Sopenharmony_ci	};
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	while ((o = getopt_long(argc, argv, "s:dwqVDr:T:ph",
76762306a36Sopenharmony_ci				&long_opts[0], NULL)) != EOF)
76862306a36Sopenharmony_ci#else				/* __GNU_LIBRARY__ */
76962306a36Sopenharmony_ci	while ((o = getopt(argc, argv, "s:dwqVDr:T:ph")) != EOF)
77062306a36Sopenharmony_ci#endif				/* __GNU_LIBRARY__ */
77162306a36Sopenharmony_ci		switch (o) {
77262306a36Sopenharmony_ci		case 'd':
77362306a36Sopenharmony_ci			flag_debug++;
77462306a36Sopenharmony_ci			break;
77562306a36Sopenharmony_ci		case 'w':
77662306a36Sopenharmony_ci			flag_warnings = 1;
77762306a36Sopenharmony_ci			break;
77862306a36Sopenharmony_ci		case 'q':
77962306a36Sopenharmony_ci			flag_warnings = 0;
78062306a36Sopenharmony_ci			break;
78162306a36Sopenharmony_ci		case 'V':
78262306a36Sopenharmony_ci			fputs("genksyms version 2.5.60\n", stderr);
78362306a36Sopenharmony_ci			break;
78462306a36Sopenharmony_ci		case 'D':
78562306a36Sopenharmony_ci			flag_dump_defs = 1;
78662306a36Sopenharmony_ci			break;
78762306a36Sopenharmony_ci		case 'r':
78862306a36Sopenharmony_ci			flag_reference = 1;
78962306a36Sopenharmony_ci			ref_file = fopen(optarg, "r");
79062306a36Sopenharmony_ci			if (!ref_file) {
79162306a36Sopenharmony_ci				perror(optarg);
79262306a36Sopenharmony_ci				return 1;
79362306a36Sopenharmony_ci			}
79462306a36Sopenharmony_ci			break;
79562306a36Sopenharmony_ci		case 'T':
79662306a36Sopenharmony_ci			flag_dump_types = 1;
79762306a36Sopenharmony_ci			dumpfile = fopen(optarg, "w");
79862306a36Sopenharmony_ci			if (!dumpfile) {
79962306a36Sopenharmony_ci				perror(optarg);
80062306a36Sopenharmony_ci				return 1;
80162306a36Sopenharmony_ci			}
80262306a36Sopenharmony_ci			break;
80362306a36Sopenharmony_ci		case 'p':
80462306a36Sopenharmony_ci			flag_preserve = 1;
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		case 'h':
80762306a36Sopenharmony_ci			genksyms_usage();
80862306a36Sopenharmony_ci			return 0;
80962306a36Sopenharmony_ci		default:
81062306a36Sopenharmony_ci			genksyms_usage();
81162306a36Sopenharmony_ci			return 1;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci	{
81462306a36Sopenharmony_ci		extern int yydebug;
81562306a36Sopenharmony_ci		extern int yy_flex_debug;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		yydebug = (flag_debug > 1);
81862306a36Sopenharmony_ci		yy_flex_debug = (flag_debug > 2);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		debugfile = stderr;
82162306a36Sopenharmony_ci		/* setlinebuf(debugfile); */
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (flag_reference) {
82562306a36Sopenharmony_ci		read_reference(ref_file);
82662306a36Sopenharmony_ci		fclose(ref_file);
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	yyparse();
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (flag_dump_types && visited_symbols) {
83262306a36Sopenharmony_ci		while (visited_symbols != (struct symbol *)-1L) {
83362306a36Sopenharmony_ci			struct symbol *sym = visited_symbols;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci			if (sym->is_override)
83662306a36Sopenharmony_ci				fputs("override ", dumpfile);
83762306a36Sopenharmony_ci			if (symbol_types[sym->type].n) {
83862306a36Sopenharmony_ci				putc(symbol_types[sym->type].n, dumpfile);
83962306a36Sopenharmony_ci				putc('#', dumpfile);
84062306a36Sopenharmony_ci			}
84162306a36Sopenharmony_ci			fputs(sym->name, dumpfile);
84262306a36Sopenharmony_ci			putc(' ', dumpfile);
84362306a36Sopenharmony_ci			if (sym->is_extern)
84462306a36Sopenharmony_ci				fputs("extern ", dumpfile);
84562306a36Sopenharmony_ci			print_list(dumpfile, sym->defn);
84662306a36Sopenharmony_ci			putc('\n', dumpfile);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci			visited_symbols = sym->visited;
84962306a36Sopenharmony_ci			sym->visited = NULL;
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (flag_debug) {
85462306a36Sopenharmony_ci		fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
85562306a36Sopenharmony_ci			nsyms, HASH_BUCKETS,
85662306a36Sopenharmony_ci			(double)nsyms / (double)HASH_BUCKETS);
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (dumpfile)
86062306a36Sopenharmony_ci		fclose(dumpfile);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	return errors != 0;
86362306a36Sopenharmony_ci}
864