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

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

have daemon and cli use tr-getopt too.

File size: 5.1 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:$
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 option_index = 1;
24
25static const char*
26getArgName( const tr_option * opt )
27{
28    if( !opt->has_arg )   return "";
29    if( opt->argName ) return opt->argName;
30    return "<args>";
31}
32
33static void
34getopts_usage_line( const tr_option * opt,
35                    int longWidth, int shortWidth, int argWidth )
36{
37    const char * longName   = opt->longName ? opt->longName : "";
38    const char * shortName  = opt->shortName ? opt->shortName : "";
39    const char * arg        = getArgName( opt );
40    printf( "  -%*s, --%-*s %-*s  %s\n", shortWidth, shortName,
41                                         longWidth, longName,
42                                         argWidth, arg,
43                                         opt->description );
44}
45
46void
47tr_getopt_usage( const char             * progName,
48                 const char             * description,
49                 const struct tr_option    opts[] )
50{
51  int count;
52  int longWidth = 0;
53  int shortWidth = 0;
54  int argWidth = 0;
55  struct tr_option help;
56
57  for( count=0; opts[count].description; ++count )
58  {
59    const char * arg;
60
61    if( opts[count].longName ) 
62      longWidth = MAX( longWidth, (int)strlen( opts[count].longName ) );
63
64    if( opts[count].shortName )
65      shortWidth = MAX( shortWidth, (int)strlen( opts[count].shortName ) );
66
67    if(( arg = getArgName( &opts[count] )))
68      argWidth = MAX( argWidth, (int)strlen( arg ) );
69  }
70
71  if( !description )
72    description = "Usage: %s [options]";
73  printf( description, progName );
74  printf( "\n\n" );
75  printf( "Options:\n" );
76
77  help.val = -1;
78  help.longName = "help";
79  help.description = "Display this help page and exit";
80  help.shortName = "h";
81  help.has_arg = 0;
82  getopts_usage_line( &help, longWidth, shortWidth, argWidth );
83 
84  for( count=0; opts[count].description; ++count )
85      getopts_usage_line( &opts[count], longWidth, shortWidth, argWidth );
86}
87
88static const tr_option *
89findOption( const tr_option   * opts,
90            const char        * str,
91            const char       ** nested )
92{
93    size_t len;
94    const tr_option * o;
95
96    for( o=opts; o->val; ++o )
97    {
98        if( o->longName && (str[0]=='-') && (str[1]=='-') ) {
99            if( !strcmp( o->longName, str+2 ) ) {
100                if( nested ) *nested = NULL;
101                return o;
102            }
103            len = strlen( o->longName );
104            if( !memcmp( o->longName, str+2, len ) && str[len+2]=='=' ) {
105                if( nested ) *nested = str+len+3;
106                return o;
107            }
108        }
109       
110        if( o->shortName && (str[0]=='-') ) {
111            if( !strcmp( o->shortName, str+1 ) ) {
112                if( nested ) *nested = NULL;
113                return o;
114            }
115            len = strlen( o->shortName );
116            if( !memcmp( o->shortName, str+1, len ) && str[len+1]=='=' ) {
117                if( nested ) *nested = str+len+2;
118                return o;
119            }
120        }
121    }
122
123    return NULL;
124}
125
126static void
127showUsageAndExit( const char        * appName,
128                  const char        * description,
129                  const tr_option   * opts )
130{
131    tr_getopt_usage( appName, description, opts );
132    exit( 0 );
133}
134
135
136int
137tr_getopt( const char        * usage,
138           int                 argc,
139           const char       ** argv,
140           const tr_option   * opts,
141           const char       ** setme_optarg )
142{
143    int i;
144    const char * nest = NULL;
145    const tr_option * o = NULL;
146
147    *setme_optarg = NULL; 
148
149    if( argc==1 || argc==option_index )
150        return TR_OPT_DONE;
151 
152    /* handle the builtin 'help' option */
153    for( i=1; i<argc; ++i )
154        if( !strcmp(argv[i], "-h") || !strcmp(argv[i], "--help" ) )
155            showUsageAndExit( argv[0], usage, opts );
156
157    /* out of options */
158    if( option_index >= argc )
159        return TR_OPT_DONE;
160
161    o = findOption( opts, argv[option_index], &nest );
162    if( !o ) {
163        /* let the user know we got an unknown option... */
164        *setme_optarg = argv[option_index++];
165        return TR_OPT_UNK;
166    }
167
168    if( !o->has_arg ) {
169        /* no argument needed for this option, so we're done */
170        if( nest )
171            return TR_OPT_ERR;
172        *setme_optarg = NULL;
173        option_index++;
174        return o->val;
175    }
176
177    /* option needed an argument, and it was nested in this string */
178    if( nest ) {
179        *setme_optarg = nest;
180        option_index++;
181        return o->val;
182    }
183
184    /* throw an error if the option needed an argument but didn't get one */
185    if( ++option_index >= argc )
186        return TR_OPT_ERR;
187    if( findOption( opts, argv[option_index], NULL ))
188        return TR_OPT_ERR;
189
190    *setme_optarg = argv[option_index++];
191    return o->val;
192}
Note: See TracBrowser for help on using the repository browser.