1#include <sys/types.h>
2
3#include <ctype.h>
4#include <errno.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10typedef struct {
11	char	*name;			/* API name */
12
13	u_int	used_mask;		/* Bits used. */
14} API;
15API	**api_list, **api_end;
16
17typedef struct {
18	char	 *name;			/* Flag name */
19
20	int	api_cnt;		/* APIs that use this flag. */
21	API	**api, **api_end;
22
23	u_int	value;			/* Bit value */
24} FLAG;
25FLAG	**flag_list, **flag_end;
26
27int	verbose;
28char	*progname;
29
30int	add_entry(char *, char *);
31void	define_print(char *, u_int);
32void	dump_api(void);
33void	dump_flags(void);
34int	flag_cmp_alpha(const void *, const void *);
35int	flag_cmp_api_cnt(const void *, const void *);
36int	generate_flags(void);
37int	parse(void);
38void	print_api_mask(void);
39void	print_api_remainder(void);
40void	print_flag_value(void);
41int	syserr(void);
42int	usage(void);
43
44int
45main(int argc, char *argv[])
46{
47	enum { API_MASK, API_REMAINDER, FLAG_VALUE } output;
48	int ch;
49
50	if ((progname = strrchr(argv[0], '/')) == NULL)
51		progname = argv[0];
52	else
53		++progname;
54
55	output = FLAG_VALUE;
56	while ((ch = getopt(argc, argv, "mrv")) != EOF)
57		switch (ch) {
58		case 'm':
59			output = API_MASK;
60			break;
61		case 'r':
62			output = API_REMAINDER;
63			break;
64		case 'v':
65			verbose = 1;
66			break;
67		case '?':
68		default:
69			return (usage());
70		}
71	argc -= optind;
72	argv += optind;
73
74	if (parse() || generate_flags())
75		return (EXIT_FAILURE);
76
77	switch (output) {
78	case API_MASK:
79		print_api_mask();
80		break;
81	case API_REMAINDER:
82		print_api_remainder();
83		break;
84	case FLAG_VALUE:
85		print_flag_value();
86		break;
87	}
88
89	if (verbose) {
90		dump_api();
91		dump_flags();
92	}
93
94	return (EXIT_SUCCESS);
95}
96
97int
98parse()
99{
100	int lc;
101	char *p, *api, buf[256];
102
103	api = NULL;
104
105	/*
106	 * Read the method name/flag pairs.
107	 */
108	for (lc = 1; fgets(buf, sizeof(buf), stdin) != NULL; ++lc) {
109		if ((p = strchr(buf, '\n')) != NULL)
110			*p = '\0';
111		else {
112			fprintf(
113			    stderr, "%s: %d: line too long\n", progname, lc);
114			return (1);
115		}
116
117		/* Ignore any empty line or hash mark. */
118		if (buf[0] == '\0' || buf[0] == '#')
119			continue;
120
121		/*
122		 * A line without leading whitespace is an API name, a line
123		 * with leading whitespace is a flag name.
124		 */
125		if (isspace(buf[0])) {
126			if ((p = strtok(buf, " \t")) == NULL || *p == '#')
127				continue;
128
129			/* A flag without an API makes no sense. */
130			if (api == NULL)
131				goto format;
132
133			/* Enter the pair into the array. */
134			if (add_entry(api, p))
135				return (1);
136		} else {
137			if ((p = strtok(buf, " \t")) == NULL)
138				continue;
139			if (api != NULL)
140				free(api);
141			if ((api = strdup(p)) == NULL)
142				return (syserr());
143		}
144		if ((p = strtok(NULL, " \t")) != NULL && *p != '#')
145			goto format;
146	}
147
148	return (0);
149
150format:	fprintf(stderr, "%s: format error: line %d\n", progname, lc);
151	return (1);
152}
153
154int
155add_entry(char *api_name, char *flag_name)
156{
157	FLAG **fpp, *fp;
158	API **app, *ap, **p;
159	u_int cnt;
160
161	/* Search for this api's API structure. */
162	for (app = api_list;
163	    app != NULL && *app != NULL && app < api_end; ++app)
164		if (strcmp(api_name, (*app)->name) == 0)
165			break;
166
167	/* Allocate new space in the API array if necessary. */
168	if (app == NULL || app == api_end) {
169		cnt = app == NULL ? 100 : (u_int)(api_end - api_list) + 100;
170		if ((api_list = realloc(api_list, sizeof(API *) * cnt)) == NULL)
171			return (syserr());
172		api_end = api_list + cnt;
173		app = api_list + (cnt - 100);
174		memset(app, 0, (u_int)(api_end - app) * sizeof(API *));
175	}
176
177	/* Allocate a new API structure and fill in the name if necessary. */
178	if (*app == NULL &&
179	    ((*app = calloc(sizeof(API), 1)) == NULL ||
180	    ((*app)->name = strdup(api_name)) == NULL))
181		return (syserr());
182
183	ap = *app;
184
185	/*
186	 * There's a special keyword, "__MASK=<value>" that sets the initial
187	 * flags value for an API, and so prevents those flag bits from being
188	 * chosen for that API's flags.
189	 */
190	if (strncmp(flag_name, "__MASK=", sizeof("__MASK=") - 1) == 0) {
191		ap->used_mask |=
192		    strtoul(flag_name + sizeof("__MASK=") - 1, NULL, 0);
193		return (0);
194	}
195
196	/* Search for this flag's FLAG structure. */
197	for (fpp = flag_list;
198	    fpp != NULL && *fpp != NULL && fpp < flag_end; ++fpp)
199		if (strcmp(flag_name, (*fpp)->name) == 0)
200			break;
201
202	/* Realloc space in the FLAG array if necessary. */
203	if (fpp == NULL || fpp == flag_end) {
204		cnt = fpp == NULL ? 100 : (u_int)(flag_end - flag_list) + 100;
205		if ((flag_list =
206		    realloc(flag_list, sizeof(FLAG *) * cnt)) == NULL)
207			return (syserr());
208		flag_end = flag_list + cnt;
209		fpp = flag_list + (cnt - 100);
210		memset(fpp, 0, (u_int)(flag_end - fpp) * sizeof(FLAG *));
211	}
212
213	/* Allocate a new FLAG structure and fill in the name if necessary. */
214	if (*fpp == NULL &&
215	    ((*fpp = calloc(sizeof(FLAG), 1)) == NULL ||
216	    ((*fpp)->name = strdup(flag_name)) == NULL))
217		return (syserr());
218
219	fp = *fpp;
220	++fp->api_cnt;
221
222	/* Check to see if this API is already listed for this flag. */
223	for (p = fp->api; p != NULL && *p != NULL && p < fp->api_end; ++p)
224		if (strcmp(api_name, (*p)->name) == 0) {
225			fprintf(stderr,
226			    "duplicate entry: %s / %s\n", api_name, flag_name);
227			return (1);
228		}
229
230	/* Realloc space in the FLAG's API array if necessary. */
231	if (p == NULL || p == fp->api_end) {
232		cnt = p == NULL ? 20 : (u_int)(fp->api_end - fp->api) + 20;
233		if ((fp->api = realloc(fp->api, sizeof(API *) * cnt)) == NULL)
234			return (syserr());
235		fp->api_end = fp->api + cnt;
236		p = fp->api + (cnt - 20);
237		memset(p, 0, (u_int)(fp->api_end - fp->api) * sizeof(API *));
238	}
239	*p = ap;
240
241	return (0);
242}
243
244void
245dump_api()
246{
247	API **app;
248
249	printf("=============================\nAPI:\n");
250	for (app = api_list; *app != NULL; ++app)
251		printf("%s (%#x)\n", (*app)->name, (*app)->used_mask);
252}
253
254void
255dump_flags()
256{
257	FLAG **fpp;
258	API **api;
259	char *sep;
260
261	printf("=============================\nFLAGS:\n");
262	for (fpp = flag_list; *fpp != NULL; ++fpp) {
263		printf("%s (%#x, %d): ",
264		    (*fpp)->name, (*fpp)->value, (*fpp)->api_cnt);
265		sep = "";
266		for (api = (*fpp)->api; *api != NULL; ++api) {
267			printf("%s%s", sep, (*api)->name);
268			sep = ", ";
269		}
270		printf("\n");
271	}
272}
273
274int
275flag_cmp_api_cnt(const void *a, const void *b)
276{
277	FLAG *af, *bf;
278
279	af = *(FLAG **)a;
280	bf = *(FLAG **)b;
281
282	if (af == NULL) {
283		if (bf == NULL)
284			return (0);
285		return (1);
286	}
287	if (bf == NULL) {
288		if (af == NULL)
289			return (0);
290		return (-1);
291	}
292	if (af->api_cnt > bf->api_cnt)
293		return (-1);
294	if (af->api_cnt < bf->api_cnt)
295		return (1);
296	return (0);
297}
298
299int
300generate_flags()
301{
302	FLAG **fpp;
303	API **api;
304	u_int mask;
305
306	/* Sort the FLAGS array by reference count, in reverse order. */
307	qsort(flag_list,
308	    (u_int)(flag_end - flag_list), sizeof(FLAG *), flag_cmp_api_cnt);
309
310	/*
311	 * Here's the plan: walk the list of flags, allocating bits.  For
312	 * each flag, we walk the list of APIs that use it and find a bit
313	 * none of them are using.  That bit becomes the flag's value.
314	 */
315	for (fpp = flag_list; *fpp != NULL; ++fpp) {
316		mask = 0xffffffff;			/* Set to all 1's */
317		for (api = (*fpp)->api; *api != NULL; ++api)
318			mask &= ~(*api)->used_mask;	/* Clear API's bits */
319		if (mask == 0) {
320			fprintf(stderr, "%s: ran out of bits at flag %s\n",
321			   progname, (*fpp)->name);
322			return (1);
323		}
324		(*fpp)->value = mask = 1 << (ffs(mask) - 1);
325		for (api = (*fpp)->api; *api != NULL; ++api)
326			(*api)->used_mask |= mask;	/* Set bit for API */
327	}
328
329	return (0);
330}
331
332int
333flag_cmp_alpha(const void *a, const void *b)
334{
335	FLAG *af, *bf;
336
337	af = *(FLAG **)a;
338	bf = *(FLAG **)b;
339
340	if (af == NULL) {
341		if (bf == NULL)
342			return (0);
343		return (1);
344	}
345	if (bf == NULL) {
346		if (af == NULL)
347			return (0);
348		return (-1);
349	}
350	return (strcmp(af->name, bf->name));
351}
352
353void
354print_api_mask()
355{
356	API **app;
357	char *p, buf[256];
358
359	/* Output a mask for the API. */
360	for (app = api_list; *app != NULL; ++app) {
361		(void)snprintf(
362		    buf, sizeof(buf), "_%s_API_MASK", (*app)->name);
363		for (p = buf; *p != '\0'; ++p)
364			if (islower(*p))
365				*p = toupper(*p);
366			else if (!isalpha(*p))
367				*p = '_';
368		define_print(buf, (*app)->used_mask);
369	}
370}
371
372void
373print_api_remainder()
374{
375	API **app;
376	int unused, i;
377
378	/* Output the bits remaining for the API. */
379	for (app = api_list; *app != NULL; ++app) {
380		for (i = unused = 0; i < 32; ++i)
381			if (!((*app)->used_mask & (1 << i)))
382				++unused;
383		printf("%s: %d bits unused\n", (*app)->name, unused);
384	}
385}
386
387void
388print_flag_value()
389{
390	FLAG **fpp;
391
392	/* Sort the FLAGS array in alphabetical order. */
393	qsort(flag_list,
394	    (u_int)(flag_end - flag_list), sizeof(FLAG *), flag_cmp_alpha);
395
396	/* Output each flag's value. */
397	for (fpp = flag_list; *fpp != NULL; ++fpp)
398		define_print((*fpp)->name, (*fpp)->value);
399}
400
401void
402define_print(char *name, u_int value)
403{
404	char *sep;
405
406	switch (strlen(name) / 8) {
407	case 0:
408		sep = "\t\t\t\t\t";
409		break;
410	case 1:
411		sep = "\t\t\t\t";
412		break;
413	case 2:
414		sep = "\t\t\t";
415		break;
416	case 3:
417		sep = "\t\t";
418		break;
419	default:
420		sep = "\t";
421		break;
422	}
423	printf("#define\t%s%s%#010x\n", name, sep, value);
424}
425
426int
427syserr(void)
428{
429	fprintf(stderr, "%s: %s\n", progname, strerror(errno));
430	return (1);
431}
432
433int
434usage()
435{
436	(void)fprintf(stderr, "usage: %s [-mrv]\n", progname);
437	return (EXIT_FAILURE);
438}
439