Unleashed v1.4
[unleashed.git] / usr / src / cmd / isns / isnsadm / cmdparse.c
blobccad6df26c30bbdb8a64ce74e5a39d6ba29d5c20
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <libintl.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <getopt.h>
35 #include "cmdparse.h"
37 #pragma ident "@(#)cmdparse.c 1.1 07/09/21 SMI"
40 /* Usage types */
41 #define GENERAL_USAGE 1
42 #define DETAIL_USAGE 2
44 /* printable ascii character set len */
45 #define MAXOPTIONS (uint_t)('~' - '!' + 1)
48 * MAXOPTIONSTRING is the max length of the options string used in getopt and
49 * will be the printable character set + ':' for each character,
50 * providing for options with arguments. e.g. "t:Cs:hglr:"
52 #define MAXOPTIONSTRING MAXOPTIONS * 2
54 /* standard command options table to support -?, -V */
55 struct option standardCmdOptions[] = {
56 {"help", no_argument, NULL, '?'},
57 {"version", no_argument, NULL, 'V'},
58 {NULL, 0, NULL, 0}
61 /* standard subcommand options table to support -? */
62 struct option standardSubCmdOptions[] = {
63 {"help", no_argument, NULL, '?'},
64 {NULL, 0, NULL, 0}
67 /* forward declarations */
68 static int getSubcommandProps(char *, subCommandProps_t **);
69 static char *getExecBasename(char *);
70 static void usage(uint_t);
71 static void subUsage(uint_t, subCommandProps_t *);
72 static char *getLongOption(int);
73 static char *getOptionArgDesc(int);
75 /* global data */
76 static struct option *_longOptions;
77 static subCommandProps_t *_subCommandProps;
78 static optionTbl_t *_clientOptionTbl;
79 static char *commandName;
83 * input:
84 * subCommand - subcommand value
85 * output:
86 * subCommandProps - pointer to subCommandProps_t structure allocated by caller
88 * On successful return, subCommandProps contains the properties for the value
89 * in subCommand. On failure, the contents of subCommandProps is unspecified.
91 * Returns:
92 * zero on success
93 * non-zero on failure
96 static int
97 getSubcommandProps(char *subCommand, subCommandProps_t **subCommandProps)
99 subCommandProps_t *sp;
100 int len;
102 for (sp = _subCommandProps; sp->name; sp++) {
103 len = strlen(subCommand);
104 if (len == strlen(sp->name) &&
105 strncasecmp(subCommand, sp->name, len) == 0) {
106 *subCommandProps = sp;
107 return (0);
110 return (1);
114 * input:
115 * shortOption - short option character for which to return the
116 * associated long option string
118 * Returns:
119 * on success, long option name
120 * on failure, NULL
122 static char *
123 getLongOption(int shortOption)
125 struct option *op;
126 for (op = _longOptions; op->name; op++) {
127 if (shortOption == op->val) {
128 return (op->name);
131 return (NULL);
135 * input
136 * shortOption - short option character for which to return the
137 * option argument
138 * Returns:
139 * on success, argument string
140 * on failure, NULL
142 static char *
143 getOptionArgDesc(int shortOption)
145 optionTbl_t *op;
146 for (op = _clientOptionTbl; op->name; op++) {
147 if (op->val == shortOption &&
148 op->has_arg == required_argument) {
149 return (op->argDesc);
152 return (NULL);
157 * Print usage for a subcommand.
159 * input:
160 * usage type - GENERAL_USAGE, DETAIL_USAGE
161 * subcommand - pointer to subCommandProps_t structure
163 * Returns:
164 * none
167 static void
168 subUsage(uint_t usageType, subCommandProps_t *subcommand)
170 int i;
171 char *optionArgDesc;
172 char *longOpt;
174 if (usageType == GENERAL_USAGE) {
175 (void) printf("%s:\t%s %s [", gettext("Usage"), commandName,
176 subcommand->name);
178 for (i = 0; standardSubCmdOptions[i].name; i++) {
179 (void) printf("-%c", standardSubCmdOptions[i].val);
180 if (standardSubCmdOptions[i+1].name)
181 (void) printf(",");
184 (void) fprintf(stdout, "]\n");
185 return;
188 /* print subcommand usage */
189 (void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName,
190 subcommand->name);
192 /* print options if applicable */
193 if (subcommand->optionString != NULL) {
194 if (subcommand->required) {
195 (void) printf("%s", gettext("<"));
196 } else {
197 (void) printf("%s", gettext("["));
199 (void) printf("%s", gettext("OPTIONS"));
200 if (subcommand->required) {
201 (void) printf("%s ", gettext(">"));
202 } else {
203 (void) printf("%s ", gettext("]"));
207 /* print operand requirements */
208 if (!(subcommand->operand & OPERAND_NONE) &&
209 !(subcommand->operand & OPERAND_MANDATORY)) {
210 (void) printf(gettext("["));
213 if (subcommand->operand & OPERAND_MANDATORY) {
214 (void) printf(gettext("<"));
217 if (!(subcommand->operand & OPERAND_NONE)) {
218 assert(subcommand->operandDefinition);
219 (void) printf("%s", subcommand->operandDefinition);
222 if (subcommand->operand & OPERAND_MULTIPLE) {
223 (void) printf(gettext(" ..."));
226 if (subcommand->operand & OPERAND_MANDATORY) {
227 (void) printf(gettext(">"));
230 if (!(subcommand->operand & OPERAND_NONE) &&
231 !(subcommand->operand & OPERAND_MANDATORY)) {
232 (void) printf(gettext("]"));
235 /* print options for subcommand */
236 if (subcommand->optionString != NULL) {
237 (void) printf("\n\t%s:", gettext("OPTIONS"));
238 for (i = 0; i < strlen(subcommand->optionString); i++) {
239 if ((longOpt = getLongOption(
240 subcommand->optionString[i]))
241 == NULL) {
242 /* no long option exists for short option */
243 assert(0);
245 (void) printf("\n\t\t-%c, --%s ",
246 subcommand->optionString[i], longOpt);
247 optionArgDesc =
248 getOptionArgDesc(subcommand->optionString[i]);
249 if (optionArgDesc != NULL) {
250 (void) printf("<%s>", optionArgDesc);
252 if (subcommand->exclusive &&
253 strchr(subcommand->exclusive,
254 subcommand->optionString[i])) {
255 (void) printf(" (%s)", gettext("exclusive"));
259 (void) fprintf(stdout, "\n");
263 * input:
264 * type of usage statement to print
266 * Returns:
267 * return value of subUsage
269 static void
270 usage(uint_t usageType)
272 int i;
273 subCommandProps_t *sp;
275 /* print general command usage */
276 (void) printf("%s:\t%s ", gettext("Usage"), commandName);
278 for (i = 0; standardCmdOptions[i].name; i++) {
279 (void) printf("-%c", standardCmdOptions[i].val);
280 if (standardCmdOptions[i+1].name)
281 (void) printf(",");
284 if (usageType == GENERAL_USAGE) {
285 for (i = 0; standardSubCmdOptions[i].name; i++) {
286 (void) printf(",--%s", standardSubCmdOptions[i].name);
287 if (standardSubCmdOptions[i+1].name)
288 (void) printf(",");
292 (void) fprintf(stdout, "\n");
295 /* print all subcommand usage */
296 for (sp = _subCommandProps; sp->name; sp++) {
297 subUsage(usageType, sp);
302 * input:
303 * execFullName - exec name of program (argv[0])
305 * Returns:
306 * command name portion of execFullName
308 static char *
309 getExecBasename(char *execFullname)
311 char *lastSlash, *execBasename;
313 /* guard against '/' at end of command invocation */
314 for (;;) {
315 lastSlash = strrchr(execFullname, '/');
316 if (lastSlash == NULL) {
317 execBasename = execFullname;
318 break;
319 } else {
320 execBasename = lastSlash + 1;
321 if (*execBasename == '\0') {
322 *lastSlash = '\0';
323 continue;
325 break;
328 return (execBasename);
332 * cmdParse is a parser that checks syntax of the input command against
333 * various rules tables.
335 * It provides usage feedback based upon the passed rules tables by calling
336 * two usage functions, usage, subUsage
338 * When syntax is successfully validated, the associated function is called
339 * using the subcommands table functions.
341 * Syntax is as follows:
342 * command subcommand [<options>] [<operand>]
344 * There are two standard short and long options assumed:
345 * -?, --help Provides usage on a command or subcommand
346 * and stops further processing of the arguments
348 * -V, --version Provides version information on the command
349 * and stops further processing of the arguments
351 * These options are loaded by this function.
353 * input:
354 * argc, argv from main
355 * syntax rules tables (synTables_t structure)
356 * callArgs - void * passed by caller to be passed to subcommand function
358 * output:
359 * funcRet - pointer to int that holds subcommand function return value
361 * Returns:
363 * zero on successful syntax parse and function call
365 * 1 on unsuccessful syntax parse (no function has been called)
366 * This could be due to a version or help call or simply a
367 * general usage call.
369 * -1 check errno, call failed
371 * This module is not MT-safe.
375 cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs,
376 int *funcRet)
378 int getoptargc;
379 char **getoptargv;
380 int opt;
381 int operInd;
382 int i, j;
383 int len;
384 char *availOptions;
385 char *versionString;
386 char optionStringAll[MAXOPTIONSTRING + 1];
387 subCommandProps_t *subcommand;
388 cmdOptions_t cmdOptions[MAXOPTIONS + 1];
389 optionTbl_t *optionTbl;
390 struct option *lp;
391 struct option intLongOpt[MAXOPTIONS + 1];
394 * Check for NULLs on mandatory input arguments
396 * Note: longOptionTbl can be NULL in the case
397 * where there is no caller defined options
400 assert(synTable.versionString);
401 assert(synTable.subCommandPropsTbl);
402 assert(funcRet);
404 versionString = synTable.versionString;
406 /* set global command name */
407 commandName = getExecBasename(argv[0]);
409 /* Set unbuffered output */
410 setbuf(stdout, NULL);
412 /* load globals */
413 _subCommandProps = synTable.subCommandPropsTbl;
414 _clientOptionTbl = synTable.longOptionTbl;
416 /* There must be at least two arguments */
417 if (argc < 2) {
418 usage(GENERAL_USAGE);
419 return (1);
422 (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt));
425 * load standard subcommand options to internal long options table
426 * Two separate getopt_long(3C) tables are used.
428 for (i = 0; standardSubCmdOptions[i].name; i++) {
429 intLongOpt[i].name = standardSubCmdOptions[i].name;
430 intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg;
431 intLongOpt[i].flag = standardSubCmdOptions[i].flag;
432 intLongOpt[i].val = standardSubCmdOptions[i].val;
436 * copy caller's long options into internal long options table
437 * We do this for two reasons:
438 * 1) We need to use the getopt_long option structure internally
439 * 2) We need to prepend the table with the standard option
440 * for all subcommands (currently -?)
442 for (optionTbl = synTable.longOptionTbl;
443 optionTbl && optionTbl->name; optionTbl++, i++) {
444 if (i > MAXOPTIONS - 1) {
445 /* option table too long */
446 assert(0);
448 intLongOpt[i].name = optionTbl->name;
449 intLongOpt[i].has_arg = optionTbl->has_arg;
450 intLongOpt[i].flag = NULL;
451 intLongOpt[i].val = optionTbl->val;
454 /* set option table global */
455 _longOptions = &intLongOpt[0];
459 * Check for help/version request immediately following command
460 * '+' in option string ensures POSIX compliance in getopt_long()
461 * which means that processing will stop at first non-option
462 * argument.
464 while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions,
465 NULL)) != EOF) {
466 switch (opt) {
467 case '?':
469 * getopt can return a '?' when no
470 * option letters match string. Check for
471 * the 'real' '?' in optopt.
473 if (optopt == '?') {
474 usage(DETAIL_USAGE);
475 return (1);
476 } else {
477 usage(GENERAL_USAGE);
478 return (1);
480 case 'V':
481 (void) fprintf(stdout, "%s: %s %s\n",
482 commandName, gettext("Version"),
483 versionString);
484 return (1);
485 default:
486 break;
491 * subcommand is always in the second argument. If there is no
492 * recognized subcommand in the second argument, print error,
493 * general usage and then return.
495 if (getSubcommandProps(argv[1], &subcommand) != 0) {
496 (void) printf("%s: %s\n", commandName,
497 gettext("invalid subcommand"));
498 usage(GENERAL_USAGE);
499 return (1);
502 getoptargv = argv;
503 getoptargv++;
504 getoptargc = argc;
505 getoptargc -= 1;
507 (void) memset(optionStringAll, 0, sizeof (optionStringAll));
508 (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions));
510 j = 0;
512 * Build optionStringAll from long options table
514 for (lp = _longOptions; lp->name; lp++, j++) {
515 /* sanity check on string length */
516 if (j + 1 >= sizeof (optionStringAll)) {
517 /* option table too long */
518 assert(0);
520 optionStringAll[j] = lp->val;
521 if (lp->has_arg == required_argument) {
522 optionStringAll[++j] = ':';
526 i = 0;
528 * Run getopt for all arguments against all possible options
529 * Store all options/option arguments in an array for retrieval
530 * later.
532 * Once all options are retrieved, a validity check against
533 * subcommand table is performed.
535 while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll,
536 _longOptions, NULL)) != EOF) {
537 switch (opt) {
538 case '?':
539 subUsage(DETAIL_USAGE, subcommand);
540 return (1);
541 default:
542 cmdOptions[i].optval = opt;
543 if (optarg) {
544 len = strlen(optarg);
545 if (len > sizeof (cmdOptions[i].optarg)
546 - 1) {
547 (void) printf("%s: %s\n",
548 commandName,
549 gettext("option too long"));
550 errno = EINVAL;
551 return (-1);
553 (void) strncpy(cmdOptions[i].optarg,
554 optarg, len);
556 i++;
557 break;
562 * increment past last option
564 operInd = optind + 1;
567 * Check validity of given options, if any were given
570 /* get option string for this subcommand */
571 availOptions = subcommand->optionString;
573 if (cmdOptions[0].optval != 0) { /* options were input */
574 if (availOptions == NULL) { /* no options permitted */
575 (void) printf("%s: %s\n", commandName,
576 gettext("no options permitted"));
577 subUsage(DETAIL_USAGE, subcommand);
578 return (1);
580 for (i = 0; cmdOptions[i].optval; i++) {
581 /* is the option in the available option string? */
582 if (!(strchr(availOptions, cmdOptions[i].optval))) {
583 (void) printf("%s: '-%c': %s\n", commandName,
584 cmdOptions[i].optval,
585 gettext("invalid option"));
586 subUsage(DETAIL_USAGE, subcommand);
587 return (1);
589 /* Check for exclusive options */
590 } else if (cmdOptions[1].optval != 0 &&
591 subcommand->exclusive &&
592 strchr(subcommand->exclusive,
593 cmdOptions[i].optval)) {
594 (void) printf("%s: '-%c': %s\n",
595 commandName, cmdOptions[i].optval,
596 gettext("is an exclusive option"));
597 subUsage(DETAIL_USAGE, subcommand);
598 return (1);
601 } else { /* no options were input */
602 if (availOptions != NULL && subcommand->required) {
603 (void) printf("%s: %s\n", commandName,
604 gettext("at least one option required"));
605 subUsage(DETAIL_USAGE, subcommand);
606 return (1);
611 * If there are no operands,
612 * check to see if this is okay
614 if ((operInd == argc) &&
615 (subcommand->operand & OPERAND_MANDATORY)) {
616 (void) printf("%s: %s %s\n", commandName, subcommand->name,
617 gettext("requires an operand"));
618 subUsage(DETAIL_USAGE, subcommand);
619 return (1);
623 * If there are more operands,
624 * check to see if this is okay
626 if ((argc > operInd) &&
627 (subcommand->operand & OPERAND_NONE)) {
628 (void) fprintf(stderr, "%s: %s %s\n",
629 commandName, subcommand->name,
630 gettext("takes no operands"));
631 subUsage(DETAIL_USAGE, subcommand);
632 return (1);
636 * If there is more than one more operand,
637 * check to see if this is okay
639 if ((argc > operInd) && ((argc - operInd) != 1) &&
640 (subcommand->operand & OPERAND_SINGLE)) {
641 (void) printf("%s: %s %s\n", commandName,
642 subcommand->name, gettext("accepts only a single operand"));
643 subUsage(DETAIL_USAGE, subcommand);
644 return (1);
647 /* Finished syntax checks */
650 /* Call appropriate function */
651 *funcRet = subcommand->handler(argc - operInd, &argv[operInd],
652 &cmdOptions[0], callArgs);
654 return (0);