diff --git a/ChangeLog b/ChangeLog index e7c73f44..70eaafda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ - added VIPS_ARGUMENT_APPEND to help control arg ordering - generate has a 'stop' param to signal successful early termination - added optional output args, eg. x/y for min +- CLI supports optional output args 10/8/11 started 7.26.3 - don't use G_VALUE_COLLECT_INIT(), many platforms do not have a glib this diff --git a/TODO b/TODO index 8bcadb73..2403ff58 100644 --- a/TODO +++ b/TODO @@ -1,34 +1,3 @@ -- see TODO comment on vips_call_options_set() - - - - -- cmd line interface needs to print optional output args, if requested - - eg. - - $ vips min fred.v - 12 - $ vips min -x fred.v - 12 - 300 - - the switch is there, but breaks an assert: - - $ vips min -x babe.jpg - VIPS:ERROR:object.c:1178:vips_object_set_argument_from_string: assertion - failed: (argument_class->flags & VIPS_ARGUMENT_INPUT) - Aborted - - options are being made incorrectly: - - $ vips min --help - -y, --y=gint Vertical position of minimum - - --y should not have an arg - - - - C interface needs to look for optional output args in final psss, eg. vips_min( im, &min, diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index bbc42627..a9b67986 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -134,6 +134,10 @@ typedef struct _VipsArgumentClass { typedef struct _VipsArgumentInstance { VipsArgument parent; + /* The class we are part of. + */ + VipsArgumentClass *argument_class; + /* The object we are attached to. */ VipsObject *object; @@ -154,9 +158,9 @@ typedef struct _VipsArgumentInstance { typedef GHashTable VipsArgumentTable; VipsArgumentInstance *vips__argument_get_instance( VipsArgumentClass *, - VipsObject *); + VipsObject * ); VipsArgument *vips__argument_table_lookup( VipsArgumentTable *, - GParamSpec *); + GParamSpec * ); void vips__object_set_member( VipsObject *object, GParamSpec *pspec, GObject **member, GObject *argument ); typedef void *(*VipsArgumentMapFn)( VipsObject *, GParamSpec *, diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 027e67f0..fbffd4b6 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -366,6 +366,7 @@ vips_argument_init( VipsObject *object ) argument_instance = g_new( VipsArgumentInstance, 1 ); ((VipsArgument *) argument_instance)->pspec = pspec; + argument_instance->argument_class = argument_class; argument_instance->object = object; argument_instance->assigned = FALSE; argument_instance->close_id = 0; diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index 3e19b72a..0c8c973b 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -504,7 +504,7 @@ vips_call_split( const char *operation_name, va_list optional, ... ) } static void * -vips_call_find_pspec_char( VipsObject *object, +vips_call_find_pspec( VipsObject *object, GParamSpec *pspec, VipsArgumentClass *argument_class, VipsArgumentInstance *argument_instance, @@ -512,31 +512,42 @@ vips_call_find_pspec_char( VipsObject *object, { const char *name = (const char *) a; + /* One char names we assume are "-x" style abbreviations, longer names + * we match the whole string. + */ if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) && (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) && - !argument_instance->assigned && - g_param_spec_get_name( pspec )[0] == name[0] ) - return( pspec ); + !argument_instance->assigned ) + if( (strlen( name ) == 1 && + g_param_spec_get_name( pspec )[0] == name[0]) || + strcmp( g_param_spec_get_name( pspec ), name ) == 0 ) + return( argument_instance ); return( NULL ); } -static void * -vips_call_find_pspec_name( VipsObject *object, - GParamSpec *pspec, - VipsArgumentClass *argument_class, - VipsArgumentInstance *argument_instance, - void *a, void *b ) +/* Keep this stuff around for output args. + */ +typedef struct _VipsCallOptionOutput { + VipsArgumentInstance *argument_instance; + const char *value; +} VipsCallOptionOutput; + +static void +vips_call_option_output( VipsObject *object, + VipsCallOptionOutput *output ) { - const char *name = (const char *) a; + VipsArgumentInstance *argument_instance = output->argument_instance; + GParamSpec *pspec = ((VipsArgument *) argument_instance)->pspec; - if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) && - (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) && - !argument_instance->assigned && - strcmp( g_param_spec_get_name( pspec ), name ) == 0 ) - return( pspec ); + if( vips_object_get_argument_to_string( object, + g_param_spec_get_name( pspec ), output->value ) ) { + /* FIXME .. Hmm what can we do here? If an arg is image + * output, for example, we will lose the error. + */ + } - return( NULL ); + g_free( output ); } static gboolean @@ -545,6 +556,8 @@ vips_call_options_set( const gchar *option_name, const gchar *value, { VipsOperation *operation = (VipsOperation *) data; const char *name; + VipsArgumentInstance *argument_instance; + VipsArgumentClass *argument_class; GParamSpec *pspec; VIPS_DEBUG_MSG( "vips_call_options_set: %s = %s\n", @@ -555,34 +568,39 @@ vips_call_options_set( const gchar *option_name, const gchar *value, for( name = option_name; *name == '-'; name++ ) ; - /* If this is a single-character name, find the first unset pspec with - * that initial. Otherwise, search for a spec of that name. - */ - if( strlen( name ) == 1 ) - pspec = (GParamSpec *) vips_argument_map( + if( !(argument_instance = (VipsArgumentInstance *) + vips_argument_map( VIPS_OBJECT( operation ), - vips_call_find_pspec_char, - (void *) name, NULL ); - else - pspec = (GParamSpec *) vips_argument_map( - VIPS_OBJECT( operation ), - vips_call_find_pspec_name, - (void *) name, NULL ); - if( !pspec ) { + vips_call_find_pspec, (void *) name, NULL )) ) { vips_error( VIPS_OBJECT( operation )->nickname, _( "unknown argument '%s'" ), name ); return( FALSE ); } + argument_class = argument_instance->argument_class; + pspec = ((VipsArgument *) argument_instance)->pspec; - /* - * - * FIXME ... for output args, need to attach a close callback to - * operation to write the value - * - */ - if( vips_object_set_argument_from_string( VIPS_OBJECT( operation ), - name, value ) ) - return( FALSE ); + if( (argument_class->flags & VIPS_ARGUMENT_INPUT) ) { + if( vips_object_set_argument_from_string( + VIPS_OBJECT( operation ), + g_param_spec_get_name( pspec ), value ) ) + return( FALSE ); + } + else if( (argument_class->flags & VIPS_ARGUMENT_OUTPUT) ) { + VipsCallOptionOutput *output; + + /* We can't do output now, we have to attach a callback to do + * the processing after the operation has run. + * + * FIXME ... something like posteval or postbuild might be + * better for this? + */ + output = g_new( VipsCallOptionOutput, 1 ); + output->argument_instance = argument_instance; + output->value = value; + g_signal_connect( operation, "preclose", + G_CALLBACK( vips_call_option_output ), + output ); + } return( TRUE ); }