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

Last change on this file since 7200 was 7200, checked in by charles, 12 years ago

enable keywords properties for $Id$

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