1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2006,2008 Oracle.  All rights reserved.
5 *
6 * $Id: code_parse.c,v 1.6 2008/01/08 20:58:12 bostic Exp $
7 */
8
9#include "db_codegen.h"
10
11static enum					/* Parse state */
12    { PS_UNSET, PS_ENV_SET, PS_DB_SET } parse_status;
13static ENV_OBJ *cur_env;			/* Current objects */
14static  DB_OBJ *cur_db;
15
16static int parse_line __P((char *, int));
17
18int
19parse_input(fp)
20	FILE *fp;
21{
22	int lc;
23	char *p, *t, buf[256];
24
25	parse_status = PS_UNSET;
26
27	for (lc = 1; fgets(buf, sizeof(buf), fp) != NULL; ++lc) {
28		if ((p = strchr(buf, '\n')) != NULL)
29			*p = '\0';
30		else if (strlen(buf) + 1 == sizeof(buf)) {
31			fprintf(stderr, "%s: %d: line too long", progname, lc);
32			return (1);
33		}
34
35		/* Skip leading whitespace. */
36		for (p = buf; *p != '\0' && isspace((int)*p); ++p)
37			;
38
39		/*
40		 * Any empty line or hash mark to the end of the line is
41		 * a comment.
42		 */
43		if (*p == '\0' || *p == '#')
44			continue;
45		for (t = p; *t != '\0' && *t != '#'; ++t)
46			;
47		*t = '\0';
48
49		if (parse_line(p, lc))
50			return (1);
51	}
52	(void)fclose(fp);
53
54	return (0);
55}
56
57#undef	CONFIG_SLOTS
58#define	CONFIG_SLOTS	10
59
60#undef	CONFIG_GET_UINT32
61#define	CONFIG_GET_UINT32(s, vp) do {					\
62	if (__db_getulong(NULL, progname, s, 0, UINT32_MAX, vp) != 0)	\
63		return (EINVAL);					\
64} while (0)
65
66static int
67parse_line(s, lc)
68	char *s;
69	int lc;
70{
71	u_long uv;
72	int nf;
73	char *argv[CONFIG_SLOTS], *p;
74
75	nf = __config_split(s, argv);	/* Split the line by white-space. */
76
77	/*
78	 * Environment keywords.
79	 */
80	if (strcasecmp(argv[0], "environment") == 0) {
81		if (nf != 3 ||
82		    strcmp(argv[2], "{") != 0 || parse_status != PS_UNSET)
83			goto format;
84
85		if (__os_calloc(NULL, 1, sizeof(*cur_env), &cur_env) ||
86		    __os_strdup(NULL, argv[1], &cur_env->prefix))
87			goto memory;
88		TAILQ_INIT(&cur_env->dbq);
89
90		TAILQ_INSERT_TAIL(&env_tree, cur_env, q);
91
92		/*
93		 * Default settings.
94		 */
95		cur_env->home = ".";
96
97		parse_status = PS_ENV_SET;
98		return (0);
99	}
100	if (strcasecmp(argv[0], "home") == 0) {
101		if (nf != 2 || parse_status != PS_ENV_SET)
102			goto format;
103		if (__os_strdup(NULL, argv[1], &cur_env->home))
104			goto memory;
105		return (0);
106	}
107	if (strcasecmp(argv[0], "cachesize") == 0) {
108		if (nf != 4 || parse_status != PS_ENV_SET)
109			goto format;
110		CONFIG_GET_UINT32(argv[1], &uv);
111		cur_env->gbytes = uv;
112		CONFIG_GET_UINT32(argv[1], &uv);
113		cur_env->bytes = uv;
114		CONFIG_GET_UINT32(argv[1], &uv);
115		cur_env->ncache = uv;
116		return (0);
117	}
118	if (strcasecmp(argv[0], "private") == 0) {
119		if (nf != 1 || parse_status != PS_ENV_SET)
120			goto format;
121		cur_env->private = 1;
122		return (0);
123	}
124
125	/*
126	 * Database keywords.
127	 */
128	if (strcasecmp(argv[0], "database") == 0) {
129		if (nf != 3 ||
130		    strcmp(argv[2], "{") != 0 || parse_status == PS_DB_SET)
131			goto format;
132
133		/*
134		 * Databases can be specified standalone.   If we don't have an
135		 * environment, create a fake one to hold the information.
136		 */
137		if (parse_status == PS_UNSET) {
138			if (__os_calloc(NULL, 1, sizeof(*cur_env), &cur_env))
139				goto memory;
140			TAILQ_INIT(&cur_env->dbq);
141			cur_env->standalone = 1;
142
143			TAILQ_INSERT_TAIL(&env_tree, cur_env, q);
144		}
145
146		if (__os_calloc(NULL, 1, sizeof(*cur_db), &cur_db) ||
147		    __os_strdup(NULL, argv[1], &cur_db->name))
148			goto memory;
149		TAILQ_INSERT_TAIL(&cur_env->dbq, cur_db, q);
150
151		/*
152		 * Default settings.
153		 */
154		cur_db->dbtype = "DB_BTREE";
155
156		parse_status = PS_DB_SET;
157		return (0);
158	}
159	if (strcasecmp(argv[0], "custom") == 0) {
160		if (nf != 1 || parse_status != PS_DB_SET)
161			goto format;
162		cur_db->custom = 1;
163		return (0);
164	}
165	if (strcasecmp(argv[0], "dupsort") == 0) {
166		if (nf != 1 || parse_status != PS_DB_SET)
167			goto format;
168		cur_db->dupsort = 1;
169		return (0);
170	}
171	if (strcasecmp(argv[0], "extentsize") == 0) {
172		if (nf != 2 || parse_status != PS_DB_SET)
173			goto format;
174		CONFIG_GET_UINT32(argv[1], &uv);
175		cur_db->extentsize = uv;
176		return (0);
177	}
178	if (strcasecmp(argv[0], "key_type") == 0) {
179		if (nf != 2 || parse_status != PS_DB_SET)
180			goto format;
181		if (__os_strdup(NULL, argv[1], &cur_db->key_type))
182			goto memory;
183		return (0);
184	}
185	if (strcasecmp(argv[0], "pagesize") == 0) {
186		if (nf != 2 || parse_status != PS_DB_SET)
187			goto format;
188		CONFIG_GET_UINT32(argv[1], &uv);
189		cur_db->pagesize = uv;
190		return (0);
191	}
192	if (strcasecmp(argv[0], "primary") == 0) {
193		if (nf != 2 || parse_status != PS_DB_SET)
194			goto format;
195		if (__os_strdup(NULL, argv[1], &cur_db->primary))
196			goto memory;
197		return (0);
198	}
199	if (strcasecmp(argv[0], "recnum") == 0) {
200		if (nf != 1 || parse_status != PS_DB_SET)
201			goto format;
202		cur_db->recnum = 1;
203		return (0);
204	}
205	if (strcasecmp(argv[0], "re_len") == 0) {
206		if (nf != 2 || parse_status != PS_DB_SET)
207			goto format;
208		CONFIG_GET_UINT32(argv[1], &uv);
209		cur_db->re_len = uv;
210		return (0);
211	}
212	if (strcasecmp(argv[0], "secondary_offset") == 0) {
213		if (nf != 3 || parse_status != PS_DB_SET)
214			goto format;
215		CONFIG_GET_UINT32(argv[1], &uv);
216		cur_db->secondary_off = uv;
217		CONFIG_GET_UINT32(argv[2], &uv);
218		cur_db->secondary_len = uv;
219		return (0);
220	}
221	if (strcasecmp(argv[0], "transaction") == 0) {
222		if (nf != 1 || parse_status != PS_DB_SET)
223			goto format;
224		cur_env->transaction = cur_db->transaction = 1;
225		return (0);
226	}
227	if (strcasecmp(argv[0], "type") == 0) {
228		if (nf != 2 || parse_status != PS_DB_SET)
229			goto format;
230		if (strcasecmp(argv[1], "btree") == 0)
231			p = "DB_BTREE";
232		else if (strcasecmp(argv[1], "hash") == 0)
233			p = "DB_HASH";
234		else if (strcasecmp(argv[1], "queue") == 0)
235			p = "DB_QUEUE";
236		else if (strcasecmp(argv[1], "recno") == 0)
237			p = "DB_RECNO";
238		else
239			goto format;
240		if (__os_strdup(NULL, p, &cur_db->dbtype))
241			goto memory;
242		return (0);
243	}
244
245	/*
246	 * End block.
247	 */
248	if (strcmp(argv[0], "}") == 0) {
249		if (nf != 1)
250			goto format;
251		/*
252		 * Pop up a level -- if we finished a database that's part of
253		 * an environment, go back to the environment level; if we
254		 * finished a standalone database or an environment, go back to
255		 * unset.
256		 */
257		switch (parse_status) {
258		case PS_UNSET:
259			goto format;
260		case PS_DB_SET:
261			parse_status =
262			    cur_env->standalone ? PS_UNSET : PS_ENV_SET;
263			break;
264		case PS_ENV_SET:
265			parse_status = PS_UNSET;
266		}
267		return (0);
268	}
269
270format:	fprintf(stderr,
271	    "%s: line %d: %s: invalid input\n", progname, lc, s);
272	return (1);
273
274memory:	fprintf(stderr, "%s: %s\n", progname, db_strerror(errno));
275	return (1);
276}
277
278#ifdef DEBUG
279int
280parse_dump()
281{
282	TAILQ_FOREACH(cur_env, &env_tree, q) {
283		printf("environment: %s\n",
284		    cur_env->standalone ? "standalone" : cur_env->prefix);
285
286		if (cur_env->home != NULL)
287			printf("\thome: %s\n", cur_env->home);
288		if (cur_env->gbytes != 0 || cur_env->bytes != 0)
289			printf("\tcachesize: %luGB, %luB, %lu\n",
290			    (u_long)cur_env->gbytes,
291			    (u_long)cur_env->bytes,
292			    (u_long)cur_env->ncache);
293
294		if (cur_env->private)
295			printf("\tprivate: yes\n");
296		if (cur_env->transaction)
297			printf("\ttransaction: yes\n");
298
299		TAILQ_FOREACH(cur_db, &cur_env->dbq, q) {
300			printf("\tdatabase: %s\n", cur_db->name);
301			printf("\t\tdbtype: %s\n", cur_db->dbtype);
302
303			if (cur_db->extentsize)
304				printf("\t\textentsize: %lu\n",
305				    (u_long)cur_db->extentsize);
306			if (cur_db->pagesize)
307				printf("\t\tpagesize: %lu\n",
308				    (u_long)cur_db->pagesize);
309			if (cur_db->re_len)
310				printf("\t\tre_len: %lu\n",
311				    (u_long)cur_db->re_len);
312
313			if (cur_db->key_type != NULL)
314				printf("\t\tkey_type: %s\n",
315				    cur_db->key_type);
316
317			if (cur_db->primary != NULL)
318				printf("\t\tprimary: %s\n",
319				    cur_db->primary);
320			if (cur_db->custom)
321				printf("\t\tcustom: yes\n");
322			if (cur_db->secondary_off)
323				printf("\t\tsecondary_offset: %lu/%lu\n",
324				    (u_long)cur_db->secondary_off,
325				    (u_long)cur_db->secondary_len);
326
327			if (cur_db->dupsort)
328				printf("\t\tdupsort: yes\n");
329			if (cur_db->recnum)
330				printf("\t\trecnum: yes\n");
331			if (cur_db->transaction)
332				printf("\t\ttransaction: yes\n");
333		}
334	}
335
336	return (0);
337}
338#endif
339