interpret \ as an escape char in break_token

So:

	vips_break_token( "hello\ world", " " )

Sees a single token, `"hello world"`.

This means you can now do things like:

	$ vips arrayjoin "k\ 2.jpg" x.png

Where "k 2.jpg" is a filename containing a space.

See https://github.com/libvips/libvips/issues/1493
This commit is contained in:
John Cupitt 2020-01-26 13:59:04 +00:00
parent c14d7c254b
commit f8c7f9dac9
2 changed files with 59 additions and 4 deletions

View File

@ -2,6 +2,7 @@
- more conformat IIIF output from dzsave [regisrob]
- add @id to dzsave to set IIIF id property [regisrob]
- add max and min to region shrink [rgluskin]
- allow \ as an escape character in vips_break_token() [akemrir]
20/6/19 started 8.9.1
- don't use the new source loaders for new_from_file or new_from_buffer, it

View File

@ -357,10 +357,46 @@ vips_isprefix( const char *a, const char *b )
return( TRUE );
}
/* Exactly like strcspn(), but allow \ as an escape character.
*
* strspne( "hello world", " " ) == 5
* strspne( "hello\\ world", " " ) == 12
*/
static size_t
strcspne( const char *s, const char *reject )
{
size_t skip;
/* If \ is one of the reject chars, no need for any looping.
*/
if( strchr( reject, '\\' ) )
return( strcspn( s, reject ) );
skip = 0;
for(;;) {
skip += strcspn( s + skip, reject );
/* s[skip] is at the start of the string, or the end, or on a
* break character.
*/
if( skip == 0 ||
!s[skip] ||
s[skip - 1] != '\\' )
break;
/* So skip points at break char and we have a '\' in the char
* before. Step over the break.
*/
skip += 1;
}
return( skip );
}
/* Like strtok(). Give a string and a list of break characters. Then:
* - skip initial break characters
* - EOS? return NULL
* - skip a series of non-break characters
* - skip a series of non-break characters, allow `\` as a break escape
* - write a '\0' over the next break character and return a pointer to the
* char after that
*
@ -388,15 +424,22 @@ vips_isprefix( const char *a, const char *b )
*
* for( i = 0; p; p = vips_break_token( p, " " ) )
* v[i] = atoi( p );
*
* You can use \ to escape breaks, for example:
*
* vips_break_token( "hello\ world", " " ) will see a single token containing
* a space. The \ characters are squashed out.
*/
char *
vips_break_token( char *str, const char *brk )
{
char *p;
char *q;
/* Is the string empty? If yes, return NULL immediately.
*/
if( !str || !*str )
if( !str ||
!*str )
return( NULL );
/* Skip initial break characters.
@ -409,9 +452,9 @@ vips_break_token( char *str, const char *brk )
return( NULL );
/* We have a token ... search for the first break character after the
* token.
* token. strcspne() allows '\' to escape breaks, see above.
*/
p += strcspn( p, brk );
p += strcspne( p, brk );
/* Is there string left?
*/
@ -423,6 +466,17 @@ vips_break_token( char *str, const char *brk )
p += strspn( p, brk );
}
/* There may be escaped break characters in str. Loop, squashing them
* out.
*/
for( q = strchr( str, '\\' ); q && *q; q = strchr( q, '\\' ) ) {
memmove( q, q + 1, strlen( q ) );
/* If there's \\, we don't want to squash out the second \.
*/
q += 1;
}
return( p );
}