2009-04-20 17:00:56 +04:00
# include "util.h"
# include "parse-options.h"
# include "cache.h"
2012-07-30 06:53:51 +04:00
# include "header.h"
2009-04-20 17:00:56 +04:00
# define OPT_SHORT 1
# define OPT_UNSET 2
static int opterror ( const struct option * opt , const char * reason , int flags )
{
if ( flags & OPT_SHORT )
return error ( " switch `%c' %s " , opt - > short_name , reason ) ;
if ( flags & OPT_UNSET )
return error ( " option `no-%s' %s " , opt - > long_name , reason ) ;
return error ( " option `%s' %s " , opt - > long_name , reason ) ;
}
static int get_arg ( struct parse_opt_ctx_t * p , const struct option * opt ,
int flags , const char * * arg )
{
if ( p - > opt ) {
* arg = p - > opt ;
p - > opt = NULL ;
2009-07-02 19:58:20 +04:00
} else if ( ( opt - > flags & PARSE_OPT_LASTARG_DEFAULT ) & & ( p - > argc = = 1 | |
* * ( p - > argv + 1 ) = = ' - ' ) ) {
2009-04-20 17:00:56 +04:00
* arg = ( const char * ) opt - > defval ;
} else if ( p - > argc > 1 ) {
p - > argc - - ;
* arg = * + + p - > argv ;
} else
return opterror ( opt , " requires a value " , flags ) ;
return 0 ;
}
static int get_value ( struct parse_opt_ctx_t * p ,
const struct option * opt , int flags )
{
2009-06-03 23:48:40 +04:00
const char * s , * arg = NULL ;
2009-04-20 17:00:56 +04:00
const int unset = flags & OPT_UNSET ;
if ( unset & & p - > opt )
return opterror ( opt , " takes no value " , flags ) ;
if ( unset & & ( opt - > flags & PARSE_OPT_NONEG ) )
return opterror ( opt , " isn't available " , flags ) ;
if ( ! ( flags & OPT_SHORT ) & & p - > opt ) {
switch ( opt - > type ) {
case OPTION_CALLBACK :
if ( ! ( opt - > flags & PARSE_OPT_NOARG ) )
break ;
/* FALLTHROUGH */
case OPTION_BOOLEAN :
2010-04-13 12:37:33 +04:00
case OPTION_INCR :
2009-04-20 17:00:56 +04:00
case OPTION_BIT :
2010-05-17 23:22:41 +04:00
case OPTION_SET_UINT :
2009-04-20 17:00:56 +04:00
case OPTION_SET_PTR :
return opterror ( opt , " takes no value " , flags ) ;
2009-08-15 14:26:57 +04:00
case OPTION_END :
case OPTION_ARGUMENT :
case OPTION_GROUP :
case OPTION_STRING :
case OPTION_INTEGER :
2010-05-17 22:30:00 +04:00
case OPTION_UINTEGER :
2009-08-15 14:26:57 +04:00
case OPTION_LONG :
2010-05-17 19:16:48 +04:00
case OPTION_U64 :
2009-04-20 17:00:56 +04:00
default :
break ;
}
}
switch ( opt - > type ) {
case OPTION_BIT :
if ( unset )
* ( int * ) opt - > value & = ~ opt - > defval ;
else
* ( int * ) opt - > value | = opt - > defval ;
return 0 ;
case OPTION_BOOLEAN :
2010-04-13 12:37:33 +04:00
* ( bool * ) opt - > value = unset ? false : true ;
return 0 ;
case OPTION_INCR :
2009-04-20 17:00:56 +04:00
* ( int * ) opt - > value = unset ? 0 : * ( int * ) opt - > value + 1 ;
return 0 ;
2010-05-17 23:22:41 +04:00
case OPTION_SET_UINT :
* ( unsigned int * ) opt - > value = unset ? 0 : opt - > defval ;
2009-04-20 17:00:56 +04:00
return 0 ;
case OPTION_SET_PTR :
* ( void * * ) opt - > value = unset ? NULL : ( void * ) opt - > defval ;
return 0 ;
case OPTION_STRING :
if ( unset )
* ( const char * * ) opt - > value = NULL ;
else if ( opt - > flags & PARSE_OPT_OPTARG & & ! p - > opt )
* ( const char * * ) opt - > value = ( const char * ) opt - > defval ;
else
return get_arg ( p , opt , flags , ( const char * * ) opt - > value ) ;
return 0 ;
case OPTION_CALLBACK :
if ( unset )
return ( * opt - > callback ) ( opt , NULL , 1 ) ? ( - 1 ) : 0 ;
if ( opt - > flags & PARSE_OPT_NOARG )
return ( * opt - > callback ) ( opt , NULL , 0 ) ? ( - 1 ) : 0 ;
if ( opt - > flags & PARSE_OPT_OPTARG & & ! p - > opt )
return ( * opt - > callback ) ( opt , NULL , 0 ) ? ( - 1 ) : 0 ;
if ( get_arg ( p , opt , flags , & arg ) )
return - 1 ;
return ( * opt - > callback ) ( opt , arg , 0 ) ? ( - 1 ) : 0 ;
case OPTION_INTEGER :
if ( unset ) {
* ( int * ) opt - > value = 0 ;
return 0 ;
}
if ( opt - > flags & PARSE_OPT_OPTARG & & ! p - > opt ) {
* ( int * ) opt - > value = opt - > defval ;
return 0 ;
}
if ( get_arg ( p , opt , flags , & arg ) )
return - 1 ;
* ( int * ) opt - > value = strtol ( arg , ( char * * ) & s , 10 ) ;
if ( * s )
return opterror ( opt , " expects a numerical value " , flags ) ;
2009-06-03 13:24:33 +04:00
return 0 ;
2010-05-17 22:30:00 +04:00
case OPTION_UINTEGER :
if ( unset ) {
* ( unsigned int * ) opt - > value = 0 ;
return 0 ;
}
if ( opt - > flags & PARSE_OPT_OPTARG & & ! p - > opt ) {
* ( unsigned int * ) opt - > value = opt - > defval ;
return 0 ;
}
if ( get_arg ( p , opt , flags , & arg ) )
return - 1 ;
* ( unsigned int * ) opt - > value = strtol ( arg , ( char * * ) & s , 10 ) ;
if ( * s )
return opterror ( opt , " expects a numerical value " , flags ) ;
return 0 ;
2009-06-03 13:24:33 +04:00
case OPTION_LONG :
if ( unset ) {
* ( long * ) opt - > value = 0 ;
return 0 ;
}
if ( opt - > flags & PARSE_OPT_OPTARG & & ! p - > opt ) {
* ( long * ) opt - > value = opt - > defval ;
return 0 ;
}
if ( get_arg ( p , opt , flags , & arg ) )
return - 1 ;
* ( long * ) opt - > value = strtol ( arg , ( char * * ) & s , 10 ) ;
if ( * s )
return opterror ( opt , " expects a numerical value " , flags ) ;
2009-04-20 17:00:56 +04:00
return 0 ;
2010-05-17 19:16:48 +04:00
case OPTION_U64 :
if ( unset ) {
* ( u64 * ) opt - > value = 0 ;
return 0 ;
}
if ( opt - > flags & PARSE_OPT_OPTARG & & ! p - > opt ) {
* ( u64 * ) opt - > value = opt - > defval ;
return 0 ;
}
if ( get_arg ( p , opt , flags , & arg ) )
return - 1 ;
* ( u64 * ) opt - > value = strtoull ( arg , ( char * * ) & s , 10 ) ;
if ( * s )
return opterror ( opt , " expects a numerical value " , flags ) ;
return 0 ;
2009-08-15 14:26:57 +04:00
case OPTION_END :
case OPTION_ARGUMENT :
case OPTION_GROUP :
2009-04-20 17:00:56 +04:00
default :
die ( " should not happen, someone must be hit on the forehead " ) ;
}
}
static int parse_short_opt ( struct parse_opt_ctx_t * p , const struct option * options )
{
for ( ; options - > type ! = OPTION_END ; options + + ) {
if ( options - > short_name = = * p - > opt ) {
p - > opt = p - > opt [ 1 ] ? p - > opt + 1 : NULL ;
return get_value ( p , options , OPT_SHORT ) ;
}
}
return - 2 ;
}
static int parse_long_opt ( struct parse_opt_ctx_t * p , const char * arg ,
const struct option * options )
{
const char * arg_end = strchr ( arg , ' = ' ) ;
const struct option * abbrev_option = NULL , * ambiguous_option = NULL ;
int abbrev_flags = 0 , ambiguous_flags = 0 ;
if ( ! arg_end )
arg_end = arg + strlen ( arg ) ;
for ( ; options - > type ! = OPTION_END ; options + + ) {
const char * rest ;
int flags = 0 ;
if ( ! options - > long_name )
continue ;
rest = skip_prefix ( arg , options - > long_name ) ;
if ( options - > type = = OPTION_ARGUMENT ) {
if ( ! rest )
continue ;
if ( * rest = = ' = ' )
return opterror ( options , " takes no value " , flags ) ;
if ( * rest )
continue ;
p - > out [ p - > cpidx + + ] = arg - 2 ;
return 0 ;
}
if ( ! rest ) {
/* abbreviated? */
if ( ! strncmp ( options - > long_name , arg , arg_end - arg ) ) {
is_abbreviated :
if ( abbrev_option ) {
/*
* If this is abbreviated , it is
* ambiguous . So when there is no
* exact match later , we need to
* error out .
*/
ambiguous_option = abbrev_option ;
ambiguous_flags = abbrev_flags ;
}
if ( ! ( flags & OPT_UNSET ) & & * arg_end )
p - > opt = arg_end + 1 ;
abbrev_option = options ;
abbrev_flags = flags ;
continue ;
}
/* negated and abbreviated very much? */
if ( ! prefixcmp ( " no- " , arg ) ) {
flags | = OPT_UNSET ;
goto is_abbreviated ;
}
/* negated? */
if ( strncmp ( arg , " no- " , 3 ) )
continue ;
flags | = OPT_UNSET ;
rest = skip_prefix ( arg + 3 , options - > long_name ) ;
/* abbreviated and negated? */
if ( ! rest & & ! prefixcmp ( options - > long_name , arg + 3 ) )
goto is_abbreviated ;
if ( ! rest )
continue ;
}
if ( * rest ) {
if ( * rest ! = ' = ' )
continue ;
p - > opt = rest + 1 ;
}
return get_value ( p , options , flags ) ;
}
if ( ambiguous_option )
return error ( " Ambiguous option: %s "
" (could be --%s%s or --%s%s) " ,
arg ,
( ambiguous_flags & OPT_UNSET ) ? " no- " : " " ,
ambiguous_option - > long_name ,
( abbrev_flags & OPT_UNSET ) ? " no- " : " " ,
abbrev_option - > long_name ) ;
if ( abbrev_option )
return get_value ( p , abbrev_option , abbrev_flags ) ;
return - 2 ;
}
static void check_typos ( const char * arg , const struct option * options )
{
if ( strlen ( arg ) < 3 )
return ;
if ( ! prefixcmp ( arg , " no- " ) ) {
error ( " did you mean `--%s` (with two dashes ?) " , arg ) ;
exit ( 129 ) ;
}
for ( ; options - > type ! = OPTION_END ; options + + ) {
if ( ! options - > long_name )
continue ;
if ( ! prefixcmp ( options - > long_name , arg ) ) {
error ( " did you mean `--%s` (with two dashes ?) " , arg ) ;
exit ( 129 ) ;
}
}
}
void parse_options_start ( struct parse_opt_ctx_t * ctx ,
int argc , const char * * argv , int flags )
{
memset ( ctx , 0 , sizeof ( * ctx ) ) ;
ctx - > argc = argc - 1 ;
ctx - > argv = argv + 1 ;
ctx - > out = argv ;
ctx - > cpidx = ( ( flags & PARSE_OPT_KEEP_ARGV0 ) ! = 0 ) ;
ctx - > flags = flags ;
if ( ( flags & PARSE_OPT_KEEP_UNKNOWN ) & &
( flags & PARSE_OPT_STOP_AT_NON_OPTION ) )
die ( " STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together " ) ;
}
static int usage_with_options_internal ( const char * const * ,
const struct option * , int ) ;
int parse_options_step ( struct parse_opt_ctx_t * ctx ,
const struct option * options ,
const char * const usagestr [ ] )
{
int internal_help = ! ( ctx - > flags & PARSE_OPT_NO_INTERNAL_HELP ) ;
/* we must reset ->opt, unknown short option leave it dangling */
ctx - > opt = NULL ;
for ( ; ctx - > argc ; ctx - > argc - - , ctx - > argv + + ) {
const char * arg = ctx - > argv [ 0 ] ;
if ( * arg ! = ' - ' | | ! arg [ 1 ] ) {
if ( ctx - > flags & PARSE_OPT_STOP_AT_NON_OPTION )
break ;
ctx - > out [ ctx - > cpidx + + ] = ctx - > argv [ 0 ] ;
continue ;
}
if ( arg [ 1 ] ! = ' - ' ) {
ctx - > opt = arg + 1 ;
if ( internal_help & & * ctx - > opt = = ' h ' )
return parse_options_usage ( usagestr , options ) ;
switch ( parse_short_opt ( ctx , options ) ) {
case - 1 :
return parse_options_usage ( usagestr , options ) ;
case - 2 :
goto unknown ;
2009-08-15 14:26:57 +04:00
default :
break ;
2009-04-20 17:00:56 +04:00
}
if ( ctx - > opt )
check_typos ( arg + 1 , options ) ;
while ( ctx - > opt ) {
if ( internal_help & & * ctx - > opt = = ' h ' )
return parse_options_usage ( usagestr , options ) ;
switch ( parse_short_opt ( ctx , options ) ) {
case - 1 :
return parse_options_usage ( usagestr , options ) ;
case - 2 :
/* fake a short option thing to hide the fact that we may have
* started to parse aggregated stuff
*
* This is leaky , too bad .
*/
ctx - > argv [ 0 ] = strdup ( ctx - > opt - 1 ) ;
* ( char * ) ctx - > argv [ 0 ] = ' - ' ;
goto unknown ;
2009-08-15 14:26:57 +04:00
default :
break ;
2009-04-20 17:00:56 +04:00
}
}
continue ;
}
if ( ! arg [ 2 ] ) { /* "--" */
if ( ! ( ctx - > flags & PARSE_OPT_KEEP_DASHDASH ) ) {
ctx - > argc - - ;
ctx - > argv + + ;
}
break ;
}
if ( internal_help & & ! strcmp ( arg + 2 , " help-all " ) )
return usage_with_options_internal ( usagestr , options , 1 ) ;
if ( internal_help & & ! strcmp ( arg + 2 , " help " ) )
return parse_options_usage ( usagestr , options ) ;
2012-10-02 19:21:34 +04:00
if ( ! strcmp ( arg + 2 , " list-opts " ) )
return PARSE_OPT_LIST ;
2009-04-20 17:00:56 +04:00
switch ( parse_long_opt ( ctx , arg + 2 , options ) ) {
case - 1 :
return parse_options_usage ( usagestr , options ) ;
case - 2 :
goto unknown ;
2009-08-15 14:26:57 +04:00
default :
break ;
2009-04-20 17:00:56 +04:00
}
continue ;
unknown :
if ( ! ( ctx - > flags & PARSE_OPT_KEEP_UNKNOWN ) )
return PARSE_OPT_UNKNOWN ;
ctx - > out [ ctx - > cpidx + + ] = ctx - > argv [ 0 ] ;
ctx - > opt = NULL ;
}
return PARSE_OPT_DONE ;
}
int parse_options_end ( struct parse_opt_ctx_t * ctx )
{
memmove ( ctx - > out + ctx - > cpidx , ctx - > argv , ctx - > argc * sizeof ( * ctx - > out ) ) ;
ctx - > out [ ctx - > cpidx + ctx - > argc ] = NULL ;
return ctx - > cpidx + ctx - > argc ;
}
int parse_options ( int argc , const char * * argv , const struct option * options ,
const char * const usagestr [ ] , int flags )
{
struct parse_opt_ctx_t ctx ;
2012-07-30 06:53:51 +04:00
perf_header__set_cmdline ( argc , argv ) ;
2009-04-20 17:00:56 +04:00
parse_options_start ( & ctx , argc , argv , flags ) ;
switch ( parse_options_step ( & ctx , options , usagestr ) ) {
case PARSE_OPT_HELP :
exit ( 129 ) ;
case PARSE_OPT_DONE :
break ;
2012-10-02 19:21:34 +04:00
case PARSE_OPT_LIST :
while ( options - > type ! = OPTION_END ) {
printf ( " --%s " , options - > long_name ) ;
options + + ;
}
exit ( 130 ) ;
2009-04-20 17:00:56 +04:00
default : /* PARSE_OPT_UNKNOWN */
if ( ctx . argv [ 0 ] [ 1 ] = = ' - ' ) {
error ( " unknown option `%s' " , ctx . argv [ 0 ] + 2 ) ;
} else {
error ( " unknown switch `%c' " , * ctx . opt ) ;
}
usage_with_options ( usagestr , options ) ;
}
return parse_options_end ( & ctx ) ;
}
# define USAGE_OPTS_WIDTH 24
# define USAGE_GAP 2
int usage_with_options_internal ( const char * const * usagestr ,
const struct option * opts , int full )
{
if ( ! usagestr )
return PARSE_OPT_HELP ;
2009-03-13 05:20:49 +03:00
fprintf ( stderr , " \n usage: %s \n " , * usagestr + + ) ;
2009-04-20 17:00:56 +04:00
while ( * usagestr & & * * usagestr )
2008-04-16 00:39:31 +04:00
fprintf ( stderr , " or: %s \n " , * usagestr + + ) ;
2009-04-20 17:00:56 +04:00
while ( * usagestr ) {
fprintf ( stderr , " %s%s \n " ,
* * usagestr ? " " : " " ,
* usagestr ) ;
usagestr + + ;
}
if ( opts - > type ! = OPTION_GROUP )
fputc ( ' \n ' , stderr ) ;
for ( ; opts - > type ! = OPTION_END ; opts + + ) {
size_t pos ;
int pad ;
if ( opts - > type = = OPTION_GROUP ) {
fputc ( ' \n ' , stderr ) ;
if ( * opts - > help )
fprintf ( stderr , " %s \n " , opts - > help ) ;
continue ;
}
if ( ! full & & ( opts - > flags & PARSE_OPT_HIDDEN ) )
continue ;
pos = fprintf ( stderr , " " ) ;
if ( opts - > short_name )
pos + = fprintf ( stderr , " -%c " , opts - > short_name ) ;
2009-12-10 10:22:17 +03:00
else
pos + = fprintf ( stderr , " " ) ;
2009-04-20 17:00:56 +04:00
if ( opts - > long_name & & opts - > short_name )
pos + = fprintf ( stderr , " , " ) ;
if ( opts - > long_name )
pos + = fprintf ( stderr , " --%s " , opts - > long_name ) ;
switch ( opts - > type ) {
case OPTION_ARGUMENT :
break ;
2010-05-17 22:30:00 +04:00
case OPTION_LONG :
case OPTION_U64 :
2009-04-20 17:00:56 +04:00
case OPTION_INTEGER :
2010-05-17 22:30:00 +04:00
case OPTION_UINTEGER :
2009-04-20 17:00:56 +04:00
if ( opts - > flags & PARSE_OPT_OPTARG )
if ( opts - > long_name )
pos + = fprintf ( stderr , " [=<n>] " ) ;
else
pos + = fprintf ( stderr , " [<n>] " ) ;
else
pos + = fprintf ( stderr , " <n> " ) ;
break ;
case OPTION_CALLBACK :
if ( opts - > flags & PARSE_OPT_NOARG )
break ;
/* FALLTHROUGH */
case OPTION_STRING :
if ( opts - > argh ) {
if ( opts - > flags & PARSE_OPT_OPTARG )
if ( opts - > long_name )
pos + = fprintf ( stderr , " [=<%s>] " , opts - > argh ) ;
else
pos + = fprintf ( stderr , " [<%s>] " , opts - > argh ) ;
else
pos + = fprintf ( stderr , " <%s> " , opts - > argh ) ;
} else {
if ( opts - > flags & PARSE_OPT_OPTARG )
if ( opts - > long_name )
pos + = fprintf ( stderr , " [=...] " ) ;
else
pos + = fprintf ( stderr , " [...] " ) ;
else
pos + = fprintf ( stderr , " ... " ) ;
}
break ;
2010-05-17 23:22:41 +04:00
default : /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
2009-08-15 14:26:57 +04:00
case OPTION_END :
case OPTION_GROUP :
case OPTION_BIT :
case OPTION_BOOLEAN :
2010-04-13 12:37:33 +04:00
case OPTION_INCR :
2010-05-17 23:22:41 +04:00
case OPTION_SET_UINT :
2009-08-15 14:26:57 +04:00
case OPTION_SET_PTR :
2009-04-20 17:00:56 +04:00
break ;
}
if ( pos < = USAGE_OPTS_WIDTH )
pad = USAGE_OPTS_WIDTH - pos ;
else {
fputc ( ' \n ' , stderr ) ;
pad = USAGE_OPTS_WIDTH ;
}
fprintf ( stderr , " %*s%s \n " , pad + USAGE_GAP , " " , opts - > help ) ;
}
fputc ( ' \n ' , stderr ) ;
return PARSE_OPT_HELP ;
}
void usage_with_options ( const char * const * usagestr ,
const struct option * opts )
{
2010-03-22 19:10:29 +03:00
exit_browser ( false ) ;
2009-04-20 17:00:56 +04:00
usage_with_options_internal ( usagestr , opts , 0 ) ;
exit ( 129 ) ;
}
int parse_options_usage ( const char * const * usagestr ,
const struct option * opts )
{
return usage_with_options_internal ( usagestr , opts , 0 ) ;
}
2012-09-11 02:15:03 +04:00
int parse_opt_verbosity_cb ( const struct option * opt ,
const char * arg __maybe_unused ,
2009-04-20 17:00:56 +04:00
int unset )
{
int * target = opt - > value ;
if ( unset )
/* --no-quiet, --no-verbose */
* target = 0 ;
else if ( opt - > short_name = = ' v ' ) {
if ( * target > = 0 )
( * target ) + + ;
else
* target = 1 ;
} else {
if ( * target < = 0 )
( * target ) - - ;
else
* target = - 1 ;
}
return 0 ;
}