2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
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.
10 * $Id: tr-getopt.c 10782 2010-06-16 14:05:18Z charles $
13 #include <ctype.h> /* isspace() */
15 #include <stdlib.h> /* exit() */
18 #include "tr-getopt.h"
21 #define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )
27 getArgName( const tr_option
* opt
)
33 else if( opt
->argName
)
42 get_next_line_len( const char * description
, int maxlen
)
45 int len
= strlen( description
);
50 end
= maxlen
< len
? maxlen
: len
;
51 while( ( end
> 0 ) && !isspace( description
[end
] ) )
54 return end
? end
: len
;
58 getopts_usage_line( const tr_option
* opt
,
64 const char * longName
= opt
->longName
? opt
->longName
: "";
65 const char * shortName
= opt
->shortName
? opt
->shortName
: "";
66 const char * arg
= getArgName( opt
);
68 const int d_indent
= shortWidth
+ longWidth
+ argWidth
+ 7;
69 const int d_width
= 80 - d_indent
;
70 const char * d
= opt
->description
;
72 printf( " %s%-*s %s%-*s %-*s ",
73 (shortName
&& *shortName
? "-" : " "), shortWidth
, shortName
,
74 (longName
&& *longName
? "--" : " "), longWidth
, longName
,
76 len
= get_next_line_len( d
, d_width
);
77 printf( "%*.*s\n", len
, len
, d
);
80 while( isspace( *d
) ) ++d
;
82 while(( len
= get_next_line_len( d
, d_width
))) {
83 printf( "%*.*s%*.*s\n", d_indent
, d_indent
, "", len
, len
, d
);
85 while( isspace( *d
) ) ++d
;
90 maxWidth( const struct tr_option
* o
,
98 *longWidth
= MAX( *longWidth
, (int)strlen( o
->longName
) );
101 *shortWidth
= MAX( *shortWidth
, (int)strlen( o
->shortName
) );
103 if( ( arg
= getArgName( o
) ) )
104 *argWidth
= MAX( *argWidth
, (int)strlen( arg
) );
108 tr_getopt_usage( const char * progName
,
109 const char * description
,
110 const struct tr_option opts
[] )
115 struct tr_option help
;
116 const struct tr_option
* o
;
118 for( o
= opts
; o
->val
; ++o
)
119 maxWidth( o
, &longWidth
, &shortWidth
, &argWidth
);
122 help
.longName
= "help";
123 help
.description
= "Display this help page and exit";
124 help
.shortName
= "h";
126 maxWidth( &help
, &longWidth
, &shortWidth
, &argWidth
);
128 if( description
== NULL
)
129 description
= "Usage: %s [options]";
130 printf( description
, progName
);
131 printf( "\n\nOptions:\n" );
132 getopts_usage_line( &help
, longWidth
, shortWidth
, argWidth
);
133 for( o
= opts
; o
->val
; ++o
)
134 getopts_usage_line( o
, longWidth
, shortWidth
, argWidth
);
137 static const tr_option
*
138 findOption( const tr_option
* opts
,
140 const char ** setme_arg
)
143 const char * arg
= NULL
;
145 const tr_option
* match
= NULL
;
147 /* find the longest matching option */
148 for( o
= opts
; o
->val
; ++o
)
150 size_t len
= o
->longName
? strlen( o
->longName
) : 0;
152 if( ( matchlen
< len
) && !memcmp( str
, "--", 2 )
153 && !memcmp( str
+ 2, o
->longName
, len
)
154 && ( str
[len
+ 2] == '\0' || ( o
->has_arg
&& str
[len
+ 2] == '=' ) ) )
158 arg
= str
[len
+ 2] == '=' ? str
+ len
+ 3 : NULL
;
161 len
= o
->shortName
? strlen( o
->shortName
) : 0;
163 if( ( matchlen
< len
) && !memcmp( str
, "-", 1 )
164 && !memcmp( str
+ 1, o
->shortName
, len
)
165 && ( str
[len
+ 1] == '\0' || o
->has_arg
) )
169 switch( str
[len
+ 1] )
175 arg
= str
+ len
+ 2; break;
178 arg
= str
+ len
+ 1; break;
190 tr_getopt( const char * usage
,
193 const tr_option
* opts
,
194 const char ** setme_optarg
)
197 const char * arg
= NULL
;
198 const tr_option
* o
= NULL
;
200 *setme_optarg
= NULL
;
202 /* handle the builtin 'help' option */
203 for( i
= 1; i
< argc
; ++i
)
205 if( !strcmp( argv
[i
], "-h" ) || !strcmp( argv
[i
], "--help" ) )
207 tr_getopt_usage( argv
[0], usage
, opts
);
212 /* out of options? */
213 if( argc
== 1 || tr_optind
>= argc
)
216 o
= findOption( opts
, argv
[tr_optind
], &arg
);
219 /* let the user know we got an unknown option... */
220 *setme_optarg
= argv
[tr_optind
++];
226 /* no argument needed for this option, so we're done */
229 *setme_optarg
= NULL
;
234 /* option needed an argument, and it was embedded in this string */
242 /* throw an error if the option needed an argument but didn't get one */
243 if( ++tr_optind
>= argc
)
245 if( findOption( opts
, argv
[tr_optind
], NULL
) )
248 *setme_optarg
= argv
[tr_optind
++];