1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 2003
5 *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Bill Paul.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <sys/types.h>
42
43#include <sys/queue.h>
44
45#include "inf.h"
46
47extern FILE *yyin;
48int yyparse (void);
49
50const char *words[W_MAX];	/* More than we'll need. */
51int idx;
52
53static struct section_head sh;
54static struct reg_head rh;
55static struct assign_head ah;
56
57static char	*sstrdup	(const char *);
58static struct assign
59		*find_assign	(const char *, const char *);
60static struct assign
61		*find_next_assign
62				(struct assign *);
63static struct section
64		*find_section	(const char *);
65static int	dump_deviceids_pci	(void);
66static int	dump_deviceids_pcmcia	(void);
67static int	dump_deviceids_usb	(void);
68static void	dump_pci_id	(const char *);
69static void	dump_pcmcia_id	(const char *);
70static void	dump_usb_id	(const char *);
71static void	dump_regvals	(void);
72static void	dump_paramreg	(const struct section *,
73				const struct reg *, int);
74
75static FILE	*ofp;
76
77int
78inf_parse (FILE *fp, FILE *outfp)
79{
80	TAILQ_INIT(&sh);
81	TAILQ_INIT(&rh);
82	TAILQ_INIT(&ah);
83
84	ofp = outfp;
85	yyin = fp;
86	yyparse();
87
88	if (dump_deviceids_pci() == 0 &&
89	    dump_deviceids_pcmcia() == 0 &&
90	    dump_deviceids_usb() == 0)
91		return (-1);
92
93	fprintf(outfp, "#ifdef NDIS_REGVALS\n");
94	dump_regvals();
95	fprintf(outfp, "#endif /* NDIS_REGVALS */\n");
96
97	return (0);
98}
99
100void
101section_add (const char *s)
102{
103	struct section *sec;
104
105	sec = malloc(sizeof(struct section));
106	bzero(sec, sizeof(struct section));
107	sec->name = s;
108	TAILQ_INSERT_TAIL(&sh, sec, link);
109
110	return;
111}
112
113static struct assign *
114find_assign (const char *s, const char *k)
115{
116	struct assign *assign;
117	char newkey[256];
118
119	/* Deal with string section lookups. */
120
121	if (k != NULL && k[0] == '%') {
122		bzero(newkey, sizeof(newkey));
123		strncpy(newkey, k + 1, strlen(k) - 2);
124		k = newkey;
125	}
126
127	TAILQ_FOREACH(assign, &ah, link) {
128		if (strcasecmp(assign->section->name, s) == 0) {
129			if (k == NULL)
130				return(assign);
131			else
132				if (strcasecmp(assign->key, k) == 0)
133					return(assign);
134		}
135	}
136	return(NULL);
137}
138
139static struct assign *
140find_next_assign (struct assign *a)
141{
142	struct assign *assign;
143
144	TAILQ_FOREACH(assign, &ah, link) {
145		if (assign == a)
146			break;
147	}
148
149	assign = assign->link.tqe_next;
150
151	if (assign == NULL || assign->section != a->section)
152		return(NULL);
153
154	return (assign);
155}
156
157static const char *
158stringcvt(const char *s)
159{
160	struct assign *manf;
161
162	manf = find_assign("strings", s);
163	if (manf == NULL)
164		return(s);
165	return(manf->vals[0]);
166}
167
168struct section *
169find_section (const char *s)
170{
171	struct section *section;
172
173	TAILQ_FOREACH(section, &sh, link) {
174		if (strcasecmp(section->name, s) == 0)
175			return(section);
176	}
177	return(NULL);
178}
179
180static void
181dump_pcmcia_id(const char *s)
182{
183	char *manstr, *devstr;
184	char *p0, *p;
185
186	p0 = __DECONST(char *, s);
187
188	p = strchr(p0, '\\');
189	if (p == NULL)
190		return;
191	p0 = p + 1;
192
193	p = strchr(p0, '-');
194	if (p == NULL)
195		return;
196	*p = '\0';
197
198	manstr = p0;
199
200	/* Convert any underscores to spaces. */
201
202	while (*p0 != '\0') {
203		if (*p0 == '_')
204			*p0 = ' ';
205		p0++;
206	}
207
208	p0 = p + 1;
209	p = strchr(p0, '-');
210	if (p == NULL)
211		return;
212	*p = '\0';
213
214	devstr = p0;
215
216	/* Convert any underscores to spaces. */
217
218	while (*p0 != '\0') {
219		if (*p0 == '_')
220			*p0 = ' ';
221		p0++;
222	}
223
224	fprintf(ofp, "\t\\\n\t{ \"%s\", \"%s\", ", manstr, devstr);
225	return;
226}
227
228static void
229dump_pci_id(const char *s)
230{
231	char *p;
232	char vidstr[7], didstr[7], subsysstr[14];
233
234	p = strcasestr(s, "VEN_");
235	if (p == NULL)
236		return;
237	p += 4;
238	strcpy(vidstr, "0x");
239	strncat(vidstr, p, 4);
240	p = strcasestr(s, "DEV_");
241	if (p == NULL)
242		return;
243	p += 4;
244	strcpy(didstr, "0x");
245	strncat(didstr, p, 4);
246	if (p == NULL)
247		return;
248	p = strcasestr(s, "SUBSYS_");
249	if (p == NULL)
250		strcpy(subsysstr, "0x00000000");
251	else {
252		p += 7;
253		strcpy(subsysstr, "0x");
254		strncat(subsysstr, p, 8);
255	}
256
257	fprintf(ofp, "\t\\\n\t{ %s, %s, %s, ", vidstr, didstr, subsysstr);
258	return;
259}
260
261static void
262dump_usb_id(const char *s)
263{
264	char *p;
265	char vidstr[7], pidstr[7];
266
267	p = strcasestr(s, "VID_");
268	if (p == NULL)
269		return;
270	p += 4;
271	strcpy(vidstr, "0x");
272	strncat(vidstr, p, 4);
273	p = strcasestr(s, "PID_");
274	if (p == NULL)
275		return;
276	p += 4;
277	strcpy(pidstr, "0x");
278	strncat(pidstr, p, 4);
279	if (p == NULL)
280		return;
281
282	fprintf(ofp, "\t\\\n\t{ %s, %s, ", vidstr, pidstr);
283}
284
285static int
286dump_deviceids_pci()
287{
288	struct assign *manf, *dev;
289	struct section *sec;
290	struct assign *assign;
291	char xpsec[256];
292	int first = 1, found = 0;
293
294	/* Find manufacturer name */
295	manf = find_assign("Manufacturer", NULL);
296
297nextmanf:
298
299	/* Find manufacturer section */
300	if (manf->vals[1] != NULL &&
301	    (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
302	    strcasecmp(manf->vals[1], "NTx86") == 0 ||
303	    strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
304	    strcasecmp(manf->vals[1], "NTamd64") == 0)) {
305		/* Handle Windows XP INF files. */
306		snprintf(xpsec, sizeof(xpsec), "%s.%s",
307		    manf->vals[0], manf->vals[1]);
308		sec = find_section(xpsec);
309	} else
310		sec = find_section(manf->vals[0]);
311
312	/* See if there are any PCI device definitions. */
313
314	TAILQ_FOREACH(assign, &ah, link) {
315		if (assign->section == sec) {
316			dev = find_assign("strings", assign->key);
317			if (strcasestr(assign->vals[1], "PCI") != NULL) {
318				found++;
319				break;
320			}
321		}
322	}
323
324	if (found == 0)
325		goto done;
326
327	found = 0;
328
329	if (first == 1) {
330		/* Emit start of PCI device table */
331		fprintf (ofp, "#define NDIS_PCI_DEV_TABLE");
332		first = 0;
333	}
334
335retry:
336
337	/*
338	 * Now run through all the device names listed
339	 * in the manufacturer section and dump out the
340	 * device descriptions and vendor/device IDs.
341	 */
342
343	TAILQ_FOREACH(assign, &ah, link) {
344		if (assign->section == sec) {
345			dev = find_assign("strings", assign->key);
346			/* Emit device IDs. */
347			if (strcasestr(assign->vals[1], "PCI") != NULL)
348				dump_pci_id(assign->vals[1]);
349			else
350				continue;
351			/* Emit device description */
352			fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]);
353			found++;
354		}
355	}
356
357	/* Someone tried to fool us. Shame on them. */
358	if (!found) {
359		found++;
360		sec = find_section(manf->vals[0]);
361		goto retry;
362	}
363
364	/* Handle Manufacturer sections with multiple entries. */
365	manf = find_next_assign(manf);
366
367	if (manf != NULL)
368		goto nextmanf;
369
370done:
371	/* Emit end of table */
372
373	fprintf(ofp, "\n\n");
374
375	return (found);
376}
377
378static int
379dump_deviceids_pcmcia()
380{
381	struct assign *manf, *dev;
382	struct section *sec;
383	struct assign *assign;
384	char xpsec[256];
385	int first = 1, found = 0;
386
387	/* Find manufacturer name */
388	manf = find_assign("Manufacturer", NULL);
389
390nextmanf:
391
392	/* Find manufacturer section */
393	if (manf->vals[1] != NULL &&
394	    (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
395	    strcasecmp(manf->vals[1], "NTx86") == 0 ||
396	    strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
397	    strcasecmp(manf->vals[1], "NTamd64") == 0)) {
398		/* Handle Windows XP INF files. */
399		snprintf(xpsec, sizeof(xpsec), "%s.%s",
400		    manf->vals[0], manf->vals[1]);
401		sec = find_section(xpsec);
402	} else
403		sec = find_section(manf->vals[0]);
404
405	/* See if there are any PCMCIA device definitions. */
406
407	TAILQ_FOREACH(assign, &ah, link) {
408		if (assign->section == sec) {
409			dev = find_assign("strings", assign->key);
410			if (strcasestr(assign->vals[1], "PCMCIA") != NULL) {
411				found++;
412				break;
413			}
414		}
415	}
416
417	if (found == 0)
418		goto done;
419
420	found = 0;
421
422	if (first == 1) {
423		/* Emit start of PCMCIA device table */
424		fprintf (ofp, "#define NDIS_PCMCIA_DEV_TABLE");
425		first = 0;
426	}
427
428retry:
429
430	/*
431	 * Now run through all the device names listed
432	 * in the manufacturer section and dump out the
433	 * device descriptions and vendor/device IDs.
434	 */
435
436	TAILQ_FOREACH(assign, &ah, link) {
437		if (assign->section == sec) {
438			dev = find_assign("strings", assign->key);
439			/* Emit device IDs. */
440			if (strcasestr(assign->vals[1], "PCMCIA") != NULL)
441				dump_pcmcia_id(assign->vals[1]);
442			else
443				continue;
444			/* Emit device description */
445			fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]);
446			found++;
447		}
448	}
449
450	/* Someone tried to fool us. Shame on them. */
451	if (!found) {
452		found++;
453		sec = find_section(manf->vals[0]);
454		goto retry;
455	}
456
457	/* Handle Manufacturer sections with multiple entries. */
458	manf = find_next_assign(manf);
459
460	if (manf != NULL)
461		goto nextmanf;
462
463done:
464	/* Emit end of table */
465
466	fprintf(ofp, "\n\n");
467
468	return (found);
469}
470
471static int
472dump_deviceids_usb()
473{
474	struct assign *manf, *dev;
475	struct section *sec;
476	struct assign *assign;
477	char xpsec[256];
478	int first = 1, found = 0;
479
480	/* Find manufacturer name */
481	manf = find_assign("Manufacturer", NULL);
482
483nextmanf:
484
485	/* Find manufacturer section */
486	if (manf->vals[1] != NULL &&
487	    (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
488	    strcasecmp(manf->vals[1], "NTx86") == 0 ||
489	    strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
490	    strcasecmp(manf->vals[1], "NTamd64") == 0)) {
491		/* Handle Windows XP INF files. */
492		snprintf(xpsec, sizeof(xpsec), "%s.%s",
493		    manf->vals[0], manf->vals[1]);
494		sec = find_section(xpsec);
495	} else
496		sec = find_section(manf->vals[0]);
497
498	/* See if there are any USB device definitions. */
499
500	TAILQ_FOREACH(assign, &ah, link) {
501		if (assign->section == sec) {
502			dev = find_assign("strings", assign->key);
503			if (strcasestr(assign->vals[1], "USB") != NULL) {
504				found++;
505				break;
506			}
507		}
508	}
509
510	if (found == 0)
511		goto done;
512
513	found = 0;
514
515	if (first == 1) {
516		/* Emit start of USB device table */
517		fprintf (ofp, "#define NDIS_USB_DEV_TABLE");
518		first = 0;
519	}
520
521retry:
522
523	/*
524	 * Now run through all the device names listed
525	 * in the manufacturer section and dump out the
526	 * device descriptions and vendor/device IDs.
527	 */
528
529	TAILQ_FOREACH(assign, &ah, link) {
530		if (assign->section == sec) {
531			dev = find_assign("strings", assign->key);
532			/* Emit device IDs. */
533			if (strcasestr(assign->vals[1], "USB") != NULL)
534				dump_usb_id(assign->vals[1]);
535			else
536				continue;
537			/* Emit device description */
538			fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]);
539			found++;
540		}
541	}
542
543	/* Someone tried to fool us. Shame on them. */
544	if (!found) {
545		found++;
546		sec = find_section(manf->vals[0]);
547		goto retry;
548	}
549
550	/* Handle Manufacturer sections with multiple entries. */
551	manf = find_next_assign(manf);
552
553	if (manf != NULL)
554		goto nextmanf;
555
556done:
557	/* Emit end of table */
558
559	fprintf(ofp, "\n\n");
560
561	return (found);
562}
563
564static void
565dump_addreg(const char *s, int devidx)
566{
567	struct section *sec;
568	struct reg *reg;
569
570	/* Find the addreg section */
571	sec = find_section(s);
572
573	/* Dump all the keys defined in it. */
574	TAILQ_FOREACH(reg, &rh, link) {
575		/*
576		 * Keys with an empty subkey are very easy to parse,
577		 * so just deal with them here. If a parameter key
578		 * of the same name also exists, prefer that one and
579		 * skip this one.
580		 */
581		if (reg->section == sec) {
582			if (reg->subkey == NULL) {
583				fprintf(ofp, "\n\t{ \"%s\",", reg->key);
584				fprintf(ofp,"\n\t\"%s \",", reg->key);
585				fprintf(ofp, "\n\t{ \"%s\" }, %d },",
586				    reg->value == NULL ? "" :
587				    stringcvt(reg->value), devidx);
588			} else if (strncasecmp(reg->subkey,
589			    "Ndi\\params", strlen("Ndi\\params")-1) == 0 &&
590			    (reg->key != NULL && strcasecmp(reg->key,
591			    "ParamDesc") == 0))
592				dump_paramreg(sec, reg, devidx);
593		}
594	}
595
596	return;
597}
598
599static void
600dump_enumreg(const struct section *s, const struct reg *r)
601{
602	struct reg *reg;
603	char enumkey[256];
604
605	sprintf(enumkey, "%s\\enum", r->subkey);
606	TAILQ_FOREACH(reg, &rh, link) {
607		if (reg->section != s)
608			continue;
609		if (reg->subkey == NULL || strcasecmp(reg->subkey, enumkey))
610			continue;
611		fprintf(ofp, " [%s=%s]", reg->key,
612		    stringcvt(reg->value));
613	}
614	return;
615}
616
617static void
618dump_editreg(const struct section *s, const struct reg *r)
619{
620	struct reg *reg;
621
622	TAILQ_FOREACH(reg, &rh, link) {
623		if (reg->section != s)
624			continue;
625		if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
626			continue;
627		if (reg->key == NULL)
628			continue;
629		if (strcasecmp(reg->key, "LimitText") == 0)
630			fprintf(ofp, " [maxchars=%s]", reg->value);
631		if (strcasecmp(reg->key, "Optional") == 0 &&
632		    strcmp(reg->value, "1") == 0)
633			fprintf(ofp, " [optional]");
634	}
635	return;
636}
637
638/* Use this for int too */
639static void
640dump_dwordreg(const struct section *s, const struct reg *r)
641{
642	struct reg *reg;
643
644	TAILQ_FOREACH(reg, &rh, link) {
645		if (reg->section != s)
646			continue;
647		if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
648			continue;
649		if (reg->key == NULL)
650			continue;
651		if (strcasecmp(reg->key, "min") == 0)
652			fprintf(ofp, " [min=%s]", reg->value);
653		if (strcasecmp(reg->key, "max") == 0)
654			fprintf(ofp, " [max=%s]", reg->value);
655	}
656	return;
657}
658
659static void
660dump_defaultinfo(const struct section *s, const struct reg *r, int devidx)
661{
662	struct reg *reg;
663	TAILQ_FOREACH(reg, &rh, link) {
664		if (reg->section != s)
665			continue;
666		if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
667			continue;
668		if (reg->key == NULL || strcasecmp(reg->key, "Default"))
669			continue;
670		fprintf(ofp, "\n\t{ \"%s\" }, %d },", reg->value == NULL ? "" :
671		    stringcvt(reg->value), devidx);
672		return;
673	}
674	/* Default registry entry missing */
675	fprintf(ofp, "\n\t{ \"\" }, %d },", devidx);
676	return;
677}
678
679static void
680dump_paramdesc(const struct section *s, const struct reg *r)
681{
682	struct reg *reg;
683	TAILQ_FOREACH(reg, &rh, link) {
684		if (reg->section != s)
685			continue;
686		if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
687			continue;
688		if (reg->key == NULL || strcasecmp(reg->key, "ParamDesc"))
689			continue;
690		fprintf(ofp, "\n\t\"%s", stringcvt(r->value));
691			break;
692	}
693	return;
694}
695
696static void
697dump_typeinfo(const struct section *s, const struct reg *r)
698{
699	struct reg *reg;
700	TAILQ_FOREACH(reg, &rh, link) {
701		if (reg->section != s)
702			continue;
703		if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
704			continue;
705		if (reg->key == NULL)
706			continue;
707		if (strcasecmp(reg->key, "type"))
708			continue;
709		if (strcasecmp(reg->value, "dword") == 0 ||
710		    strcasecmp(reg->value, "int") == 0)
711			dump_dwordreg(s, r);
712		if (strcasecmp(reg->value, "enum") == 0)
713			dump_enumreg(s, r);
714		if (strcasecmp(reg->value, "edit") == 0)
715			dump_editreg(s, r);
716	}
717	return;
718}
719
720static void
721dump_paramreg(const struct section *s, const struct reg *r, int devidx)
722{
723	const char *keyname;
724
725	keyname = r->subkey + strlen("Ndi\\params\\");
726	fprintf(ofp, "\n\t{ \"%s\",", keyname);
727	dump_paramdesc(s, r);
728	dump_typeinfo(s, r);
729	fprintf(ofp, "\",");
730	dump_defaultinfo(s, r, devidx);
731
732	return;
733}
734
735static void
736dump_regvals(void)
737{
738	struct assign *manf, *dev;
739	struct section *sec;
740	struct assign *assign;
741	char sname[256];
742	int found = 0, i, is_winxp = 0, is_winnt = 0, devidx = 0;
743
744	/* Find signature to check for special case of WinNT. */
745	assign = find_assign("version", "signature");
746	if (strcasecmp(assign->vals[0], "$windows nt$") == 0)
747		is_winnt++;
748
749	/* Emit start of block */
750	fprintf (ofp, "ndis_cfg ndis_regvals[] = {");
751
752	/* Find manufacturer name */
753	manf = find_assign("Manufacturer", NULL);
754
755nextmanf:
756
757	/* Find manufacturer section */
758	if (manf->vals[1] != NULL &&
759	    (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
760	    strcasecmp(manf->vals[1], "NTx86") == 0 ||
761	    strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
762	    strcasecmp(manf->vals[1], "NTamd64") == 0)) {
763		is_winxp++;
764		/* Handle Windows XP INF files. */
765		snprintf(sname, sizeof(sname), "%s.%s",
766		    manf->vals[0], manf->vals[1]);
767		sec = find_section(sname);
768	} else
769		sec = find_section(manf->vals[0]);
770
771retry:
772
773	TAILQ_FOREACH(assign, &ah, link) {
774		if (assign->section == sec) {
775			found++;
776			/*
777			 * Find all the AddReg sections.
778			 * Look for section names with .NT, unless
779			 * this is a WinXP .INF file.
780			 */
781
782			if (is_winxp) {
783				sprintf(sname, "%s.NTx86", assign->vals[0]);
784				dev = find_assign(sname, "AddReg");
785				if (dev == NULL) {
786					sprintf(sname, "%s.NT",
787					    assign->vals[0]);
788					dev = find_assign(sname, "AddReg");
789				}
790				if (dev == NULL)
791					dev = find_assign(assign->vals[0],
792					    "AddReg");
793			} else {
794				sprintf(sname, "%s.NT", assign->vals[0]);
795				dev = find_assign(sname, "AddReg");
796				if (dev == NULL && is_winnt)
797					dev = find_assign(assign->vals[0],
798					    "AddReg");
799			}
800			/* Section not found. */
801			if (dev == NULL)
802				continue;
803			for (i = 0; i < W_MAX; i++) {
804				if (dev->vals[i] != NULL)
805					dump_addreg(dev->vals[i], devidx);
806			}
807			devidx++;
808		}
809	}
810
811	if (!found) {
812		sec = find_section(manf->vals[0]);
813		is_winxp = 0;
814		found++;
815		goto retry;
816	}
817
818	manf = find_next_assign(manf);
819
820	if (manf != NULL)
821		goto nextmanf;
822
823	fprintf(ofp, "\n\t{ NULL, NULL, { 0 }, 0 }\n};\n\n");
824
825	return;
826}
827
828void
829assign_add (const char *a)
830{
831	struct assign *assign;
832	int i;
833
834	assign = malloc(sizeof(struct assign));
835	bzero(assign, sizeof(struct assign));
836	assign->section = TAILQ_LAST(&sh, section_head);
837	assign->key = sstrdup(a);
838	for (i = 0; i < idx; i++)
839		assign->vals[(idx - 1) - i] = sstrdup(words[i]);
840	TAILQ_INSERT_TAIL(&ah, assign, link);
841
842	clear_words();
843	return;
844}
845
846void
847define_add (const char *d __unused)
848{
849#ifdef notdef
850	fprintf(stderr, "define \"%s\"\n", d);
851#endif
852	return;
853}
854
855static char *
856sstrdup(const char *str)
857{
858	if (str != NULL && strlen(str))
859		return (strdup(str));
860	return (NULL);
861}
862
863static int
864satoi (const char *nptr)
865{
866	if (nptr != NULL && strlen(nptr))
867		return (atoi(nptr));
868	return (0);
869}
870
871void
872regkey_add (const char *r)
873{
874	struct reg *reg;
875
876	reg = malloc(sizeof(struct reg));
877	bzero(reg, sizeof(struct reg));
878	reg->section = TAILQ_LAST(&sh, section_head);
879	reg->root = sstrdup(r);
880	reg->subkey = sstrdup(words[3]);
881	reg->key = sstrdup(words[2]);
882	reg->flags = satoi(words[1]);
883	reg->value = sstrdup(words[0]);
884	TAILQ_INSERT_TAIL(&rh, reg, link);
885
886	free(__DECONST(char *, r));
887	clear_words();
888	return;
889}
890
891void
892push_word (const char *w)
893{
894
895	if (idx == W_MAX) {
896		fprintf(stderr, "too many words; try bumping W_MAX in inf.h\n");
897		exit(1);
898	}
899
900	if (w && strlen(w))
901		words[idx++] = w;
902	else
903		words[idx++] = NULL;
904	return;
905}
906
907void
908clear_words (void)
909{
910	int i;
911
912	for (i = 0; i < idx; i++) {
913		if (words[i]) {
914			free(__DECONST(char *, words[i]));
915		}
916	}
917	idx = 0;
918	bzero(words, sizeof(words));
919	return;
920}
921