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