From df3f033ad8586bb28503d204eac4e02990f64a44 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 19 Sep 2017 17:21:01 +0300 Subject: Fix some escaping related issues --- libpkgconf/argvsplit.c | 7 ++ libpkgconf/argvsplit.c.orig | 156 ++++++++++++++++++++++++++++++++++++++++++++ libpkgconf/fileio.c | 8 ++- libpkgconf/fileio.c.orig | 120 ++++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 libpkgconf/argvsplit.c.orig create mode 100644 libpkgconf/fileio.c.orig diff --git a/libpkgconf/argvsplit.c b/libpkgconf/argvsplit.c index 85f7f57..cb5844a 100644 --- a/libpkgconf/argvsplit.c +++ b/libpkgconf/argvsplit.c @@ -88,8 +88,15 @@ pkgconf_argv_split(const char *src, int *argc, char ***argv) } else { + /* + * There is no reason to keep space character escaped in fragment + * objects, especially given that other characters are unescaped (issue + * #139 is reported). + */ + /* if (isspace((unsigned int) *src_iter)) *dst_iter++ = '\\'; + */ *dst_iter++ = *src_iter; } diff --git a/libpkgconf/argvsplit.c.orig b/libpkgconf/argvsplit.c.orig new file mode 100644 index 0000000..85f7f57 --- /dev/null +++ b/libpkgconf/argvsplit.c.orig @@ -0,0 +1,156 @@ +/* + * argvsplit.c + * argv_split() routine + * + * Copyright (c) 2012, 2017 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include + +/* + * !doc + * + * libpkgconf `argvsplit` module + * ============================= + * + * This is a lowlevel module which provides parsing of strings into argument vectors, + * similar to what a shell would do. + */ + +/* + * !doc + * + * .. c:function:: void pkgconf_argv_free(char **argv) + * + * Frees an argument vector. + * + * :param char** argv: The argument vector to free. + * :return: nothing + */ +void +pkgconf_argv_free(char **argv) +{ + free(argv[0]); + free(argv); +} + +/* + * !doc + * + * .. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv) + * + * Splits a string into an argument vector. + * + * :param char* src: The string to split. + * :param int* argc: A pointer to an integer to store the argument count. + * :param char*** argv: A pointer to a pointer for an argument vector. + * :return: 0 on success, -1 on error. + * :rtype: int + */ +int +pkgconf_argv_split(const char *src, int *argc, char ***argv) +{ + char *buf = malloc(strlen(src) + 1); + const char *src_iter; + char *dst_iter; + int argc_count = 0; + int argv_size = 5; + char quote = 0; + bool escaped = false; + + src_iter = src; + dst_iter = buf; + + memset(buf, 0, strlen(src) + 1); + + *argv = calloc(sizeof (void *), argv_size); + (*argv)[argc_count] = dst_iter; + + while (*src_iter) + { + if (escaped) + { + /* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */ + if (quote == '\"') + { + if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\')) + *dst_iter++ = '\\'; + + *dst_iter++ = *src_iter; + } + else + { + if (isspace((unsigned int) *src_iter)) + *dst_iter++ = '\\'; + + *dst_iter++ = *src_iter; + } + + escaped = false; + } + else if (quote) + { + if (*src_iter == quote) + quote = 0; + else if (*src_iter == '\\') + escaped = true; + else + *dst_iter++ = *src_iter; + } + else if (isspace((unsigned int)*src_iter)) + { + if ((*argv)[argc_count] != NULL) + { + argc_count++, dst_iter++; + + if (argc_count == argv_size) + { + argv_size += 5; + *argv = realloc(*argv, sizeof(void *) * argv_size); + } + + (*argv)[argc_count] = dst_iter; + } + } + else switch(*src_iter) + { + case '\\': + escaped = true; + break; + + case '\"': + case '\'': + quote = *src_iter; + break; + + default: + *dst_iter++ = *src_iter; + break; + } + + src_iter++; + } + + if (escaped || quote) + { + free(*argv); + free(buf); + return -1; + } + + if (strlen((*argv)[argc_count])) + { + argc_count++; + } + + *argc = argc_count; + return 0; +} diff --git a/libpkgconf/fileio.c b/libpkgconf/fileio.c index d1c848f..bcc9c50 100644 --- a/libpkgconf/fileio.c +++ b/libpkgconf/fileio.c @@ -28,12 +28,16 @@ pkgconf_fgetline(char *line, size_t size, FILE *stream) while (s < end && (c = getc(stream)) != EOF) { - if (c == '\\') + /* + * Fix double-backslash swallowing (issue #140 is reported). + */ + if (c == '\\' && !quoted) { quoted = true; continue; } - else if (c == '#') + + if (c == '#') { if (!quoted) { /* Skip the rest of the line */ diff --git a/libpkgconf/fileio.c.orig b/libpkgconf/fileio.c.orig new file mode 100644 index 0000000..d1c848f --- /dev/null +++ b/libpkgconf/fileio.c.orig @@ -0,0 +1,120 @@ +/* + * fileio.c + * File reading utilities + * + * Copyright (c) 2012 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include + +char * +pkgconf_fgetline(char *line, size_t size, FILE *stream) +{ + char *s = line; + char *end = line + size - 1; + bool quoted = false; + int c = '\0', c2; + + if (s == NULL) + return NULL; + + while (s < end && (c = getc(stream)) != EOF) + { + if (c == '\\') + { + quoted = true; + continue; + } + else if (c == '#') + { + if (!quoted) { + /* Skip the rest of the line */ + do { + c = getc(stream); + } while (c != '\n' && c != EOF); + *s++ = c; + break; + } + quoted = false; + continue; + } + else if (c == '\n') + { + if (quoted) + { + /* Trim spaces */ + do { + c2 = getc(stream); + } while (c2 == '\t' || c2 == ' '); + + ungetc(c2, stream); + + quoted = false; + continue; + } + else + { + *s++ = c; + } + + break; + } + else if (c == '\r') + { + *s++ = '\n'; + + if ((c2 = getc(stream)) == '\n') + { + if (quoted) + { + quoted = false; + continue; + } + + break; + } + + ungetc(c2, stream); + + if (quoted) + { + quoted = false; + continue; + } + + break; + } + else + { + if (quoted) { + *s++ = '\\'; + quoted = false; + } + *s++ = c; + } + + } + + if (c == EOF && (s == line || ferror(stream))) + return NULL; + + *s = '\0'; + + /* Remove newline character. */ + if (s > line && *(--s) == '\n') { + *s = '\0'; + + if (s > line && *(--s) == '\r') + *s = '\0'; + } + + return line; +} -- cgit v1.1