argvsplit: refactor splitting state machine

feature/tap-sh
William Pitcock 2017-02-25 15:04:08 -06:00
parent a3fa9732b3
commit ae42261c3f
1 changed files with 42 additions and 43 deletions

View File

@ -2,7 +2,7 @@
* argvsplit.c * argvsplit.c
* argv_split() routine * argv_split() routine
* *
* Copyright (c) 2012 pkgconf authors (see AUTHORS). * Copyright (c) 2012, 2017 pkgconf authors (see AUTHORS).
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -64,7 +64,7 @@ pkgconf_argv_split(const char *src, int *argc, char ***argv)
int argc_count = 0; int argc_count = 0;
int argv_size = 5; int argv_size = 5;
char quote = 0; char quote = 0;
bool in_single_quotes = false; bool escaped = false;
src_iter = src; src_iter = src;
dst_iter = buf; dst_iter = buf;
@ -76,23 +76,31 @@ pkgconf_argv_split(const char *src, int *argc, char ***argv)
while (*src_iter) while (*src_iter)
{ {
if (quote == *src_iter) if (escaped)
quote = 0;
else if (quote)
{ {
if (*src_iter == '\\') /* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */
if (quote == '\"')
{ {
src_iter++; if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\'))
if (!*src_iter) *dst_iter++ = '\\';
*dst_iter++ = *src_iter;
}
else
{ {
free(*argv); if (!isspace((unsigned int) *src_iter))
free(buf); *dst_iter++ = *src_iter;
return -1;
} }
if (*src_iter != quote) escaped = false;
*dst_iter++ = '\\';
} }
else if (quote)
{
if (*src_iter == quote)
quote = 0;
else if (*src_iter == '\\')
escaped = true;
else
*dst_iter++ = *src_iter; *dst_iter++ = *src_iter;
} }
else if (isspace((unsigned int)*src_iter)) else if (isspace((unsigned int)*src_iter))
@ -112,32 +120,15 @@ pkgconf_argv_split(const char *src, int *argc, char ***argv)
} }
else switch(*src_iter) else switch(*src_iter)
{ {
case '\"':
if (in_single_quotes)
*dst_iter++ = *src_iter;
break;
case '\'':
in_single_quotes ^= true;
*dst_iter++ = *src_iter;
break;
case '\\': case '\\':
src_iter++; escaped = true;
if (!*src_iter)
{
free(argv);
free(buf);
return -1;
}
else
{
*dst_iter++ = '\\';
*dst_iter++ = *src_iter;
}
break; break;
case '\"':
case '\'':
quote = *src_iter;
break;
default: default:
*dst_iter++ = *src_iter; *dst_iter++ = *src_iter;
break; break;
@ -146,7 +137,15 @@ pkgconf_argv_split(const char *src, int *argc, char ***argv)
src_iter++; src_iter++;
} }
if (strlen((*argv)[argc_count])) { if (escaped || quote)
{
free(*argv);
free(buf);
return -1;
}
if (strlen((*argv)[argc_count]))
{
argc_count++; argc_count++;
} }