1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2005,2008 Oracle.  All rights reserved.
5 *
6 * $Id: code.c,v 1.16 2008/01/08 20:58:23 bostic Exp $
7 */
8
9#include "csv.h"
10
11typedef struct {
12	char		*name;		/* Field name */
13	char		*upper;		/* Field name in upper-case */
14	datatype	 type;		/* Data type */
15	int		 indx;		/* Index */
16} FIELD;
17
18int	 code_source(void);
19int	 code_header(void);
20int	 desc_dump(void);
21int	 desc_load(void);
22char	*type_to_string(datatype);
23int	 usage(void);
24
25/*
26 * Globals
27 */
28FILE	*cfp;				/* C source file */
29FILE	*hfp;				/* C source file */
30char	*progname;			/* Program name */
31int	 verbose;			/* Verbose flag */
32
33u_int	 field_cnt;			/* Count of fields */
34FIELD	*fields;			/* Field list */
35
36int
37main(int argc, char *argv[])
38{
39	int ch;
40	char *cfile, *hfile;
41
42	/* Initialize globals. */
43	if ((progname = strrchr(argv[0], '/')) == NULL)
44		progname = argv[0];
45	else
46		++progname;
47
48	/* Initialize arguments. */
49	cfile = "csv_local.c";		/* Default header/source files */
50	hfile = "csv_local.h";
51
52	/* Process arguments. */
53	while ((ch = getopt(argc, argv, "c:f:h:v")) != EOF)
54		switch (ch) {
55		case 'c':
56			cfile = optarg;
57			break;
58		case 'f':
59			if (freopen(optarg, "r", stdin) == NULL) {
60				fprintf(stderr,
61				    "%s: %s\n", optarg, strerror(errno));
62				return (EXIT_FAILURE);
63			}
64			break;
65		case 'h':
66			hfile = optarg;
67			break;
68		case 'v':
69			++verbose;
70			break;
71		case '?':
72		default:
73			return (usage());
74		}
75	argc -= optind;
76	argv += optind;
77
78	if (*argv != NULL)
79		return (usage());
80
81	/* Load records from the input file. */
82	if (desc_load())
83		return (EXIT_FAILURE);
84
85	/* Dump records for debugging. */
86	if (verbose && desc_dump())
87		return (EXIT_FAILURE);
88
89	/* Open output files. */
90	if ((cfp = fopen(cfile, "w")) == NULL) {
91		fprintf(stderr,
92		    "%s: %s: %s\n", progname, cfile, strerror(errno));
93		return (EXIT_FAILURE);
94	}
95	if ((hfp = fopen(hfile, "w")) == NULL) {
96		fprintf(stderr,
97		    "%s: %s: %s\n", progname, hfile, strerror(errno));
98		return (EXIT_FAILURE);
99	}
100
101	/* Build the source and header files. */
102	if (code_header())
103		return (EXIT_FAILURE);
104	if (code_source())
105		return (EXIT_FAILURE);
106
107	return (EXIT_SUCCESS);
108}
109
110/*
111 * desc_load --
112 *	Load a description file.
113 */
114int
115desc_load()
116{
117	u_int field_alloc;
118	int version;
119	char *p, *t, save_ch, buf[256];
120
121	field_alloc = version = 0;
122	while (fgets(buf, sizeof(buf), stdin) != NULL) {
123		if ((p = strchr(buf, '\n')) == NULL) {
124			fprintf(stderr, "%s: input line too long\n", progname);
125			return (1);
126		}
127		*p = '\0';
128
129		/* Skip leading whitespace. */
130		for (p = buf; isspace(*p); ++p)
131			;
132
133		/* Skip empty lines or lines beginning with '#'. */
134		if (*p == '\0' || *p == '#')
135			continue;
136
137		/* Get a version. */
138		if (!version) {
139			if (strncasecmp(
140			    p, "version", sizeof("version") - 1) == 0) {
141				version = 1;
142				continue;
143			}
144			fprintf(stderr,
145			    "%s: expected \"version\" line\n", progname);
146			return (1);
147		}
148
149		/*
150		 * Skip block close -- not currently useful, but when this
151		 * code supports versioned descriptions, it will matter.
152		 */
153		if (*p == '}') {
154			version = 0;
155			continue;
156		}
157
158		/* Allocate a new field structure as necessary. */
159		if (field_cnt == field_alloc &&
160		    (fields = realloc(fields, field_alloc += 100)) == NULL) {
161			fprintf(stderr, "%s: %s\n", progname, strerror(errno));
162			return (1);
163		}
164
165		/* Find the end of the field name. */
166		for (t = p; *t != '\0' && !isspace(*t); ++t)
167			;
168		save_ch = *t;
169		*t = '\0';
170		if ((fields[field_cnt].name = strdup(p)) == NULL ||
171		    (fields[field_cnt].upper = strdup(p)) == NULL) {
172			fprintf(stderr, "%s: %s\n", progname, strerror(errno));
173			return (1);
174		}
175		*t = save_ch;
176		p = t;
177
178		fields[field_cnt].indx = 0;
179		fields[field_cnt].type = NOTSET;
180		for (;;) {
181			/* Skip to the next field, if any. */
182			for (; *p != '\0' && isspace(*p); ++p)
183				;
184			if (*p == '\0')
185				break;
186
187			/* Find the end of the field. */
188			for (t = p; *t != '\0' && !isspace(*t); ++t)
189				;
190			save_ch = *t;
191			*t = '\0';
192			if (strcasecmp(p, "double") == 0)
193				fields[field_cnt].type = DOUBLE;
194			else if (strcasecmp(p, "index") == 0)
195				fields[field_cnt].indx = 1;
196			else if (strcasecmp(p, "string") == 0)
197				fields[field_cnt].type = STRING;
198			else if (strcasecmp(p, "unsigned_long") == 0)
199				fields[field_cnt].type = UNSIGNED_LONG;
200			else {
201				fprintf(stderr,
202				    "%s: unknown keyword: %s\n", progname, p);
203				return (1);
204			}
205			*t = save_ch;
206			p = t;
207		}
208
209		/* Create a copy of the field name that's upper-case. */
210		for (p = fields[field_cnt].upper; *p != '\0'; ++p)
211			if (islower(*p))
212				*p = (char)toupper(*p);
213		++field_cnt;
214	}
215	if (ferror(stdin)) {
216		fprintf(stderr, "%s: stdin: %s\n", progname, strerror(errno));
217		return (1);
218	}
219	return (0);
220}
221
222/*
223 * desc_dump --
224 *	Dump a set of FIELD structures.
225 */
226int
227desc_dump()
228{
229	FIELD *f;
230	u_int i;
231
232	for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
233		fprintf(stderr, "field {%s}: (", f->name);
234		switch (f->type) {
235		case NOTSET:
236			fprintf(stderr, "ignored");
237			break;
238		case DOUBLE:
239			fprintf(stderr, "double");
240			break;
241		case STRING:
242			fprintf(stderr, "string");
243			break;
244		case UNSIGNED_LONG:
245			fprintf(stderr, "unsigned_long");
246			break;
247		}
248		if (f->indx)
249			fprintf(stderr, ", indexed");
250		fprintf(stderr, ")\n");
251	}
252	return (0);
253}
254
255/*
256 * code_header --
257 *	Print out the C #include file.
258 */
259int
260code_header()
261{
262	FIELD *f;
263	u_int i;
264
265	fprintf(hfp, "/*\n");
266	fprintf(hfp, " *  DO NOT EDIT: automatically built by %s.\n", progname);
267	fprintf(hfp, " *\n");
268	fprintf(hfp, " * Record structure.\n");
269	fprintf(hfp, " */\n");
270	fprintf(hfp, "typedef struct __DbRecord {\n");
271	fprintf(hfp, "\tu_int32_t\t recno;\t\t/* Record number */\n");
272	fprintf(hfp, "\n");
273	fprintf(hfp, "\t/*\n");
274	fprintf(hfp, "\t * Management fields\n");
275	fprintf(hfp, "\t */\n");
276	fprintf(hfp, "\tvoid\t\t*raw;\t\t/* Memory returned by DB */\n");
277	fprintf(hfp, "\tu_char\t\t*record;\t/* Raw record */\n");
278	fprintf(hfp, "\tsize_t\t\t record_len;\t/* Raw record length */\n\n");
279	fprintf(hfp, "\tu_int32_t\t field_count;\t/* Field count */\n");
280	fprintf(hfp, "\tu_int32_t\t version;\t/* Record version */\n\n");
281	fprintf(hfp, "\tu_int32_t\t*offset;\t/* Offset table */\n");
282	fprintf(hfp, "\n");
283
284	fprintf(hfp, "\t/*\n");
285	fprintf(hfp, "\t * Indexed fields\n");
286	fprintf(hfp, "\t */\n");
287	for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
288		if (f->type == NOTSET)
289			continue;
290		if (i != 0)
291			fprintf(hfp, "\n");
292		fprintf(hfp, "#define	CSV_INDX_%s\t%d\n", f->upper, i + 1);
293		switch (f->type) {
294		case NOTSET:
295			/* NOTREACHED */
296			abort();
297			break;
298		case DOUBLE:
299			fprintf(hfp, "\tdouble\t\t %s;\n", f->name);
300			break;
301		case STRING:
302			fprintf(hfp, "\tchar\t\t*%s;\n", f->name);
303			break;
304		case UNSIGNED_LONG:
305			fprintf(hfp, "\tu_long\t\t %s;\n", f->name);
306			break;
307		}
308	}
309	fprintf(hfp, "} DbRecord;\n");
310
311	return (0);
312}
313
314/*
315 * code_source --
316 *	Print out the C structure initialization.
317 */
318int
319code_source()
320{
321	FIELD *f;
322	u_int i;
323
324	fprintf(cfp, "/*\n");
325	fprintf(cfp,
326	   " *  DO NOT EDIT: automatically built by %s.\n", progname);
327	fprintf(cfp, " *\n");
328	fprintf(cfp, " * Initialized record structure.\n");
329	fprintf(cfp, " */\n");
330	fprintf(cfp, "\n");
331	fprintf(cfp, "#include \"csv.h\"\n");
332	fprintf(cfp, "#include \"csv_local.h\"\n");
333	fprintf(cfp, "\n");
334	fprintf(cfp, "DbRecord DbRecord_base = {\n");
335	fprintf(cfp, "\t0,\t\t/* Record number */\n");
336	fprintf(cfp, "\tNULL,\t\t/* Memory returned by DB */\n");
337	fprintf(cfp, "\tNULL,\t\t/* Raw record */\n");
338	fprintf(cfp, "\t0,\t\t/* Raw record length */\n");
339	fprintf(cfp, "\t%d,\t\t/* Field count */\n", field_cnt);
340	fprintf(cfp, "\t0,\t\t/* Record version */\n");
341	fprintf(cfp, "\tNULL,\t\t/* Offset table */\n");
342	fprintf(cfp, "\n");
343	for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
344		if (f->type == NOTSET)
345			continue;
346		switch (f->type) {
347		case NOTSET:
348			abort();
349			/* NOTREACHED */
350			break;
351		case DOUBLE:
352		case UNSIGNED_LONG:
353			fprintf(cfp, "\t0,\t\t/* %s */\n", f->name);
354			break;
355		case STRING:
356			fprintf(cfp, "\tNULL,\t\t/* %s */\n", f->name);
357			break;
358		}
359	}
360	fprintf(cfp, "};\n");
361
362	fprintf(cfp, "\n");
363	fprintf(cfp, "DbField fieldlist[] = {\n");
364	for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
365		if (f->type == NOTSET)
366			continue;
367		fprintf(cfp, "\t{ \"%s\",", f->name);
368		fprintf(cfp, " CSV_INDX_%s,", f->upper);
369		fprintf(cfp, "\n\t    %s,", type_to_string(f->type));
370		fprintf(cfp, " %d,", f->indx ? 1 : 0);
371		fprintf(cfp, " NULL,");
372		fprintf(cfp, " FIELD_OFFSET(%s)},\n", f->name);
373	}
374	fprintf(cfp, "\t{NULL, 0, STRING, 0, NULL, 0}\n};\n");
375
376	return (0);
377}
378
379char *
380type_to_string(type)
381	datatype type;
382{
383	switch (type) {
384	case NOTSET:
385		return ("NOTSET");
386	case DOUBLE:
387		return ("DOUBLE");
388	case STRING:
389		return ("STRING");
390	case UNSIGNED_LONG:
391		return ("UNSIGNED_LONG");
392	}
393
394	abort();
395	/* NOTREACHED */
396}
397
398int
399usage()
400{
401	(void)fprintf(stderr,
402	    "usage: %s [-v] [-c source-file] [-f input] [-h header-file]\n",
403	    progname);
404	exit(1);
405}
406