1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * This module implements the routine to parse the configuration file.
28 */
29
30
31#include <stdio.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <stdlib.h>
35#include <stdarg.h>
36#include <string.h>
37#include <ctype.h>
38#include <alloca.h>
39#include <limits.h>
40#include <sys/utsname.h>
41#include <sys/systeminfo.h>
42#include <sys/types.h>
43#include <libintl.h>
44#include <syslog.h>
45#include <locale.h>
46#include <picl.h>
47#include <picltree.h>
48#include "picld_pluginutil.h"
49#include "picld_pluginutil_impl.h"
50
51/* error codes returned from syntax checking */
52#define	EC_SYNTAX_OK		0
53#define	EC_INSUFFICIENT_TOKEN	1
54#define	EC_SYNTAX_ERR		2
55#define	EC_UNSUPPORTED		3
56#define	EC_PATH_ERR		4
57#define	EC_NODE_MISMATCH	5
58#define	EC_FAILURE		6
59#define	EC_PICL_ERR		7
60#define	EC_TABLE_MISMATCH	8
61#define	EC_ROW_MISMATCH		9
62#define	EC_ROW_EMPTY		10
63
64/*
65 * Error message texts
66 */
67static	char	*err_msg[] = {
68	"%s: Syntax OK",					/* 0 */
69	"%s::%s[line %d]: Insufficient token\n",		/* 1 */
70	"%s::%s[line %d]: Syntax error\n",			/* 2 */
71	"%s::%s[line %d]: Unsupported or missing version\n",	/* 3 */
72	"%s::%s[line %d]: Illegal use of nodepath or namepath\n",	/* 4 */
73	"%s::%s[line %d]: Node and endnode mismatch\n",		/* 5 */
74	"%s::%s[line %d]: General system failure\n",		/* 6 */
75	"%s: PICL error code %d\n",				/* 7 */
76	"%s::%s[line %d]: Table and endtable mismatch\n",	/* 8 */
77	"%s::%s[line %d]: Row and endrow mismatch\n",		/* 9 */
78	"%s::%s[line %d]: Row has no entries \n"		/* 10 */
79};
80
81/* token per directive */
82#define	TOK_CLASSPATH	0
83#define	TOK_NAMEPATH	1
84#define	TOK_NODE	2
85#define	TOK_ENDNODE	3
86#define	TOK_PROP	4
87#define	TOK_REFPROP	5
88#define	TOK_VERSION	6
89#define	TOK_REFNODE	7
90#define	TOK_VERBOSE	8
91#define	TOK_TABLE	9
92#define	TOK_ENDTABLE	10
93#define	TOK_ROW		11
94#define	TOK_ENDROW	12
95
96static const char	*tokens[] = {
97	"_class",	/* _CLASS:<classpath> */
98	"name",		/* NAME:<namepath> */
99	"node",		/* NODE <name> <class> */
100	"endnode",	/* ENDNODE */
101	"prop",		/* PROP <name> <type> <access_mode> <size> <value> */
102	"refprop",	/* REFPROP <prop> <destnode> */
103	"version",	/* VERSION <version_number> */
104	"refnode",	/* REFNODE <node> <class> WITH <destnode> */
105	"verbose",	/* VERBOSE <level> */
106	"table",	/* TABLE   <table_prop_name> */
107	"endtable",	/* ENDTABLE */
108	"row",		/* ROW  */
109	"endrow"	/* ENDROW */
110};
111
112#define	BUF_SIZE_MAX	1024
113
114/*
115 * print error message
116 */
117/*VARARGS2*/
118static void
119verbose_log(int pri, const char *fmt, ...)
120{
121	va_list	ap;
122
123	va_start(ap, fmt);
124	vsyslog(pri, fmt, ap);
125	va_end(ap);
126}
127
128/*
129 * Undo the commands which have created valid node/prop handle
130 * The undo order is from last command to the first command.
131 */
132static void
133undo_commands(cmdbuf_t *cmds, int last_cmd_index)
134{
135	int 		i;
136	command_t	*com = cmds->commands;
137
138	for (i = last_cmd_index; i >= 0; i--) {
139		switch (com[i].type) {
140		case TOK_NODE:
141			if (com[i].nodecmd_nodeh == NULL)
142				break;
143
144			(void) ptree_delete_node(com[i].nodecmd_nodeh);
145			(void) ptree_destroy_node(com[i].nodecmd_nodeh);
146			break;
147		case TOK_REFNODE:
148			if (com[i].refnodecmd_nodeh == NULL)
149				break;
150			(void) ptree_delete_node(com[i].refnodecmd_nodeh);
151			(void) ptree_destroy_node(com[i].refnodecmd_nodeh);
152			break;
153		case TOK_PROP:
154			if (com[i].propcmd_proph == NULL)
155				break;
156			(void) ptree_delete_prop(com[i].propcmd_proph);
157			(void) ptree_destroy_prop(com[i].propcmd_proph);
158			break;
159		case TOK_REFPROP:
160			if (com[i].refpropcmd_proph == NULL)
161				break;
162			(void) ptree_delete_prop(com[i].refpropcmd_proph);
163			(void) ptree_destroy_prop(com[i].refpropcmd_proph);
164			break;
165		case TOK_TABLE:
166			if ((com[i].tablecmd_tblh == NULL) ||
167			    (com[i].tablecmd_newtbl == 0))
168				break;
169			(void) ptree_delete_prop(com[i].tablecmd_tblh);
170			(void) ptree_destroy_prop(com[i].tablecmd_tblh);
171			break;
172		case TOK_ENDTABLE:
173			/*FALLTHROUGH*/
174		case TOK_ROW:
175			/*FALLTHROUGH*/
176		case TOK_ENDROW:
177			/*FALLTHROUGH*/
178		case TOK_NAMEPATH:
179			/*FALLTHROUGH*/
180		case TOK_CLASSPATH:
181			/*FALLTHROUGH*/
182		case TOK_ENDNODE:
183			/*FALLTHROUGH*/
184		case TOK_VERBOSE:
185			/*FALLTHROUGH*/
186		default:
187			break;
188		}
189	}
190}
191
192/*
193 * Get the token index from the tokens table
194 */
195static int
196get_token_id(char *t)
197{
198	int	i;
199
200	for (i = 0; i < sizeof (tokens)/ sizeof (char *); ++i)
201		if (strcasecmp(tokens[i], t) == 0)
202			return (i);
203
204	return (-1);
205}
206
207/*
208 * Check the version syntax and set the version_no
209 *
210 * VERSION <version_num> --   specify the configuration version
211 */
212static int
213parse_version(cmdbuf_t *cmds, char *line)
214{
215	char	*tok;
216	char	*vertok;
217	char	*last;
218	char	*endptr;
219
220	/* get the VERSION directive */
221	tok = strtok_r(line, WHITESPACE, &last);
222	if (tok == NULL)
223		return (EC_INSUFFICIENT_TOKEN);
224
225	/* get the version number */
226	vertok = strtok_r(last, WHITESPACE, &last);
227	if (vertok == NULL)
228		return (EC_INSUFFICIENT_TOKEN);
229
230	cmds->version_no = (float)strtod(vertok, &endptr);
231	if (endptr != (vertok + strlen(vertok)))
232		return (EC_UNSUPPORTED);
233
234	if (cmds->version_no > (float)SUPPORTED_VERSION_NUM)
235		return (EC_UNSUPPORTED);
236
237	/* check if more tokens */
238	tok = strtok_r(last, WHITESPACE, &last);
239	if (tok != NULL)
240		return (EC_SYNTAX_ERR);
241
242	return (EC_SYNTAX_OK);
243}
244
245/*
246 * free path_cmd_t
247 */
248static void
249free_path(command_t *command)
250{
251	free(command->pathcmd_name);
252}
253
254/*
255 * Check the path syntax
256 * NAMEPATH:<namepath> --     gives the anchor node
257 * or
258 * CLASSPATH:<classpath> --   gives the anchor node
259 */
260static int
261parse_path(char *line, command_t *command)
262{
263	char	*tok;
264	char	*pathtok;
265	char	*last;
266
267	pathtok = strtok_r(line, WHITESPACE, &last);
268	if (pathtok == NULL)
269		return (EC_INSUFFICIENT_TOKEN);
270
271	/* check if more tokens */
272	tok = strtok_r(last, WHITESPACE, &last);
273	if (tok != NULL)
274		return (EC_SYNTAX_ERR);
275
276	command->pathcmd_name = strdup(pathtok);
277	if (command->pathcmd_name == NULL)
278		return (EC_FAILURE);
279
280	return (EC_SYNTAX_OK);
281}
282
283/*
284 * Process the path command and return PICL node handle
285 */
286static int
287process_path(command_t *command, picl_nodehdl_t *nodeh)
288{
289	int	err;
290
291	err = ptree_get_node_by_path(command->pathcmd_name, nodeh);
292	return (err);
293}
294
295/*
296 * free node_cmd_t
297 */
298static void
299free_node(command_t *command)
300{
301	free(command->nodecmd_nodename);
302	free(command->nodecmd_classname);
303}
304
305/*
306 * Check the NODE syntax
307 * NODE <name> <class>
308 */
309static int
310parse_node(char *line, command_t *command)
311{
312	char	*tok;
313	char	*nametok;
314	char	*classtok;
315	char	*last;
316
317	/* get the NODE directive */
318	tok = strtok_r(line, WHITESPACE, &last);
319	if (tok == NULL)
320		return (EC_INSUFFICIENT_TOKEN);
321
322	/* get name */
323	nametok = strtok_r(last, WHITESPACE, &last);
324	if (nametok == NULL)
325		return (EC_INSUFFICIENT_TOKEN);
326
327	classtok = strtok_r(last, WHITESPACE, &last);
328	if (classtok == NULL)
329		return (EC_INSUFFICIENT_TOKEN);
330
331	/* check if more tokens */
332	tok = strtok_r(last, WHITESPACE, &last);
333	if (tok != NULL)
334		return (EC_SYNTAX_ERR);
335
336	command->nodecmd_nodename = strdup(nametok);
337	command->nodecmd_classname = strdup(classtok);
338	command->nodecmd_nodeh = NULL;
339	if ((command->nodecmd_nodename == NULL) ||
340	    (command->nodecmd_classname == NULL))
341		return (EC_FAILURE);
342
343	return (EC_SYNTAX_OK);
344}
345
346/*
347 * Process the NODE command and return PICL node handle
348 */
349static int
350process_node(command_t *command, picl_nodehdl_t parh, picl_nodehdl_t *nodeh)
351{
352	int	err;
353
354	err = ptree_create_and_add_node(parh, command->nodecmd_nodename,
355	    command->nodecmd_classname, nodeh);
356
357	if (err == PICL_SUCCESS)
358		command->nodecmd_nodeh = *nodeh;
359
360	return (err);
361}
362
363/*
364 * get the PICL property type
365 */
366static int
367getpicltype(char *type)
368{
369	if (strcasecmp(type, KEYWORD_INT_TYPE) == 0)
370		return (PICL_PTYPE_INT);
371	else if (strcasecmp(type, KEYWORD_UINT_TYPE) == 0)
372		return (PICL_PTYPE_UNSIGNED_INT);
373	else if (strcasecmp(type, KEYWORD_FLOAT_TYPE) == 0)
374		return (PICL_PTYPE_FLOAT);
375	else if (strcasecmp(type, KEYWORD_STRING_TYPE) == 0)
376		return (PICL_PTYPE_CHARSTRING);
377	else if (strcasecmp(type, KEYWORD_VOID_TYPE) == 0)
378		return (PICL_PTYPE_VOID);
379	else
380		return (-1);
381}
382
383/*
384 * get the PICL accessmode mode
385 */
386static int
387getpiclmode(char *mode)
388{
389	if (strcasecmp(mode, KEYWORD_READ_MODE) == 0)
390		return (PICL_READ);
391	else if (strcasecmp(mode, KEYWORD_WRITE_MODE) == 0)
392		return (PICL_WRITE);
393	else if (strcasecmp(mode, KEYWORD_READWRITE_MODE) == 0)
394		return (PICL_READ|PICL_WRITE);
395	else
396		return (-1);
397}
398
399/*
400 * check if the size and value are valid given by the prop type
401 */
402static int
403validate_size_and_cvt_val(void *outbuf, size_t size, int type, char *val)
404{
405	int64_t 	llval;
406	int32_t		intval;
407	int16_t		sval;
408	int8_t		cval;
409	uint64_t 	ullval;
410	uint32_t	uintval;
411	uint16_t	usval;
412	uint8_t		ucval;
413	float		fval;
414	double		dval;
415	char		*endptr;
416
417	switch (type) {
418	case PICL_PTYPE_CHARSTRING:
419		break;
420	case PICL_PTYPE_INT:
421		switch (size) {
422		case sizeof (int64_t):
423			llval = strtoll(val, &endptr, 0);
424			if (endptr != (val + strlen(val)))
425				return (EC_SYNTAX_ERR);
426			(void) memcpy(outbuf, &llval, size);
427			break;
428		case sizeof (int32_t):
429			intval = strtol(val, &endptr, 0);
430			if (endptr != (val + strlen(val)))
431				return (EC_SYNTAX_ERR);
432			(void) memcpy(outbuf, &intval, size);
433			break;
434		case sizeof (int16_t):
435			sval = (int16_t)strtol(val, &endptr, 0);
436			if (endptr != (val + strlen(val)))
437				return (EC_SYNTAX_ERR);
438			(void) memcpy(outbuf, &sval, size);
439			break;
440		case sizeof (int8_t):
441			cval = (int8_t)strtol(val, &endptr, 0);
442			if (endptr != (val + strlen(val)))
443				return (EC_SYNTAX_ERR);
444			(void) memcpy(outbuf, &cval, size);
445			break;
446		default:	/* invalid size */
447			return (EC_SYNTAX_ERR);
448		}
449		break;
450	case PICL_PTYPE_UNSIGNED_INT:
451		switch (size) {
452		case sizeof (uint64_t):
453			ullval = strtoull(val, &endptr, 0);
454			if (endptr != (val + strlen(val)))
455				return (EC_SYNTAX_ERR);
456			(void) memcpy(outbuf, &ullval, size);
457			break;
458		case sizeof (uint32_t):
459			uintval = strtoul(val, &endptr, 0);
460			if (endptr != (val + strlen(val)))
461				return (EC_SYNTAX_ERR);
462			(void) memcpy(outbuf, &uintval, size);
463			break;
464		case sizeof (uint16_t):
465			usval = (uint16_t)strtoul(val, &endptr, 0);
466			if (endptr != (val + strlen(val)))
467				return (EC_SYNTAX_ERR);
468			(void) memcpy(outbuf, &usval, size);
469			break;
470		case sizeof (uint8_t):
471			ucval = (uint8_t)strtoul(val, &endptr, 0);
472			if (endptr != (val + strlen(val)))
473				return (EC_SYNTAX_ERR);
474			(void) memcpy(outbuf, &ucval, size);
475			break;
476		default:	/* invalid size */
477			return (EC_SYNTAX_ERR);
478		}
479		break;
480	case PICL_PTYPE_FLOAT:
481		switch (size) {
482		case sizeof (double):
483			dval = strtod(val, &endptr);
484			if (endptr != (val + strlen(val)))
485				return (EC_SYNTAX_ERR);
486			(void) memcpy(outbuf, &dval, size);
487			break;
488		case sizeof (float):
489			fval = (float)strtod(val, &endptr);
490			if (endptr != (val + strlen(val)))
491				return (EC_SYNTAX_ERR);
492			(void) memcpy(outbuf, &fval, size);
493			break;
494		default:	/* invalid size */
495			return (EC_SYNTAX_ERR);
496		}
497		break;
498	default:	/* not supported type */
499		return (EC_SYNTAX_ERR);
500	}
501
502	return (EC_SYNTAX_OK);
503}
504
505/*
506 * free prop_cmd_t
507 */
508static void
509free_prop(command_t *command)
510{
511	free(command->propcmd_pname);
512	if (command->propcmd_type != PICL_PTYPE_VOID)
513		free(command->propcmd_valbuf);
514}
515
516/*
517 * return the string token in two double quotes
518 * The current version won't support multiple-line string
519 */
520static int
521get_string_token(char *line, char **valtok)
522{
523	char	*optr;	/* ptr to the open quote */
524	char	*cptr;	/* ptr to the close quote */
525	char	*ptr;
526	char	*tmpbuf;
527
528	if (line == NULL)
529		return (EC_INSUFFICIENT_TOKEN);
530
531	/* skipping leading white spaces */
532	optr = line;
533	while ((*optr == ' ') || (*optr == '\t') || (*optr == '\n'))
534		optr++;
535
536	/* reach end of string */
537	if (*optr == '\0')
538		return (EC_INSUFFICIENT_TOKEN);
539
540	/* it's not an open double quote */
541	if (*optr != '"')
542		return (EC_SYNTAX_ERR);
543
544	/* skipping ending white spaces */
545	cptr = line + strlen(line) - 1;
546	while ((*cptr == ' ') || (*cptr == '\t') || (*cptr == '\n'))
547		cptr--;
548
549	/* it's not an close double quote */
550	if (*cptr != '"')
551		return (EC_SYNTAX_ERR);
552
553	/* close double quote is missing */
554	if (cptr == optr)
555		return (EC_SYNTAX_ERR);
556
557	/* replace close qoute by null to make a string */
558	*cptr = '\0';
559	/* move the begin pointer to the first char of string */
560	optr++;
561
562	tmpbuf = malloc(strlen(optr) + 1);
563	if (tmpbuf == NULL)
564		return (EC_FAILURE);
565
566	for (ptr = tmpbuf; *optr != '\0'; ptr++, optr++) {
567		/* if escape character, go to next character */
568		if (*optr == '\\') {
569			optr++;
570			if (*optr == '\0') {	/* for exampe, "xxx\" */
571				free(tmpbuf);
572				return (EC_SYNTAX_ERR);
573			}
574		}
575		*ptr = *optr;
576	}
577
578	*ptr = '\0';
579	*valtok = tmpbuf;
580	return (EC_SYNTAX_OK);
581}
582
583/*
584 * Check the PROP syntax
585 * PROP <name> <type> <access_mode> [<size> <value>]
586 * supported prop types: void, int, uint, float, string
587 * supported prop access_modes: r, w, rw
588 * For void prop, <size> and <value> are not needed
589 * For string prop, <size> will be set the actual string size if <size>
590 * is 0
591 */
592static int
593parse_prop(char *line, command_t *command)
594{
595	char	*tok;
596	char	*pnametok;
597	int	typetok;
598	size_t	sizetok;
599	int	modetok;
600	char	*valtok;
601	char	*last;
602	char	*endptr;
603	int	err;
604
605	/* get the PROP directive */
606	tok = strtok_r(line, WHITESPACE, &last);
607	if (tok == NULL)
608		return (EC_INSUFFICIENT_TOKEN);
609
610	/* get the property name */
611	pnametok = strtok_r(last, WHITESPACE, &last);
612	if (pnametok == NULL)
613		return (EC_INSUFFICIENT_TOKEN);
614
615	/* get the type */
616	tok = strtok_r(last, WHITESPACE, &last);
617	if (tok == NULL)
618		return (EC_INSUFFICIENT_TOKEN);
619
620	if ((typetok = getpicltype(tok)) < 0)
621		return (EC_SYNTAX_ERR);
622
623	/* get mode */
624	tok = strtok_r(last, WHITESPACE, &last);
625	if (tok == NULL)
626		return (EC_INSUFFICIENT_TOKEN);
627
628	if ((modetok = getpiclmode(tok)) < 0)
629		return (EC_SYNTAX_ERR);
630
631	if (typetok == PICL_PTYPE_VOID) {
632		/* ignore the rest of arguments */
633		command->propcmd_valbuf = NULL;
634		command->propcmd_pname = strdup(pnametok);
635		if (command->propcmd_pname == NULL)
636			return (EC_FAILURE);
637		command->propcmd_type = typetok;
638		command->propcmd_accessmode = modetok;
639		command->propcmd_size = 0;
640		command->propcmd_proph = NULL;
641		return (EC_SYNTAX_OK);
642	}
643
644	/* get size */
645	tok = strtok_r(last, WHITESPACE, &last);
646	if (tok == NULL)
647		return (EC_INSUFFICIENT_TOKEN);
648
649	sizetok = (size_t)strtol(tok, &endptr, 0);
650	if (endptr != (tok + strlen(tok)))
651		return (EC_SYNTAX_ERR);
652
653	/* get val */
654	if (typetok == PICL_PTYPE_CHARSTRING) {
655		err = get_string_token(last, &valtok);
656		if (err != EC_SYNTAX_OK)
657			return (err);
658		if (sizetok == 0)
659			sizetok = strlen(valtok) + 1;
660		command->propcmd_valbuf = valtok;
661	} else {
662		valtok = strtok_r(last, WHITESPACE, &last);
663		if (valtok == NULL)
664			return (EC_INSUFFICIENT_TOKEN);
665		/* check if more tokens */
666		tok = strtok_r(last, WHITESPACE, &last);
667		if (tok != NULL)
668			return (EC_SYNTAX_ERR);
669		command->propcmd_valbuf = malloc(sizetok);
670		if (command->propcmd_valbuf == NULL)
671			return (EC_FAILURE);
672		err = validate_size_and_cvt_val(command->propcmd_valbuf,
673		    sizetok, typetok, valtok);
674		if (err != EC_SYNTAX_OK) {
675			free(command->propcmd_valbuf);
676			return (err);
677		}
678	}
679
680	command->propcmd_pname = strdup(pnametok);
681	if (command->propcmd_pname == NULL)
682		return (EC_FAILURE);
683	command->propcmd_type = typetok;
684	command->propcmd_accessmode = modetok;
685	command->propcmd_size = sizetok;
686	command->propcmd_proph = NULL;
687	return (EC_SYNTAX_OK);
688}
689
690/*
691 * Add a property to the row, the row gets added to the node at endrow
692 */
693static int
694add_proph_to_row(command_t *command, picl_prophdl_t proph)
695{
696	if (command->rowcmd_index >= command->rowcmd_nproph)
697		return (PICL_FAILURE);
698	command->rowcmd_prophs[command->rowcmd_index] = proph;
699	command->rowcmd_index++;
700	return (PICL_SUCCESS);
701}
702
703/*
704 * Process the PROP command and add the specified property under the given
705 * node handle
706 */
707static int
708process_prop(cmdbuf_t *cmds, command_t *command, picl_nodehdl_t nodeh)
709{
710	ptree_propinfo_t	propinfo;
711	picl_prophdl_t		proph;
712	int			err;
713
714	/* prop in discarded row */
715	if (cmds->inside_row_block &&
716	    cmds->commands[cmds->current_row].rowcmd_nproph == 0)
717		return (PICL_SUCCESS);
718
719	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
720	    command->propcmd_type, command->propcmd_accessmode,
721	    command->propcmd_size, command->propcmd_pname, NULL,
722	    NULL);
723
724	if (err != PICL_SUCCESS)
725		return (err);
726
727	err = ptree_create_prop(&propinfo, command->propcmd_valbuf, &proph);
728
729	if (err != PICL_SUCCESS)
730		return (err);
731
732	command->propcmd_proph = proph;
733
734	if (cmds->inside_row_block) {
735		err = add_proph_to_row(&cmds->commands[cmds->current_row],
736		    proph);
737	} else {
738		err = ptree_add_prop(nodeh, proph);
739	}
740
741	return (err);
742}
743
744/*
745 * free refnode_cmd_t
746 */
747static void
748free_refnode(command_t *command)
749{
750	free(command->refnodecmd_name);
751	free(command->refnodecmd_class);
752	free(command->refnodecmd_dstnode);
753}
754
755/*
756 * Check the REFNODE syntax
757 *
758 * REFNODE <name> <class> with <destnode> -- if <destnode> exists,
759 * create node with nodename <name> and piclclass <class>
760 */
761static int
762parse_refnode(char *line, command_t *command)
763{
764	char	*tok;
765	char	*dsttok;
766	char	*classnm;
767	char	*nodenm;
768	char	*last;
769
770	/* get the directive */
771	tok = strtok_r(line, WHITESPACE, &last);
772	if (tok == NULL)
773		return (EC_INSUFFICIENT_TOKEN);
774
775	/* get the nodename */
776	nodenm = strtok_r(last, WHITESPACE, &last);
777	if (nodenm == NULL)
778		return (EC_INSUFFICIENT_TOKEN);
779
780	/* get the class */
781	classnm = strtok_r(last, WHITESPACE, &last);
782	if (classnm == NULL)
783		return (EC_INSUFFICIENT_TOKEN);
784
785	/* get the WITH keyword */
786	tok = strtok_r(last, WHITESPACE, &last);
787	if (tok == NULL)
788		return (EC_INSUFFICIENT_TOKEN);
789
790	if (strcasecmp(tok, KEYWORD_WITH_STR) != 0)
791		return (EC_SYNTAX_ERR);
792
793	/* get the dst node */
794	dsttok = strtok_r(last, WHITESPACE, &last);
795	if (dsttok == NULL)
796		return (EC_INSUFFICIENT_TOKEN);
797
798	/* check if more tokens */
799	tok = strtok_r(last, WHITESPACE, &last);
800	if (tok != NULL)
801		return (EC_SYNTAX_ERR);
802
803	command->refnodecmd_name = strdup(nodenm);
804	command->refnodecmd_class = strdup(classnm);
805	command->refnodecmd_dstnode = strdup(dsttok);
806	command->refnodecmd_nodeh = NULL;
807	if ((command->refnodecmd_name == NULL) ||
808	    (command->refnodecmd_class == NULL) ||
809	    (command->refnodecmd_dstnode == NULL))
810		return (EC_FAILURE);
811
812	return (EC_SYNTAX_OK);
813}
814
815/*
816 * Process the REFNODE command
817 */
818static int
819process_refnode(command_t *command, picl_nodehdl_t parh)
820{
821	picl_nodehdl_t	dsth;
822	picl_nodehdl_t	nodeh;
823	int		err;
824
825	if ((ptree_get_node_by_path(command->refnodecmd_dstnode,
826	    &dsth) == PICL_SUCCESS)) {
827		err = ptree_create_and_add_node(parh, command->refnodecmd_name,
828		    command->refnodecmd_class, &nodeh);
829		if (err == PICL_SUCCESS)
830			command->refnodecmd_nodeh = nodeh;
831
832		return (err);
833	}
834
835	return (PICL_SUCCESS);
836}
837
838/*
839 * free refprop_cmd_t
840 */
841static void
842free_refprop(command_t *command)
843{
844	free(command->refpropcmd_pname);
845	free(command->refpropcmd_dstnode);
846}
847
848/*
849 * Check the REFPROP syntax
850 *
851 * REFPROP <prop> <destnode> -- creates a reference property to <destnode>
852 */
853static int
854parse_refprop(char *line, command_t *command)
855{
856	char	*tok;
857	char	*pnametok;
858	char	*dsttok;
859	char	*last;
860
861	/* get the REFPROP directive */
862	tok = strtok_r(line, WHITESPACE, &last);
863	if (tok == NULL)
864		return (EC_INSUFFICIENT_TOKEN);
865
866	/* get the propname  */
867	pnametok = strtok_r(last, WHITESPACE, &last);
868	if (pnametok == NULL)
869		return (EC_INSUFFICIENT_TOKEN);
870
871	dsttok = strtok_r(last, WHITESPACE, &last);
872	if (dsttok == NULL)
873		return (EC_INSUFFICIENT_TOKEN);
874
875	/* check if more tokens */
876	tok = strtok_r(last, WHITESPACE, &last);
877	if (tok != NULL)
878		return (EC_SYNTAX_ERR);
879
880	command->refpropcmd_pname = strdup(pnametok);
881	command->refpropcmd_dstnode = strdup(dsttok);
882	command->refpropcmd_proph = NULL;
883	if ((command->refpropcmd_pname == NULL) ||
884	    (command->refpropcmd_dstnode == NULL))
885		return (EC_FAILURE);
886
887	return (EC_SYNTAX_OK);
888}
889
890/*
891 * Process the REFPROP command
892 */
893static int
894process_refprop(cmdbuf_t *cmds, command_t *command, picl_nodehdl_t nodeh)
895{
896	int			err;
897	picl_nodehdl_t 		dsth;
898	picl_prophdl_t 		proph;
899	ptree_propinfo_t	propinfo;
900
901	/* refprop in discarded row */
902	if (cmds->inside_row_block &&
903	    cmds->commands[cmds->current_row].rowcmd_nproph == 0)
904		return (PICL_SUCCESS);
905
906	/* try finding the refprop's dstnode */
907	err = ptree_get_node_by_path(command->refpropcmd_dstnode, &dsth);
908
909	/* dstnode doesn't exist, return */
910	if (err != PICL_SUCCESS)
911		return (err);
912
913	/* dstnode exists, try adding the refprop to nodeh */
914	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
915	    PICL_PTYPE_REFERENCE, PICL_READ, sizeof (picl_nodehdl_t),
916	    command->refpropcmd_pname, NULL, NULL);
917
918	if (err != PICL_SUCCESS)
919		return (err);
920
921	err = ptree_create_prop(&propinfo, &dsth, &proph);
922
923	if (err != PICL_SUCCESS)
924		return (err);
925
926	command->refpropcmd_proph = proph;
927
928	if (cmds->inside_row_block) {
929		err = add_proph_to_row(&cmds->commands[cmds->current_row],
930		    proph);
931	} else {
932		err = ptree_add_prop(nodeh, proph);
933	}
934
935	return (err);
936}
937
938/*
939 * free table_cmd_t
940 */
941static void
942free_table(command_t *command)
943{
944	if (command->tablecmd_tname)
945		free(command->tablecmd_tname);
946}
947
948/*
949 * Check the TABLE syntax
950 * TABLE <table_prop_name>
951 *
952 */
953static int
954parse_table(char *line, command_t *command)
955{
956	char	*tok = NULL;
957	char	*tnametok = NULL;
958	char	*last = NULL;
959
960	/* get the TABLE directive */
961	tok = strtok_r(line, WHITESPACE, &last);
962	if (tok == NULL)
963		return (EC_INSUFFICIENT_TOKEN);
964
965	/* get the property name */
966	tnametok = strtok_r(last, WHITESPACE, &last);
967	if (tnametok == NULL)
968		return (EC_INSUFFICIENT_TOKEN);
969
970	command->tablecmd_tname = strdup(tnametok);
971	if (command->tablecmd_tname == NULL)
972		return (EC_FAILURE);
973
974	command->tablecmd_newtbl = 0;
975	command->tablecmd_tblh = NULL;
976
977	return (EC_SYNTAX_OK);
978}
979
980/*
981 * Process the TABLE command and add the specified property under the given
982 * node handle
983 */
984static int
985process_table(command_t *command, picl_nodehdl_t nodeh)
986{
987	int			err;
988	picl_prophdl_t 		tblh;
989	picl_prophdl_t 		proph;
990	ptree_propinfo_t	propinfo;
991
992	/* find if table already exists */
993	err = ptree_get_prop_by_name(nodeh, command->tablecmd_tname, &tblh);
994	if (err == PICL_SUCCESS) {
995		err = ptree_get_propinfo(tblh, &propinfo);
996		if (err != PICL_SUCCESS)
997			return (err);
998		/* prop with the same name as table? */
999		if (propinfo.piclinfo.type != PICL_PTYPE_TABLE)
1000			return (EC_SYNTAX_ERR);
1001		command->tablecmd_newtbl = 0;
1002		command->tablecmd_tblh = tblh;
1003		return (PICL_SUCCESS);
1004	}
1005
1006	/* init and create a new table */
1007	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1008	    PICL_PTYPE_TABLE, PICL_READ|PICL_WRITE,
1009	    sizeof (picl_prophdl_t), command->tablecmd_tname, NULL, NULL);
1010	if (err != PICL_SUCCESS)
1011		return (err);
1012
1013	err = ptree_create_table(&tblh);
1014	if (err != PICL_SUCCESS)
1015		return (err);
1016
1017	command->tablecmd_newtbl = 1;
1018	command->tablecmd_tblh = tblh;
1019
1020	err = ptree_create_prop(&propinfo, &tblh, &proph);
1021	if (err != PICL_SUCCESS)
1022		return (err);
1023
1024	err = ptree_add_prop(nodeh, proph);
1025
1026	return (err);
1027}
1028
1029/*
1030 * Process the ROW command by alloc'ing space to store the prop handles for
1031 * the whole row. The number of props in the row gets known while parsing.
1032 */
1033static int
1034process_row(command_t *command)
1035{
1036	command->rowcmd_index = 0;
1037	command->rowcmd_prophs =
1038	    malloc(command->rowcmd_nproph * sizeof (picl_prophdl_t));
1039
1040	if (command->rowcmd_prophs == NULL)
1041		return (PICL_FAILURE);
1042
1043	return (PICL_SUCCESS);
1044}
1045
1046/*
1047 * Process the ENDROW command. If a valid row, add the row to the ptree.
1048 */
1049static int
1050process_endrow(cmdbuf_t *cmds)
1051{
1052	int		err;
1053	int		i;
1054	command_t	*curr_row;
1055
1056	curr_row = &cmds->commands[cmds->current_row];
1057
1058	/* if nproph == 0, some row prop had problems, don't add  */
1059	if (curr_row->rowcmd_nproph == 0) {
1060		for (i = 0; i < curr_row->rowcmd_index; i++) {
1061			(void) ptree_delete_prop(curr_row->rowcmd_prophs[i]);
1062			(void) ptree_destroy_prop(curr_row->rowcmd_prophs[i]);
1063		}
1064		err = PICL_SUCCESS;
1065	} else
1066		err = ptree_add_row_to_table(
1067		    cmds->commands[cmds->current_tbl].tablecmd_tblh,
1068		    curr_row->rowcmd_nproph,
1069		    curr_row->rowcmd_prophs);
1070
1071	/* let go the space alloc'd in process_row */
1072	free(curr_row->rowcmd_prophs);
1073	curr_row->rowcmd_prophs = NULL;
1074
1075	return (err);
1076}
1077
1078/*
1079 * Check the VERBOSE syntax
1080 * VERBOSE <level>
1081 */
1082static int
1083parse_verbose(cmdbuf_t *cmds, char *line, command_t *command)
1084{
1085	char	*tok;
1086	char	*level;
1087	char	*last;
1088	char	*endptr;
1089	int	verbose_level;
1090
1091	/* get the VERBOSE directive */
1092	tok = strtok_r(line, WHITESPACE, &last);
1093	if (tok == NULL)
1094		return (EC_INSUFFICIENT_TOKEN);
1095
1096	/* get verbose level */
1097	level = strtok_r(last, WHITESPACE, &last);
1098	if (level == NULL)
1099		return (EC_INSUFFICIENT_TOKEN);
1100	verbose_level = strtol(level, &endptr, 0);
1101	if (endptr != (level + strlen(level)))
1102		return (EC_SYNTAX_ERR);
1103
1104	/* check if more tokens */
1105	tok = strtok_r(last, WHITESPACE, &last);
1106	if (tok != NULL)
1107		return (EC_SYNTAX_ERR);
1108
1109	cmds->verbose = verbose_level;
1110	command->verbosecmd_level = verbose_level;
1111
1112	return (EC_SYNTAX_OK);
1113}
1114
1115/*
1116 * Process the VERBOSE command to set the verbose level
1117 */
1118static int
1119process_verbose(cmdbuf_t *cmds, command_t *command)
1120{
1121	cmds->verbose = command->verbosecmd_level;
1122	return (PICL_SUCCESS);
1123}
1124
1125/*
1126 * parse and tokenize the line
1127 */
1128static int
1129parse_and_tokenize_line(cmdbuf_t *cmds, char *buf, command_t *command)
1130{
1131	char		rec[RECORD_SIZE_MAX];
1132	char		*tok;
1133	int		err;
1134	char		*last;
1135	int		id;
1136
1137	(void) strcpy(rec, buf);
1138	tok = strtok_r(rec, RECORD_WHITESPACE, &last);
1139	if (tok == NULL)
1140		return (EC_INSUFFICIENT_TOKEN);
1141
1142	id = get_token_id(tok);
1143
1144	(void) strcpy(rec, buf);
1145
1146	switch (id) {
1147	case TOK_VERSION:
1148		err = parse_version(cmds, rec);
1149		break;
1150	case TOK_CLASSPATH:
1151	case TOK_NAMEPATH:
1152		if (cmds->inside_node_block != 0)
1153			return (EC_PATH_ERR);
1154
1155		err = parse_path(rec, command);
1156		if (err != EC_SYNTAX_OK)
1157			return (err);
1158		break;
1159	case TOK_NODE:
1160		/* Check for NODE outside of TABLE, ROW */
1161		if ((cmds->inside_table_block != 0) ||
1162		    (cmds->inside_row_block != 0))
1163			return (EC_SYNTAX_ERR);
1164		err = parse_node(rec, command);
1165		if (err != EC_SYNTAX_OK)
1166			return (err);
1167		cmds->inside_node_block++;
1168		break;
1169	case TOK_ENDNODE:
1170		/* Check for ENDNODE outside of TABLE, ROW */
1171		if ((cmds->inside_table_block != 0) ||
1172		    (cmds->inside_row_block != 0))
1173			return (EC_SYNTAX_ERR);
1174		cmds->inside_node_block--;
1175		err = EC_SYNTAX_OK;
1176		break;
1177	case TOK_PROP:
1178		/* Check if inside TABLE, but not in ROW */
1179		if ((cmds->inside_table_block != 0) &&
1180		    (cmds->inside_row_block == 0))
1181			return (EC_SYNTAX_ERR);
1182		err = parse_prop(rec, command);
1183		if (err != EC_SYNTAX_OK)
1184			return (err);
1185		if (cmds->inside_row_block) {
1186			cmds->commands[cmds->current_row].rowcmd_nproph++;
1187		}
1188		break;
1189	case TOK_REFNODE:
1190		err = parse_refnode(rec, command);
1191		if (err != EC_SYNTAX_OK)
1192			return (err);
1193		break;
1194	case TOK_REFPROP:
1195		/* Check if inside TABLE, but not in ROW */
1196		if ((cmds->inside_table_block != 0) &&
1197		    (cmds->inside_row_block == 0))
1198			return (EC_SYNTAX_ERR);
1199		err = parse_refprop(rec, command);
1200		if (err != EC_SYNTAX_OK)
1201			return (err);
1202		if (cmds->inside_row_block) {
1203			cmds->commands[cmds->current_row].rowcmd_nproph++;
1204		}
1205		break;
1206	case TOK_TABLE:
1207		/* Table/Row supported in version 1.1 and above */
1208		if (cmds->version_no < (float)SUPPORTED_VERSION_NUM)
1209			return (EC_UNSUPPORTED);
1210		if (cmds->inside_table_block != 0)
1211			return (EC_SYNTAX_ERR);
1212		err = parse_table(rec, command);
1213		if (err != EC_SYNTAX_OK)
1214			return (err);
1215		cmds->inside_table_block = 1;
1216		break;
1217	case TOK_ENDTABLE:
1218		/* Check for ENDTABLE before TABLE */
1219		if (cmds->inside_table_block == 0)
1220			return (EC_SYNTAX_ERR);
1221
1222		cmds->inside_table_block = 0;
1223
1224		break;
1225	case TOK_ROW:
1226		/* Check for ROW outside of TABLE, ROW inside ROW */
1227		if ((cmds->inside_table_block == 0) ||
1228		    (cmds->inside_row_block != 0))
1229			return (EC_SYNTAX_ERR);
1230		cmds->inside_row_block = 1;
1231		break;
1232	case TOK_ENDROW:
1233		/* Check for ENDROW outside of TABLE, ENDROW before ROW */
1234		if ((cmds->inside_table_block == 0) ||
1235		    (cmds->inside_row_block == 0))
1236			return (EC_SYNTAX_ERR);
1237		else
1238			err = EC_SYNTAX_OK;
1239
1240		cmds->inside_row_block = 0;
1241
1242		/* error if row is empty */
1243		if (cmds->commands[cmds->current_row].rowcmd_nproph <= 0)
1244			return (EC_ROW_EMPTY);
1245		break;
1246	case TOK_VERBOSE:
1247		err = parse_verbose(cmds, rec, command);
1248		if (err != EC_SYNTAX_OK)
1249			return (err);
1250		break;
1251	default:	/* unsupported command */
1252		return (EC_SYNTAX_ERR);
1253	}
1254
1255	command->type = id;
1256	return (EC_SYNTAX_OK);
1257}
1258
1259/*
1260 * Check the syntax and save the tokens in the commands buffer
1261 */
1262static int
1263check_line_syntax(cmdbuf_t *cmds, char *buf)
1264{
1265	int		err;
1266	command_t	command;
1267
1268	(void) memset(&command, 0, sizeof (command_t));
1269	err = parse_and_tokenize_line(cmds, buf, &command);
1270	if (err != EC_SYNTAX_OK)
1271		return (err);
1272
1273	/*
1274	 * don't add and count version command in the command buffer
1275	 */
1276	if (command.type == TOK_VERSION)
1277		return (EC_SYNTAX_OK);
1278
1279	/*
1280	 * check if the commands buffer has been filled
1281	 * If it is full, reallocate the buffer.
1282	 */
1283	if (cmds->count == cmds->allocated) {
1284		cmds->commands = realloc(cmds->commands,
1285		    sizeof (command_t) * (cmds->allocated + PER_ALLOC_COUNT));
1286		if (cmds->commands == NULL)
1287			return (EC_FAILURE);
1288		cmds->allocated += PER_ALLOC_COUNT;
1289	}
1290
1291	cmds->commands[cmds->count] = command;	/* copy */
1292
1293	/*
1294	 * make a note of the row/endrow command, to keep track of # of props
1295	 */
1296	if (command.type == TOK_ROW)
1297		cmds->current_row = cmds->count;
1298
1299	if (command.type == TOK_ENDROW)
1300		cmds->current_row = 0;
1301
1302	cmds->count++;
1303
1304	return (EC_SYNTAX_OK);
1305}
1306
1307/*
1308 * get the line control information
1309 * return 1 if it's the line control information, else return 0
1310 */
1311static int
1312get_line_control_info(char *buf, uint32_t *linenum, char *filename)
1313{
1314	char		*ptr;
1315	char		*last;
1316	uint32_t	num;
1317	char		*fname;
1318	char		*endptr;
1319
1320	/* skip # and get next string */
1321	ptr = strtok_r(buf + 1, WHITESPACE, &last);
1322	if (ptr == NULL) {
1323		return (0);
1324	}
1325
1326	num = strtoul(ptr, &endptr, 0);
1327
1328	/*
1329	 * It's not the line control information
1330	 */
1331	if (endptr != (ptr + strlen(ptr))) {
1332		return (0);
1333	}
1334
1335	/*
1336	 * get the filename
1337	 */
1338
1339	/* get the beginning double quote */
1340	last = strchr(last, '"');
1341	if (last == NULL)
1342		return (0);
1343
1344	last++;
1345
1346	/* get the ending double quote */
1347	fname = strtok_r(last, DOUBLE_QUOTE, &last);
1348	if (fname == NULL)
1349		return (0);
1350
1351	*linenum = num;
1352	(void) strlcpy(filename, fname, PATH_MAX);
1353	return (1);
1354}
1355
1356/*
1357 * check the syntax of the configuration file
1358 */
1359static int
1360check_conffile_syntax(cmdbuf_t *cmds, FILE *fp)
1361{
1362	char		lbuf[RECORD_SIZE_MAX];
1363	char		buf[RECORD_SIZE_MAX];
1364	uint32_t	linenum;
1365	char		cppfile[PATH_MAX] = "";
1366	int		err = EC_SYNTAX_OK;
1367
1368	linenum = 0;
1369	while (fgets(buf, sizeof (buf), fp) != NULL) {
1370		/*
1371		 * get cpp line control information, if any
1372		 */
1373		if (buf[0] == '#') {
1374			if (!get_line_control_info(buf, &linenum, cppfile))
1375				++linenum;
1376			continue;
1377		}
1378
1379		++linenum;
1380		/*
1381		 * skip line whose first char is a newline char
1382		 */
1383		if (buf[0] == '\n') {
1384			continue;
1385		}
1386
1387		if (err == EC_SYNTAX_OK)
1388			(void) strlcpy(lbuf, buf, RECORD_SIZE_MAX);
1389		else if (strlcat(lbuf, buf, RECORD_SIZE_MAX) >=
1390		    RECORD_SIZE_MAX) { 	/* buffer overflow */
1391			err = EC_FAILURE;
1392			break;
1393		}
1394
1395		err = check_line_syntax(cmds, lbuf);
1396		if ((err != EC_INSUFFICIENT_TOKEN) && (err != EC_SYNTAX_OK))
1397			break;
1398	}
1399
1400	if (err != EC_SYNTAX_OK) {
1401		if (cmds->verbose) {
1402			verbose_log(LOG_ERR, err_msg[err],
1403			    cmds->fname, cppfile, linenum);
1404		}
1405		return (err);
1406	}
1407
1408	/*
1409	 * check if the version has been set
1410	 */
1411	if (cmds->version_no > (float)SUPPORTED_VERSION_NUM) {
1412		if (cmds->verbose) {
1413			verbose_log(LOG_ERR, err_msg[EC_UNSUPPORTED],
1414			    cmds->fname, cppfile, linenum);
1415		}
1416		return (EC_UNSUPPORTED);
1417	}
1418
1419	/*
1420	 * check if node and endnode command mismatch
1421	 */
1422	if (cmds->inside_node_block != 0) {
1423		if (cmds->verbose) {
1424			verbose_log(LOG_ERR, err_msg[EC_NODE_MISMATCH],
1425			    cmds->fname, cppfile, linenum);
1426		}
1427		return (EC_NODE_MISMATCH);
1428	}
1429
1430	/*
1431	 * check if row and endrow command mismatch
1432	 */
1433	if (cmds->inside_row_block != 0) {
1434		if (cmds->verbose) {
1435			verbose_log(LOG_ERR, err_msg[EC_ROW_MISMATCH],
1436			    cmds->fname, cppfile, linenum);
1437		}
1438		return (EC_ROW_MISMATCH);
1439	}
1440
1441	/*
1442	 * check if table and endtable command mismatch
1443	 */
1444	if (cmds->inside_table_block != 0) {
1445		if (cmds->verbose) {
1446			verbose_log(LOG_ERR, err_msg[EC_TABLE_MISMATCH],
1447			    cmds->fname, cppfile, linenum);
1448		}
1449		return (EC_TABLE_MISMATCH);
1450	}
1451
1452	return (EC_SYNTAX_OK);
1453}
1454
1455/*
1456 * If classpath/namepath given is not found in the picl tree,
1457 * skip the whole blocks until next valid classpath or namepath
1458 */
1459static void
1460skip_to_next_valid_path(cmdbuf_t *cmds, int starting_index,
1461	picl_nodehdl_t *parent, int *last_processed_index)
1462{
1463	int	err;
1464	int	index;
1465
1466	for (index = starting_index; index < cmds->count; ++index) {
1467		switch (cmds->commands[index].type) {
1468		case TOK_CLASSPATH:
1469		case TOK_NAMEPATH:
1470			err = process_path(&cmds->commands[index], parent);
1471			if (err == PICL_SUCCESS) {
1472				*last_processed_index = index;
1473				return;
1474			}
1475		default:
1476			/* skipped this line */
1477			break;
1478		}
1479	}
1480
1481	/* reach last command  */
1482	*last_processed_index = cmds->count - 1;
1483}
1484
1485/*
1486 * Process the command buffer and return last command index and the new head of
1487 * the handle list
1488 */
1489static int
1490process_commands(cmdbuf_t *cmds, int starting_index, picl_nodehdl_t parent,
1491    int *last_processed_index)
1492{
1493	int		err;
1494	int		index;
1495	picl_nodehdl_t	rooth;
1496	picl_nodehdl_t	nodeh;
1497	command_t	*commands = cmds->commands;
1498
1499	for (index = starting_index; index < cmds->count; ++index) {
1500		switch (commands[index].type) {
1501		case TOK_CLASSPATH:
1502		case TOK_NAMEPATH:
1503			err = process_path(&commands[index], &rooth);
1504			if (err != PICL_SUCCESS) {
1505				index++;
1506				(void) skip_to_next_valid_path(cmds, index,
1507				    &rooth, &index);
1508			}
1509			parent = rooth;
1510			continue;
1511		case TOK_NODE:
1512			err = process_node(&commands[index], parent, &nodeh);
1513			if (err == PICL_SUCCESS) {
1514				index++;
1515				err = process_commands(cmds, index, nodeh,
1516				    &index);
1517			}
1518			break;
1519		case TOK_ENDNODE:
1520			*last_processed_index = index;
1521			return (PICL_SUCCESS);
1522		case TOK_PROP:
1523			err =  process_prop(cmds, &commands[index], parent);
1524			break;
1525		case TOK_REFPROP:
1526			err = process_refprop(cmds, &commands[index], parent);
1527			/* no reference node */
1528			if (err == PICL_NOTNODE) {
1529				err = PICL_SUCCESS;	/* discard prop */
1530				/* discard row by setting nproph = 0 */
1531				if (cmds->inside_row_block)
1532					cmds->commands[cmds->current_row]
1533					    .rowcmd_nproph = 0;
1534			}
1535			break;
1536		case TOK_REFNODE:
1537			err =  process_refnode(&commands[index], parent);
1538			break;
1539		case TOK_TABLE:
1540			cmds->inside_table_block = 1;
1541			err = process_table(&commands[index], parent);
1542			cmds->current_tbl = index;
1543			break;
1544		case TOK_ENDTABLE:
1545			cmds->inside_table_block = 0;
1546			cmds->current_tbl = 0;
1547			break;
1548		case TOK_ROW:
1549			cmds->inside_row_block = 1;
1550			err = process_row(&commands[index]);
1551			cmds->current_row = index;
1552			break;
1553		case TOK_ENDROW:
1554			err = process_endrow(cmds);
1555			cmds->inside_row_block = 0;
1556			cmds->current_row = 0;
1557			break;
1558		case TOK_VERBOSE:
1559			err = process_verbose(cmds, &commands[index]);
1560			break;
1561		default:	/* won't reach here */
1562			err =  PICL_FAILURE;
1563			break;
1564		}
1565
1566		if ((err != PICL_SUCCESS) && (err != PICL_PROPEXISTS)) {
1567			*last_processed_index = index;
1568			return (err);
1569		}
1570	}
1571
1572	/* reach last command */
1573	*last_processed_index = cmds->count - 1;
1574	return (PICL_SUCCESS);
1575}
1576
1577/*
1578 * clean up the commands buffer
1579 */
1580static void
1581clean_up(cmdbuf_t *cmds)
1582{
1583	int	cmd_index;
1584
1585	for (cmd_index = 0; cmd_index < cmds->count; cmd_index++) {
1586		switch (cmds->commands[cmd_index].type) {
1587		case TOK_CLASSPATH:
1588		case TOK_NAMEPATH:
1589			free_path(&cmds->commands[cmd_index]);
1590			break;
1591		case TOK_NODE:
1592			free_node(&cmds->commands[cmd_index]);
1593			break;
1594		case TOK_PROP:
1595			free_prop(&cmds->commands[cmd_index]);
1596			break;
1597		case TOK_REFPROP:
1598			free_refprop(&cmds->commands[cmd_index]);
1599			break;
1600		case TOK_REFNODE:
1601			free_refnode(&cmds->commands[cmd_index]);
1602			break;
1603		case TOK_TABLE:
1604			free_table(&cmds->commands[cmd_index]);
1605			break;
1606		case TOK_ENDTABLE:
1607		case TOK_ROW:
1608		case TOK_ENDROW:
1609		case TOK_ENDNODE:
1610		case TOK_VERBOSE:
1611		default:
1612			break;
1613		}
1614	}
1615	if (cmds->commands)
1616		free(cmds->commands);
1617}
1618
1619/*
1620 * Parse the configuration file and create nodes/properties under nh
1621 *
1622 * It checks the syntax first.  If there is any syntax error,
1623 * it returns 1 and won't continue processing the file to add nodes or props.
1624 *
1625 * If any error happens during command processing, all nodes
1626 * and properties just created will be deleted, i.e. undo
1627 * commands which have been processed.  It returns 1.
1628 *
1629 * If success, return 0.
1630 */
1631int
1632picld_pluginutil_parse_config_file(picl_nodehdl_t nh, const char *filename)
1633{
1634	FILE		*ifp;
1635	int		last_processed_index;
1636	int		err;
1637	cmdbuf_t	*cmds;
1638
1639	/* set correct locale for use inside pluginutil */
1640	setlocale(LC_ALL, "C");
1641
1642	/*
1643	 * Initialize the command buffer
1644	 */
1645
1646	cmds = malloc(sizeof (*cmds));
1647	if (cmds == NULL) {
1648		setlocale(LC_ALL, "");
1649		return (1);
1650	}
1651
1652	memset(cmds, 0, sizeof (cmdbuf_t));
1653
1654	cmds->fname = filename;
1655
1656	ifp = fopen(filename, "r");
1657	if (ifp == NULL) {
1658		setlocale(LC_ALL, "");
1659		free(cmds);
1660		return (1);
1661	}
1662
1663	/*
1664	 * check the syntax of the configuration file
1665	 */
1666	err = check_conffile_syntax(cmds, ifp);
1667
1668	(void) fclose(ifp);
1669
1670	if (err != EC_SYNTAX_OK) {
1671		clean_up(cmds);
1672		free(cmds);
1673		setlocale(LC_ALL, "");
1674		return (1);
1675	}
1676
1677	/*
1678	 * Process the commands
1679	 */
1680	err = process_commands(cmds, STARTING_INDEX, nh, &last_processed_index);
1681
1682	/*
1683	 * If any PICL error, remove the newly created node/prop
1684	 * handles from the PICL tree.
1685	 */
1686	if (err != PICL_SUCCESS) {
1687		undo_commands(cmds, last_processed_index);
1688		if (cmds->verbose)
1689			verbose_log(LOG_ERR, err_msg[EC_PICL_ERR], filename,
1690			    err);
1691	}
1692
1693	clean_up(cmds);
1694	free(cmds);
1695
1696	/* reset the locale */
1697	setlocale(LC_ALL, "");
1698
1699	return ((err == PICL_SUCCESS) ? 0 : 1);
1700}
1701