xref: /netbsd-src/sbin/iscsictl/iscsic_parse.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: iscsic_parse.c,v 1.2 2011/10/30 18:40:06 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Wasabi Systems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "iscsic_globals.h"
33 
34 #include <ctype.h>
35 #include <assert.h>
36 
37 /*
38  * get_address:
39  *    Get an address specification that may include port and group tag.
40  *
41  *    Parameter:
42  *       portal   The portal address
43  *       str      The parameter string to scan
44  *
45  *    Aborts app on error.
46  */
47 
48 STATIC void
49 get_address(iscsi_portal_address_t * portal, char *str, char *arg)
50 {
51 	char *sp, *sp2;
52 	int val;
53 
54 	if (!str || !*str)
55 		arg_error(arg, "Address is missing");
56 
57 	/* is there a port? don't check inside square brackets (IPv6 addr) */
58 	for (sp = str + 1, val = 0; *sp && (*sp != ':' || val); sp++) {
59 		if (*sp == '[')
60 			val = 1;
61 		else if (*sp == ']')
62 			val = 0;
63 	}
64 
65 	/* */
66 	if (*sp) {
67 		for (sp2 = sp + 1; *sp2 && *sp2 != ':'; sp2++);
68 		/* if there's a second colon, assume it's an unbracketed IPv6 address */
69 		if (!*sp2) {
70 			/* truncate source, that's the address */
71 			*sp++ = '\0';
72 			if (sscanf(sp, "%d", &val) != 1)
73 				arg_error(arg, "Bad address format: Expected port number");
74 			if (val < 0 || val > 0xffff)
75 				arg_error(arg, "Bad address format: Port number out of range");
76 			portal->port = (uint16_t) val;
77 		}
78 		/* is there a group tag? */
79 		for (; isdigit((unsigned char)*sp); sp++);
80 		if (*sp && *sp != ',')
81 			arg_error(arg, "Bad address format: Extra character(s) '%c'", *sp);
82 	} else
83 		for (sp = str + 1; *sp && *sp != ','; sp++);
84 
85 	if (*sp) {
86 		if (sscanf(sp + 1, "%d", &val) != 1)
87 			arg_error(arg, "Bad address format: Expected group tag");
88 		if (val < 0 || val > 0xffff)
89 			arg_error(arg, "Bad address format: Group tag out of range");
90 		portal->group_tag = (uint16_t) val;
91 		/* truncate source, that's the address */
92 		*sp = '\0';
93 	}
94 	/* only check length, don't verify correct format (too many possibilities) */
95 	if (strlen(str) >= sizeof(portal->address))
96 		arg_error(arg, "Bad address format: Address string too long");
97 
98 	strlcpy((char *)portal->address, str, sizeof(portal->address));
99 }
100 
101 
102 
103 /*
104  * get_short_int:
105  *    Get a short integer.
106  *
107  *    Parameter:
108  *       sp       The parameter string to scan
109  *       arg      The associated option argument (for error message)
110  *       name     The argument name
111  *
112  *    Returns given integer, aborts app on error.
113  */
114 
115 STATIC uint16_t
116 get_short_int(char *sp, char *arg, const char *name)
117 {
118 	int val;
119 
120 	if (!sp || !*sp)
121 		arg_error(arg, "%s is missing", name);
122 
123 	if (!sscanf(sp, "%d", &val))
124 		arg_error(arg, "Expected integer %s", name);
125 	if (val < 0 || val > 0xffff)
126 		arg_error(arg, "%s out of range", name);
127 
128 	return (uint16_t) val;
129 }
130 
131 
132 /*
133  * get_dsl:
134  *    Get MaxRecvDataSegmentLength
135  *
136  *    Parameter:
137  *       sp       The parameter string to scan
138  *       arg      The associated option argument (for error message)
139  *
140  *    Returns given integer, aborts app on error.
141  */
142 
143 STATIC uint32_t
144 get_dsl(char *sp, char *arg)
145 {
146 	int val;
147 
148 	if (!sp || !*sp)
149 		arg_error(arg, "Missing MaxRecvDataSegmentLength");
150 	if (!sscanf(sp, "%d", &val))
151 		arg_error(arg, "Integer MaxRecvDataSegmentLength expected");
152 	if (val < 512 || val > 0xffffff)
153 		arg_error(arg, "MaxRecvDataSegmentLength out of range");
154 
155 	return (uint32_t) val;
156 }
157 
158 
159 /*
160  * get_str:
161  *    Get a string.
162  *
163  *    Parameter:
164  *       dest     The destination string
165  *       sp       The parameter string to scan
166  *       arg      The associated option argument (for error message)
167  *       name     The argument name
168  *
169  *    Aborts app on error.
170  */
171 
172 STATIC void
173 get_str(char *dest, char *sp, char *arg, const char *name)
174 {
175 
176 	if (!sp || !*sp)
177 		arg_error(arg, "%s is missing", name);
178 	if (strlen(sp) >= ISCSI_STRING_LENGTH)
179 		arg_error(arg, "%s is too long", name);
180 
181 	strlcpy(dest, sp, ISCSI_STRING_LENGTH);
182 }
183 
184 /*
185  * cl_get_target:
186  *    Get a target address specification that may include name, address, port,
187  *    and group tag, with address/port/tag possibly repeated.
188  *
189  *    Parameter:
190  *       ptarg       pointer to hold the resulting add target request parameter
191  *       argc, argv  program parameters (shifted)
192  *       nreq        target name is required if TRUE
193  *
194  *    Returns:    0 if there is no target, else the size of the allocated
195  *                  request.
196  *                Aborts app on bad parameter or mem allocation error.
197  */
198 
199 int
200 cl_get_target(iscsid_add_target_req_t ** ptarg, int argc, char **argv, int nreq)
201 {
202 	iscsid_add_target_req_t *targ;
203 	char *sp;
204 	size_t num, len, name;
205 	int i, p;
206 
207 	/* count number of addreses first, so we know how much memory to allocate */
208 	for (i = (int)(num = name = 0); i < argc; i++) {
209 		if (!argv[i] || argv[i][0] != '-')
210 			continue;
211 		if (argv[i][1] == 'a')
212 			num++;
213 		if (argv[i][1] == 'n')
214 			name++;
215 	}
216 
217 	if (!name && nreq)
218 		return 0;
219 
220 	len = sizeof(iscsid_add_target_req_t) +
221 		num * sizeof(iscsi_portal_address_t);
222 
223 	if (NULL == (targ = calloc(1, len)))
224 		gen_error("Can't allocate %zu bytes of memory", len);
225 
226 	*ptarg = targ;
227 	p = -1;
228 
229 	for (i = 0; i < argc; i++) {
230 		if (!argv[i] || argv[i][0] != '-')
231 			continue;
232 
233 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
234 
235 		switch (argv[i][1]) {
236 		case 'n':				/* target name */
237 			get_str((char *)targ->TargetName, sp, argv[i], "Target name");
238 			break;
239 
240 		case 'a':				/* target address */
241 			get_address(&targ->portal[++p], sp, argv[i]);
242 			break;
243 
244 		case 'p':				/* port */
245 			assert(p >= 0);
246 			targ->portal[p].port = get_short_int(sp, argv[i], "Port");
247 			break;
248 
249 		case 'g':				/* group tag */
250 			assert(p >= 0);
251 			targ->portal[p].group_tag = get_short_int(sp, argv[i],
252 														"Group tag");
253 			break;
254 
255 		default:
256 			continue;
257 		}
258 		if (!argv[i][2])
259 			argv[i + 1] = NULL;
260 
261 		argv[i] = NULL;
262 	}
263 	targ->num_portals = p + 1;
264 
265 	DEB(9, ("cl_get_target returns %zu\n", len));
266 	return (int)len;
267 }
268 
269 
270 /*
271  * cl_get_isns:
272  *    Get an iSNS server address specification that may include name, address
273  *    and port.
274  *
275  *    Parameter:
276  *       srv         add_isns_server request parameter
277  *       argc, argv  program parameters (shifted)
278  *
279  *    Returns:    0 on error, 1 if OK.
280  */
281 
282 int
283 cl_get_isns(iscsid_add_isns_server_req_t * srv, int argc, char **argv)
284 {
285 	iscsi_portal_address_t addr;
286 	char *sp;
287 	int i, found;
288 
289 	(void) memset(&addr, 0x0, sizeof(addr));
290 	found = FALSE;
291 
292 	for (i = 0; i < argc; i++) {
293 		if (!argv[i] || argv[i][0] != '-')
294 			continue;
295 
296 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
297 
298 		switch (argv[i][1]) {
299 		case 'N':				/* symbolic name */
300 			get_str((char *)srv->name, sp, argv[i], "Server name");
301 			break;
302 
303 		case 'a':				/* target address */
304 			get_address(&addr, sp, argv[i]);
305 			found = TRUE;
306 			break;
307 
308 		case 'p':				/* port */
309 			addr.port = get_short_int(sp, argv[i], "Port");
310 			break;
311 
312 		default:
313 			continue;
314 		}
315 		if (!argv[i][2]) {
316 			argv[i + 1] = NULL;
317 		}
318 		argv[i] = NULL;
319 	}
320 
321 	strlcpy((char *)srv->address, (char *)addr.address, sizeof(srv->address));
322 	srv->port = addr.port;
323 
324 	return found;
325 }
326 
327 
328 /*
329  * cl_get_auth_opts:
330  *    Get authentication options.
331  *
332  *    Parameter:
333  *          auth        authentication parameters
334  *          argc, argv  program parameters (shifted)
335  *
336  *    Returns:    0 if there are no authorization options, 1 otherwise.
337  *                Aborts app on bad parameter.
338  */
339 
340 int
341 cl_get_auth_opts(iscsid_set_target_authentication_req_t *auth,
342 				 int argc, char **argv)
343 {
344 	int n, i, found;
345 	char *sp;
346 
347 	found = FALSE;
348 	memset(auth, 0, sizeof(*auth));
349 
350 	for (i = 0; i < argc; i++) {
351 		if (!argv[i] || argv[i][0] != '-') {
352 			continue;
353 		}
354 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
355 
356 		switch (argv[i][1]) {
357 		case 't':				/* authentication type */
358 			if (!sp || !*sp)
359 				arg_error(argv[i], "Missing authentication type");
360 			n = 0;
361 			while (*sp) {
362 				switch (*sp) {
363 				case 'n':		/* no authentication */
364 					auth->auth_info.auth_type[n] = ISCSI_AUTH_None;
365 					break;
366 				case 'c':		/* CHAP authentication */
367 					auth->auth_info.auth_type[n] = ISCSI_AUTH_CHAP;
368 					break;
369 				case 'C':		/* Mutual CHAP authentication */
370 					auth->auth_info.auth_type[n] = ISCSI_AUTH_CHAP;
371 					auth->auth_info.mutual_auth = 1;
372 					break;
373 				default:
374 					arg_error(argv[i], "Bad authentication type '%c'", *sp);
375 				}
376 				sp++;
377 				n++;
378 			}
379 			auth->auth_info.auth_number = n;
380 			break;
381 
382 		case 'u':				/* user name */
383 			get_str((char *)auth->user_name, sp, argv[i], "User name");
384 			break;
385 
386 		case 's':				/* secret */
387 			get_str((char *)auth->password, sp, argv[i], "Secret");
388 			break;
389 
390 		case 'S':				/* target secret */
391 			get_str((char *)auth->target_password, sp, argv[i], "Target secret");
392 			break;
393 
394 		default:
395 			continue;
396 		}
397 		if (!argv[i][2])
398 			argv[i + 1] = NULL;
399 
400 		argv[i] = NULL;
401 		found = TRUE;
402 	}
403 	DEB(9, ("cl_get_auth_opts returns %d\n", found));
404 	return found;
405 }
406 
407 
408 /*
409  * cl_get_target_opts:
410  *    Get session/connection options.
411  *
412  *    Parameter:
413  *          opt         target options
414  *          argc, argv  program parameters (shifted)
415  *
416  *    Returns:    0 if there are no target options, 1 otherwise.
417  *                Aborts app on bad parameter.
418  */
419 
420 int
421 cl_get_target_opts(iscsid_get_set_target_options_t * opt, int argc, char **argv)
422 {
423 	int i, found;
424 	char *sp;
425 
426 	found = FALSE;
427 	memset(opt, 0, sizeof(*opt));
428 
429 	for (i = 0; i < argc; i++) {
430 		if (!argv[i] || argv[i][0] != '-')
431 			continue;
432 
433 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
434 
435 		switch (argv[i][1]) {
436 		case 'h':				/* Header Digest */
437 			opt->HeaderDigest = ISCSI_DIGEST_CRC32C;
438 			opt->is_present.HeaderDigest = 1;
439 			break;
440 
441 		case 'd':				/* Data Digest */
442 			opt->DataDigest = ISCSI_DIGEST_CRC32C;
443 			opt->is_present.DataDigest = 1;
444 			break;
445 
446 		case 'w':				/* Time 2 Wait */
447 			opt->DefaultTime2Wait = get_short_int(sp, argv[i], "Time to wait");
448 			opt->is_present.DefaultTime2Wait = 1;
449 			if (!argv[i][2])
450 				argv[i + 1] = NULL;
451 			break;
452 
453 		case 'r':				/* Time 2 Retain */
454 			opt->DefaultTime2Retain = get_short_int(sp, argv[i],
455 													"Time to retain");
456 			opt->is_present.DefaultTime2Retain = 1;
457 			if (!argv[i][2])
458 				argv[i + 1] = NULL;
459 			break;
460 
461 		case 'e':				/* Error Recovery Level */
462 			opt->ErrorRecoveryLevel = get_short_int(sp, argv[i],
463 													"ErrorRecoveryLevel");
464 			opt->is_present.ErrorRecoveryLevel = 1;
465 			if (!argv[i][2])
466 				argv[i + 1] = NULL;
467 			break;
468 
469 		case 'l':				/* Data Segment Length */
470 			opt->MaxRecvDataSegmentLength = get_dsl(sp, argv[i]);
471 			opt->is_present.MaxRecvDataSegmentLength = 1;
472 			if (!argv[i][2])
473 				argv[i + 1] = NULL;
474 			break;
475 
476 		default:
477 			continue;
478 		}
479 		argv[i] = NULL;
480 		found = TRUE;
481 	}
482 	DEB(9, ("cl_get_target_opts returns %d\n", found));
483 	return found;
484 }
485 
486 
487 /*
488  * cl_get_portal:
489  *    Get a portal address specification that may include address, port,
490  *    and group tag, plus portal options.
491  *
492  *    Parameter:
493  *          port        add portal request parameter
494  *          argc, argv  program parameters (shifted)
495  *
496  *    Returns:    FALSE if there is no portal, else TRUE.
497  *                Aborts app on bad parameter or mem allocation error.
498  */
499 
500 int
501 cl_get_portal(iscsid_add_portal_req_t * port, int argc, char **argv)
502 {
503 	char *sp;
504 	int i, found;
505 	iscsid_portal_options_t *opt = &port->options;
506 
507 	found = FALSE;
508 	memset(port, 0, sizeof(*port));
509 
510 	for (i = 0; i < argc; i++) {
511 		if (!argv[i] || argv[i][0] != '-')
512 			continue;
513 
514 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
515 
516 		switch (argv[i][1]) {
517 		case 'a':				/* target address */
518 			get_address(&port->portal, sp, argv[i]);
519 			found = TRUE;
520 			break;
521 
522 		case 'p':				/* port */
523 			port->portal.port = get_short_int(sp, argv[i], "Port");
524 			break;
525 
526 		case 'g':				/* group tag */
527 			port->portal.group_tag = get_short_int(sp, argv[i], "Group tag");
528 			break;
529 
530 		case 'h':				/* Header Digest */
531 			opt->HeaderDigest = ISCSI_DIGEST_CRC32C;
532 			opt->is_present.HeaderDigest = 1;
533 			break;
534 
535 		case 'd':				/* Data Digest */
536 			opt->DataDigest = ISCSI_DIGEST_CRC32C;
537 			opt->is_present.DataDigest = 1;
538 			break;
539 
540 		case 'l':				/* Data Segment Length */
541 			opt->MaxRecvDataSegmentLength = get_dsl(sp, argv[i]);
542 			opt->is_present.MaxRecvDataSegmentLength = 1;
543 			if (!argv[i][2])
544 				argv[i + 1] = NULL;
545 			break;
546 
547 		default:
548 			continue;
549 		}
550 		if (!argv[i][2])
551 			argv[i + 1] = NULL;
552 
553 		argv[i] = NULL;
554 	}
555 	DEB(9, ("cl_get_portal returns %d\n", found));
556 	return found;
557 }
558 
559 
560 /*
561  * cl_get_id:
562  *    Get an identifier (symbolic or numeric)
563  *
564  *    Parameter:
565  *          ident       the parameter identifier character
566  *          sid         the ID
567  *          argc, argv  program parameters (shifted)
568  *
569  *    Returns:    0 if there is no ID, 1 otherwise.
570  *                Aborts app on bad parameter.
571  */
572 
573 int
574 cl_get_id(char ident, iscsid_sym_id_t * sid, int argc, char **argv)
575 {
576 	int i, found;
577 	char *sp;
578 
579 	found = FALSE;
580 	memset(sid, 0, sizeof(*sid));
581 
582 	for (i = 0; i < argc && !found; i++) {
583 		if (!argv[i] || argv[i][0] != '-')
584 			continue;
585 
586 		if (argv[i][1] == ident) {
587 			sp = (argv[i][2]) ? &argv[i][2] :
588 				((i + 1 < argc) ? argv[i + 1] : NULL);
589 
590 			if (!sp || !*sp)
591 				arg_error(argv[i], "Missing ID");
592 			if (strlen(sp) >= ISCSI_STRING_LENGTH)
593 				arg_error(argv[i], "ID String too long");
594 			if (!sscanf(sp, "%d", &sid->id))
595 				strlcpy((char *)sid->name, sp, sizeof(sid->name));
596 			else if (!sid->id)
597 				arg_error(argv[i], "Invalid ID");
598 
599 			if (!argv[i][2])
600 				argv[i + 1] = NULL;
601 
602 			argv[i] = NULL;
603 			found = TRUE;
604 		}
605 	}
606 	DEB(9, ("cl_get_id returns %d\n", found));
607 	return found;
608 }
609 
610 
611 /*
612  * cl_get_symname:
613  *    Get a symbolic name
614  *
615  *    Parameter:
616  *          sn          the name
617  *          argc, argv  program parameters (shifted)
618  *
619  *    Returns:    0 if there is no symbolic name, 1 otherwise.
620  *                Aborts app on bad parameter.
621  */
622 
623 int
624 cl_get_symname(uint8_t * sn, int argc, char **argv)
625 {
626 	int i, found;
627 	char *sp;
628 
629 	found = FALSE;
630 	*sn = '\0';
631 
632 	for (i = 0; i < argc && !found; i++) {
633 		if (!argv[i] || argv[i][0] != '-')
634 			continue;
635 
636 		if (argv[i][1] == 'N') {
637 			sp = (argv[i][2]) ? &argv[i][2]
638 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
639 
640 			if (!sp || !*sp)
641 				arg_error(argv[i], "Symbolic name missing");
642 			if (isdigit((unsigned char)*sp))
643 				arg_error(argv[i], "Symbolic name must not be numeric");
644 			if (strlen(sp) >= ISCSI_STRING_LENGTH)
645 				arg_error(argv[i], "Symbolic name too long");
646 
647 			strlcpy((char *)sn, sp, ISCSI_STRING_LENGTH);
648 
649 			if (!argv[i][2])
650 				argv[i + 1] = NULL;
651 
652 			argv[i] = NULL;
653 			found = TRUE;
654 		}
655 	}
656 	DEB(9, ("cl_get_symname returns %d\n", found));
657 	return found;
658 }
659 
660 
661 /*
662  * cl_get_string:
663  *    Get a string value
664  *
665  *    Parameter:
666  *          ident       the parameter identifier character
667  *          pstr        the result string
668  *          argc, argv  program parameters (shifted)
669  *
670  *    Returns:    0 if there is no string, 1 otherwise.
671  *                Aborts app on bad parameter.
672  */
673 
674 int
675 cl_get_string(char ident, char *pstr, int argc, char **argv)
676 {
677 	int i, found;
678 	char *sp;
679 
680 	found = FALSE;
681 	*pstr = '\0';
682 
683 	for (i = 0; i < argc && !found; i++) {
684 		if (!argv[i] || argv[i][0] != '-')
685 			continue;
686 
687 		if (argv[i][1] == ident) {
688 			sp = (argv[i][2]) ? &argv[i][2]
689 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
690 
691 			get_str(pstr, sp, argv[i], "String");
692 
693 			if (!argv[i][2])
694 				argv[i + 1] = NULL;
695 
696 			argv[i] = NULL;
697 			found = TRUE;
698 		}
699 	}
700 	DEB(9, ("cl_get_string returns %d\n", found));
701 	return found;
702 }
703 
704 
705 /*
706  * cl_get_opt:
707  *    Get an option with no value
708  *
709  *    Parameter:
710  *          ident       the parameter identifier character
711  *          argc, argv  program parameters (shifted)
712  *
713  *    Returns:    0 if the option was not found, 1 otherwise.
714  *                Aborts app on bad parameter.
715  */
716 
717 int
718 cl_get_opt(char ident, int argc, char **argv)
719 {
720 	int i, found;
721 
722 	found = FALSE;
723 
724 	for (i = 0; i < argc && !found; i++) {
725 		if (!argv[i] || argv[i][0] != '-')
726 			continue;
727 
728 		if (argv[i][1] == ident) {
729 			argv[i] = NULL;
730 			found = TRUE;
731 		}
732 	}
733 	DEB(9, ("cl_get_opt returns %d\n", found));
734 	return found;
735 }
736 
737 
738 /*
739  * cl_get_char:
740  *    Get an option with a character value
741  *
742  *    Parameter:
743  *          ident       the parameter identifier character
744  *          argc, argv  program parameters (shifted)
745  *
746  *    Returns:    The option character (0 if not found).
747  *                Aborts app on bad parameter.
748  */
749 
750 char
751 cl_get_char(char ident, int argc, char **argv)
752 {
753 	int i, found;
754 	char *sp;
755 	char ch = 0;
756 
757 	found = FALSE;
758 
759 	for (i = 0; i < argc && !found; i++) {
760 		if (!argv[i] || argv[i][0] != '-')
761 			continue;
762 
763 		if (argv[i][1] == ident) {
764 			sp = (argv[i][2]) ? &argv[i][2]
765 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
766 
767 			if (!sp || !*sp)
768 				arg_error(argv[i], "Option character missing");
769 			if (strlen(sp) > 1)
770 				arg_error(argv[i], "Option invalid");
771 			ch = *sp;
772 
773 			if (!argv[i][2])
774 				argv[i + 1] = NULL;
775 
776 			argv[i] = NULL;
777 			found = TRUE;
778 		}
779 	}
780 	DEB(9, ("cl_get_charopt returns %c\n", ch));
781 
782 	return ch;
783 }
784 
785 
786 /*
787  * cl_get_int:
788  *    Get an option with an integer value
789  *
790  *    Parameter:
791  *          ident       the parameter identifier character
792  *          argc, argv  program parameters (shifted)
793  *
794  *    Returns:    The option value (0 if not found).
795  *                Aborts app on bad parameter.
796  */
797 
798 int
799 cl_get_int(char ident, int argc, char **argv)
800 {
801 	int i, found;
802 	char *sp;
803 	int val = 0;
804 
805 	found = FALSE;
806 
807 	for (i = 0; i < argc && !found; i++) {
808 		if (!argv[i] || argv[i][0] != '-')
809 			continue;
810 
811 		if (argv[i][1] == ident) {
812 			sp = (argv[i][2]) ? &argv[i][2]
813 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
814 
815 			if (!sp || !*sp)
816 				arg_error(argv[i], "Option value missing");
817 			if (!sscanf(sp, "%i", &val))
818 				arg_error(argv[i], "Integer expected");
819 
820 			if (!argv[i][2])
821 				argv[i + 1] = NULL;
822 
823 			argv[i] = NULL;
824 			found = TRUE;
825 		}
826 	}
827 	DEB(9, ("cl_get_int returns %d\n", val));
828 
829 	return val;
830 }
831 
832 
833 /*
834  * cl_get_uint:
835  *    Get an option with a positive integer value
836  *
837  *    Parameter:
838  *          ident       the parameter identifier character
839  *          argc, argv  program parameters (shifted)
840  *
841  *    Returns:    The option value (-1 if not found).
842  *                Aborts app on bad parameter.
843  */
844 
845 #if 0
846 int
847 cl_get_uint(char ident, int argc, char **argv)
848 {
849 	int i, found;
850 	char *sp;
851 	int val = -1;
852 
853 	found = FALSE;
854 
855 	for (i = 0; i < argc && !found; i++) {
856 		if (!argv[i] || argv[i][0] != '-')
857 			continue;
858 
859 		if (argv[i][1] == ident) {
860 			sp = (argv[i][2]) ? &argv[i][2]
861 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
862 
863 			if (!sp || !*sp)
864 				arg_error(argv[i], "Option value missing");
865 			if (!sscanf(sp, "%i", &val))
866 				arg_error(argv[i], "Positive integer expected");
867 
868 			if (!argv[i][2])
869 				argv[i + 1] = NULL;
870 
871 			argv[i] = NULL;
872 			found = TRUE;
873 		}
874 	}
875 	DEB(9, ("cl_get_uint returns %d\n", val));
876 
877 	return val;
878 }
879 #endif
880 
881 
882 /*
883  * cl_get_longlong:
884  *    Get an option with a 64-bit value
885  *
886  *    Parameter:
887  *          ident       the parameter identifier character
888  *          argc, argv  program parameters (shifted)
889  *
890  *    Returns:    The option value (0 if not found).
891  *                Aborts app on bad parameter.
892  */
893 
894 uint64_t
895 cl_get_longlong(char ident, int argc, char **argv)
896 {
897 	int i, found;
898 	char *sp;
899 	uint64_t val = 0;
900 
901 	found = FALSE;
902 
903 	for (i = 0; i < argc && !found; i++) {
904 		if (!argv[i] || argv[i][0] != '-')
905 			continue;
906 
907 		if (argv[i][1] == ident) {
908 			sp = (argv[i][2]) ? &argv[i][2]
909 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
910 
911 			if (!sp || !*sp)
912 				arg_error(argv[i], "Option value missing");
913 			if (!sscanf(sp, "%qi", (long long *)(void *)&val))
914 				arg_error(argv[i], "Integer expected");
915 
916 			if (!argv[i][2])
917 				argv[i + 1] = NULL;
918 
919 			argv[i] = NULL;
920 			found = TRUE;
921 		}
922 	}
923 	DEB(9, ("cl_get_longlong returns %qd\n", val));
924 
925 	return val;
926 }
927