From 2dec30da2c875b57800c3da1dda73b5a3b3bad12 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 7 Jan 2012 14:10:02 +0000 Subject: [PATCH] better options parser now only gets the options from the very end of a filename, so this works: "this should work (I hope!!).tif[compression=jpeg]" --- TODO | 20 +++++------ libvips/foreign/foreign.c | 5 ++- libvips/include/vips/util.h | 1 + libvips/iofuncs/object.c | 32 +++++++++-------- libvips/iofuncs/util.c | 68 +++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 29 deletions(-) diff --git a/TODO b/TODO index 5695f104..75258676 100644 --- a/TODO +++ b/TODO @@ -1,24 +1,20 @@ -- cache tracing is very handy, maybe make the VIPS_DEBUG macros in cache.c - into glog things, or have a --vips-cache-log flag? - - test this - - Vips.Image has members like chain, __subclasshook__ etc etc, are we really subclassing it correctly? -- get cli arg hooks working - - add support for constants + how do boxed types work? confusing + + we need to be able to make a VipsArrayDouble + - add __add__ etc overloads -- vips options: lex entire string to an array of tokens, noting the position - of each +- vips__token_get() will assert(0) if a string token is too large ... it + should return an error or silently truncate + + - if the final token is a right-bracket, count brackets leftwards to get the - matching start bracket - parse options from that point diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 61bedc89..fc270f9e 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -505,7 +505,7 @@ vips_foreign_find_load( const char *filename ) (VipsSListMap2Fn) vips_foreign_load_new_from_foreign_sub, (void *) filename, NULL )) ) { vips_error( "VipsForeignLoad", - _( "\"%s\" not a known file format" ), filename ); + _( "\"%s\" is not a known file format" ), filename ); return( NULL ); } @@ -850,8 +850,7 @@ vips_foreign_find_save( const char *filename ) (VipsSListMap2Fn) vips_foreign_find_save_sub, (void *) filename, NULL )) ) { vips_error( "VipsForeignSave", - _( "\"%s\" is not a supported image file." ), - filename ); + _( "\"%s\" is not a known file format" ), filename ); return( NULL ); } diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index a9fcdfe1..d43ed485 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -242,6 +242,7 @@ const char *vips__token_must( const char *buffer, VipsToken *token, char *string, int size ); const char *vips__token_need( const char *buffer, VipsToken need_token, char *string, int size ); +const char *vips__find_rightmost_brackets( const char *p ); int vips_ispoweroftwo( int p ); int vips_amiMSBfirst( void ); diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index b1617637..1856116e 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -1662,8 +1662,8 @@ vips_object_new( GType type, VipsObjectSetArguments set, void *a, void *b ) return( object ); } -/* Set object args from a string. We've seen the '(', we need to check for the - * closing ')' and make sure there's no extra stuff. +/* Set object args from a string. @p should be the initial left bracket and + * there should be no tokens after the matching right bracket. */ static int vips_object_set_args( VipsObject *object, const char *p ) @@ -1675,6 +1675,9 @@ vips_object_set_args( VipsObject *object, const char *p ) VipsArgumentClass *argument_class; VipsArgumentInstance *argument_instance; + if( !(p = vips__token_need( p, VIPS_TOKEN_LEFT, string, PATH_MAX )) ) + return( -1 ); + do { if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, string, PATH_MAX )) ) @@ -1742,29 +1745,30 @@ vips_object_set_args( VipsObject *object, const char *p ) VipsObject * vips_object_new_from_string( VipsObjectClass *object_class, const char *p ) { + const char *q; char str[PATH_MAX]; VipsObject *object; - VipsToken token; g_assert( object_class ); g_assert( object_class->new_from_string ); - /* The first string in p is the main construct arg, eg. a filename. + /* Find the start of the optional args on the end of the string, take + * everything before that as the principal arg for the constructor. */ - if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, str, PATH_MAX )) || - !(object = object_class->new_from_string( str )) ) + if( (q = vips__find_rightmost_brackets( p )) ) + vips_strncpy( str, p, VIPS_MIN( PATH_MAX, q - p + 1 ) ); + else + vips_strncpy( str, p, PATH_MAX ); + if( !(object = object_class->new_from_string( str )) ) return( NULL ); /* More tokens there? Set any other args. */ - if( (p = vips__token_get( p, &token, str, PATH_MAX )) ) { - if( token == VIPS_TOKEN_LEFT && - vips_object_set_args( object, p ) ) { - vips_error( "VipsObject", - "%s", _( "bad object arguments" ) ); - g_object_unref( object ); - return( NULL ); - } + if( q && + vips_object_set_args( object, q ) ) { + vips_error( "VipsObject", "%s", _( "bad object arguments" ) ); + g_object_unref( object ); + return( NULL ); } return( object ); diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index 0e74c759..ffea6d47 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -1313,6 +1313,74 @@ vips__token_need( const char *p, VipsToken need_token, return( p ); } +/* Maximum number of tokens we allow in a filename. Surely this will be + * plenty. + */ +#define MAX_TOKENS (1000) + +/* Find the start of the right-most pair of brackets in the string. + * + * A string can be of the form: + * + * "hello world! (no really).tif[fred=12]" + * + * we need to be able to find the fred=12 at the end. + * + * We lex the whole string noting the position of each token, then, if the + * final token is a right-bracket, search left for the matching left-bracket. + * + * This can get confused if the lefts are hidden inside another token :-( But + * a fixing that would require us to write a separate right-to-left lexer, + * argh. + */ +const char * +vips__find_rightmost_brackets( const char *p ) +{ + const char *start[MAX_TOKENS]; + VipsToken tokens[MAX_TOKENS]; + char str[PATH_MAX]; + int n, i; + int nest; + + start[0] = p; + for( n = 0; + n < MAX_TOKENS && + (p = vips__token_get( start[n], &tokens[n], str, PATH_MAX )); + n++, start[n] = p ) + ; + + /* Too many tokens? + */ + if( n == MAX_TOKENS ) + return( NULL ); + + /* No rightmost close bracket? + */ + if( n == 0 || + tokens[n - 1] != VIPS_TOKEN_RIGHT ) + return( NULL ); + + nest = 0; + for( i = n - 1; i >= 0; i-- ) { + if( tokens[i] == VIPS_TOKEN_RIGHT ) + nest += 1; + else if( tokens[i] == VIPS_TOKEN_LEFT ) + nest -= 1; + + if( nest == 0 ) + break; + } + + /* No matching left bracket? + */ + if( nest != 0 ) + return( NULL ); + + /* This should be the matching left. + */ + return( start[i] ); +} + /* True if an int is a power of two ... 1, 2, 4, 8, 16, 32, etc. Do with just * integer arithmetic for portability. A previous Nicos version using doubles * and log/log failed on x86 with rounding problems. Return 0 for not