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