/* * 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 #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 we are outside a quoted string/char, an escaped space is usually used to preserve spaces in file names. */ if (*src_iter != ' ') *dst_iter++ = '\\'; *dst_iter++ = *src_iter; } escaped = false; } else if (quote) { if (*src_iter == quote) quote = 0; else if (*src_iter == '\\' && quote != '\'') 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; }