1 /* 2 * argvsplit.c 3 * argv_split() routine 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2012, 2017 pkgconf authors (see AUTHORS). 8 * 9 * Permission to use, copy, modify, and/or distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * This software is provided 'as is' and without any warranty, express or 14 * implied. In no event shall the authors be liable for any damages arising 15 * from the use of this software. 16 */ 17 18 #include <libpkgconf/stdinc.h> 19 #include <libpkgconf/libpkgconf.h> 20 21 /* 22 * !doc 23 * 24 * libpkgconf `argvsplit` module 25 * ============================= 26 * 27 * This is a lowlevel module which provides parsing of strings into argument vectors, 28 * similar to what a shell would do. 29 */ 30 31 /* 32 * !doc 33 * 34 * .. c:function:: void pkgconf_argv_free(char **argv) 35 * 36 * Frees an argument vector. 37 * 38 * :param char** argv: The argument vector to free. 39 * :return: nothing 40 */ 41 void 42 pkgconf_argv_free(char **argv) 43 { 44 if (argv == NULL) 45 return; 46 47 free(argv[0]); 48 free(argv); 49 } 50 51 /* 52 * !doc 53 * 54 * .. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv) 55 * 56 * Splits a string into an argument vector. 57 * 58 * :param char* src: The string to split. 59 * :param int* argc: A pointer to an integer to store the argument count. 60 * :param char*** argv: A pointer to a pointer for an argument vector. 61 * :return: 0 on success, -1 on error. 62 * :rtype: int 63 */ 64 int 65 pkgconf_argv_split(const char *src, int *argc, char ***argv) 66 { 67 char *buf = calloc(1, strlen(src) + 1); 68 if (buf == NULL) 69 return -1; 70 71 const char *src_iter; 72 char *dst_iter; 73 int argc_count = 0; 74 int argv_size = 5; 75 char quote = 0; 76 bool escaped = false; 77 78 src_iter = src; 79 dst_iter = buf; 80 81 *argv = calloc(argv_size, sizeof (void *)); 82 if (*argv == NULL) 83 { 84 free(buf); 85 return -1; 86 } 87 88 (*argv)[argc_count] = dst_iter; 89 90 while (*src_iter) 91 { 92 if (escaped) 93 { 94 /* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */ 95 if (quote == '"') 96 { 97 if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\')) 98 *dst_iter++ = '\\'; 99 100 *dst_iter++ = *src_iter; 101 } 102 else 103 { 104 *dst_iter++ = *src_iter; 105 } 106 107 escaped = false; 108 } 109 else if (quote) 110 { 111 if (*src_iter == quote) 112 quote = 0; 113 else if (*src_iter == '\\' && quote != '\'') 114 escaped = true; 115 else 116 *dst_iter++ = *src_iter; 117 } 118 else if (isspace((unsigned char)*src_iter)) 119 { 120 if ((*argv)[argc_count] != NULL) 121 { 122 argc_count++, dst_iter++; 123 124 if (argc_count == argv_size) 125 { 126 char **new_argv; 127 128 argv_size += 5; 129 new_argv = pkgconf_reallocarray(*argv, argv_size, sizeof(void *)); 130 if (new_argv == NULL) 131 { 132 free(*argv); 133 free(buf); 134 *argv = NULL; 135 return -1; 136 } 137 138 *argv = new_argv; 139 } 140 141 (*argv)[argc_count] = dst_iter; 142 } 143 } 144 else switch(*src_iter) 145 { 146 case '\\': 147 escaped = true; 148 break; 149 150 case '\"': 151 case '\'': 152 quote = *src_iter; 153 break; 154 155 default: 156 *dst_iter++ = *src_iter; 157 break; 158 } 159 160 src_iter++; 161 } 162 163 if (escaped || quote) 164 { 165 free(*argv); 166 free(buf); 167 *argv = NULL; 168 return -1; 169 } 170 171 if (strlen((*argv)[argc_count])) 172 { 173 argc_count++; 174 } 175 176 *argc = argc_count; 177 return 0; 178 } 179