199ebb4caSwyllys /*
299ebb4caSwyllys  * CDDL HEADER START
399ebb4caSwyllys  *
499ebb4caSwyllys  * The contents of this file are subject to the terms of the
599ebb4caSwyllys  * Common Development and Distribution License (the "License").
699ebb4caSwyllys  * You may not use this file except in compliance with the License.
799ebb4caSwyllys  *
899ebb4caSwyllys  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
999ebb4caSwyllys  * or http://www.opensolaris.org/os/licensing.
1099ebb4caSwyllys  * See the License for the specific language governing permissions
1199ebb4caSwyllys  * and limitations under the License.
1299ebb4caSwyllys  *
1399ebb4caSwyllys  * When distributing Covered Code, include this CDDL HEADER in each
1499ebb4caSwyllys  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1599ebb4caSwyllys  * If applicable, add the following below this CDDL HEADER, with the
1699ebb4caSwyllys  * fields enclosed by brackets "[]" replaced with your own identifying
1799ebb4caSwyllys  * information: Portions Copyright [yyyy] [name of copyright owner]
1899ebb4caSwyllys  *
1999ebb4caSwyllys  * CDDL HEADER END
2099ebb4caSwyllys  *
2130a5e8faSwyllys  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2299ebb4caSwyllys  * Use is subject to license terms.
2399ebb4caSwyllys  */
2499ebb4caSwyllys 
2599ebb4caSwyllys #include <stdio.h>
2699ebb4caSwyllys #include <strings.h>
2799ebb4caSwyllys #include <ctype.h>
2899ebb4caSwyllys #include <libgen.h>
2999ebb4caSwyllys #include <libintl.h>
3099ebb4caSwyllys 
3199ebb4caSwyllys #include <libxml/tree.h>
3299ebb4caSwyllys #include <libxml/parser.h>
3399ebb4caSwyllys 
3499ebb4caSwyllys #include <kmfapiP.h>
3599ebb4caSwyllys #include "util.h"
3699ebb4caSwyllys 
37431deaa0Shylee 
3899ebb4caSwyllys /* Supporting structures and global variables for getopt_av(). */
3999ebb4caSwyllys typedef struct	av_opts_s {
4099ebb4caSwyllys 	int		shortnm;	/* short name character */
4199ebb4caSwyllys 	char		*longnm;	/* long name string, NOT terminated */
4299ebb4caSwyllys 	int		longnm_len;	/* length of long name string */
4399ebb4caSwyllys 	boolean_t	has_arg;	/* takes optional argument */
4499ebb4caSwyllys } av_opts;
4599ebb4caSwyllys 
4699ebb4caSwyllys static av_opts		*opts_av = NULL;
4799ebb4caSwyllys static const char	*_save_optstr = NULL;
4899ebb4caSwyllys static int		_save_numopts = 0;
4999ebb4caSwyllys int			optind_av = 1;
5099ebb4caSwyllys char			*optarg_av = NULL;
5199ebb4caSwyllys 
5299ebb4caSwyllys void
free_policy_list(POLICY_LIST * plist)5399ebb4caSwyllys free_policy_list(POLICY_LIST *plist)
5499ebb4caSwyllys {
5599ebb4caSwyllys 	POLICY_LIST *n = plist, *old;
5699ebb4caSwyllys 
5799ebb4caSwyllys 	if (plist == NULL)
5899ebb4caSwyllys 		return;
5999ebb4caSwyllys 
6099ebb4caSwyllys 	while (n != NULL) {
6199ebb4caSwyllys 		old = n;
6230a5e8faSwyllys 		kmf_free_policy_record(&n->plc);
6399ebb4caSwyllys 		n = n->next;
6499ebb4caSwyllys 		free(old);
6599ebb4caSwyllys 	}
6699ebb4caSwyllys 	plist = NULL;
6799ebb4caSwyllys }
6899ebb4caSwyllys 
6999ebb4caSwyllys int
load_policies(char * file,POLICY_LIST ** policy_list)7099ebb4caSwyllys load_policies(char *file, POLICY_LIST **policy_list)
7199ebb4caSwyllys {
7299ebb4caSwyllys 	int rv = KC_OK;
7399ebb4caSwyllys 	KMF_RETURN kmfrv = KMF_OK;
7499ebb4caSwyllys 	POLICY_LIST *newitem, *plist = NULL;
7599ebb4caSwyllys 	xmlParserCtxtPtr ctxt;
7699ebb4caSwyllys 	xmlDocPtr doc = NULL;
7799ebb4caSwyllys 	xmlNodePtr cur, node;
7899ebb4caSwyllys 
7999ebb4caSwyllys 	/* Create a parser context */
8099ebb4caSwyllys 	ctxt = xmlNewParserCtxt();
8199ebb4caSwyllys 	if (ctxt == NULL)
8299ebb4caSwyllys 		return (KMF_ERR_POLICY_DB_FORMAT);
8399ebb4caSwyllys 
8499ebb4caSwyllys 	/* Read the policy DB and verify it against the schema. */
8599ebb4caSwyllys 	doc = xmlCtxtReadFile(ctxt, file, NULL,
8699ebb4caSwyllys 	    XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
8799ebb4caSwyllys 	if (doc == NULL || ctxt->valid == 0) {
8899ebb4caSwyllys 		kmfrv = KMF_ERR_POLICY_DB_FORMAT;
8999ebb4caSwyllys 		goto end;
9099ebb4caSwyllys 	}
9199ebb4caSwyllys 
9299ebb4caSwyllys 	cur = xmlDocGetRootElement(doc);
9399ebb4caSwyllys 	if (cur == NULL) {
9499ebb4caSwyllys 		kmfrv = KMF_ERR_POLICY_DB_FORMAT;
9599ebb4caSwyllys 		goto end;
9699ebb4caSwyllys 	}
9799ebb4caSwyllys 
9899ebb4caSwyllys 	node = cur->xmlChildrenNode;
9999ebb4caSwyllys 	while (node != NULL) {
10099ebb4caSwyllys 		char *c;
10199ebb4caSwyllys 		/*
10299ebb4caSwyllys 		 * Search for the policy that matches the given name.
10399ebb4caSwyllys 		 */
10499ebb4caSwyllys 		if (!xmlStrcmp((const xmlChar *)node->name,
10530a5e8faSwyllys 		    (const xmlChar *)KMF_POLICY_ELEMENT)) {
10699ebb4caSwyllys 			/* Check the name attribute */
10799ebb4caSwyllys 			c = (char *)xmlGetProp(node,
10830a5e8faSwyllys 			    (const xmlChar *)KMF_POLICY_NAME_ATTR);
10999ebb4caSwyllys 
11099ebb4caSwyllys 			/* If a match, parse the rest of the data */
11199ebb4caSwyllys 			if (c != NULL) {
11299ebb4caSwyllys 				xmlFree(c);
11399ebb4caSwyllys 				newitem = malloc(sizeof (POLICY_LIST));
11499ebb4caSwyllys 				if (newitem != NULL) {
11599ebb4caSwyllys 					(void) memset(newitem, 0,
11630a5e8faSwyllys 					    sizeof (POLICY_LIST));
11799ebb4caSwyllys 					kmfrv = parsePolicyElement(node,
11830a5e8faSwyllys 					    &newitem->plc);
11999ebb4caSwyllys 				} else {
12099ebb4caSwyllys 					kmfrv = KMF_ERR_MEMORY;
12199ebb4caSwyllys 					goto end;
12299ebb4caSwyllys 				}
12399ebb4caSwyllys 				/* add to linked list */
12499ebb4caSwyllys 				if (plist == NULL) {
12599ebb4caSwyllys 					plist = newitem;
12699ebb4caSwyllys 				} else {
12799ebb4caSwyllys 					POLICY_LIST *n = plist;
12899ebb4caSwyllys 					while (n->next != NULL)
12999ebb4caSwyllys 						n = n->next;
13099ebb4caSwyllys 
13199ebb4caSwyllys 					n->next = newitem;
13299ebb4caSwyllys 					newitem->next = NULL;
13399ebb4caSwyllys 				}
13499ebb4caSwyllys 			}
13599ebb4caSwyllys 		}
13699ebb4caSwyllys 		node = node->next;
13799ebb4caSwyllys 	}
13899ebb4caSwyllys 
13999ebb4caSwyllys end:
14099ebb4caSwyllys 	if (ctxt != NULL)
14199ebb4caSwyllys 		xmlFreeParserCtxt(ctxt);
14299ebb4caSwyllys 
14399ebb4caSwyllys 	if (doc != NULL)
14499ebb4caSwyllys 		xmlFreeDoc(doc);
14599ebb4caSwyllys 
14699ebb4caSwyllys 	if (kmfrv != KMF_OK) {
14799ebb4caSwyllys 		free_policy_list(plist);
14899ebb4caSwyllys 		rv = KC_ERR_LOADDB;
14999ebb4caSwyllys 	} else {
15099ebb4caSwyllys 		*policy_list = plist;
15199ebb4caSwyllys 	}
15299ebb4caSwyllys 
15399ebb4caSwyllys 	return (rv);
15499ebb4caSwyllys }
15599ebb4caSwyllys 
15699ebb4caSwyllys /*
15799ebb4caSwyllys  * Return 0 if there is any error in the input string.
15899ebb4caSwyllys  */
15999ebb4caSwyllys uint16_t
parseKUlist(char * kustring)16099ebb4caSwyllys parseKUlist(char *kustring)
16199ebb4caSwyllys {
16299ebb4caSwyllys 	uint16_t cur_bit;
16399ebb4caSwyllys 	uint16_t kubits = 0;
16499ebb4caSwyllys 	char *p;
16599ebb4caSwyllys 
16699ebb4caSwyllys 	p = strtok(kustring, ",");
16799ebb4caSwyllys 	while (p != NULL) {
16830a5e8faSwyllys 		cur_bit = kmf_string_to_ku(p);
16999ebb4caSwyllys 		if (cur_bit == 0) {
17099ebb4caSwyllys 			kubits = 0;
17199ebb4caSwyllys 			break;
17299ebb4caSwyllys 		}
17399ebb4caSwyllys 		kubits |= cur_bit;
17499ebb4caSwyllys 		p = strtok(NULL, ",");
17599ebb4caSwyllys 	}
17699ebb4caSwyllys 
17799ebb4caSwyllys 	return (kubits);
17899ebb4caSwyllys }
17999ebb4caSwyllys 
18099ebb4caSwyllys static void
addToEKUList(KMF_EKU_POLICY * ekus,KMF_OID * newoid)18199ebb4caSwyllys addToEKUList(KMF_EKU_POLICY *ekus, KMF_OID *newoid)
18299ebb4caSwyllys {
18399ebb4caSwyllys 	if (newoid != NULL && ekus != NULL) {
18499ebb4caSwyllys 		ekus->eku_count++;
18599ebb4caSwyllys 		ekus->ekulist = realloc(
18630a5e8faSwyllys 		    ekus->ekulist, ekus->eku_count * sizeof (KMF_OID));
18799ebb4caSwyllys 		if (ekus->ekulist != NULL) {
18899ebb4caSwyllys 			ekus->ekulist[ekus->eku_count-1] = *newoid;
18999ebb4caSwyllys 		}
19099ebb4caSwyllys 	}
19199ebb4caSwyllys }
19299ebb4caSwyllys 
19399ebb4caSwyllys int
parseEKUNames(char * ekulist,KMF_POLICY_RECORD * plc)19499ebb4caSwyllys parseEKUNames(char *ekulist, KMF_POLICY_RECORD *plc)
19599ebb4caSwyllys {
19699ebb4caSwyllys 	int rv = KC_OK;
19799ebb4caSwyllys 	char *p;
19899ebb4caSwyllys 	KMF_OID *newoid;
19999ebb4caSwyllys 	KMF_EKU_POLICY *ekus = &plc->eku_set;
20099ebb4caSwyllys 
20199ebb4caSwyllys 	if (ekulist == NULL || !strlen(ekulist))
20299ebb4caSwyllys 		return (0);
20399ebb4caSwyllys 
20499ebb4caSwyllys 	/*
20599ebb4caSwyllys 	 * The list should be comma separated list of EKU Names.
20699ebb4caSwyllys 	 */
20799ebb4caSwyllys 	p = strtok(ekulist, ",");
20899ebb4caSwyllys 
20999ebb4caSwyllys 	/* If no tokens found, then maybe its just a single EKU value */
21099ebb4caSwyllys 	if (p == NULL) {
21130a5e8faSwyllys 		newoid = kmf_ekuname_to_oid(ekulist);
21299ebb4caSwyllys 		if (newoid != NULL) {
21399ebb4caSwyllys 			addToEKUList(ekus, newoid);
21499ebb4caSwyllys 			free(newoid);
21599ebb4caSwyllys 		} else {
21699ebb4caSwyllys 			rv = KC_ERR_USAGE;
21799ebb4caSwyllys 		}
21899ebb4caSwyllys 	}
21999ebb4caSwyllys 
22099ebb4caSwyllys 	while (p != NULL) {
22130a5e8faSwyllys 		newoid = kmf_ekuname_to_oid(p);
22299ebb4caSwyllys 		if (newoid != NULL) {
22399ebb4caSwyllys 			addToEKUList(ekus, newoid);
22499ebb4caSwyllys 			free(newoid);
22599ebb4caSwyllys 		} else {
22699ebb4caSwyllys 			rv = KC_ERR_USAGE;
22799ebb4caSwyllys 			break;
22899ebb4caSwyllys 		}
22999ebb4caSwyllys 		p = strtok(NULL, ",");
23099ebb4caSwyllys 	}
23199ebb4caSwyllys 
23299ebb4caSwyllys 	if (rv != KC_OK)
23330a5e8faSwyllys 		kmf_free_eku_policy(ekus);
23499ebb4caSwyllys 
23599ebb4caSwyllys 	return (rv);
23699ebb4caSwyllys }
23799ebb4caSwyllys 
23899ebb4caSwyllys int
parseEKUOIDs(char * ekulist,KMF_POLICY_RECORD * plc)23999ebb4caSwyllys parseEKUOIDs(char *ekulist, KMF_POLICY_RECORD *plc)
24099ebb4caSwyllys {
24199ebb4caSwyllys 	int rv = KC_OK;
24299ebb4caSwyllys 	char *p;
243*6b35cb3cSRichard PALO 	KMF_OID newoid = { 0, NULL };
24499ebb4caSwyllys 	KMF_EKU_POLICY *ekus = &plc->eku_set;
24599ebb4caSwyllys 
24699ebb4caSwyllys 	if (ekulist == NULL || !strlen(ekulist))
24799ebb4caSwyllys 		return (0);
24899ebb4caSwyllys 
24999ebb4caSwyllys 	/*
25099ebb4caSwyllys 	 * The list should be comma separated list of EKU Names.
25199ebb4caSwyllys 	 */
25299ebb4caSwyllys 	p = strtok(ekulist, ",");
25399ebb4caSwyllys 	if (p == NULL) {
25430a5e8faSwyllys 		if (kmf_string_to_oid(ekulist, &newoid) == KMF_OK) {
25530a5e8faSwyllys 			addToEKUList(ekus, &newoid);
25699ebb4caSwyllys 		} else {
25799ebb4caSwyllys 			rv = KC_ERR_USAGE;
25899ebb4caSwyllys 		}
25999ebb4caSwyllys 	}
26099ebb4caSwyllys 
26199ebb4caSwyllys 	while (p != NULL && rv == 0) {
26230a5e8faSwyllys 		if (kmf_string_to_oid(p, &newoid) == KMF_OK) {
26330a5e8faSwyllys 			addToEKUList(ekus, &newoid);
26499ebb4caSwyllys 		} else {
26599ebb4caSwyllys 			rv = KC_ERR_USAGE;
26699ebb4caSwyllys 			break;
26799ebb4caSwyllys 		}
26899ebb4caSwyllys 		p = strtok(NULL, ",");
26999ebb4caSwyllys 	}
27099ebb4caSwyllys 
27199ebb4caSwyllys 	if (rv != KC_OK)
27230a5e8faSwyllys 		kmf_free_eku_policy(ekus);
27399ebb4caSwyllys 
27499ebb4caSwyllys 	return (rv);
27599ebb4caSwyllys }
27699ebb4caSwyllys 
27799ebb4caSwyllys int
get_boolean(char * arg)27899ebb4caSwyllys get_boolean(char *arg)
27999ebb4caSwyllys {
28099ebb4caSwyllys 	if (arg == NULL)
28199ebb4caSwyllys 		return (-1);
28299ebb4caSwyllys 	if (strcasecmp(arg, "true") == 0)
28399ebb4caSwyllys 		return (1);
28499ebb4caSwyllys 	if (strcasecmp(arg, "false") == 0)
28599ebb4caSwyllys 		return (0);
28699ebb4caSwyllys 	return (-1);
28799ebb4caSwyllys }
28899ebb4caSwyllys 
28999ebb4caSwyllys /*
29099ebb4caSwyllys  * This function processes the input string.  It removes the beginning
29199ebb4caSwyllys  * and ending blank's first, makes a copy of the resulting string and
29299ebb4caSwyllys  * return it.
29399ebb4caSwyllys  *
29499ebb4caSwyllys  * This function returns NULL, if there is an error in the
29599ebb4caSwyllys  * input string or when the system is out of memory.  The output
29699ebb4caSwyllys  * "err_flag" argument will record the error code, if it is not NULL.
29799ebb4caSwyllys  */
29899ebb4caSwyllys char *
get_string(char * str,int * err_flag)29999ebb4caSwyllys get_string(char *str, int *err_flag)
30099ebb4caSwyllys {
30199ebb4caSwyllys 	char *p;
30299ebb4caSwyllys 	int len, i;
30399ebb4caSwyllys 	char *retstr = NULL;
30499ebb4caSwyllys 
30599ebb4caSwyllys 	if (str == NULL) {
30699ebb4caSwyllys 		if (err_flag != NULL)
30799ebb4caSwyllys 			*err_flag = KC_ERR_USAGE;
30899ebb4caSwyllys 		return (NULL);
30999ebb4caSwyllys 	}
31099ebb4caSwyllys 
31199ebb4caSwyllys 	/* Remove beginning whitespace */
31299ebb4caSwyllys 	p = str;
31399ebb4caSwyllys 	while (p != NULL && isspace(*p))
31499ebb4caSwyllys 		p++;
31599ebb4caSwyllys 
31699ebb4caSwyllys 	if (p == NULL) {
31799ebb4caSwyllys 		if (err_flag != NULL)
31899ebb4caSwyllys 			*err_flag = KC_ERR_USAGE;
31999ebb4caSwyllys 		return (NULL);
32099ebb4caSwyllys 	}
32199ebb4caSwyllys 
32299ebb4caSwyllys 	/* Remove the trailing blanks */
32399ebb4caSwyllys 	len = strlen(p);
32499ebb4caSwyllys 	while (len > 0 && isspace(p[len-1]))
32599ebb4caSwyllys 		len--;
32699ebb4caSwyllys 
32799ebb4caSwyllys 	if (len == 0) {
32899ebb4caSwyllys 		if (err_flag != NULL)
32999ebb4caSwyllys 			*err_flag = KC_ERR_USAGE;
33099ebb4caSwyllys 		return (NULL);
33199ebb4caSwyllys 	}
33299ebb4caSwyllys 
33399ebb4caSwyllys 	/* Check if there is any non-printable character */
33499ebb4caSwyllys 	i = 0;
33599ebb4caSwyllys 	while (i < len) {
33699ebb4caSwyllys 		if (isprint(p[i]))
33799ebb4caSwyllys 			i++;
33899ebb4caSwyllys 		else {
33999ebb4caSwyllys 			if (err_flag != NULL)
34099ebb4caSwyllys 				*err_flag = KC_ERR_USAGE;
34199ebb4caSwyllys 			return (NULL);
34299ebb4caSwyllys 		}
34399ebb4caSwyllys 	}
34499ebb4caSwyllys 
34599ebb4caSwyllys 	/* Make a copy of the string and return it */
34699ebb4caSwyllys 	retstr = malloc(len + 1);
34799ebb4caSwyllys 	if (retstr == NULL) {
34899ebb4caSwyllys 		if (err_flag != NULL)
34999ebb4caSwyllys 			*err_flag = KC_ERR_MEMORY;
35099ebb4caSwyllys 		return (NULL);
35199ebb4caSwyllys 	}
35299ebb4caSwyllys 
35399ebb4caSwyllys 	if (err_flag != NULL)
35499ebb4caSwyllys 		*err_flag = KC_OK;
35599ebb4caSwyllys 
35699ebb4caSwyllys 	(void) strncpy(retstr, p, len);
35799ebb4caSwyllys 	retstr[len] = '\0';
35899ebb4caSwyllys 	return (retstr);
35999ebb4caSwyllys }
36099ebb4caSwyllys 
36199ebb4caSwyllys /*
36299ebb4caSwyllys  * Breaks out the getopt-style option string into a structure that can be
36399ebb4caSwyllys  * traversed later for calls to getopt_av().  Option string is NOT altered,
36499ebb4caSwyllys  * but the struct fields point to locations within option string.
36599ebb4caSwyllys  */
36699ebb4caSwyllys static int
populate_opts(char * optstring)36799ebb4caSwyllys populate_opts(char *optstring)
36899ebb4caSwyllys {
36999ebb4caSwyllys 	int		i;
37099ebb4caSwyllys 	av_opts		*temp;
37199ebb4caSwyllys 	char		*marker;
37299ebb4caSwyllys 
37399ebb4caSwyllys 	if (optstring == NULL || *optstring == '\0')
37499ebb4caSwyllys 		return (0);
37599ebb4caSwyllys 
37699ebb4caSwyllys 	/*
37799ebb4caSwyllys 	 * This tries to imitate getopt(3c) Each option must conform to:
37899ebb4caSwyllys 	 * <short name char> [ ':' ] [ '(' <long name string> ')' ]
37999ebb4caSwyllys 	 * If long name is missing, the short name is used for long name.
38099ebb4caSwyllys 	 */
38199ebb4caSwyllys 	for (i = 0; *optstring != '\0'; i++) {
38299ebb4caSwyllys 		if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
38399ebb4caSwyllys 		    realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
38499ebb4caSwyllys 			free(opts_av);
38599ebb4caSwyllys 			opts_av = NULL;
38699ebb4caSwyllys 			return (0);
38799ebb4caSwyllys 		} else
38899ebb4caSwyllys 			opts_av = (av_opts *)temp;
38999ebb4caSwyllys 
39099ebb4caSwyllys 		marker = optstring;		/* may need optstring later */
39199ebb4caSwyllys 
39299ebb4caSwyllys 		opts_av[i].shortnm = *marker++;	/* set short name */
39399ebb4caSwyllys 
39499ebb4caSwyllys 		if (*marker == ':') {		/* check for opt arg */
39599ebb4caSwyllys 			marker++;
39699ebb4caSwyllys 			opts_av[i].has_arg = B_TRUE;
39799ebb4caSwyllys 		}
39899ebb4caSwyllys 
39999ebb4caSwyllys 		if (*marker == '(') {		/* check and set long name */
40099ebb4caSwyllys 			marker++;
40199ebb4caSwyllys 			opts_av[i].longnm = marker;
40299ebb4caSwyllys 			opts_av[i].longnm_len = strcspn(marker, ")");
40399ebb4caSwyllys 			optstring = marker + opts_av[i].longnm_len + 1;
40499ebb4caSwyllys 		} else {
40599ebb4caSwyllys 			/* use short name option character */
40699ebb4caSwyllys 			opts_av[i].longnm = optstring;
40799ebb4caSwyllys 			opts_av[i].longnm_len = 1;
40899ebb4caSwyllys 			optstring = marker;
40999ebb4caSwyllys 		}
41099ebb4caSwyllys 	}
41199ebb4caSwyllys 
41299ebb4caSwyllys 	return (i);
41399ebb4caSwyllys }
41499ebb4caSwyllys 
41599ebb4caSwyllys /*
41699ebb4caSwyllys  * getopt_av() is very similar to getopt(3c) in that the takes an option
41799ebb4caSwyllys  * string, compares command line arguments for matches, and returns a single
41899ebb4caSwyllys  * letter option when a match is found.  However, getopt_av() differs from
41999ebb4caSwyllys  * getopt(3c) by allowing both longname options and values be found
42099ebb4caSwyllys  * on the command line.
42199ebb4caSwyllys  */
42299ebb4caSwyllys int
getopt_av(int argc,char * const * argv,const char * optstring)42399ebb4caSwyllys getopt_av(int argc, char * const *argv, const char *optstring)
42499ebb4caSwyllys {
42599ebb4caSwyllys 	int	i;
42699ebb4caSwyllys 	int	len;
42799ebb4caSwyllys 
42899ebb4caSwyllys 	if (optind_av >= argc)
42999ebb4caSwyllys 		return (EOF);
43099ebb4caSwyllys 
43199ebb4caSwyllys 	/* First time or when optstring changes from previous one */
43299ebb4caSwyllys 	if (_save_optstr != optstring) {
43399ebb4caSwyllys 		if (opts_av != NULL)
43430a5e8faSwyllys 			free(opts_av);
43599ebb4caSwyllys 		opts_av = NULL;
43699ebb4caSwyllys 		_save_optstr = optstring;
43799ebb4caSwyllys 		_save_numopts = populate_opts((char *)optstring);
43899ebb4caSwyllys 	}
43999ebb4caSwyllys 
44099ebb4caSwyllys 	for (i = 0; i < _save_numopts; i++) {
44199ebb4caSwyllys 		if (strcmp(argv[optind_av], "--") == 0) {
44299ebb4caSwyllys 			optind_av++;
44399ebb4caSwyllys 			break;
44499ebb4caSwyllys 		}
44599ebb4caSwyllys 
44699ebb4caSwyllys 		len = strcspn(argv[optind_av], "=");
44799ebb4caSwyllys 
44899ebb4caSwyllys 		if (len == opts_av[i].longnm_len && strncmp(argv[optind_av],
44999ebb4caSwyllys 		    opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
45099ebb4caSwyllys 			/* matched */
45199ebb4caSwyllys 			if (!opts_av[i].has_arg) {
45299ebb4caSwyllys 				optind_av++;
45399ebb4caSwyllys 				return (opts_av[i].shortnm);
45499ebb4caSwyllys 			}
45599ebb4caSwyllys 
45699ebb4caSwyllys 			/* needs optarg */
45799ebb4caSwyllys 			if (argv[optind_av][len] == '=') {
45899ebb4caSwyllys 				optarg_av = &(argv[optind_av][len+1]);
45999ebb4caSwyllys 				optind_av++;
46099ebb4caSwyllys 				return (opts_av[i].shortnm);
46199ebb4caSwyllys 			}
46299ebb4caSwyllys 
46399ebb4caSwyllys 			optarg_av = NULL;
46499ebb4caSwyllys 			optind_av++;
46599ebb4caSwyllys 			return ((int)'?');
46699ebb4caSwyllys 		}
46799ebb4caSwyllys 	}
46899ebb4caSwyllys 
46999ebb4caSwyllys 	return (EOF);
47099ebb4caSwyllys }
47199ebb4caSwyllys