1# getopt.awk --- Do C library getopt(3) function in awk
2#                Also supports long options.
3#
4# Arnold Robbins, arnold@skeeve.com, Public Domain
5#
6# Initial version: March, 1991
7# Revised: May, 1993
8# Long options added by Greg Minshall, January 2020
9
10# External variables:
11#    Optind -- index in ARGV of first nonoption argument
12#    Optarg -- string value of argument to current option
13#    Opterr -- if nonzero, print our own diagnostic
14#    Optopt -- current option letter
15
16# Returns:
17#    -1     at end of options
18#    "?"    for unrecognized option
19#    <s>    a string representing the current option
20
21# Private Data:
22#    _opti  -- index in multiflag option, e.g., -abc
23function getopt(argc, argv, options, longopts,    thisopt, i, j)
24{
25    if (length(options) == 0 && length(longopts) == 0)
26        return -1                # no options given
27
28    if (argv[Optind] == "--") {  # all done
29        Optind++
30        _opti = 0
31        return -1
32    } else if (argv[Optind] !~ /^-[^:[:space:]]/) {
33        _opti = 0
34        return -1
35    }
36    if (argv[Optind] !~ /^--/) {        # if this is a short option
37        if (_opti == 0)
38            _opti = 2
39        thisopt = substr(argv[Optind], _opti, 1)
40        Optopt = thisopt
41        i = index(options, thisopt)
42        if (i == 0) {
43            if (Opterr)
44                printf("%c -- invalid option\n", thisopt) > "/dev/stderr"
45            if (_opti >= length(argv[Optind])) {
46                Optind++
47                _opti = 0
48            } else
49                _opti++
50            return "?"
51        }
52        if (substr(options, i + 1, 1) == ":") {
53            # get option argument
54            if (length(substr(argv[Optind], _opti + 1)) > 0)
55                Optarg = substr(argv[Optind], _opti + 1)
56            else
57                Optarg = argv[++Optind]
58            _opti = 0
59        } else
60            Optarg = ""
61        if (_opti == 0 || _opti >= length(argv[Optind])) {
62            Optind++
63            _opti = 0
64        } else
65            _opti++
66        return thisopt
67    } else {
68        j = index(argv[Optind], "=")
69        if (j > 0)
70            thisopt = substr(argv[Optind], 3, j - 3)
71        else
72            thisopt = substr(argv[Optind], 3)
73        Optopt = thisopt
74        i = match(longopts, "(^|,)" thisopt "($|[,:])")
75        if (i == 0) {
76            if (Opterr)
77                 printf("%s -- invalid option\n", thisopt) > "/dev/stderr"
78            Optind++
79            return "?"
80        }
81        if (substr(longopts, i+1+length(thisopt), 1) == ":") {
82            if (j > 0)
83                Optarg = substr(argv[Optind], j + 1)
84            else
85                Optarg = argv[++Optind]
86        } else
87            Optarg = ""
88        Optind++
89        return thisopt
90    }
91}
92BEGIN {
93    Opterr = 1    # default is to diagnose
94    Optind = 1    # skip ARGV[0]
95
96    # test program
97    if (_getopt_test) {
98        _myshortopts = "ab:cd"
99        _mylongopts = "longa,longb:,otherc,otherd"
100
101        while ((_go_c = getopt(ARGC, ARGV, _myshortopts, _mylongopts)) != -1)
102            printf("c = <%s>, Optarg = <%s>\n", _go_c, Optarg)
103        printf("non-option arguments:\n")
104        for (; Optind < ARGC; Optind++)
105            printf("\tARGV[%d] = <%s>\n", Optind, ARGV[Optind])
106    }
107}
108