348 lines
7.1 KiB
C
348 lines
7.1 KiB
C
/*
|
|
* gehopt; options processing with both single-character and whole-word
|
|
* options both introduced with -
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "gethopt.h"
|
|
|
|
|
|
void
|
|
hoptset(struct h_context *ctx, int argc, char **argv)
|
|
{
|
|
memset(ctx, 0, sizeof *ctx);
|
|
ctx->argc = argc;
|
|
ctx->argv = argv;
|
|
ctx->optind = 1;
|
|
}
|
|
|
|
|
|
char *
|
|
hoptarg(struct h_context *ctx)
|
|
{
|
|
return ctx->optarg;
|
|
}
|
|
|
|
int
|
|
hoptind(struct h_context *ctx)
|
|
{
|
|
return ctx->optind;
|
|
}
|
|
|
|
char
|
|
hoptopt(struct h_context *ctx)
|
|
{
|
|
return ctx->optopt;
|
|
}
|
|
|
|
|
|
int
|
|
hopterr(struct h_context *ctx, int val)
|
|
{
|
|
int old = ctx->opterr;
|
|
|
|
ctx->opterr = !!val;
|
|
return old;
|
|
}
|
|
|
|
|
|
struct h_opt *
|
|
gethopt(struct h_context *ctx, struct h_opt *opts, int nropts)
|
|
{
|
|
int i;
|
|
int dashes;
|
|
|
|
|
|
if ( (ctx == 0) || ctx->optend || (ctx->optind >= ctx->argc) )
|
|
return 0;
|
|
|
|
ctx->optarg = 0;
|
|
ctx->optopt = 0;
|
|
|
|
if ( ctx->optchar == 0) {
|
|
/* check for leading -
|
|
*/
|
|
if ( ctx->argv[ctx->optind][0] != '-' ) {
|
|
/* out of arguments */
|
|
ctx->optend = 1;
|
|
return 0;
|
|
}
|
|
|
|
if ( ctx->argv[ctx->optind][1] == 0
|
|
|| strcmp(ctx->argv[ctx->optind], "--") == 0 ) {
|
|
/* option list finishes with - or -- token
|
|
*/
|
|
ctx->optend = 1;
|
|
ctx->optind++;
|
|
return 0;
|
|
}
|
|
|
|
dashes = 1;
|
|
if ( ctx->argv[ctx->optind][dashes] == '-' ) {
|
|
/* support GNU-style long option double-dash prefix
|
|
* (if gethopt is passed an unknown option with a double-dash
|
|
* prefix, it won't match a word and then the second dash
|
|
* will be scanned as if it was a regular old single-character
|
|
* option.)
|
|
*/
|
|
dashes = 2;
|
|
}
|
|
|
|
for ( i=0; i < nropts; i++ ) {
|
|
if ( ! opts[i].optword )
|
|
continue;
|
|
|
|
if (strcmp(opts[i].optword, dashes+(ctx->argv[ctx->optind]) ) == 0 ) {
|
|
if ( opts[i].opthasarg ) {
|
|
if ( ctx->argc > ctx->optind ) {
|
|
ctx->optarg = ctx->argv[ctx->optind+1];
|
|
ctx->optind += 2;
|
|
}
|
|
else {
|
|
/* word argument with required arg at end of
|
|
*command line
|
|
*/
|
|
if ( ctx->opterr )
|
|
fprintf(stderr,
|
|
"%s: option requires an argument -- %s\n",
|
|
ctx->argv[0], opts[i].optword);
|
|
ctx->optind ++;
|
|
return HOPTERR;
|
|
}
|
|
}
|
|
else {
|
|
ctx->optind ++;
|
|
}
|
|
return &opts[i];
|
|
}
|
|
}
|
|
ctx->optchar = 1;
|
|
}
|
|
|
|
ctx->optopt = ctx->argv[ctx->optind][ctx->optchar++];
|
|
|
|
if ( !ctx->optopt ) {
|
|
/* fell off the end of this argument */
|
|
ctx->optind ++;
|
|
ctx->optchar = 0;
|
|
return gethopt(ctx, opts, nropts);
|
|
}
|
|
|
|
for ( i=0; i<nropts; i++ ) {
|
|
if ( opts[i].optchar == ctx->optopt ) {
|
|
/* found a single-char option!
|
|
*/
|
|
if ( opts[i].opthasarg ) {
|
|
if ( ctx->argv[ctx->optind][ctx->optchar] ) {
|
|
/* argument immediately follows this options (-Oc)
|
|
*/
|
|
ctx->optarg = &ctx->argv[ctx->optind][ctx->optchar];
|
|
ctx->optind ++;
|
|
ctx->optchar = 0;
|
|
}
|
|
else if ( ctx->optind < ctx->argc-1 ) {
|
|
/* argument is next arg (-O c)
|
|
*/
|
|
ctx->optarg = &ctx->argv[ctx->optind+1][0];
|
|
ctx->optind += 2;
|
|
ctx->optchar = 0;
|
|
}
|
|
else {
|
|
/* end of arg string (-O); set optarg to null, return
|
|
* (should it opterr on me?)
|
|
*/
|
|
ctx->optarg = 0;
|
|
ctx->optind ++;
|
|
ctx->optchar = 0;
|
|
if ( ctx->opterr )
|
|
fprintf(stderr,
|
|
"%s: option requires an argument -- %c\n",
|
|
ctx->argv[0], opts[i].optchar);
|
|
return HOPTERR;
|
|
}
|
|
}
|
|
else {
|
|
if ( !ctx->argv[ctx->optind][ctx->optchar] ) {
|
|
ctx->optind ++;
|
|
ctx->optchar = 0;
|
|
}
|
|
}
|
|
return &opts[i];
|
|
}
|
|
}
|
|
if ( ctx->opterr )
|
|
fprintf(stderr, "%s: illegal option -- %c\n", ctx->argv[0], ctx->optopt);
|
|
return HOPTERR;
|
|
}
|
|
|
|
|
|
void
|
|
hoptdescribe(char *pgm, struct h_opt opts[], int nropts, char *arguments, int verbose)
|
|
{
|
|
int i;
|
|
int sz;
|
|
|
|
if ( verbose ) {
|
|
fprintf(stderr, "usage: %s", pgm);
|
|
if ( nropts > 0 )
|
|
fprintf(stderr, " [options]");
|
|
if ( arguments )
|
|
fprintf(stderr, " %s", arguments);
|
|
fputc('\n', stderr);
|
|
}
|
|
else
|
|
fprintf(stderr, "usage: %s", pgm);
|
|
|
|
/* print out the options that don't have flags first */
|
|
|
|
if ( verbose ) {
|
|
int maxoptwidth = 0;
|
|
int maxargwidth = 0;
|
|
int hasoptchar = 0;
|
|
int j;
|
|
|
|
if ( nropts > 0 )
|
|
fprintf(stderr, "options:\n");
|
|
|
|
/* find column widths */
|
|
for ( i=0; i < nropts; i++ ) {
|
|
if ( opts[i].optword && (( sz = strlen(opts[i].optword) ) > maxoptwidth ) )
|
|
maxoptwidth = sz;
|
|
if ( opts[i].opthasarg && (( sz = strlen(opts[i].opthasarg) ) > maxargwidth ) )
|
|
maxargwidth = sz;
|
|
if ( opts[i].optchar )
|
|
hasoptchar = 1;
|
|
}
|
|
|
|
for ( i=0; i < nropts; i++ ) {
|
|
if ( opts[i].optword ) {
|
|
fprintf(stderr, " -%s", opts[i].optword);
|
|
j = strlen(opts[i].optword);
|
|
}
|
|
else j = -2;
|
|
|
|
while ( j++ < maxoptwidth )
|
|
fputc(' ', stderr);
|
|
|
|
if ( opts[i].optchar )
|
|
fprintf(stderr, " -%c ", opts[i].optchar);
|
|
else if ( hasoptchar )
|
|
fprintf(stderr, " ");
|
|
|
|
if ( maxargwidth > 0 ) {
|
|
if ( opts[i].opthasarg ) {
|
|
fprintf(stderr, " [%s]", opts[i].opthasarg);
|
|
j = strlen(opts[i].opthasarg);
|
|
}
|
|
else {
|
|
fprintf(stderr, " ");
|
|
j = 0;
|
|
}
|
|
|
|
while ( j++ < maxargwidth )
|
|
fputc(' ', stderr);
|
|
}
|
|
|
|
if ( opts[i].optdesc )
|
|
fprintf(stderr, " %s", opts[i].optdesc);
|
|
|
|
fputc('\n', stderr);
|
|
}
|
|
}
|
|
else {
|
|
int optcount;
|
|
|
|
for ( optcount=i=0; i < nropts; i++ ) {
|
|
if ( opts[i].optchar && !opts[i].opthasarg) {
|
|
if (optcount == 0 )
|
|
fputs(" [-", stderr);
|
|
fputc(opts[i].optchar, stderr);
|
|
optcount++;
|
|
}
|
|
}
|
|
if ( optcount )
|
|
fputc(']', stderr);
|
|
|
|
/* print out the options WITH flags */
|
|
for ( i = 0; i < nropts; i++ )
|
|
if ( opts[i].optchar && opts[i].opthasarg)
|
|
fprintf(stderr, " [-%c %s]", opts[i].optchar, opts[i].opthasarg);
|
|
|
|
/* print out the long options */
|
|
for ( i = 0; i < nropts; i++ )
|
|
if ( opts[i].optword ) {
|
|
fprintf(stderr, " [-%s", opts[i].optword);
|
|
if ( opts[i].opthasarg )
|
|
fprintf(stderr, " %s", opts[i].opthasarg);
|
|
fputc(']', stderr);
|
|
}
|
|
|
|
if ( arguments )
|
|
fprintf(stderr, " %s", arguments);
|
|
}
|
|
|
|
/* and we're done */
|
|
fputc('\n', stderr);
|
|
}
|
|
|
|
|
|
void
|
|
hoptusage(char *pgm, struct h_opt opts[], int nropts, char *arguments)
|
|
{
|
|
hoptdescribe(pgm, opts, nropts, arguments, 0);
|
|
}
|
|
|
|
|
|
#if DEBUG
|
|
struct h_opt opts[] = {
|
|
{ 0, "css", 0, 1, "css file" },
|
|
{ 1, "header", 0, 1, "header file" },
|
|
{ 2, 0, 'a', 0, "option a (no arg)" },
|
|
{ 3, 0, 'b', 1, "option B (with arg)" },
|
|
{ 4, "help", '?', 0, "help message" },
|
|
} ;
|
|
|
|
#define NROPT (sizeof opts/sizeof opts[0])
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct h_opt *ret;
|
|
struct h_context ctx;
|
|
int i;
|
|
|
|
|
|
hoptset(&ctx, argc, argv);
|
|
hopterr(&ctx, 1);
|
|
|
|
while (( ret = gethopt(&ctx, opts, NROPT) )) {
|
|
|
|
if ( ret != HOPTERR ) {
|
|
if ( ret->optword )
|
|
printf("%s", ret->optword);
|
|
else
|
|
printf("%c", ret->optchar);
|
|
|
|
if ( ret->opthasarg ) {
|
|
if ( hoptarg(&ctx) )
|
|
printf(" with argument \"%s\"", hoptarg(&ctx));
|
|
else
|
|
printf(" with no argument?");
|
|
}
|
|
printf(" (%s)\n", ret->optdesc);
|
|
}
|
|
}
|
|
|
|
argc -= hoptind(&ctx);
|
|
argv += hoptind(&ctx);
|
|
|
|
for ( i=0; i < argc; i++ )
|
|
printf("%d: %s\n", i, argv[i]);
|
|
return 0;
|
|
}
|
|
|
|
#endif /*DEBUG*/
|