discount/gethopt.c
2025-12-13 00:40:57 +04:00

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*/