more command-line interface
more hacker ... this time a better way to do optional args
This commit is contained in:
parent
db48961c46
commit
67df101545
|
@ -68,6 +68,9 @@ VipsOperation *vips_operation_new( const char *name );
|
|||
int vips_call( const char *operation_name, ... );
|
||||
int vips_call_split( const char *operation_name, va_list optional, ... );
|
||||
|
||||
GOptionGroup *vips_call_options( VipsOperation *operation );
|
||||
int vips_call_argv( VipsOperation *operation, int argc, char **argv );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
|
|
@ -366,54 +366,6 @@ vips_object_set_arg( VipsObject *object, const char *name, const char *value )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
/* We've seen the '[', get the optional args and check for a ']'.
|
||||
*/
|
||||
static int
|
||||
vips_call_argv_set_optional( VipsOperation *operation, const char *p )
|
||||
{
|
||||
VipsToken token;
|
||||
char name[PATH_MAX];
|
||||
char value[PATH_MAX];
|
||||
|
||||
do {
|
||||
if( !(p = vips__token_need( p, VIPS_TOKEN_STRING,
|
||||
name, PATH_MAX )) )
|
||||
return( -1 );
|
||||
if( (p = vips__token_must( p, &token, value, PATH_MAX )) )
|
||||
return( -1 );
|
||||
if( token != VIPS_TOKEN_EQUALS ) {
|
||||
vips_error( "VipsOperation",
|
||||
"%s", _( "no '=' after optional parameter" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( !(p = vips__token_need( p, VIPS_TOKEN_STRING,
|
||||
value, PATH_MAX )) )
|
||||
return( -1 );
|
||||
if( vips_object_set_arg( VIPS_OBJECT( operation ),
|
||||
name, value ) )
|
||||
return( -1 );
|
||||
|
||||
/* Now it's comma if there's another arg, or ']' for end of
|
||||
* args.
|
||||
*/
|
||||
if( (p = vips__token_must( p, &token, value, PATH_MAX )) )
|
||||
return( -1 );
|
||||
if( token != VIPS_TOKEN_RIGHT && token != VIPS_TOKEN_COMMA ) {
|
||||
vips_error( "VipsOperation",
|
||||
"%s", _( "no ',' or ')' after parameter" ) );
|
||||
return( -1 );
|
||||
}
|
||||
} while( token != VIPS_TOKEN_RIGHT );
|
||||
|
||||
if( (p = vips__token_get( p, &token, name, PATH_MAX )) ) {
|
||||
vips_error( "VipsOperation",
|
||||
"%s", _( "extra tokens after ')'" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_object_set_required_test( VipsObject *object,
|
||||
GParamSpec *pspec,
|
||||
|
@ -452,42 +404,84 @@ vips_call_argv_set_required( VipsOperation *operation, const char *value )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
/* A command-line version. This gets called by im_run_command() if lookup
|
||||
* fails, so "vips add a b c" works.
|
||||
static gboolean
|
||||
vips_call_options_set( const gchar *option_name, const gchar *value,
|
||||
gpointer data, GError **error )
|
||||
{
|
||||
VipsOperation *operation = (VipsOperation *) data;
|
||||
|
||||
printf( "setting optional arg %s = %s\n", option_name, value );
|
||||
|
||||
/*
|
||||
if( !vips_object_has_arg( VIPS_OBJECT( operation ),
|
||||
option_name, value ) ) {
|
||||
g_set_error( error, "vips", -1, "bad argument" );
|
||||
return( FALSE );
|
||||
}
|
||||
*/
|
||||
|
||||
vips_object_set_arg( VIPS_OBJECT( operation ), option_name, value );
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_call_options_add( VipsObject *object,
|
||||
GParamSpec *pspec,
|
||||
VipsArgumentClass *argument_class,
|
||||
VipsArgumentInstance *argument_instance,
|
||||
void *a, void *b )
|
||||
{
|
||||
GOptionGroup *group = (GOptionGroup *) a;
|
||||
|
||||
if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
||||
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
||||
!argument_instance->assigned ) {
|
||||
GOptionEntry entry[2];
|
||||
|
||||
entry[0].long_name = pspec->name;
|
||||
entry[0].short_name = pspec->name[0];
|
||||
entry[0].flags = G_OPTION_FLAG_OPTIONAL_ARG;
|
||||
entry[0].arg = G_OPTION_ARG_CALLBACK;
|
||||
entry[0].arg_data = (gpointer) vips_call_options_set;
|
||||
entry[0].description = pspec->name;
|
||||
entry[0].arg_description = pspec->name;
|
||||
|
||||
entry[1].long_name = NULL;
|
||||
|
||||
g_option_group_add_entries( group, &entry[0] );
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
GOptionGroup *
|
||||
vips_call_options( VipsOperation *operation )
|
||||
{
|
||||
GOptionGroup *group;
|
||||
|
||||
group = g_option_group_new( VIPS_OBJECT( operation )->nickname,
|
||||
VIPS_OBJECT( operation )->description,
|
||||
_( "Show operation options" ),
|
||||
operation,
|
||||
NULL );
|
||||
|
||||
(void) vips_argument_map( VIPS_OBJECT( operation ),
|
||||
vips_call_options_add, group, NULL );
|
||||
|
||||
return( group );
|
||||
}
|
||||
|
||||
/* Our main command-line entry point. Optional args should have been set by
|
||||
* the GOption parser already, see above.
|
||||
*/
|
||||
int
|
||||
vips_call_argv( const char *name, int argc, const char **argv )
|
||||
vips_call_argv( VipsOperation *operation, int argc, char **argv )
|
||||
{
|
||||
const char *p;
|
||||
char string[PATH_MAX];
|
||||
VipsOperation *operation;
|
||||
VipsToken token;
|
||||
int i;
|
||||
|
||||
g_assert( argc > 0 );
|
||||
|
||||
/* argv[0] is the operation name, plus any optional parameters. Eg.
|
||||
* "add{saturated=true,expand=12}"
|
||||
*/
|
||||
p = argv[0];
|
||||
if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, string, PATH_MAX )) ||
|
||||
!(operation = vips_operation_new( string )) )
|
||||
return( -1 );
|
||||
|
||||
if( (p = vips__token_get( p, &token, string, PATH_MAX )) ) {
|
||||
if( token != VIPS_TOKEN_LEFT ) {
|
||||
g_object_unref( operation );
|
||||
vips_error( "VipsOperation",
|
||||
"%s", _( "bad object arguments" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( vips_call_argv_set_optional( operation, p ) ) {
|
||||
g_object_unref( operation );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Now set required args from the rest of the command-line.
|
||||
*/
|
||||
for( i = 1; i < argc; i++ )
|
||||
|
@ -506,5 +500,3 @@ vips_call_argv( const char *name, int argc, const char **argv )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
245
tools/vips.c
245
tools/vips.c
|
@ -90,33 +90,13 @@
|
|||
#define strcasecmp(a,b) _stricmp(a,b)
|
||||
#endif
|
||||
|
||||
static char *main_option_list = NULL;
|
||||
static char *main_option_usage = NULL;
|
||||
static char *main_option_plugin = NULL;
|
||||
static gboolean main_option_links;
|
||||
static char *main_option_cpph = NULL;
|
||||
static char *main_option_cppc = NULL;
|
||||
static gboolean *main_option_version;
|
||||
|
||||
static GOptionEntry main_option[] = {
|
||||
{ "list", 'l', 0, G_OPTION_ARG_STRING, &main_option_list,
|
||||
N_( "list operations in PACKAGE "
|
||||
"(or \"all\", \"packages\", \"classes\")" ),
|
||||
N_( "PACKAGE" ) },
|
||||
{ "usage", 'u', 0, G_OPTION_ARG_STRING, &main_option_usage,
|
||||
N_( "show usage message for OPERATION" ),
|
||||
N_( "OPERATION" ) },
|
||||
{ "plugin", 'p', 0, G_OPTION_ARG_FILENAME, &main_option_plugin,
|
||||
N_( "load PLUGIN" ),
|
||||
N_( "PLUGIN" ) },
|
||||
{ "links", 'k', 0, G_OPTION_ARG_NONE, &main_option_links,
|
||||
N_( "print link lines for all operations" ), NULL },
|
||||
{ "cpph", 'h', 0, G_OPTION_ARG_STRING, &main_option_cpph,
|
||||
N_( "print C++ decls for PACKAGE (or \"all\")" ),
|
||||
N_( "PACKAGE" ) },
|
||||
{ "cppc", 'c', 0, G_OPTION_ARG_STRING, &main_option_cppc,
|
||||
N_( "print C++ binding for PACKAGE (or \"all\")" ),
|
||||
N_( "PACKAGE" ) },
|
||||
{ "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version,
|
||||
N_( "print im_version_string" ), NULL },
|
||||
{ NULL }
|
||||
|
@ -192,9 +172,11 @@ list_class( VipsObjectClass *class )
|
|||
return( NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
print_list( const char *name )
|
||||
static int
|
||||
print_list( int argc, char **argv )
|
||||
{
|
||||
const char *name = argv[1];
|
||||
|
||||
if( strcmp( name, "packages" ) == 0 )
|
||||
im_map_packages( (VSListMap2Fn) list_package, NULL );
|
||||
else if( strcmp( name, "classes" ) == 0 )
|
||||
|
@ -204,6 +186,8 @@ print_list( const char *name )
|
|||
if( map_name( name, list_function ) )
|
||||
error_exit( "unknown package \"%s\"", name );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Is s1 a prefix of s2?
|
||||
|
@ -236,7 +220,7 @@ ispostfix( const char *s1, const char *s2 )
|
|||
/* Print "ln -s" lines for this package.
|
||||
*/
|
||||
static void *
|
||||
print_links( im_package *pack )
|
||||
print_links_package( im_package *pack )
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -248,6 +232,16 @@ print_links( im_package *pack )
|
|||
return( NULL );
|
||||
}
|
||||
|
||||
/* Print "ln -s" lines for this package.
|
||||
*/
|
||||
static int
|
||||
print_links( int argc, char **argv )
|
||||
{
|
||||
im_map_packages( (VSListMap2Fn) print_links_package, NULL );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Does a function have any printing output?
|
||||
*/
|
||||
static int
|
||||
|
@ -876,26 +870,91 @@ print_cppdef( im_function *fn )
|
|||
|
||||
/* Print C++ decls for function, package or all.
|
||||
*/
|
||||
static void
|
||||
print_cppdecls( char *name )
|
||||
static int
|
||||
print_cppdecls( int argc, char **argv )
|
||||
{
|
||||
printf( "// this file automatically generated from\n"
|
||||
"// VIPS library %s\n", im_version_string() );
|
||||
|
||||
if( map_name( name, print_cppdecl ) )
|
||||
error_exit( "unknown package \"%s\"", name );
|
||||
if( map_name( argv[1], print_cppdecl ) )
|
||||
error_exit( "unknown package \"%s\"", argv[1] );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Print C++ bindings for function, package or all.
|
||||
*/
|
||||
static void
|
||||
print_cppdefs( char *name )
|
||||
static int
|
||||
print_cppdefs( int argc, char **argv )
|
||||
{
|
||||
printf( "// this file automatically generated from\n"
|
||||
"// VIPS library %s\n", im_version_string() );
|
||||
|
||||
if( map_name( name, print_cppdef ) )
|
||||
error_exit( "unknown package \"%s\"", name );
|
||||
if( map_name( argv[1], print_cppdef ) )
|
||||
error_exit( "unknown package \"%s\"", argv[1] );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* All our built-in actions.
|
||||
*/
|
||||
|
||||
typedef int (*Action)( int argc, char **argv );
|
||||
|
||||
typedef struct _ActionEntry {
|
||||
char *name;
|
||||
GOptionEntry *group;
|
||||
Action action;
|
||||
} ActionEntry;
|
||||
|
||||
static GOptionEntry empty_options[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static ActionEntry actions[] = {
|
||||
{ "cpph", &empty_options[0], print_cppdecls },
|
||||
{ "cppc", &empty_options[0], print_cppdefs },
|
||||
{ "links", &empty_options[0], print_links },
|
||||
{ "list", &empty_options[0], print_list }
|
||||
};
|
||||
|
||||
static void
|
||||
parse_options( GOptionContext *context, int *argc, char **argv )
|
||||
{
|
||||
GError *error = NULL;
|
||||
int i, j;
|
||||
|
||||
if( !g_option_context_parse( context, argc, &argv, &error ) ) {
|
||||
if( error ) {
|
||||
fprintf( stderr, "%s\n", error->message );
|
||||
g_error_free( error );
|
||||
}
|
||||
|
||||
error_exit( "%s", g_get_prgname() );
|
||||
}
|
||||
|
||||
/* We support --plugin for all cases.
|
||||
*/
|
||||
if( main_option_plugin ) {
|
||||
if( !im_load_plugin( main_option_plugin ) )
|
||||
error_exit( NULL );
|
||||
}
|
||||
|
||||
if( main_option_version )
|
||||
printf( "vips-%s\n", im_version_string() );
|
||||
|
||||
/* Remove any "--" argument. If one of our arguments is a negative
|
||||
* number, the user will need to have added the "--" flag to stop
|
||||
* GOption parsing. But "--" is still passed down to us and we need to
|
||||
* ignore it.
|
||||
*/
|
||||
for( i = 1; i < *argc - 1; i++ )
|
||||
if( strcmp( argv[i], "--" ) == 0 ) {
|
||||
for( j = i; j < *argc; j++ )
|
||||
argv[j] = argv[j + 1];
|
||||
|
||||
*argc -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* VIPS universal main program.
|
||||
|
@ -903,10 +962,11 @@ print_cppdefs( char *name )
|
|||
int
|
||||
main( int argc, char **argv )
|
||||
{
|
||||
char *action;
|
||||
GOptionContext *context;
|
||||
GError *error = NULL;
|
||||
VipsOperation *operation;
|
||||
im_function *fn;
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
if( im_init_world( argv[0] ) )
|
||||
error_exit( NULL );
|
||||
|
@ -936,92 +996,43 @@ main( int argc, char **argv )
|
|||
main_option, GETTEXT_PACKAGE );
|
||||
g_option_context_add_group( context, im_get_option_group() );
|
||||
|
||||
if( !g_option_context_parse( context, &argc, &argv, &error ) ) {
|
||||
if( error ) {
|
||||
fprintf( stderr, "%s\n", error->message );
|
||||
g_error_free( error );
|
||||
}
|
||||
|
||||
error_exit( "try \"%s --help\"", g_get_prgname() );
|
||||
}
|
||||
|
||||
g_option_context_free( context );
|
||||
|
||||
if( main_option_plugin ) {
|
||||
if( !im_load_plugin( main_option_plugin ) )
|
||||
error_exit( NULL );
|
||||
}
|
||||
if( main_option_cpph )
|
||||
print_cppdecls( main_option_cpph );
|
||||
if( main_option_cppc )
|
||||
print_cppdefs( main_option_cppc );
|
||||
if( main_option_links )
|
||||
im_map_packages( (VSListMap2Fn) print_links, NULL );
|
||||
if( main_option_list )
|
||||
print_list( main_option_list );
|
||||
if( main_option_usage ) {
|
||||
if( !(fn = im_find_function( main_option_usage )) )
|
||||
error_exit( NULL );
|
||||
usage( fn );
|
||||
}
|
||||
if( main_option_version )
|
||||
printf( "vips-%s\n", im_version_string() );
|
||||
|
||||
/* Remove any "--" argument. If one of our arguments is a negative
|
||||
* number, the user will need to have added the "--" flag to stop
|
||||
* GOption parsing. But "--" is still passed down to us and we need to
|
||||
* ignore it.
|
||||
/* We generate part of our g_options dynamically depending on the
|
||||
* action, so we can't parse our args before getting the action. So
|
||||
* therefore the action must always be the first argument.
|
||||
*/
|
||||
for( i = 1; i < argc - 1; i++ )
|
||||
if( strcmp( argv[i], "--" ) == 0 ) {
|
||||
for( j = i; j < argc; j++ )
|
||||
argv[j] = argv[j + 1];
|
||||
|
||||
argc -= 1;
|
||||
}
|
||||
|
||||
/* Should we try to run the thing we are named as?
|
||||
*/
|
||||
if( !im_isprefix( "vips", g_get_prgname() ) ) {
|
||||
char name[256];
|
||||
if( !im_isprefix( "vips", g_get_prgname() ) )
|
||||
action = argv[0];
|
||||
else
|
||||
action = argv[1];
|
||||
|
||||
/* Drop any .exe suffix.
|
||||
*/
|
||||
im_strncpy( name, g_get_prgname(), 256 );
|
||||
if( ispostfix( ".exe", name ) )
|
||||
name[strlen( name ) - 4] = '\0';
|
||||
/* Could be one of our actions.
|
||||
*/
|
||||
for( i = 0; i < VIPS_NUMBER( actions ); i++ )
|
||||
if( strcmp( action, actions[i].name ) == 0 ) {
|
||||
GOptionGroup *group;
|
||||
|
||||
/* If unknown, try with "im_" prepended.
|
||||
*/
|
||||
if( !(fn = im_find_function( name )) ) {
|
||||
im_snprintf( name, 256, "im_%s", g_get_prgname() );
|
||||
if( ispostfix( ".exe", name ) )
|
||||
name[strlen( name ) - 4] = '\0';
|
||||
group = g_option_group_new( actions[i].name,
|
||||
"vips action", "show action options",
|
||||
NULL, NULL );
|
||||
g_option_group_add_entries( group, actions[i].group );
|
||||
g_option_context_add_group( context, group );
|
||||
parse_options( context, &argc, argv );
|
||||
|
||||
if( !(fn = im_find_function( name )) )
|
||||
error_exit( NULL );
|
||||
if( actions[i].action( argc, argv ) )
|
||||
error_exit( "%s", action );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Execute it!
|
||||
*/
|
||||
if( im_run_command( name, argc - 1, argv + 1 ) ) {
|
||||
/* If there are no arguments and the operation failed,
|
||||
* show usage. There are no-arg operations, so we have
|
||||
* to try running it.
|
||||
*/
|
||||
if( argc == 1 )
|
||||
usage( fn );
|
||||
else
|
||||
error_exit( NULL );
|
||||
}
|
||||
}
|
||||
else if( argc > 1 ) {
|
||||
/* Nope ... run the first arg instead.
|
||||
*/
|
||||
if( !(fn = im_find_function( argv[1] )) )
|
||||
error_exit( NULL );
|
||||
/* Could be a vips7 im_function.
|
||||
*/
|
||||
if( (fn = im_find_function( action )) ) {
|
||||
parse_options( context, &argc, argv );
|
||||
|
||||
if( im_run_command( argv[1], argc - 2, argv + 2 ) ) {
|
||||
if( im_run_command( action, argc - 2, argv + 2 ) ) {
|
||||
if( argc == 2 )
|
||||
usage( fn );
|
||||
else
|
||||
|
@ -1029,6 +1040,22 @@ main( int argc, char **argv )
|
|||
}
|
||||
}
|
||||
|
||||
/* Could be a vips8 VipsOperation.
|
||||
*/
|
||||
if( (operation = vips_operation_new( action )) ) {
|
||||
GOptionGroup *group;
|
||||
|
||||
if( !(group = vips_call_options( operation )) )
|
||||
error_exit( NULL );
|
||||
g_option_context_add_group( context, group );
|
||||
parse_options( context, &argc, argv );
|
||||
|
||||
if( vips_call_argv( operation, argc, argv ) )
|
||||
error_exit( NULL );
|
||||
}
|
||||
|
||||
g_option_context_free( context );
|
||||
|
||||
im_close_plugins();
|
||||
|
||||
#ifdef DEBUG_LEAK
|
||||
|
|
Loading…
Reference in New Issue