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

Last change on this file since 6307 was 6307, checked in by charles, 14 years ago

(libT) let tr_getopt() support short options' arguments to be embedded, such as -p51413

File size: 5.9 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * $Id:$
23 */
24
25#include <stdio.h>
26#include <stdlib.h> /* exit() */
27#include <string.h>
28
29#include "tr-getopt.h"
30
31#ifndef MAX
32#define MAX(a,b) (((a) > (b)) ? (a) : (b))
33#endif
34
35int tr_optind = 1;
36
37static const char*
38getArgName( const tr_option * opt )
39{
40    char * arg;
41
42    if( !opt->has_arg )
43        arg = "";
44    else if( opt->argName )
45        arg = opt->argName;
46    else
47        arg = "<args>";
48
49    return arg;
50}
51
52static void
53getopts_usage_line( const tr_option * opt,
54                    int longWidth, int shortWidth, int argWidth )
55{
56    const char * longName   = opt->longName ? opt->longName : "";
57    const char * shortName  = opt->shortName ? opt->shortName : "";
58    const char * arg        = getArgName( opt );
59    printf( "  -%*s, --%-*s %-*s  %s\n", shortWidth, shortName,
60                                         longWidth, longName,
61                                         argWidth, arg,
62                                         opt->description );
63}
64
65static void
66maxWidth( const struct tr_option * o,
67          int * longWidth, int * shortWidth, int * argWidth )
68{
69    const char * arg;
70
71    if( o->longName ) 
72        *longWidth = MAX( *longWidth, (int)strlen( o->longName ) );
73
74    if( o->shortName )
75        *shortWidth = MAX( *shortWidth, (int)strlen( o->shortName ) );
76
77    if(( arg = getArgName( o )))
78        *argWidth = MAX( *argWidth, (int)strlen( arg ) );
79}
80
81void
82tr_getopt_usage( const char              * progName,
83                 const char              * description,
84                 const struct tr_option    opts[] )
85{
86    int longWidth = 0;
87    int shortWidth = 0;
88    int argWidth = 0;
89    struct tr_option help;
90    const struct tr_option * o;
91
92    for( o=opts; o->val; ++o )
93        maxWidth( o, &longWidth, &shortWidth, &argWidth );
94
95    help.val = -1;
96    help.longName = "help";
97    help.description = "Display this help page and exit";
98    help.shortName = "h";
99    help.has_arg = 0;
100    maxWidth( &help, &longWidth, &shortWidth, &argWidth );
101
102    if( description == NULL )
103        description = "Usage: %s [options]";
104    printf( description, progName );
105    printf( "\n\nOptions:\n" );
106    getopts_usage_line( &help, longWidth, shortWidth, argWidth );
107    for( o=opts; o->val; ++o )
108        getopts_usage_line( o, longWidth, shortWidth, argWidth );
109}
110
111static const tr_option *
112findOption( const tr_option   * opts,
113            const char        * str,
114            const char       ** nested )
115{
116    size_t len;
117    const tr_option * o;
118
119    for( o=opts; o->val; ++o )
120    {
121        if( o->longName && (str[0]=='-') && (str[1]=='-') ) {
122            if( !strcmp( o->longName, str+2 ) ) {
123                if( nested ) *nested = NULL;
124                return o;
125            }
126            len = strlen( o->longName );
127            if( !memcmp( o->longName, str+2, len ) && str[len+2]=='=' ) {
128                if( nested ) *nested = str+len+3;
129                return o;
130            }
131        }
132       
133        if( o->shortName && (str[0]=='-') ) {
134            if( !strcmp( o->shortName, str+1 ) ) {
135                if( nested ) *nested = NULL;
136                return o;
137            }
138            len = strlen( o->shortName );
139            if( !memcmp( o->shortName, str+1, len ) ) {
140                if( nested )
141                    *nested = str[len+1]=='=' ? str+len+2 : str+len+1;
142                return o;
143            }
144        }
145    }
146
147    return NULL;
148}
149
150int
151tr_getopt( const char        * usage,
152           int                 argc,
153           const char       ** argv,
154           const tr_option   * opts,
155           const char       ** setme_optarg )
156{
157    int i;
158    const char * nest = NULL;
159    const tr_option * o = NULL;
160
161    *setme_optarg = NULL; 
162 
163    /* handle the builtin 'help' option */
164    for( i=1; i<argc; ++i ) {
165        if( !strcmp(argv[i], "-h") || !strcmp(argv[i], "--help" ) ) {
166            tr_getopt_usage( argv[0], usage, opts );
167            exit( 0 );
168        }
169    }
170
171    /* out of options? */
172    if( argc==1 || tr_optind>=argc )
173        return TR_OPT_DONE;
174
175    o = findOption( opts, argv[tr_optind], &nest );
176    if( !o ) {
177        /* let the user know we got an unknown option... */
178        *setme_optarg = argv[tr_optind++];
179        return TR_OPT_UNK;
180    }
181
182    if( !o->has_arg ) {
183        /* no argument needed for this option, so we're done */
184        if( nest )
185            return TR_OPT_ERR;
186        *setme_optarg = NULL;
187        tr_optind++;
188        return o->val;
189    }
190
191    /* option needed an argument, and it was nested in this string */
192    if( nest ) {
193        *setme_optarg = nest;
194        tr_optind++;
195        return o->val;
196    }
197
198    /* throw an error if the option needed an argument but didn't get one */
199    if( ++tr_optind >= argc )
200        return TR_OPT_ERR;
201    if( findOption( opts, argv[tr_optind], NULL ))
202        return TR_OPT_ERR;
203
204    *setme_optarg = argv[tr_optind++];
205    return o->val;
206}
Note: See TracBrowser for help on using the repository browser.