150880Smarkm/*
250880Smarkm * Copyright (c) 1997, 1998 Kungliga Tekniska H�gskolan
350880Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
450880Smarkm * All rights reserved.
550880Smarkm *
650880Smarkm * Redistribution and use in source and binary forms, with or without
750880Smarkm * modification, are permitted provided that the following conditions
850880Smarkm * are met:
950880Smarkm *
1050880Smarkm * 1. Redistributions of source code must retain the above copyright
1150880Smarkm *    notice, this list of conditions and the following disclaimer.
1250880Smarkm *
1350880Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1450880Smarkm *    notice, this list of conditions and the following disclaimer in the
1550880Smarkm *    documentation and/or other materials provided with the distribution.
1650880Smarkm *
1750880Smarkm * 3. All advertising materials mentioning features or use of this software
1850880Smarkm *    must display the following acknowledgement:
1950880Smarkm *      This product includes software developed by Kungliga Tekniska
2050880Smarkm *      H�gskolan and its contributors.
2150880Smarkm *
2250880Smarkm * 4. Neither the name of the Institute nor the names of its contributors
2350880Smarkm *    may be used to endorse or promote products derived from this software
2450880Smarkm *    without specific prior written permission.
2550880Smarkm *
2650880Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2750880Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2850880Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2950880Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
3050880Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3150880Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3250880Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3350880Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3450880Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3550880Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3650880Smarkm * SUCH DAMAGE.
37108950Speter *
38108950Speter * $FreeBSD$
3950880Smarkm */
4050880Smarkm
4150880Smarkm#if 0
4250880SmarkmRCSID("$Id: getarg.c,v 1.25 1998/11/22 09:45:05 assar Exp $");
4350880Smarkm#endif
4450880Smarkm
4550880Smarkm#include <sys/ttycom.h>
4650880Smarkm#include <time.h>
4750880Smarkm#include <stdio.h>
48108950Speter#include <stdlib.h>
4950880Smarkm#include "getarg.h"
5050880Smarkm
5150880Smarkm#define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)
5250880Smarkm
5350880Smarkmstatic size_t
5450880Smarkmprint_arg (char *string, size_t len, int mdoc, int longp, struct getargs *arg)
5550880Smarkm{
5650880Smarkm    const char *s;
5750880Smarkm
5850880Smarkm    *string = '\0';
5950880Smarkm
6050880Smarkm    if (ISFLAG(*arg))
6150880Smarkm	return 0;
6250880Smarkm
6350880Smarkm    if(mdoc){
6450880Smarkm	if(longp)
6550880Smarkm	    strncat(string, "= Ns", len);
6650880Smarkm	strncat(string, " Ar ", len);
6750880Smarkm    }else
6850880Smarkm	if (longp)
6950880Smarkm	    strncat (string, "=", len);
7050880Smarkm	else
7150880Smarkm	    strncat (string, " ", len);
7250880Smarkm
7350880Smarkm    if (arg->arg_help)
7450880Smarkm	s = arg->arg_help;
7550880Smarkm    else if (arg->type == arg_integer)
7650880Smarkm	s = "number";
7750880Smarkm    else if (arg->type == arg_string)
7850880Smarkm	s = "string";
7950880Smarkm    else
8050880Smarkm	s = "<undefined>";
8150880Smarkm
8250880Smarkm    strncat(string, s, len);
8350880Smarkm    return 1 + strlen(s);
8450880Smarkm}
8550880Smarkm
8650880Smarkmstatic int
8750880Smarkmcheck_column(FILE *f, int col, int len, int columns)
8850880Smarkm{
8950880Smarkm    if(col + len > columns) {
9050880Smarkm	fprintf(f, "\n");
9150880Smarkm	col = fprintf(f, "  ");
9250880Smarkm    }
9350880Smarkm    return col;
9450880Smarkm}
9550880Smarkm
9650880Smarkmvoid
9750880Smarkmarg_printusage (struct getargs *args,
9850880Smarkm		size_t num_args,
9950880Smarkm		const char *progname,
10050880Smarkm		const char *extra_string)
10150880Smarkm{
10250880Smarkm    int i;
10350880Smarkm    size_t max_len = 0;
10450880Smarkm    char buf[128];
10550880Smarkm    int col = 0, columns;
10650880Smarkm    struct winsize ws;
10750880Smarkm
10850880Smarkm    columns = 80;
10950880Smarkm    col = 0;
11050880Smarkm    col += fprintf (stderr, "Usage: %s", progname);
11150880Smarkm    for (i = 0; i < num_args; ++i) {
11250880Smarkm	size_t len = 0;
11350880Smarkm
11450880Smarkm	if (args[i].long_name) {
11550880Smarkm	    buf[0] = '\0';
11650880Smarkm	    strncat(buf, "[--", sizeof(buf));
11750880Smarkm	    len += 2;
11850880Smarkm	    if(args[i].type == arg_negative_flag) {
11950880Smarkm		strncat(buf, "no-", sizeof(buf));
12050880Smarkm		len += 3;
12150880Smarkm	    }
12250880Smarkm	    strncat(buf, args[i].long_name, sizeof(buf));
12350880Smarkm	    len += strlen(args[i].long_name);
12450880Smarkm	    len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
12550880Smarkm			     0, 1, &args[i]);
12650880Smarkm	    strncat(buf, "]", sizeof(buf));
12750880Smarkm	    if(args[i].type == arg_strings)
12850880Smarkm		strncat(buf, "...", sizeof(buf));
12950880Smarkm	    col = check_column(stderr, col, strlen(buf) + 1, columns);
13050880Smarkm	    col += fprintf(stderr, " %s", buf);
13150880Smarkm	}
13250880Smarkm	if (args[i].short_name) {
13350880Smarkm	    snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);
13450880Smarkm	    len += 2;
13550880Smarkm	    len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
13650880Smarkm			     0, 0, &args[i]);
13750880Smarkm	    strncat(buf, "]", sizeof(buf));
13850880Smarkm	    if(args[i].type == arg_strings)
13950880Smarkm		strncat(buf, "...", sizeof(buf));
14050880Smarkm	    col = check_column(stderr, col, strlen(buf) + 1, columns);
14150880Smarkm	    col += fprintf(stderr, " %s", buf);
14250880Smarkm	}
14350880Smarkm	if (args[i].long_name && args[i].short_name)
14450880Smarkm	    len += 2; /* ", " */
14550880Smarkm	max_len = max(max_len, len);
14650880Smarkm    }
14750880Smarkm    if (extra_string) {
14850880Smarkm	col = check_column(stderr, col, strlen(extra_string) + 1, columns);
14950880Smarkm	fprintf (stderr, " %s\n", extra_string);
15050880Smarkm    } else
15150880Smarkm	fprintf (stderr, "\n");
15250880Smarkm    for (i = 0; i < num_args; ++i) {
15350880Smarkm	if (args[i].help) {
15450880Smarkm	    size_t count = 0;
15550880Smarkm
15650880Smarkm	    if (args[i].short_name) {
15750880Smarkm		count += fprintf (stderr, "-%c", args[i].short_name);
15850880Smarkm		print_arg (buf, sizeof(buf), 0, 0, &args[i]);
15950880Smarkm		count += fprintf(stderr, "%s", buf);
16050880Smarkm	    }
16150880Smarkm	    if (args[i].short_name && args[i].long_name)
16250880Smarkm		count += fprintf (stderr, ", ");
16350880Smarkm	    if (args[i].long_name) {
16450880Smarkm		count += fprintf (stderr, "--");
16550880Smarkm		if (args[i].type == arg_negative_flag)
16650880Smarkm		    count += fprintf (stderr, "no-");
16750880Smarkm		count += fprintf (stderr, "%s", args[i].long_name);
16850880Smarkm		print_arg (buf, sizeof(buf), 0, 1, &args[i]);
16950880Smarkm		count += fprintf(stderr, "%s", buf);
17050880Smarkm	    }
17150880Smarkm	    while(count++ <= max_len)
17250880Smarkm		putc (' ', stderr);
17350880Smarkm	    fprintf (stderr, "%s\n", args[i].help);
17450880Smarkm	}
17550880Smarkm    }
17650880Smarkm}
17750880Smarkm
17850880Smarkmstatic void
17950880Smarkmadd_string(getarg_strings *s, char *value)
18050880Smarkm{
18150880Smarkm    s->strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));
18250880Smarkm    s->strings[s->num_strings] = value;
18350880Smarkm    s->num_strings++;
18450880Smarkm}
18550880Smarkm
18650880Smarkmstatic int
18750880Smarkmarg_match_long(struct getargs *args, size_t num_args,
18850880Smarkm	       char *argv)
18950880Smarkm{
19050880Smarkm    int i;
19150880Smarkm    char *optarg = NULL;
19250880Smarkm    int negate = 0;
19350880Smarkm    int partial_match = 0;
19450880Smarkm    struct getargs *partial = NULL;
19550880Smarkm    struct getargs *current = NULL;
19650880Smarkm    int argv_len;
19750880Smarkm    char *p;
19850880Smarkm
19950880Smarkm    argv_len = strlen(argv);
20050880Smarkm    p = strchr (argv, '=');
20150880Smarkm    if (p != NULL)
20250880Smarkm	argv_len = p - argv;
20350880Smarkm
20450880Smarkm    for (i = 0; i < num_args; ++i) {
20550880Smarkm	if(args[i].long_name) {
20650880Smarkm	    int len = strlen(args[i].long_name);
20750880Smarkm	    char *p = argv;
20850880Smarkm	    int p_len = argv_len;
20950880Smarkm	    negate = 0;
21050880Smarkm
21150880Smarkm	    for (;;) {
21250880Smarkm		if (strncmp (args[i].long_name, p, p_len) == 0) {
21350880Smarkm		    if(p_len == len)
21450880Smarkm			current = &args[i];
21550880Smarkm		    else {
21650880Smarkm			++partial_match;
21750880Smarkm			partial = &args[i];
21850880Smarkm		    }
21950880Smarkm		    optarg  = p + p_len;
22050880Smarkm		} else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {
22150880Smarkm		    negate = !negate;
22250880Smarkm		    p += 3;
22350880Smarkm		    p_len -= 3;
22450880Smarkm		    continue;
22550880Smarkm		}
22650880Smarkm		break;
22750880Smarkm	    }
22850880Smarkm	    if (current)
22950880Smarkm		break;
23050880Smarkm	}
23150880Smarkm    }
23250880Smarkm    if (current == NULL) {
23350880Smarkm	if (partial_match == 1)
23450880Smarkm	    current = partial;
23550880Smarkm	else
23650880Smarkm	    return ARG_ERR_NO_MATCH;
23750880Smarkm    }
23850880Smarkm
23950880Smarkm    if(*optarg == '\0' && !ISFLAG(*current))
24050880Smarkm	return ARG_ERR_NO_MATCH;
24150880Smarkm    switch(current->type){
24250880Smarkm    case arg_integer:
24350880Smarkm    {
24450880Smarkm	int tmp;
24550880Smarkm	if(sscanf(optarg + 1, "%d", &tmp) != 1)
24650880Smarkm	    return ARG_ERR_BAD_ARG;
24750880Smarkm	*(int*)current->value = tmp;
24850880Smarkm	return 0;
24950880Smarkm    }
25050880Smarkm    case arg_string:
25150880Smarkm    {
25250880Smarkm	*(char**)current->value = optarg + 1;
25350880Smarkm	return 0;
25450880Smarkm    }
25550880Smarkm    case arg_strings:
25650880Smarkm    {
25750880Smarkm	add_string((getarg_strings*)current->value, optarg + 1);
25850880Smarkm	return 0;
25950880Smarkm    }
26050880Smarkm    case arg_flag:
26150880Smarkm    case arg_negative_flag:
26250880Smarkm    {
26350880Smarkm	int *flag = current->value;
26450880Smarkm	if(*optarg == '\0' ||
26550880Smarkm	   strcmp(optarg + 1, "yes") == 0 ||
26650880Smarkm	   strcmp(optarg + 1, "true") == 0){
26750880Smarkm	    *flag = !negate;
26850880Smarkm	    return 0;
26950880Smarkm	} else if (*optarg && strcmp(optarg + 1, "maybe") == 0) {
27050880Smarkm	    *flag = rand() & 1;
27150880Smarkm	} else {
27250880Smarkm	    *flag = negate;
27350880Smarkm	    return 0;
27450880Smarkm	}
27550880Smarkm	return ARG_ERR_BAD_ARG;
27650880Smarkm    }
27750880Smarkm    default:
27850880Smarkm	abort ();
27950880Smarkm    }
28050880Smarkm}
28150880Smarkm
28250880Smarkmint
28350880Smarkmgetarg(struct getargs *args, size_t num_args,
28450880Smarkm       int argc, char **argv, int *optind)
28550880Smarkm{
28650880Smarkm    int i, j, k;
28750880Smarkm    int ret = 0;
28850880Smarkm
28950880Smarkm    srand (time(NULL));
29050880Smarkm    (*optind)++;
29150880Smarkm    for(i = *optind; i < argc; i++) {
29250880Smarkm	if(argv[i][0] != '-')
29350880Smarkm	    break;
29450880Smarkm	if(argv[i][1] == '-'){
29550880Smarkm	    if(argv[i][2] == 0){
29650880Smarkm		i++;
29750880Smarkm		break;
29850880Smarkm	    }
29950880Smarkm	    ret = arg_match_long (args, num_args, argv[i] + 2);
30050880Smarkm	    if(ret)
30150880Smarkm		return ret;
30250880Smarkm	}else{
30350880Smarkm	    for(j = 1; argv[i][j]; j++) {
30450880Smarkm		for(k = 0; k < num_args; k++) {
30550880Smarkm		    char *optarg;
30650880Smarkm		    if(args[k].short_name == 0)
30750880Smarkm			continue;
30850880Smarkm		    if(argv[i][j] == args[k].short_name){
30950880Smarkm			if(args[k].type == arg_flag){
31050880Smarkm			    *(int*)args[k].value = 1;
31150880Smarkm			    break;
31250880Smarkm			}
31350880Smarkm			if(args[k].type == arg_negative_flag){
31450880Smarkm			    *(int*)args[k].value = 0;
31550880Smarkm			    break;
31650880Smarkm			}
31750880Smarkm			if(argv[i][j + 1])
31850880Smarkm			    optarg = &argv[i][j + 1];
31950880Smarkm			else{
32050880Smarkm			    i++;
32150880Smarkm			    optarg = argv[i];
32250880Smarkm			}
32350880Smarkm			if(optarg == NULL)
32450880Smarkm			    return ARG_ERR_NO_ARG;
32550880Smarkm			if(args[k].type == arg_integer){
32650880Smarkm			    int tmp;
32750880Smarkm			    if(sscanf(optarg, "%d", &tmp) != 1)
32850880Smarkm				return ARG_ERR_BAD_ARG;
32950880Smarkm			    *(int*)args[k].value = tmp;
33050880Smarkm			    goto out;
33150880Smarkm			}else if(args[k].type == arg_string){
33250880Smarkm			    *(char**)args[k].value = optarg;
33350880Smarkm			    goto out;
33450880Smarkm			}else if(args[k].type == arg_strings){
33550880Smarkm			    add_string((getarg_strings*)args[k].value, optarg);
33650880Smarkm			    goto out;
33750880Smarkm			}
33850880Smarkm			return ARG_ERR_BAD_ARG;
33950880Smarkm		    }
34050880Smarkm
34150880Smarkm		}
34250880Smarkm		if (k == num_args)
34350880Smarkm		    return ARG_ERR_NO_MATCH;
34450880Smarkm	    }
34550880Smarkm	out:;
34650880Smarkm	}
34750880Smarkm    }
34850880Smarkm    *optind = i;
34950880Smarkm    return 0;
35050880Smarkm}
35150880Smarkm
35250880Smarkm#if TEST
35350880Smarkmint foo_flag = 2;
35450880Smarkmint flag1 = 0;
35550880Smarkmint flag2 = 0;
35650880Smarkmint bar_int;
35750880Smarkmchar *baz_string;
35850880Smarkm
35950880Smarkmstruct getargs args[] = {
36050880Smarkm    { NULL, '1', arg_flag, &flag1, "one", NULL },
36150880Smarkm    { NULL, '2', arg_flag, &flag2, "two", NULL },
36250880Smarkm    { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },
36350880Smarkm    { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},
36450880Smarkm    { "baz", 'x', arg_string, &baz_string, "baz", "name" },
36550880Smarkm};
36650880Smarkm
36750880Smarkmint main(int argc, char **argv)
36850880Smarkm{
36950880Smarkm    int optind = 0;
37050880Smarkm    while(getarg(args, 5, argc, argv, &optind))
37150880Smarkm	printf("Bad arg: %s\n", argv[optind]);
37250880Smarkm    printf("flag1 = %d\n", flag1);
37350880Smarkm    printf("flag2 = %d\n", flag2);
37450880Smarkm    printf("foo_flag = %d\n", foo_flag);
37550880Smarkm    printf("bar_int = %d\n", bar_int);
37650880Smarkm    printf("baz_flag = %s\n", baz_string);
37750880Smarkm    arg_printusage (args, 5, argv[0], "nothing here");
37850880Smarkm}
37950880Smarkm#endif
380