Home | History | Annotate | Line # | Download | only in ifconfig
parse.c revision 1.1
      1 #include <err.h>
      2 #include <errno.h>
      3 #include <limits.h>
      4 #include <netdb.h>
      5 #include <stddef.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include <arpa/inet.h>
     11 #include <sys/param.h>
     12 #include <net/if.h>
     13 #include <netatalk/at.h>
     14 #include <netiso/iso.h>
     15 
     16 #include "env.h"
     17 #include "parse.h"
     18 #include "util.h"
     19 
     20 #define dbg_warnx(__fmt, ...)	/* empty */
     21 
     22 static int parser_default_init(struct parser *);
     23 static int pbranch_init(struct parser *);
     24 static int pkw_init(struct parser *);
     25 
     26 static int pterm_match(const struct parser *, const struct match *,
     27     struct match *, int, const char *);
     28 
     29 static int paddr_match(const struct parser *, const struct match *,
     30     struct match *, int, const char *);
     31 
     32 static int pbranch_match(const struct parser *, const struct match *,
     33     struct match *, int, const char *);
     34 
     35 static int piface_match(const struct parser *, const struct match *,
     36     struct match *, int, const char *);
     37 
     38 static int pstr_match(const struct parser *, const struct match *,
     39     struct match *, int, const char *);
     40 
     41 static int pinteger_match(const struct parser *, const struct match *,
     42     struct match *, int, const char *);
     43 
     44 static int pkw_match(const struct parser *, const struct match *,
     45     struct match *, int, const char *);
     46 
     47 const struct parser_methods pterm_methods = {
     48 	  .pm_match = pterm_match
     49 	, .pm_init = NULL
     50 };
     51 
     52 const struct parser_methods pstr_methods = {
     53 	  .pm_match = pstr_match
     54 	, .pm_init = parser_default_init
     55 };
     56 
     57 const struct parser_methods pinteger_methods = {
     58 	  .pm_match = pinteger_match
     59 	, .pm_init = parser_default_init
     60 };
     61 
     62 const struct parser_methods paddr_methods = {
     63 	  .pm_match = paddr_match
     64 	, .pm_init = parser_default_init
     65 };
     66 
     67 const struct parser_methods piface_methods = {
     68 	  .pm_match = piface_match
     69 	, .pm_init = parser_default_init
     70 };
     71 
     72 const struct parser_methods pbranch_methods = {
     73 	  .pm_match = pbranch_match
     74 	, .pm_init = pbranch_init
     75 };
     76 
     77 const struct parser_methods pkw_methods = {
     78 	  .pm_match = pkw_match
     79 	, .pm_init = pkw_init
     80 };
     81 
     82 static int
     83 match_setenv(const struct match *im, struct match *om, const char *key,
     84     prop_object_t o)
     85 {
     86 	if (im == NULL)
     87 		om->m_env = prop_dictionary_create();
     88 	else
     89 		om->m_env = prop_dictionary_copy(im->m_env);
     90 
     91 	if (om->m_env == NULL)
     92 		goto delobj;
     93 
     94 	if (key != NULL && !prop_dictionary_set(om->m_env, key, o))
     95 		goto deldict;
     96 
     97 	if (o != NULL)
     98 		prop_object_release((prop_object_t)o);
     99 
    100 	return 0;
    101 deldict:
    102 	prop_object_release((prop_object_t)om->m_env);
    103 	om->m_env = NULL;
    104 delobj:
    105 	prop_object_release((prop_object_t)o);
    106 	errno = ENOMEM;
    107 	return -1;
    108 }
    109 
    110 int
    111 pstr_match(const struct parser *p, const struct match *im, struct match *om,
    112     int argidx, const char *arg)
    113 {
    114 	prop_object_t o;
    115 	const struct pstr *ps = (const struct pstr *)p;
    116 	uint8_t buf[128];
    117 	int len;
    118 
    119 	len = (int)sizeof(buf);
    120 	if (get_string(arg, NULL, buf, &len) == NULL) {
    121 		errno = EINVAL;
    122 		return -1;
    123 	}
    124 
    125 	o = (prop_object_t)prop_data_create_data(buf, len);
    126 
    127 	if (o == NULL) {
    128 		errno = ENOMEM;
    129 		return -1;
    130 	}
    131 
    132 	if (match_setenv(im, om, ps->ps_key, o) == -1)
    133 		return -1;
    134 
    135 	om->m_argidx = argidx;
    136 	om->m_parser = p;
    137 	om->m_nextparser = p->p_nextparser;
    138 
    139 	return 0;
    140 }
    141 
    142 int
    143 pinteger_match(const struct parser *p, const struct match *im, struct match *om,
    144     int argidx, const char *arg)
    145 {
    146 	prop_object_t o;
    147 	const struct pinteger *pi = (const struct pinteger *)p;
    148 	char *end;
    149 	int64_t val;
    150 
    151 	val = strtoimax(arg, &end, pi->pi_base);
    152 	if ((val == INTMAX_MIN || val == INTMAX_MAX) && errno == ERANGE)
    153 		return -1;
    154 
    155 	if (*end != '\0') {
    156 		errno = EINVAL;
    157 		return -1;
    158 	}
    159 
    160 	if (val < pi->pi_min || val > pi->pi_max) {
    161 		errno = ERANGE;
    162 		return -1;
    163 	}
    164 
    165 	o = (prop_object_t)prop_number_create_integer(val);
    166 
    167 	if (o == NULL) {
    168 		errno = ENOMEM;
    169 		return -1;
    170 	}
    171 
    172 	if (match_setenv(im, om, pi->pi_key, o) == -1)
    173 		return -1;
    174 
    175 	om->m_argidx = argidx;
    176 	om->m_parser = p;
    177 	om->m_nextparser = p->p_nextparser;
    178 
    179 	return 0;
    180 }
    181 
    182 static int
    183 paddr_match(const struct parser *p, const struct match *im, struct match *om,
    184     int argidx, const char *arg0)
    185 {
    186 	unsigned int net, node;
    187 	int nread;
    188 	union {
    189 		struct sockaddr sa;
    190 		struct sockaddr_at sat;
    191 		struct sockaddr_iso siso;
    192 		struct sockaddr_in sin;
    193 	} u;
    194 	const struct paddr *pa = (const struct paddr *)p;
    195 	prop_data_t d;
    196 	prop_object_t o;
    197 	prop_number_t num;
    198 	int af, rc;
    199 	struct paddr_prefix *pfx, *mask;
    200 	const struct sockaddr *sa = NULL;
    201 	struct addrinfo hints, *result = NULL;
    202 	char *arg, *end, *plen = NULL, *servname0;
    203 	const char *servname;
    204 	long prefixlen = 0;
    205 	size_t len;
    206 
    207 	if (arg0 == NULL)
    208 		return -1;
    209 
    210 	if (pa->pa_activator != NULL &&
    211 	    prop_dictionary_get(im->m_env, pa->pa_activator) == NULL)
    212 		return -1;
    213 
    214 	if (pa->pa_deactivator != NULL &&
    215 	    prop_dictionary_get(im->m_env, pa->pa_deactivator) != NULL)
    216 		return -1;
    217 
    218 	num = (prop_number_t)prop_dictionary_get(im->m_env, "af");
    219 
    220 	af = (num != NULL) ? (int)prop_number_integer_value(num) : AF_UNSPEC;
    221 
    222 	switch (af) {
    223 	case AF_UNSPEC:
    224 	case AF_INET:
    225 	case AF_INET6:
    226 		if ((arg = strdup(arg0)) == NULL)
    227 			return -1;
    228 
    229 		servname0 = arg;
    230 		(void)strsep(&servname0, ",");
    231 		servname = (servname0 == NULL) ? "0" : servname0;
    232 
    233 		if (pa->pa_maskkey == NULL)
    234 			;
    235 		else if ((plen = strrchr(arg, '/')) != NULL)
    236 			*plen++ = '\0';
    237 
    238 		memset(&u, 0, sizeof(u));
    239 
    240 		memset(&hints, 0, sizeof(hints));
    241 
    242 		hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
    243 		hints.ai_family = af;
    244 		hints.ai_socktype = SOCK_DGRAM;
    245 
    246 		for (;;) {
    247 			rc = getaddrinfo(arg, servname, &hints, &result);
    248 			if (rc == 0) {
    249 				if (result->ai_next == NULL)
    250 					sa = result->ai_addr;
    251 				else
    252 					errno = EMLINK;
    253 				break;
    254 			} else if ((hints.ai_flags & AI_NUMERICHOST) != 0 &&
    255 			    (af == AF_INET || af == AF_UNSPEC) &&
    256 			    inet_aton(arg, &u.sin.sin_addr) == 1) {
    257 				u.sin.sin_family = AF_INET;
    258 				u.sin.sin_len = sizeof(u.sin);
    259 				sa = &u.sa;
    260 				break;
    261 			} else if ((hints.ai_flags & AI_NUMERICHOST) == 0 ||
    262 				 rc != EAI_NONAME) {
    263 				errno = ENOENT;
    264 				break;
    265 			}
    266 			hints.ai_flags &= ~AI_NUMERICHOST;
    267 		}
    268 
    269 
    270 		if (plen == NULL)
    271 			prefixlen = 0;
    272 		else {
    273 			prefixlen = strtol(plen, &end, 10);
    274 			if (end != NULL && *end != '\0')
    275 				sa = NULL;
    276 		}
    277 
    278 		if (prefixlen < 0 || prefixlen >= UINT8_MAX) {
    279 			errno = ERANGE;
    280 			sa = NULL;
    281 		}
    282 		free(arg);
    283 		if (sa != NULL || af != AF_UNSPEC)
    284 			break;
    285 		/*FALLTHROUGH*/
    286 	case AF_APPLETALK:
    287 		if (sscanf(arg0, "%u.%u%n", &net, &node, &nread) == 2 &&
    288 		    net != 0 && net <= 0xffff && node != 0 && node <= 0xfe &&
    289 		    arg0[nread] == '\0') {
    290 			u.sat.sat_family = AF_APPLETALK;
    291 			u.sat.sat_len = sizeof(u.sat);
    292 			u.sat.sat_addr.s_net = htons(net);
    293 			u.sat.sat_addr.s_node = node;
    294 			sa = &u.sa;
    295 		}
    296 		if (af != AF_UNSPEC)
    297 			break;
    298 		/*FALLTHROUGH*/
    299 	case AF_ISO:
    300 		u.siso.siso_len = sizeof(u.siso);
    301 		u.siso.siso_family = AF_ISO;
    302 		/* XXX iso_addr(3) matches ANYTHING! */
    303 		u.siso.siso_addr = *iso_addr(arg0);
    304 		sa = &u.sa;
    305 		break;
    306 	}
    307 
    308 	if (sa == NULL)
    309 		return -1;
    310 
    311 	len = offsetof(struct paddr_prefix, pfx_addr) + sa->sa_len;
    312 
    313 	if ((pfx = malloc(len)) == NULL)
    314 		return -1;
    315 
    316 #if 0
    317 	{
    318 		int i;
    319 
    320 		for (i = 0; i < sa->sa_len; i++)
    321 			printf(" %02x", ((const uint8_t *)sa)[i]);
    322 		printf("\n");
    323 	}
    324 #endif
    325 
    326 	pfx->pfx_len = (uint8_t)prefixlen;
    327 	memcpy(&pfx->pfx_addr, sa, sa->sa_len);
    328 	af = sa->sa_family;
    329 
    330 	if (result != NULL)
    331 		freeaddrinfo(result);
    332 
    333 	o = (prop_object_t)prop_data_create_data(pfx, len);
    334 
    335 	free(pfx);
    336 
    337 	if (o == NULL)
    338 		return -1;
    339 
    340 	if (match_setenv(im, om, pa->pa_addrkey, o) == -1)
    341 		return -1;
    342 
    343 	if (pa->pa_maskkey != NULL && plen != NULL) {
    344 		size_t masklen;
    345 
    346 		if ((mask = prefixlen_to_mask(af, prefixlen)) == NULL) {
    347 			err(EXIT_FAILURE, "%s: prefixlen_to_mask(%d, %ld)",
    348 			    __func__, af, prefixlen);
    349 			return -1;
    350 		}
    351 
    352 		masklen = offsetof(struct paddr_prefix, pfx_addr) +
    353 		    mask->pfx_addr.sa_len;
    354 
    355 		d = prop_data_create_data(mask, masklen);
    356 		free(mask);
    357 
    358 		if (d == NULL) {
    359 			err(EXIT_FAILURE, "%s: prop_data_create_data",
    360 			    __func__);
    361 			return -1;
    362 		}
    363 
    364 		rc = prop_dictionary_set(om->m_env, pa->pa_maskkey,
    365 		    (prop_object_t)d) ? 0 : -1;
    366 
    367 		prop_object_release((prop_object_t)d);
    368 
    369 		if (rc != 0) {
    370 			err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__);
    371 			return rc;
    372 		}
    373 	}
    374 
    375 	om->m_argidx = argidx;
    376 	om->m_parser = p;
    377 	om->m_nextparser = p->p_nextparser;
    378 	return 0;
    379 }
    380 
    381 static int
    382 pterm_match(const struct parser *p, const struct match *im,
    383     struct match *om, int argidx, const char *arg)
    384 {
    385 	const struct pterm *pt = (const struct pterm *)p;
    386 	prop_bool_t b;
    387 
    388 	if (arg != NULL) {
    389 		errno = EINVAL;
    390 		return -1;
    391 	}
    392 	b = prop_bool_create(true);
    393 
    394 	if (match_setenv(im, om, pt->pt_key, (prop_object_t)b) == -1)
    395 		return -1;
    396 
    397 	om->m_argidx = argidx;
    398 	om->m_parser = p;
    399 	om->m_nextparser = NULL;
    400 	return 0;
    401 }
    402 
    403 static int
    404 piface_match(const struct parser *p, const struct match *im,
    405     struct match *om, int argidx, const char *arg)
    406 {
    407 	const struct piface *pif = (const struct piface *)p;
    408 	prop_object_t o;
    409 
    410 	if (arg == NULL || strlen(arg) > IFNAMSIZ) {
    411 		errno = EINVAL;
    412 		return -1;
    413 	}
    414 
    415 	if ((o = (prop_object_t)prop_string_create_cstring(arg)) == NULL) {
    416 		errno = ENOMEM;
    417 		return -1;
    418 	}
    419 
    420 	if (match_setenv(im, om, pif->pif_key, o) == -1)
    421 		return -1;
    422 
    423 	om->m_argidx = argidx;
    424 	om->m_parser = p;
    425 	om->m_nextparser = p->p_nextparser;
    426 	return 0;
    427 }
    428 
    429 static void
    430 match_cleanup(struct match *dst)
    431 {
    432 	if (dst->m_env != NULL)
    433 		prop_object_release((prop_object_t)dst->m_env);
    434 	memset(dst, 0, sizeof(*dst));
    435 }
    436 
    437 static void
    438 match_copy(struct match *dst, const struct match *src)
    439 {
    440 	match_cleanup(dst);
    441 
    442 	prop_object_retain((prop_object_t)src->m_env);
    443 	*dst = *src;
    444 }
    445 
    446 static int
    447 pbranch_match(const struct parser *p, const struct match *im,
    448     struct match *om, int argidx, const char *arg)
    449 {
    450 	const struct parser *nextp;
    451 	struct branch *b;
    452 	const struct pbranch *pb = (const struct pbranch *)p;
    453 	struct match tmpm;
    454 	int nforbid = 0, nmatch = 0, rc;
    455 	parser_match_t matchfunc;
    456 
    457 	memset(&tmpm, 0, sizeof(tmpm));
    458 
    459 	SIMPLEQ_FOREACH(b, &pb->pb_branches, b_next) {
    460 		dbg_warnx("%s: b->b_nextparser %p", __func__,
    461 		    (const void *)b->b_nextparser);
    462 		nextp = b->b_nextparser;
    463 		if (nextp == NULL) {
    464 			if (arg == NULL) {
    465 				nmatch++;
    466 				match_setenv(im, om, NULL, NULL);
    467 				om->m_nextparser = NULL;
    468 				om->m_parser = p;
    469 				om->m_argidx = argidx;
    470 			}
    471 			continue;
    472 		}
    473 		matchfunc = nextp->p_methods->pm_match;
    474 		rc = (*matchfunc)(nextp, im, &tmpm, argidx, arg);
    475 		if (rc == 0) {
    476 			match_copy(om, &tmpm);
    477 			match_cleanup(&tmpm);
    478 			nmatch++;
    479 			dbg_warnx("%s: branch %s ok", __func__, nextp->p_name);
    480 			if (pb->pb_match_first)
    481 				break;
    482 		} else if (rc == 1) {
    483 			nforbid++;
    484 			if (pb->pb_match_first)
    485 				break;
    486 		} else {
    487 			dbg_warnx("%s: fail branch %s", __func__,
    488 			    nextp->p_name);
    489 		}
    490 	}
    491 	switch (nmatch) {
    492 	case 0:
    493 		errno = ENOENT;
    494 		return (nforbid == 0) ? -1 : 1;
    495 	case 1:
    496 		dbg_warnx("%s: branch ok", __func__);
    497 		return 0;
    498 	default:
    499 		match_cleanup(om);
    500 		errno = EMLINK;
    501 		return -1;
    502 	}
    503 }
    504 
    505 static int
    506 pkw_match(const struct parser *p, const struct match *im,
    507     struct match *om, int argidx, const char *arg)
    508 {
    509 	prop_object_t o = NULL;
    510 	struct kwinst *k;
    511 	union kwval *u = NULL;
    512 	const struct pkw *pk = (const struct pkw *)p;
    513 
    514 	if (arg == NULL) {
    515 		errno = EINVAL;
    516 		return -1;
    517 	}
    518 
    519 	SIMPLEQ_FOREACH(k, &pk->pk_keywords, k_next) {
    520 		if (k->k_act != NULL &&
    521 		    prop_dictionary_get(im->m_env, k->k_act) == NULL)
    522 			continue;
    523 
    524 		if (k->k_neg && arg[0] == '-' &&
    525 		    strcmp(k->k_word, arg + 1) == 0)
    526 			u = &k->k_negu;
    527 		else if (strcmp(k->k_word, arg) == 0)
    528 			u = &k->k_u;
    529 		else
    530 			continue;
    531 
    532 		if (k->k_altdeact != NULL &&
    533 		    prop_dictionary_get(im->m_env, k->k_altdeact) != NULL)
    534 			return 1;
    535 
    536 		if (k->k_deact != NULL &&
    537 		    prop_dictionary_get(im->m_env, k->k_deact) != NULL)
    538 			return 1;
    539 		break;
    540 	}
    541 	if (k == NULL) {
    542 		errno = ENOENT;
    543 		return -1;
    544 	}
    545 	switch (k->k_type) {
    546 	case KW_T_NONE:
    547 		break;
    548 	case KW_T_BOOL:
    549 		o = (prop_object_t)prop_bool_create(u->u_bool);
    550 		if (o == NULL)
    551 			goto err;
    552 		break;
    553 	case KW_T_NUM:
    554 		o = (prop_object_t)prop_number_create_integer(u->u_num);
    555 		if (o == NULL)
    556 			goto err;
    557 		break;
    558 	case KW_T_OBJ:
    559 		o = u->u_obj;
    560 		break;
    561 	case KW_T_STR:
    562 		o = (prop_object_t)prop_data_create_data_nocopy(u->u_str,
    563 		    strlen(u->u_str));
    564 		if (o == NULL)
    565 			goto err;
    566 		break;
    567 	default:
    568 		errx(EXIT_FAILURE, "unknown keyword type %d", k->k_type);
    569 	}
    570 
    571 	if (match_setenv(im, om, (o == NULL) ? NULL : k->k_key, o) == -1)
    572 		return -1;
    573 
    574 	om->m_argidx = argidx;
    575 	om->m_parser = p;
    576 	om->m_nextparser = k->k_nextparser;
    577 	om->m_exec = k->k_exec;
    578 	return 0;
    579 err:
    580 	errno = ENOMEM;
    581 	return -1;
    582 }
    583 
    584 struct paddr *
    585 paddr_create(const char *name, parser_exec_t pexec, const char *addrkey,
    586     const char *maskkey, struct parser *next)
    587 {
    588 	struct paddr *pa;
    589 
    590 	if ((pa = calloc(sizeof(*pa), 1)) == NULL)
    591 		return NULL;
    592 
    593 	pa->pa_parser.p_methods = &paddr_methods;
    594 	pa->pa_parser.p_exec = pexec;
    595 	pa->pa_parser.p_name = name;
    596 	pa->pa_parser.p_nextparser = next;
    597 
    598 	pa->pa_addrkey = addrkey;
    599 	pa->pa_maskkey = maskkey;
    600 
    601 	return pa;
    602 }
    603 
    604 struct piface *
    605 piface_create(const char *name, parser_exec_t pexec, const char *defkey,
    606     struct parser *defnext)
    607 {
    608 	struct piface *pif;
    609 
    610 	if ((pif = calloc(sizeof(*pif), 1)) == NULL)
    611 		return NULL;
    612 
    613 	pif->pif_parser.p_methods = &piface_methods;
    614 	pif->pif_parser.p_exec = pexec;
    615 	pif->pif_parser.p_name = name;
    616 	pif->pif_parser.p_nextparser = defnext;
    617 
    618 	pif->pif_key = defkey;
    619 
    620 	return pif;
    621 }
    622 
    623 int
    624 pbranch_setbranches(struct pbranch *pb, const struct branch *brs, size_t nbr)
    625 {
    626 	struct branch *b;
    627 	int i;
    628 
    629 	dbg_warnx("%s: nbr %zu", __func__, nbr);
    630 
    631 	while ((b = SIMPLEQ_FIRST(&pb->pb_branches)) != NULL) {
    632 		SIMPLEQ_REMOVE_HEAD(&pb->pb_branches, b_next);
    633 		free(b);
    634 	}
    635 
    636 	for (i = 0; i < nbr; i++) {
    637 		if ((b = malloc(sizeof(*b))) == NULL)
    638 			goto err;
    639 		*b = brs[i];
    640 		dbg_warnx("%s: b->b_nextparser %p", __func__,
    641 		    (const void *)b->b_nextparser);
    642 		SIMPLEQ_INSERT_TAIL(&pb->pb_branches, b, b_next);
    643 	}
    644 
    645 	return 0;
    646 err:
    647 	while ((b = SIMPLEQ_FIRST(&pb->pb_branches)) != NULL) {
    648 		SIMPLEQ_REMOVE_HEAD(&pb->pb_branches, b_next);
    649 		free(b);
    650 	}
    651 	return -1;
    652 }
    653 
    654 static int
    655 pbranch_init(struct parser *p)
    656 {
    657 	struct branch *b;
    658 	struct pbranch *pb = (struct pbranch *)p;
    659 	struct parser *np;
    660 
    661 	if (pb->pb_nbrinit == 0 || !SIMPLEQ_EMPTY(&pb->pb_branches))
    662 		return 0;
    663 
    664 	if (pbranch_setbranches(pb, pb->pb_brinit, pb->pb_nbrinit) == -1)
    665 		return -1;
    666 
    667 	SIMPLEQ_FOREACH(b, &pb->pb_branches, b_next) {
    668 		np = b->b_nextparser;
    669 		if (np != NULL && parser_init(np) == -1)
    670 			return -1;
    671 	}
    672 	return 0;
    673 }
    674 
    675 struct pbranch *
    676 pbranch_create(const char *name, const struct branch *brs, size_t nbr,
    677     bool match_first)
    678 {
    679 	struct pbranch *pb;
    680 
    681 	dbg_warnx("%s: nbr %zu", __func__, nbr);
    682 
    683 	if ((pb = calloc(1, sizeof(*pb))) == NULL)
    684 		return NULL;
    685 
    686 	pb->pb_parser.p_methods = &pbranch_methods;
    687 	pb->pb_parser.p_name = name;
    688 
    689 	SIMPLEQ_INIT(&pb->pb_branches);
    690 
    691 	if (pbranch_setbranches(pb, brs, nbr) == -1)
    692 		goto post_pb_err;
    693 
    694 	pb->pb_match_first = match_first;
    695 	return pb;
    696 post_pb_err:
    697 	free(pb);
    698 	return NULL;
    699 }
    700 
    701 static int
    702 parser_default_init(struct parser *p)
    703 {
    704 	struct parser *np;
    705 
    706 	np = p->p_nextparser;
    707 	if (np != NULL && parser_init(np) == -1)
    708 		return -1;
    709 
    710 	return 0;
    711 }
    712 
    713 static int
    714 pkw_setwords(struct pkw *pk, parser_exec_t defexec, const char *defkey,
    715     const struct kwinst *kws, size_t nkw, struct parser *defnext)
    716 {
    717 	struct kwinst *k;
    718 	int i;
    719 
    720 	for (i = 0; i < nkw; i++) {
    721 		if ((k = malloc(sizeof(*k))) == NULL)
    722 			goto post_pk_err;
    723 		*k = kws[i];
    724 		if (k->k_nextparser == NULL)
    725 			k->k_nextparser = defnext;
    726 		if (k->k_key == NULL)
    727 			k->k_key = defkey;
    728 		if (k->k_exec == NULL)
    729 			k->k_exec = defexec;
    730 		SIMPLEQ_INSERT_TAIL(&pk->pk_keywords, k, k_next);
    731 	}
    732 	return 0;
    733 
    734 post_pk_err:
    735 	while ((k = SIMPLEQ_FIRST(&pk->pk_keywords)) != NULL) {
    736 		SIMPLEQ_REMOVE_HEAD(&pk->pk_keywords, k_next);
    737 		free(k);
    738 	}
    739 	return -1;
    740 }
    741 
    742 static int
    743 pkw_init(struct parser *p)
    744 {
    745 	struct kwinst *k;
    746 	struct pkw *pk = (struct pkw *)p;
    747 	struct parser *np;
    748 
    749 	if (pk->pk_nkwinit == 0 || !SIMPLEQ_EMPTY(&pk->pk_keywords))
    750 		return 0;
    751 	if (pkw_setwords(pk, pk->pk_execinit, pk->pk_keyinit, pk->pk_kwinit,
    752 	    pk->pk_nkwinit, pk->pk_nextinit) == -1)
    753 		return -1;
    754 	SIMPLEQ_FOREACH(k, &pk->pk_keywords, k_next) {
    755 		np = k->k_nextparser;
    756 		if (np != NULL && parser_init(np) == -1)
    757 			return -1;
    758 	}
    759 	return 0;
    760 }
    761 
    762 struct pkw *
    763 pkw_create(const char *name, parser_exec_t defexec, const char *defkey,
    764     const struct kwinst *kws, size_t nkw, struct parser *defnext)
    765 {
    766 	struct pkw *pk;
    767 
    768 	if ((pk = calloc(1, sizeof(*pk))) == NULL)
    769 		return NULL;
    770 
    771 	pk->pk_parser.p_methods = &pkw_methods;
    772 	pk->pk_parser.p_exec = defexec;
    773 	pk->pk_parser.p_name = name;
    774 
    775 	SIMPLEQ_INIT(&pk->pk_keywords);
    776 
    777 	if (pkw_setwords(pk, defexec, defkey, kws, nkw, defnext) == -1)
    778 		goto err;
    779 
    780 	return pk;
    781 err:
    782 	free(pk);
    783 	return NULL;
    784 }
    785 
    786 int
    787 parse(int argc, char **argv, const struct parser *p0, struct match *matches,
    788     size_t *nmatch, int *narg)
    789 {
    790 	int i, rc = 0;
    791 	struct match *lastm = NULL, *m = matches;
    792 	const struct parser *p = p0;
    793 
    794 	for (i = 0; i < argc && p != NULL; i++) {
    795 		if (m - matches >= *nmatch) {
    796 			errno = EFBIG;
    797 			rc = -1;
    798 			break;
    799 		}
    800 		rc = (*p->p_methods->pm_match)(p, lastm, m, i, argv[i]);
    801 		if (rc != 0)
    802 			goto out;
    803 		p = m->m_nextparser;
    804 		lastm = m++;
    805 	}
    806 	for (; m - matches < *nmatch && p != NULL; ) {
    807 		rc = (*p->p_methods->pm_match)(p, lastm, m, i, NULL);
    808 		if (rc != 0)
    809 			break;
    810 		p = m->m_nextparser;
    811 		lastm = m++;
    812 	}
    813 out:
    814 	*nmatch = m - matches;
    815 	*narg = i;
    816 	return rc;
    817 }
    818 
    819 int
    820 matches_exec(const struct match *matches, prop_dictionary_t xenv, size_t nmatch)
    821 {
    822 	int i, rc = 0;
    823 	const struct match *m;
    824 	parser_exec_t pexec;
    825 	prop_dictionary_t d;
    826 
    827 	for (i = 0; i < nmatch; i++) {
    828 		m = &matches[i];
    829 		dbg_warnx("%s.%d: i %d", __func__, __LINE__, i);
    830 		pexec = (m->m_parser->p_exec != NULL)
    831 		    ? m->m_parser->p_exec : m->m_exec;
    832 		if (pexec == NULL)
    833 			continue;
    834 		dbg_warnx("%s.%d: m->m_parser->p_name %s", __func__, __LINE__,
    835 		    m->m_parser->p_name);
    836 		d = prop_dictionary_augment(m->m_env, xenv);
    837 		rc = (*pexec)(d, xenv);
    838 		prop_object_release((prop_object_t)d);
    839 		if (rc == -1)
    840 			break;
    841 	}
    842 	return rc;
    843 }
    844