1#
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (C) 2006 Daniel M. Eischen.  All rights reserved.
5# 
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14# 
15# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27#
28
29#
30# Make a list of all the library versions listed in the master file.
31#
32#   versions[] - array indexed by version name, contains number
33#                of symbols (+ 1) found for each version.
34#   successors[] - array index by version name, contains successor
35#                  version name.
36#   symbols[][] - array index by [version name, symbol index], contains
37#                 names of symbols defined for each version.
38#   names[] - array index is symbol name and value is its first version seen,
39#	      used to check for duplicate symbols and warn about them.
40#
41BEGIN {
42	brackets = 0;
43	errors = warns = 0;
44	version_count = 0;
45	current_version = "";
46	stderr = "/dev/stderr";
47	while (getline < vfile) {
48		# Strip comments.
49		sub("#.*$", "", $0);
50
51		# Strip leading and trailing whitespace.
52		sub("^[ \t]+", "", $0);
53		sub("[ \t]+$", "", $0);
54
55		if (/^[a-zA-Z0-9._]+[ \t]*{$/) {
56			# Strip brace.
57			sub("{", "", $1);
58			brackets++;
59			symver = $1;
60			versions[symver] = 1;
61			successors[symver] = "";
62			generated[symver] = 0;
63			version_count++;
64		}
65		else if (/^}[ \t]*[a-zA-Z0-9._]+[ \t]*;$/) {
66			v = $1 != "}" ? $1 : $2;
67			# Strip brace.
68			sub("}", "", v);
69			# Strip semicolon.
70			sub(";", "", v);
71			if (symver == "") {
72				printf("File %s: Unmatched bracket.\n",
73				vfile) > stderr;
74				errors++;
75			}
76			else if (versions[v] != 1) {
77				printf("File %s: `%s' has unknown " \
78				    "successor `%s'.\n",
79				    vfile, symver, v) > stderr;
80				errors++;
81			}
82			else
83				successors[symver] = v;
84			brackets--;
85		}
86		else if (/^}[ \t]*;$/) {
87			if (symver == "") {
88				printf("File %s: Unmatched bracket.\n",
89				    vfile) > stderr;
90				errors++;
91			}
92			# No successor
93			brackets--;
94		}
95		else if (/^}$/) {
96			printf("File %s: Missing final semicolon.\n",
97			    vfile) > stderr;
98			errors++;
99		}
100		else if (/^$/)
101			;  # Ignore blank lines.
102		else {
103			printf("File %s: Unknown directive: `%s'.\n",
104			    vfile, $0) > stderr;
105			errors++;
106		}
107	}
108	brackets = 0;
109}
110
111{
112	# Set meaningful filename for diagnostics.
113	filename = FILENAME != "" ? FILENAME : "<stdin>";
114
115	# Delete comments, preceding and trailing whitespace, then
116	# consume blank lines.
117	sub("#.*$", "", $0);
118	sub("^[ \t]+", "", $0);
119	sub("[ \t]+$", "", $0);
120	if ($0 == "")
121		next;
122}
123
124/^[a-zA-Z0-9._]+[ \t]*{$/ {
125	# Strip bracket from version name.
126	sub("{", "", $1);
127	if (current_version != "") {
128		printf("File %s, line %d: Illegal nesting detected.\n",
129		    filename, FNR) > stderr;
130		errors++;
131	}
132	else if (versions[$1] == 0) {
133		printf("File %s, line %d: Undefined " \
134		    "library version `%s'.\n", filename, FNR, $1) > stderr;
135		errors++;
136		# Remove this entry from the versions.
137		delete versions[$1];
138	}
139	else
140		current_version = $1;
141	brackets++;
142	next;
143}
144
145/^[a-zA-Z0-9._]+[ \t]*;$/ {
146	# Strip semicolon.
147	sub(";", "", $1);
148	if (current_version != "") {
149		count = versions[current_version];
150		versions[current_version]++;
151		symbols[current_version, count] = $1;
152		if ($1 in names && names[$1] != current_version) {
153			#
154			# A graver case when a dup symbol appears under
155			# different versions in the map.  That can result
156			# in subtle problems with the library later.
157			#
158			printf("File %s, line %d: Duplicated symbol `%s' " \
159			    "in version `%s', first seen in `%s'. " \
160			    "Did you forget to move it to ObsoleteVersions?\n",
161			    filename, FNR, $1,
162			    current_version, names[$1]) > stderr;
163			errors++;
164		}
165		else if (names[$1] == current_version) {
166			#
167			# A harmless case: a dup symbol with the same version.
168			#
169			printf("File %s, line %d: warning: " \
170			    "Duplicated symbol `%s' in version `%s'.\n",
171			    filename, FNR, $1, current_version) > stderr;
172			warns++;
173		}
174		else
175			names[$1] = current_version;
176	}
177	else {
178		printf("File %s, line %d: Symbol `%s' outside version scope.\n",
179		    filename, FNR, $1) > stderr;
180		errors++;
181	}
182	next;
183}
184
185/^}[ \t]*;$/ {
186	brackets--;
187	if (brackets < 0) {
188		printf("File %s, line %d: Unmatched bracket.\n",
189		    filename, FNR, $1) > stderr;
190		errors++;
191		brackets = 0;	# Reset
192	}
193	current_version = "";
194	next;
195}
196
197
198{
199	printf("File %s, line %d: Unknown directive: `%s'.\n",
200	    filename, FNR, $0) > stderr;
201	errors++;
202}
203
204function print_version(v)
205{
206	# This function is recursive, so return if this version
207	# has already been printed.  Otherwise, if there is an
208	# ancestral version, recursively print its symbols before
209	# printing the symbols for this version.
210	#
211	if (generated[v] == 1)
212		return;
213	if (successors[v] != "")
214		print_version(successors[v]);
215
216	printf("%s {\n", v);
217
218	# The version count is always one more that actual,
219	# so the loop ranges from 1 to n-1.
220	#
221	for (i = 1; i < versions[v]; i++) {
222		if (i == 1)
223			printf("global:\n");
224		printf("\t%s;\n", symbols[v, i]);
225	}
226
227	version_count--;
228	if (version_count == 0) {
229		printf("local:\n");
230		printf("\t*;\n");
231	}
232	if (successors[v] == "")
233		printf("};\n");
234	else
235		printf("} %s;\n", successors[v]);
236	printf("\n");
237
238	generated[v] = 1;
239    }
240
241END {
242	if (errors) {
243		printf("%d error(s) total.\n", errors) > stderr;
244		exit(1);
245	}
246	# OK, no errors.
247	for (v in versions) {
248		print_version(v);
249	}
250}
251