Path: blob/main/crypto/heimdal/lib/roken/getarg.c
108036 views
/*1* Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include <config.h>3435#include <stdio.h>36#include <stdlib.h>37#include <string.h>38#include "roken.h"39#include "getarg.h"4041#define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)4243static size_t44print_arg (char *string,45size_t len,46int mdoc,47int longp,48struct getargs *arg,49char *(i18n)(const char *))50{51const char *s;5253*string = '\0';5455if (ISFLAG(*arg) || (!longp && arg->type == arg_counter))56return 0;5758if(mdoc){59if(longp)60strlcat(string, "= Ns", len);61strlcat(string, " Ar ", len);62} else {63if (longp)64strlcat (string, "=", len);65else66strlcat (string, " ", len);67}6869if (arg->arg_help)70s = (*i18n)(arg->arg_help);71else if (arg->type == arg_integer || arg->type == arg_counter)72s = "integer";73else if (arg->type == arg_string)74s = "string";75else if (arg->type == arg_strings)76s = "strings";77else if (arg->type == arg_double)78s = "float";79else80s = "<undefined>";8182strlcat(string, s, len);83return 1 + strlen(s);84}8586static void87mandoc_template(struct getargs *args,88size_t num_args,89const char *progname,90const char *extra_string,91char *(i18n)(const char *))92{93size_t i;94char timestr[64], cmd[64];95char buf[128];96const char *p;97time_t t;9899printf(".\\\" Things to fix:\n");100printf(".\\\" * correct section, and operating system\n");101printf(".\\\" * remove Op from mandatory flags\n");102printf(".\\\" * use better macros for arguments (like .Pa for files)\n");103printf(".\\\"\n");104t = time(NULL);105strftime(timestr, sizeof(timestr), "%B %e, %Y", localtime(&t));106printf(".Dd %s\n", timestr);107p = strrchr(progname, '/');108if(p) p++; else p = progname;109strlcpy(cmd, p, sizeof(cmd));110strupr(cmd);111112printf(".Dt %s SECTION\n", cmd);113printf(".Os OPERATING_SYSTEM\n");114printf(".Sh NAME\n");115printf(".Nm %s\n", p);116printf(".Nd in search of a description\n");117printf(".Sh SYNOPSIS\n");118printf(".Nm\n");119for(i = 0; i < num_args; i++){120/* we seem to hit a limit on number of arguments if doing121short and long flags with arguments -- split on two lines */122if(ISFLAG(args[i]) ||123args[i].short_name == 0 || args[i].long_name == NULL) {124printf(".Op ");125126if(args[i].short_name) {127print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);128printf("Fl %c%s", args[i].short_name, buf);129if(args[i].long_name)130printf(" | ");131}132if(args[i].long_name) {133print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);134printf("Fl Fl %s%s%s",135args[i].type == arg_negative_flag ? "no-" : "",136args[i].long_name, buf);137}138printf("\n");139} else {140print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);141printf(".Oo Fl %c%s \\*(Ba Xo\n", args[i].short_name, buf);142print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);143printf(".Fl Fl %s%s\n.Xc\n.Oc\n", args[i].long_name, buf);144}145/*146if(args[i].type == arg_strings)147fprintf (stderr, "...");148*/149}150if (extra_string && *extra_string)151printf (".Ar %s\n", extra_string);152printf(".Sh DESCRIPTION\n");153printf("Supported options:\n");154printf(".Bl -tag -width Ds\n");155for(i = 0; i < num_args; i++){156printf(".It Xo\n");157if(args[i].short_name){158printf(".Fl %c", args[i].short_name);159print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);160printf("%s", buf);161if(args[i].long_name)162printf(" ,");163printf("\n");164}165if(args[i].long_name){166printf(".Fl Fl %s%s",167args[i].type == arg_negative_flag ? "no-" : "",168args[i].long_name);169print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);170printf("%s\n", buf);171}172printf(".Xc\n");173if(args[i].help)174printf("%s\n", args[i].help);175/*176if(args[i].type == arg_strings)177fprintf (stderr, "...");178*/179}180printf(".El\n");181printf(".\\\".Sh ENVIRONMENT\n");182printf(".\\\".Sh FILES\n");183printf(".\\\".Sh EXAMPLES\n");184printf(".\\\".Sh DIAGNOSTICS\n");185printf(".\\\".Sh SEE ALSO\n");186printf(".\\\".Sh STANDARDS\n");187printf(".\\\".Sh HISTORY\n");188printf(".\\\".Sh AUTHORS\n");189printf(".\\\".Sh BUGS\n");190}191192static int193check_column(FILE *f, int col, int len, int columns)194{195if(col + len > columns) {196fprintf(f, "\n");197col = fprintf(f, " ");198}199return col;200}201202static char *203builtin_i18n(const char *str)204{205return rk_UNCONST(str);206}207208ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL209arg_printusage (struct getargs *args,210size_t num_args,211const char *progname,212const char *extra_string)213{214arg_printusage_i18n(args, num_args, "Usage",215progname, extra_string, builtin_i18n);216}217218ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL219arg_printusage_i18n (struct getargs *args,220size_t num_args,221const char *usage,222const char *progname,223const char *extra_string,224char *(*i18n)(const char *))225{226size_t i, max_len = 0;227char buf[128];228int col = 0, columns;229230if (progname == NULL)231progname = getprogname();232233if (i18n == NULL)234i18n = builtin_i18n;235236if(getenv("GETARGMANDOC")){237mandoc_template(args, num_args, progname, extra_string, i18n);238return;239}240if(get_window_size(2, NULL, &columns) == -1)241columns = 80;242col = 0;243col += fprintf (stderr, "%s: %s", usage, progname);244buf[0] = '\0';245for (i = 0; i < num_args; ++i) {246if(args[i].short_name && ISFLAG(args[i])) {247char s[2];248if(buf[0] == '\0')249strlcpy(buf, "[-", sizeof(buf));250s[0] = args[i].short_name;251s[1] = '\0';252strlcat(buf, s, sizeof(buf));253}254}255if(buf[0] != '\0') {256strlcat(buf, "]", sizeof(buf));257col = check_column(stderr, col, strlen(buf) + 1, columns);258col += fprintf(stderr, " %s", buf);259}260261for (i = 0; i < num_args; ++i) {262size_t len = 0;263264if (args[i].long_name) {265buf[0] = '\0';266strlcat(buf, "[--", sizeof(buf));267len += 2;268if(args[i].type == arg_negative_flag) {269strlcat(buf, "no-", sizeof(buf));270len += 3;271}272strlcat(buf, args[i].long_name, sizeof(buf));273len += strlen(args[i].long_name);274len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),2750, 1, &args[i], i18n);276strlcat(buf, "]", sizeof(buf));277if(args[i].type == arg_strings)278strlcat(buf, "...", sizeof(buf));279col = check_column(stderr, col, strlen(buf) + 1, columns);280col += fprintf(stderr, " %s", buf);281}282if (args[i].short_name && !ISFLAG(args[i])) {283snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);284len += 2;285len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),2860, 0, &args[i], i18n);287strlcat(buf, "]", sizeof(buf));288if(args[i].type == arg_strings)289strlcat(buf, "...", sizeof(buf));290col = check_column(stderr, col, strlen(buf) + 1, columns);291col += fprintf(stderr, " %s", buf);292}293if (args[i].long_name && args[i].short_name)294len += 2; /* ", " */295max_len = max(max_len, len);296}297if (extra_string) {298check_column(stderr, col, strlen(extra_string) + 1, columns);299fprintf (stderr, " %s\n", extra_string);300} else301fprintf (stderr, "\n");302for (i = 0; i < num_args; ++i) {303if (args[i].help) {304size_t count = 0;305306if (args[i].short_name) {307count += fprintf (stderr, "-%c", args[i].short_name);308print_arg (buf, sizeof(buf), 0, 0, &args[i], i18n);309count += fprintf(stderr, "%s", buf);310}311if (args[i].short_name && args[i].long_name)312count += fprintf (stderr, ", ");313if (args[i].long_name) {314count += fprintf (stderr, "--");315if (args[i].type == arg_negative_flag)316count += fprintf (stderr, "no-");317count += fprintf (stderr, "%s", args[i].long_name);318print_arg (buf, sizeof(buf), 0, 1, &args[i], i18n);319count += fprintf(stderr, "%s", buf);320}321while(count++ <= max_len)322putc (' ', stderr);323fprintf (stderr, "%s\n", (*i18n)(args[i].help));324}325}326}327328static int329add_string(getarg_strings *s, char *value)330{331char **strings;332333strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));334if (strings == NULL) {335free(s->strings);336s->strings = NULL;337s->num_strings = 0;338return ENOMEM;339}340s->strings = strings;341s->strings[s->num_strings] = value;342s->num_strings++;343return 0;344}345346static int347arg_match_long(struct getargs *args, size_t num_args,348char *argv, int argc, char **rargv, int *goptind)349{350size_t i;351char *goptarg = NULL;352int negate = 0;353int partial_match = 0;354struct getargs *partial = NULL;355struct getargs *current = NULL;356int argv_len;357char *p;358int p_len;359360argv_len = strlen(argv);361p = strchr (argv, '=');362if (p != NULL)363argv_len = p - argv;364365for (i = 0; i < num_args; ++i) {366if(args[i].long_name) {367int len = strlen(args[i].long_name);368p = argv;369p_len = argv_len;370negate = 0;371372for (;;) {373if (strncmp (args[i].long_name, p, p_len) == 0) {374if(p_len == len)375current = &args[i];376else {377++partial_match;378partial = &args[i];379}380goptarg = p + p_len;381} else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {382negate = !negate;383p += 3;384p_len -= 3;385continue;386}387break;388}389if (current)390break;391}392}393if (current == NULL) {394if (partial_match == 1)395current = partial;396else397return ARG_ERR_NO_MATCH;398}399400if(*goptarg == '\0'401&& !ISFLAG(*current)402&& current->type != arg_collect403&& current->type != arg_counter)404return ARG_ERR_NO_MATCH;405switch(current->type){406case arg_integer:407{408int tmp;409if(sscanf(goptarg + 1, "%d", &tmp) != 1)410return ARG_ERR_BAD_ARG;411*(int*)current->value = tmp;412return 0;413}414case arg_string:415{416*(char**)current->value = goptarg + 1;417return 0;418}419case arg_strings:420{421return add_string((getarg_strings*)current->value, goptarg + 1);422}423case arg_flag:424case arg_negative_flag:425{426int *flag = current->value;427if(*goptarg == '\0' ||428strcmp(goptarg + 1, "yes") == 0 ||429strcmp(goptarg + 1, "true") == 0){430*flag = !negate;431return 0;432} else if (*goptarg && strcmp(goptarg + 1, "maybe") == 0) {433*flag = rk_random() & 1;434} else {435*flag = negate;436return 0;437}438return ARG_ERR_BAD_ARG;439}440case arg_counter :441{442int val;443444if (*goptarg == '\0')445val = 1;446else if(sscanf(goptarg + 1, "%d", &val) != 1)447return ARG_ERR_BAD_ARG;448*(int *)current->value += val;449return 0;450}451case arg_double:452{453double tmp;454if(sscanf(goptarg + 1, "%lf", &tmp) != 1)455return ARG_ERR_BAD_ARG;456*(double*)current->value = tmp;457return 0;458}459case arg_collect:{460struct getarg_collect_info *c = current->value;461int o = argv - rargv[*goptind];462return (*c->func)(FALSE, argc, rargv, goptind, &o, c->data);463}464465default:466abort ();467UNREACHABLE(return 0);468}469}470471static int472arg_match_short (struct getargs *args, size_t num_args,473char *argv, int argc, char **rargv, int *goptind)474{475size_t j, k;476477for(j = 1; j > 0 && j < strlen(rargv[*goptind]); j++) {478for(k = 0; k < num_args; k++) {479char *goptarg;480481if(args[k].short_name == 0)482continue;483if(argv[j] == args[k].short_name) {484if(args[k].type == arg_flag) {485*(int*)args[k].value = 1;486break;487}488if(args[k].type == arg_negative_flag) {489*(int*)args[k].value = 0;490break;491}492if(args[k].type == arg_counter) {493++*(int *)args[k].value;494break;495}496if(args[k].type == arg_collect) {497struct getarg_collect_info *c = args[k].value;498int a = (int)j;499500if((*c->func)(TRUE, argc, rargv, goptind, &a, c->data))501return ARG_ERR_BAD_ARG;502j = a;503break;504}505506if(argv[j + 1])507goptarg = &argv[j + 1];508else {509++*goptind;510goptarg = rargv[*goptind];511}512if(goptarg == NULL) {513--*goptind;514return ARG_ERR_NO_ARG;515}516if(args[k].type == arg_integer) {517int tmp;518if(sscanf(goptarg, "%d", &tmp) != 1)519return ARG_ERR_BAD_ARG;520*(int*)args[k].value = tmp;521return 0;522} else if(args[k].type == arg_string) {523*(char**)args[k].value = goptarg;524return 0;525} else if(args[k].type == arg_strings) {526return add_string((getarg_strings*)args[k].value, goptarg);527} else if(args[k].type == arg_double) {528double tmp;529if(sscanf(goptarg, "%lf", &tmp) != 1)530return ARG_ERR_BAD_ARG;531*(double*)args[k].value = tmp;532return 0;533}534return ARG_ERR_BAD_ARG;535}536}537if (k == num_args)538return ARG_ERR_NO_MATCH;539}540return 0;541}542543ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL544getarg(struct getargs *args, size_t num_args,545int argc, char **argv, int *goptind)546{547int i;548int ret = 0;549550rk_random_init();551(*goptind)++;552for(i = *goptind; i < argc; i++) {553if(argv[i][0] != '-')554break;555if(argv[i][1] == '-'){556if(argv[i][2] == 0){557i++;558break;559}560ret = arg_match_long (args, num_args, argv[i] + 2,561argc, argv, &i);562} else {563ret = arg_match_short (args, num_args, argv[i],564argc, argv, &i);565}566if(ret)567break;568}569*goptind = i;570return ret;571}572573ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL574free_getarg_strings (getarg_strings *s)575{576free (s->strings);577}578579#if TEST580int foo_flag = 2;581int flag1 = 0;582int flag2 = 0;583int bar_int;584char *baz_string;585586struct getargs args[] = {587{ NULL, '1', arg_flag, &flag1, "one", NULL },588{ NULL, '2', arg_flag, &flag2, "two", NULL },589{ "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },590{ "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},591{ "baz", 'x', arg_string, &baz_string, "baz", "name" },592};593594int main(int argc, char **argv)595{596int goptind = 0;597while(getarg(args, 5, argc, argv, &goptind))598printf("Bad arg: %s\n", argv[goptind]);599printf("flag1 = %d\n", flag1);600printf("flag2 = %d\n", flag2);601printf("foo_flag = %d\n", foo_flag);602printf("bar_int = %d\n", bar_int);603printf("baz_flag = %s\n", baz_string);604arg_printusage (args, 5, argv[0], "nothing here");605}606#endif607608609