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

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

(daemon) let file download flags and priorities be set by transmission-remote. Add examples to the man page and --help.
(libT) minor tweaks to tr-getopt
(gtk) tweak the preference dialog's "port forwarding" text for clarity as suggested in the forums

File size: 6.1 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    /* try all the longopts first to avoid collisions between
120       long options, and short options with args appended to them  */
121    for( o=opts; o->val; ++o ) {
122        if( o->longName && (str[0]=='-') && (str[1]=='-') ) {
123            if( !strcmp( o->longName, str+2 ) ) {
124                if( nested ) *nested = NULL;
125                return o;
126            }
127            len = strlen( o->longName );
128            if( !memcmp( o->longName, str+2, len ) && str[len+2]=='=' ) {
129                if( nested ) *nested = str+len+3;
130                return o;
131            }
132        }
133    }
134
135    /* look for a matching shortopt */ 
136    for( o=opts; o->val; ++o )
137    {
138        if( o->shortName && (str[0]=='-') ) {
139            if( !strcmp( o->shortName, str+1 ) ) {
140                if( nested ) *nested = NULL;
141                return o;
142            }
143            len = strlen( o->shortName );
144            if( !memcmp( o->shortName, str+1, len ) ) {
145                if( nested )
146                    *nested = str[len+1]=='=' ? str+len+2 : str+len+1;
147                return o;
148            }
149        }
150    }
151
152    return NULL;
153}
154
155int
156tr_getopt( const char        * usage,
157           int                 argc,
158           const char       ** argv,
159           const tr_option   * opts,
160           const char       ** setme_optarg )
161{
162    int i;
163    const char * nest = NULL;
164    const tr_option * o = NULL;
165
166    *setme_optarg = NULL; 
167 
168    /* handle the builtin 'help' option */
169    for( i=1; i<argc; ++i ) {
170        if( !strcmp(argv[i], "-h") || !strcmp(argv[i], "--help" ) ) {
171            tr_getopt_usage( argv[0], usage, opts );
172            exit( 0 );
173        }
174    }
175
176    /* out of options? */
177    if( argc==1 || tr_optind>=argc )
178        return TR_OPT_DONE;
179
180    o = findOption( opts, argv[tr_optind], &nest );
181    if( !o ) {
182        /* let the user know we got an unknown option... */
183        *setme_optarg = argv[tr_optind++];
184        return TR_OPT_UNK;
185    }
186
187    if( !o->has_arg ) {
188        /* no argument needed for this option, so we're done */
189        if( nest )
190            return TR_OPT_ERR;
191        *setme_optarg = NULL;
192        tr_optind++;
193        return o->val;
194    }
195
196    /* option needed an argument, and it was nested in this string */
197    if( nest ) {
198        *setme_optarg = nest;
199        tr_optind++;
200        return o->val;
201    }
202
203    /* throw an error if the option needed an argument but didn't get one */
204    if( ++tr_optind >= argc )
205        return TR_OPT_ERR;
206    if( findOption( opts, argv[tr_optind], NULL ))
207        return TR_OPT_ERR;
208
209    *setme_optarg = argv[tr_optind++];
210    return o->val;
211}
Note: See TracBrowser for help on using the repository browser.