From db11d3b973be112b598b03af78f1a5a8e82ce4ce Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 16 May 2016 11:09:47 +0100 Subject: [PATCH] allow nested [] in CLI args this now works: vips copy x y.dz[suffix=.jpg[Q=90]] --- ChangeLog | 1 + TODO | 5 --- libvips/include/vips/util.h | 4 ++ libvips/iofuncs/object.c | 9 +++-- libvips/iofuncs/util.c | 81 ++++++++++++++++++++++++++++++++++--- 5 files changed, 86 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 765836b9..e99e77f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ - added @n option to pdfload, thanks andris - dzsave won't write empty tiles in google mode, thanks bverem, perog, felixbuenemann +- allow nested [] in CLI args 15/4/16 started 8.3.1 - rename vips wrapper script, it was still vips-8.2, thanks Benjamin diff --git a/TODO b/TODO index 6692c7f0..16c41fbe 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,3 @@ -- this seems to not work: - - $ vips copy k2.jpg x.dz[suffix=.jpg[Q=90]] - dzsave: not , or ) after parameter - - add more webp tests to py suite - try moving some more of the CLI tests to py diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index 53587438..f08e71f5 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -281,6 +281,10 @@ 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__token_segment( const char *p, VipsToken *token, + char *string, int size ); +const char *vips__token_segment_need( const char *p, VipsToken need_token, + char *string, int size ); const char *vips__find_rightmost_brackets( const char *p ); void vips__filename_split8( const char *name, char *filename, char *option_string ); diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 6e311381..5e410bfa 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -2282,7 +2282,7 @@ vips_object_set_args( VipsObject *object, const char *p ) string, VIPS_PATH_MAX )) ) return( -1 ); - if( !(p = vips__token_must( p, &token, string, VIPS_PATH_MAX )) ) + if( !(p = vips__token_segment( p, &token, string, VIPS_PATH_MAX )) ) return( -1 ); for(;;) { @@ -2295,19 +2295,20 @@ vips_object_set_args( VipsObject *object, const char *p ) return( -1 ); } - /* We have to look for a '=', ')' or a ',' to see if string is + /* We have to look for a '=', ']' or a ',' to see if string is * a param name or a value. */ - if( !(p = vips__token_must( p, &token, + if( !(p = vips__token_segment( p, &token, string2, VIPS_PATH_MAX )) ) return( -1 ); if( token == VIPS_TOKEN_EQUALS ) { - if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, + if( !(p = vips__token_segment_need( p, VIPS_TOKEN_STRING, string2, VIPS_PATH_MAX )) ) return( -1 ); if( vips_object_set_argument_from_string( object, string, string2 ) ) return( -1 ); + if( !(p = vips__token_must( p, &token, string2, VIPS_PATH_MAX )) ) return( -1 ); diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index bffc0aa8..9838da20 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -1232,19 +1232,20 @@ vips__token_get( const char *p, VipsToken *token, char *string, int size ) default: /* It's an unquoted string: read up to the next non-string * character. We don't allow two strings next to each other, - * so the next break must be bracket, equals, comma. + * so the next break must be brackets, equals, comma. */ *token = VIPS_TOKEN_STRING; - n = strcspn( p, "[]=," ); - i = VIPS_MIN( n, size ); + q = p + strcspn( p, "[]=," ); + + i = VIPS_MIN( q - p, size ); vips_strncpy( string, p, i + 1 ); - p += n; + p = q; /* We remove leading whitespace, so we trim trailing * whitespace from unquoted strings too. Only if the string * hasn't been truncated. */ - if( i == n ) + if( i != size ) while( i > 0 && isspace( string[i - 1] ) ) { string[i - 1] = '\0'; i--; @@ -1291,6 +1292,76 @@ vips__token_need( const char *p, VipsToken need_token, return( p ); } +/* Fetch a token. If it's a string token terminated by a '[', fetch up to the + * matching ']' as well, for example ".jpg[Q=90]". + * + * Return NULL for end of tokens. + */ +const char * +vips__token_segment( const char *p, VipsToken *token, + char *string, int size ) +{ + const char *q; + + if( !(q = vips__token_must( p, token, string, size )) ) + return( NULL ); + + /* If we stopped on [, read up to the matching ]. + */ + if( *token == VIPS_TOKEN_STRING && + q[0] == '[' ) { + VipsToken sub_token; + char sub_string[VIPS_PATH_MAX]; + int depth; + int i; + + depth = 0; + do { + if( !(q = vips__token_must( q, &sub_token, + sub_string, VIPS_PATH_MAX )) ) + return( NULL ); + + switch( sub_token ) { + case VIPS_TOKEN_LEFT: + depth += 1; + break; + + case VIPS_TOKEN_RIGHT: + depth -= 1; + break; + + default: + break; + } + } while( !(sub_token == VIPS_TOKEN_RIGHT && depth == 0) ); + + i = VIPS_MIN( q - p, size ); + vips_strncpy( string, p, i + 1 ); + } + + return( q ); +} + +/* We expect a certain segment. + */ +const char * +vips__token_segment_need( const char *p, VipsToken need_token, + char *string, int size ) +{ + VipsToken token; + + if( !(p = vips__token_segment( p, &token, string, size )) ) + return( NULL ); + if( token != need_token ) { + vips_error( "get_token", _( "expected %s, saw %s" ), + vips_enum_nick( VIPS_TYPE_TOKEN, need_token ), + vips_enum_nick( VIPS_TYPE_TOKEN, token ) ); + return( NULL ); + } + + return( p ); +} + /* Maximum number of tokens we allow in a filename. Surely this will be * plenty. */