/*
* parseopt.c - Parse command line options
*
* Copyright (C) 1998-2003 Gero Kuhlmann <gero@gkminix.han.de>
*
* 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 <common.h>
#include <nblib.h>
#include "privlib.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#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
}
syntax highlighted by Code2HTML, v. 0.9.1