Imported Upstream version 3.3.2
[platform/upstream/ccache.git] / args.c
1 // Copyright (C) 2002 Andrew Tridgell
2 // Copyright (C) 2009-2016 Joel Rosdahl
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 3 of the License, or (at your option)
7 // any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 // more details.
13 //
14 // You should have received a copy of the GNU General Public License along with
15 // this program; if not, write to the Free Software Foundation, Inc., 51
16 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18 #include "ccache.h"
19
20 struct args *
21 args_init(int init_argc, char **init_args)
22 {
23         struct args *args = (struct args *)x_malloc(sizeof(struct args));
24         args->argc = 0;
25         args->argv = (char **)x_malloc(sizeof(char *));
26         args->argv[0] = NULL;
27         for (int i = 0; i < init_argc; i++) {
28                 args_add(args, init_args[i]);
29         }
30         return args;
31 }
32
33 struct args *
34 args_init_from_string(const char *command)
35 {
36         char *p = x_strdup(command);
37         char *q = p;
38         char *word, *saveptr = NULL;
39         struct args *args = args_init(0, NULL);
40         while ((word = strtok_r(q, " \t\r\n", &saveptr))) {
41                 args_add(args, word);
42                 q = NULL;
43         }
44
45         free(p);
46         return args;
47 }
48
49 struct args *
50 args_init_from_gcc_atfile(const char *filename)
51 {
52         char *argtext;
53         if (!(argtext = read_text_file(filename, 0))) {
54                 return NULL;
55         }
56
57         struct args *args = args_init(0, NULL);
58         char *pos = argtext;
59         char *argbuf = x_malloc(strlen(argtext) + 1);
60         char *argpos = argbuf;
61
62         // Used to track quoting state; if \0, we are not inside quotes. Otherwise
63         // stores the quoting character that started it, for matching the end quote.
64         char quoting = '\0';
65
66         while (1) {
67                 switch (*pos) {
68                 case '\\':
69                         pos++;
70                         if (*pos == '\0') {
71                                 continue;
72                         }
73                         break;
74
75                 case '\"':
76                 case '\'':
77                         if (quoting != '\0') {
78                                 if (quoting == *pos) {
79                                         quoting = '\0';
80                                         pos++;
81                                         continue;
82                                 } else {
83                                         break;
84                                 }
85                         } else {
86                                 quoting = *pos;
87                                 pos++;
88                                 continue;
89                         }
90
91                 case '\n':
92                 case '\r':
93                 case '\t':
94                 case ' ':
95                         if (quoting) {
96                                 break;
97                         }
98                 // Fall through.
99
100                 case '\0':
101                         // End of token
102                         *argpos = '\0';
103                         if (argbuf[0] != '\0') {
104                                 args_add(args, argbuf);
105                         }
106                         argpos = argbuf;
107                         if (*pos == '\0') {
108                                 goto out;
109                         } else {
110                                 pos++;
111                                 continue;
112                         }
113                 }
114
115                 *argpos = *pos;
116                 pos++;
117                 argpos++;
118         }
119
120 out:
121         free(argbuf);
122         free(argtext);
123         return args;
124 }
125
126 struct args *
127 args_copy(struct args *args)
128 {
129         return args_init(args->argc, args->argv);
130 }
131
132 // Insert all arguments in src into dest at position index. If replace is true,
133 // the element at dest->argv[index] is replaced with the contents of src and
134 // everything past it is shifted. Otherwise, dest->argv[index] is also shifted.
135 //
136 // src is consumed by this operation and should not be freed or used again by
137 // the caller.
138 void
139 args_insert(struct args *dest, int index, struct args *src, bool replace)
140 {
141         // Adjustments made if we are replacing or shifting the element currently at
142         // dest->argv[index].
143         int offset = replace ? 1 : 0;
144
145         if (replace) {
146                 free(dest->argv[index]);
147         }
148
149         if (src->argc == 0) {
150                 if (replace) {
151                         // Have to shift everything down by 1 since we replaced with an empty
152                         // list.
153                         for (int i = index; i < dest->argc; i++) {
154                                 dest->argv[i] = dest->argv[i + 1];
155                         }
156                         dest->argc--;
157                 }
158                 args_free(src);
159                 return;
160         }
161
162         if (src->argc == 1 && replace) {
163                 // Trivial case; replace with 1 element.
164                 dest->argv[index] = src->argv[0];
165                 src->argc = 0;
166                 args_free(src);
167                 return;
168         }
169
170         dest->argv = (char **)x_realloc(
171           dest->argv,
172           (src->argc + dest->argc + 1 - offset) *
173           sizeof(char *));
174
175         // Shift arguments over.
176         for (int i = dest->argc; i >= index + offset; i--) {
177                 dest->argv[i + src->argc - offset] = dest->argv[i];
178         }
179
180         // Copy the new arguments into place.
181         for (int i = 0; i < src->argc; i++) {
182                 dest->argv[i + index] = src->argv[i];
183         }
184
185         dest->argc += src->argc - offset;
186         src->argc = 0;
187         args_free(src);
188 }
189
190 void
191 args_free(struct args *args)
192 {
193         if (!args) {
194                 return;
195         }
196         for (int i = 0; i < args->argc; ++i) {
197                 if (args->argv[i]) {
198                         free(args->argv[i]);
199                 }
200         }
201         free(args->argv);
202         free(args);
203 }
204
205 void
206 args_add(struct args *args, const char *s)
207 {
208         args->argv = (char **)x_realloc(args->argv,
209                                         (args->argc + 2) * sizeof(char *));
210         args->argv[args->argc] = x_strdup(s);
211         args->argc++;
212         args->argv[args->argc] = NULL;
213 }
214
215 // Add all arguments in to_append to args.
216 void
217 args_extend(struct args *args, struct args *to_append)
218 {
219         for (int i = 0; i < to_append->argc; i++) {
220                 args_add(args, to_append->argv[i]);
221         }
222 }
223
224 // Pop the last element off the args list.
225 void
226 args_pop(struct args *args, int n)
227 {
228         while (n--) {
229                 args->argc--;
230                 free(args->argv[args->argc]);
231                 args->argv[args->argc] = NULL;
232         }
233 }
234
235 // Set argument at given index.
236 void
237 args_set(struct args *args, int index, const char *value)
238 {
239         assert(index < args->argc);
240         free(args->argv[index]);
241         args->argv[index] = x_strdup(value);
242 }
243
244 // Remove the first element of the argument list.
245 void
246 args_remove_first(struct args *args)
247 {
248         free(args->argv[0]);
249         memmove(&args->argv[0], &args->argv[1], args->argc * sizeof(args->argv[0]));
250         args->argc--;
251 }
252
253 // Add an argument into the front of the argument list.
254 void
255 args_add_prefix(struct args *args, const char *s)
256 {
257         args->argv = (char **)x_realloc(args->argv,
258                                         (args->argc + 2) * sizeof(char *));
259         memmove(&args->argv[1], &args->argv[0],
260                 (args->argc+1) * sizeof(args->argv[0]));
261         args->argv[0] = x_strdup(s);
262         args->argc++;
263 }
264
265 // Strip any arguments beginning with the specified prefix.
266 void
267 args_strip(struct args *args, const char *prefix)
268 {
269         for (int i = 0; i < args->argc; ) {
270                 if (str_startswith(args->argv[i], prefix)) {
271                         free(args->argv[i]);
272                         memmove(&args->argv[i],
273                                 &args->argv[i+1],
274                                 (args->argc - i) * sizeof(args->argv[i]));
275                         args->argc--;
276                 } else {
277                         i++;
278                 }
279         }
280 }
281
282 // Format args to a space-separated string. Does not quote spaces. Caller
283 // frees.
284 char *
285 args_to_string(struct args *args)
286 {
287         unsigned size = 0;
288         for (char **p = args->argv; *p; p++) {
289                 size += strlen(*p) + 1;
290         }
291
292         char *result = x_malloc(size + 1);
293         int pos = 0;
294         for (char **p = args->argv; *p; p++) {
295                 pos += sprintf(&result[pos], "%s ", *p);
296         }
297         result[pos - 1] = '\0';
298         return result;
299 }
300
301 // Returns true if args1 equals args2, else false.
302 bool
303 args_equal(struct args *args1, struct args *args2)
304 {
305         if (args1->argc != args2->argc) {
306                 return false;
307         }
308         for (int i = 0; i < args1->argc; i++) {
309                 if (!str_eq(args1->argv[i], args2->argv[i])) {
310                         return false;
311                 }
312         }
313         return true;
314 }