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