source: trunk/libtransmission/tr-getopt.c @ 14241

Last change on this file since 14241 was 14241, checked in by jordan, 8 years ago

Copyedit the license's revised text: (1) remove unnecessary repitition use of the word 'license' from the top of the header and source files (2) add the standard 'we hope it's useful, but no warranty' clause to COPYING (3) make explicit that linking OpenSSL is allowed (see https://people.gnome.org/~markmc/openssl-and-the-gpl.html for background) (4) sync the Qt and GTK+ clients' license popups with COPYING's revised text

  • Property svn:keywords set to Date Rev Author Id
File size: 6.2 KB
Line 
1/*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: tr-getopt.c 14241 2014-01-21 03:10:30Z jordan $
8 */
9
10#include <ctype.h> /* isspace () */
11#include <stdio.h>
12#include <stdlib.h> /* exit () */
13#include <string.h>
14
15#include "tr-getopt.h"
16
17#ifndef MAX
18 #define MAX(a, b)(((a) > (b)) ? (a) : (b))
19#endif
20
21int tr_optind = 1;
22
23static const char*
24getArgName (const tr_option * opt)
25{
26    const char * arg;
27
28    if (!opt->has_arg)
29        arg = "";
30    else if (opt->argName)
31        arg = opt->argName;
32    else
33        arg = "<args>";
34
35    return arg;
36}
37
38static int
39get_next_line_len (const char * description, int maxlen)
40{
41    int end;
42    int len = strlen (description);
43
44    if (len < maxlen)
45        return len;
46
47    end = maxlen < len ? maxlen : len;
48    while ((end > 0) && !isspace (description[end]))
49        --end;
50
51    return end ? end : len;
52}
53
54static void
55getopts_usage_line (const tr_option * opt,
56                    int               longWidth,
57                    int               shortWidth,
58                    int               argWidth)
59{
60    int len;
61    const char * longName   = opt->longName ? opt->longName : "";
62    const char * shortName  = opt->shortName ? opt->shortName : "";
63    const char * arg        = getArgName (opt);
64
65    const int d_indent = shortWidth + longWidth + argWidth + 7;
66    const int d_width = 80 - d_indent;
67    const char * d = opt->description;
68
69    printf (" %s%-*s %s%-*s %-*s ",
70          (shortName && *shortName ? "-" : " "), shortWidth, shortName,
71          (longName && *longName ? "--" : "  "), longWidth, longName,
72            argWidth, arg);
73    len = get_next_line_len (d, d_width);
74    printf ("%*.*s\n", len, len, d);
75
76    d += len;
77    while (isspace (*d)) ++d;
78
79    while ((len = get_next_line_len (d, d_width))) {
80        printf ("%*.*s%*.*s\n", d_indent, d_indent, "", len, len, d);
81        d += len;
82        while (isspace (*d)) ++d;
83    }
84}
85
86static void
87maxWidth (const struct tr_option * o,
88          int *                    longWidth,
89          int *                    shortWidth,
90          int *                    argWidth)
91{
92    const char * arg;
93
94    if (o->longName)
95        *longWidth = MAX (*longWidth, (int)strlen (o->longName));
96
97    if (o->shortName)
98        *shortWidth = MAX (*shortWidth, (int)strlen (o->shortName));
99
100    if ((arg = getArgName (o)))
101        *argWidth = MAX (*argWidth, (int)strlen (arg));
102}
103
104void
105tr_getopt_usage (const char *           progName,
106                 const char *           description,
107                 const struct tr_option opts[])
108{
109    int                      longWidth = 0;
110    int                      shortWidth = 0;
111    int                      argWidth = 0;
112    struct tr_option         help;
113    const struct tr_option * o;
114
115    for (o = opts; o->val; ++o)
116        maxWidth (o, &longWidth, &shortWidth, &argWidth);
117
118    help.val = -1;
119    help.longName = "help";
120    help.description = "Display this help page and exit";
121    help.shortName = "h";
122    help.has_arg = 0;
123    maxWidth (&help, &longWidth, &shortWidth, &argWidth);
124
125    if (description == NULL)
126        description = "Usage: %s [options]";
127    printf (description, progName);
128    printf ("\n\nOptions:\n");
129    getopts_usage_line (&help, longWidth, shortWidth, argWidth);
130    for (o = opts; o->val; ++o)
131        getopts_usage_line (o, longWidth, shortWidth, argWidth);
132}
133
134static const tr_option *
135findOption (const tr_option * opts,
136            const char *      str,
137            const char **     setme_arg)
138{
139    size_t            matchlen = 0;
140    const char *      arg = NULL;
141    const tr_option * o;
142    const tr_option * match = NULL;
143
144    /* find the longest matching option */
145    for (o = opts; o->val; ++o)
146    {
147        size_t len = o->longName ? strlen (o->longName) : 0;
148
149        if ((matchlen < len)
150          && (str[0] == '-')
151          && (str[1] == '-')
152          && !strncmp (str+2, o->longName, len)
153          && (str[len + 2] == '\0' || (o->has_arg && str[len + 2] == '=')))
154        {
155            matchlen = len;
156            match = o;
157            arg = str[len + 2] == '=' ? str + len + 3 : NULL;
158        }
159
160        len = o->shortName ? strlen (o->shortName) : 0;
161
162        if ((matchlen < len)
163          && (str[0] == '-')
164          && !strncmp (str+1, o->shortName, len)
165          && (str[len + 1] == '\0' || o->has_arg))
166        {
167            matchlen = len;
168            match = o;
169            switch (str[len + 1])
170            {
171                case '\0':
172                    arg = NULL;          break;
173
174                case '=':
175                    arg = str + len + 2; break;
176
177                default:
178                    arg = str + len + 1; break;
179            }
180        }
181    }
182
183    if (setme_arg)
184        *setme_arg = arg;
185
186    return match;
187}
188
189int
190tr_getopt (const char *      usage,
191           int               argc,
192           const char **     argv,
193           const tr_option * opts,
194           const char **     setme_optarg)
195{
196    int               i;
197    const char *      arg = NULL;
198    const tr_option * o = NULL;
199
200    *setme_optarg = NULL;
201
202    /* handle the builtin 'help' option */
203    for (i = 1; i < argc; ++i)
204    {
205        if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "--help"))
206        {
207            tr_getopt_usage (argv[0], usage, opts);
208            exit (0);
209        }
210    }
211
212    /* out of options? */
213    if (argc == 1 || tr_optind >= argc)
214        return TR_OPT_DONE;
215
216    o = findOption (opts, argv[tr_optind], &arg);
217    if (!o)
218    {
219        /* let the user know we got an unknown option... */
220        *setme_optarg = argv[tr_optind++];
221        return TR_OPT_UNK;
222    }
223
224    if (!o->has_arg)
225    {
226        /* no argument needed for this option, so we're done */
227        if (arg)
228            return TR_OPT_ERR;
229        *setme_optarg = NULL;
230        ++tr_optind;
231        return o->val;
232    }
233
234    /* option needed an argument, and it was embedded in this string */
235    if (arg)
236    {
237        *setme_optarg = arg;
238        ++tr_optind;
239        return o->val;
240    }
241
242    /* throw an error if the option needed an argument but didn't get one */
243    if (++tr_optind >= argc)
244        return TR_OPT_ERR;
245    if (findOption (opts, argv[tr_optind], NULL))
246        return TR_OPT_ERR;
247
248    *setme_optarg = argv[tr_optind++];
249    return o->val;
250}
Note: See TracBrowser for help on using the repository browser.