/* * parseopt.c - Parse command line options * * Copyright (C) 1998-2003 Gero Kuhlmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: parseopt.c,v 1.5 2003/01/25 23:29:44 gkminix Exp $ */ #include #include #include "privlib.h" #ifdef HAVE_GETOPT_H #include #endif /* * Default environment variable names */ #ifndef ENV_NETBOOT #define ENV_NETBOOT "NETBOOT" #endif #ifndef ENV_CONFIG #define ENV_CONFIG "NETBOOT_CONFIG" #endif #ifndef ENV_DATABASE #define ENV_DATABASE "NETBOOT_DATABASE" #endif /* * Layout of help screen. The option names start at FIRST_COLUMN and * the option help text starts at SECOND_COLUMN. */ #define FIRST_COLUMN 4 #define SECOND_COLUMN 35 /* * Layout of long option value */ #define LONG_FLAG 0x0100 #define LONG_MASK 0x00ff /* * Global variables exported by this module */ char *progname = NULL; /* name of program */ int verbose = FALSE; /* verbosity flag */ int quiet = FALSE; /* quiet flag */ /* * Forward declarations */ static void print_version(); static void print_help(); /* * Definition of command line options common to all programs */ static struct cmdopt common_opts[] = { { "config-file", 'C', strval, {&configname}, ENV_CONFIG, "name of configuration file", "FILE" }, { "database-file", 'D', strval, {&dbname}, ENV_DATABASE, "name of database file", "FILE" }, { "netboot-dir", 'N', strval, {&netbootdir}, ENV_NETBOOT, "netboot base directory", "DIR" }, { "quiet", 'q', boolval, {(char **)&quiet}, NULL, "suppress error messages", NULL }, { "verbose", 'x', boolval, {(char **)&verbose}, NULL, "increase verbosity level", NULL }, { "version", 'v', procval, {(char **)&print_version}, NULL, "print version number", NULL }, { "help", 'h', procval, {(char **)&print_help}, NULL, "print this help text", NULL }, { NULL, 0, noval, {NULL}, NULL, NULL, NULL } }; /* * Print version information and quit */ static void print_version() { fprintf(stderr, "%s " VERSION "\n", progname); exit(EXIT_SUCCESS); } /* * Print help text for one item */ static void print_item_help(pos, helptext) int pos; char *helptext; { char *buf = NULL; char *cp; int i = pos; copystr(&buf, helptext); cp = strtok(buf, "\n"); while (cp) { for ( ; i < SECOND_COLUMN; i++) putchar(' '); printf("%s\n", cp); cp = strtok(NULL, "\n"); i = 0; } } /* * Print help screen and exit */ static void print_help(opts) struct cmdopt *opts; { struct cmdopt *curopt; int i, prarg = 0; /* First print the usage line with all non-option arguments */ printf("\nUsage: %s [options]", progname); for (i = 0, curopt = opts; curopt->valtype != noval; curopt++) if (curopt->valtype == nonopt) { printf(" [<%s>", curopt->longopt); i++; } for (; i > 0; i--) printf("]"); printf("\n\n"); /* Now print all non-option argument help strings */ for (curopt = opts; curopt->valtype != noval; curopt++) if (curopt->valtype == nonopt) { for (i = 0; i < FIRST_COLUMN; i++) putchar(' '); printf("<%s>", curopt->longopt); i += strlen(curopt->longopt) + 2; print_item_help(i, curopt->helptext); prarg++; } if (prarg > 0) printf("\n"); /* Finally print all options */ printf("Options:\n"); for (curopt = opts; curopt->valtype != noval; curopt++) { if (curopt->valtype != nonopt) { for (i = 0; i < FIRST_COLUMN; i++) putchar(' '); #ifdef HAVE_GETOPT_LONG if (curopt->shortopt == 0) printf(" --%s", curopt->longopt); else printf("-%c, --%s", curopt->shortopt, curopt->longopt); i += strlen(curopt->longopt) + 6; if (curopt->helparg != NULL) { printf("=%s", curopt->helparg); i += strlen(curopt->helparg) + 1; } #else if (curopt->shortopt == 0) printf(" "); else printf("-%c", curopt->shortopt); i += 2; if (curopt->helparg != NULL) { printf(" %s", curopt->helparg); i += strlen(curopt->helparg) + 1; } #endif print_item_help(i, curopt->helptext); } } printf("\n"); exit(EXIT_USAGE); } /* * Compare two option descriptions for sorting */ static int cmpopt(entry1, entry2) const voidstar entry1; const voidstar entry2; { struct cmdopt *opt1 = (struct cmdopt *)entry1; struct cmdopt *opt2 = (struct cmdopt *)entry2; if (toupper(opt1->shortopt) < toupper(opt2->shortopt)) return(-1); else if (toupper(opt1->shortopt) > toupper(opt2->shortopt)) return(1); else if (opt1->shortopt < opt2->shortopt) return(-1); else if (opt1->shortopt > opt2->shortopt) return(1); else return(0); } /* * Handle option parsing */ void parseopt(argc, argv, opts) int argc; char **argv; struct cmdopt *opts; { struct cmdopt *optbuf = NULL; struct cmdopt *curopt, *tmpopt; #ifdef HAVE_GETOPT_LONG struct option *longbuf = NULL; struct option *curlong; int islong = FALSE; #endif char *cp, *valenv, *shortbuf = NULL; int optnum, optchar; long intarg; /* Determine my own program name for error output */ if ((cp = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else progname = ++cp; /* Determine the total number of options */ optnum = sizeof(common_opts) / sizeof(struct cmdopt) - 1; if (opts != NULL) for (curopt = opts; curopt->valtype != noval; curopt++) optnum++; /* * Copy all options into one big buffer to make further handling easier. * First copy all named options into the buffer, sort it, and then copy * all non-option arguments. We have to preserve their order. Note that * there are no non-option arguments in the common option list. */ tmpopt = optbuf = nbmalloc(sizeof(struct cmdopt) * (optnum + 1)); optnum = 0; if (opts != NULL) for (curopt = opts; curopt->valtype != noval; ) { if (curopt->valtype != nonopt) { *tmpopt = *curopt; tmpopt++; optnum++; } curopt++; } for (curopt = common_opts; curopt->valtype != noval; ) { if (curopt->valtype != nonopt) { *tmpopt = *curopt; tmpopt++; optnum++; } curopt++; } qsort(optbuf, optnum, sizeof(struct cmdopt), &cmpopt); if (opts != NULL) for (curopt = opts; curopt->valtype != noval; ) { if (curopt->valtype == nonopt) { *tmpopt = *curopt; tmpopt++; optnum++; } curopt++; } *tmpopt = *curopt; /* Copy end marker */ /* Setup buffer for single letter options */ shortbuf = nbmalloc((optnum + 1) * 2); for (cp = shortbuf, curopt = optbuf; curopt->valtype != noval; curopt++) if (curopt->valtype == intval || curopt->valtype == strval) { *cp++ = curopt->shortopt; *cp++ = ':'; } else if (curopt->valtype == boolval || curopt->valtype == procval) *cp++ = curopt->shortopt; *cp = '\0'; /* Setup buffer for long option names */ #ifdef HAVE_GETOPT_LONG longbuf = (struct option *)nbmalloc(sizeof(struct option) * (optnum + 1)); for (curlong = longbuf, curopt = optbuf; curopt->valtype != noval; curopt++) if (curopt->valtype != nonopt) { curlong->name = curopt->longopt; if (curopt->valtype == intval || curopt->valtype == strval) curlong->has_arg = required_argument; else curlong->has_arg = no_argument; curlong->flag = NULL; curlong->val = (curopt->shortopt & LONG_MASK) | LONG_FLAG; curlong++; } curlong->name = NULL; #endif /* Preset all string options with environment strings if required */ for (curopt = optbuf; curopt->valtype != noval; curopt++) if (curopt->valtype == strval || curopt->valtype == nonopt) { valenv = NULL; if (curopt->envdefault != NULL) valenv = getenv(curopt->envdefault); cp = *(curopt->valptr.strptr); if (valenv != NULL) cp = valenv; if (cp != NULL) { *(curopt->valptr.strptr) = NULL; copystr(curopt->valptr.strptr, cp); } } /* Now parse all options using getopt */ opterr = 0; while ( #ifdef HAVE_GETOPT_LONG (optchar = getopt_long(argc, argv, shortbuf, longbuf, NULL)) #else (optchar = getopt(argc, argv, shortbuf)) #endif != EOF) { #ifdef HAVE_GETOPT_LONG /* Check if we have a long option */ islong = (optchar & LONG_FLAG) != 0; optchar &= LONG_MASK; #endif /* Find option value in table */ for (curopt = optbuf; curopt->valtype != noval; curopt++) if (optchar == curopt->shortopt) break; switch (curopt->valtype) { case boolval: (*(curopt->valptr.intptr))++; break; case intval: intarg = strtol(optarg, &cp, 0); if (*cp || intarg < INT_MIN || intarg > INT_MAX) { fprintf(stderr, "%s: invalid numerical argument to option ", progname); #ifdef HAVE_GETOPT_LONG if (islong) fprintf(stderr, "\"--%s\"\n", curopt->longopt); else #endif fprintf(stderr, "\"-%c\"\n", curopt->shortopt); exit(EXIT_USAGE); } *(curopt->valptr.intptr) = intarg; break; case longval: intarg = strtol(optarg, &cp, 0); if (*cp) { fprintf(stderr, "%s: invalid numerical argument to option ", progname); #ifdef HAVE_GETOPT_LONG if (islong) fprintf(stderr, "\"--%s\"\n", curopt->longopt); else #endif fprintf(stderr, "\"-%c\"\n", curopt->shortopt); exit(EXIT_USAGE); } *(curopt->valptr.longptr) = intarg; break; case strval: copystr(curopt->valptr.strptr, optarg); break; case procval: (curopt->valptr.procptr)(optbuf); break; case nonopt: break; default: fprintf(stderr, "%s: invalid option ", progname); #ifdef HAVE_GETOPT_LONG if (!optopt) { if ((cp = strchr(argv[optind - 1], '=')) != NULL) *cp = '\0'; fprintf(stderr, "\"%s\"\n", argv[optind - 1]); } else #endif fprintf(stderr, "\"-%c\"\n", optopt); exit(EXIT_USAGE); } } /* Finally parse all non-option arguments */ curopt = NULL; while (optind < argc) { /* Find next non-option argument in option buffer */ if (curopt == NULL) curopt = optbuf; else curopt++; for (; curopt->valtype != noval; curopt++) if (curopt->valtype == nonopt) break; if (curopt->valtype != nonopt) { fprintf(stderr, "%s: invalid argument \"%s\"\n", progname, argv[optind]); exit(EXIT_USAGE); } copystr(curopt->valptr.strptr, argv[optind]); optind++; } /* Free all occupied memory space */ if (optbuf != NULL) free(optbuf); if (shortbuf != NULL) free(shortbuf); #ifdef HAVE_GETOPT_LONG if (longbuf != NULL) free(longbuf); #endif }