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
pkgconf_argv_free(char ** argv)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
pkgconf_argv_split(const char * src,int * argc,char *** argv)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