// Copyright (C) 2002 Andrew Tridgell // Copyright (C) 2009-2016 Joel Rosdahl // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "ccache.h" struct args * args_init(int init_argc, char **init_args) { struct args *args = (struct args *)x_malloc(sizeof(struct args)); args->argc = 0; args->argv = (char **)x_malloc(sizeof(char *)); args->argv[0] = NULL; for (int i = 0; i < init_argc; i++) { args_add(args, init_args[i]); } return args; } struct args * args_init_from_string(const char *command) { char *p = x_strdup(command); char *q = p; char *word, *saveptr = NULL; struct args *args = args_init(0, NULL); while ((word = strtok_r(q, " \t\r\n", &saveptr))) { args_add(args, word); q = NULL; } free(p); return args; } struct args * args_init_from_gcc_atfile(const char *filename) { char *argtext; if (!(argtext = read_text_file(filename, 0))) { return NULL; } struct args *args = args_init(0, NULL); char *pos = argtext; char *argbuf = x_malloc(strlen(argtext) + 1); char *argpos = argbuf; // Used to track quoting state; if \0, we are not inside quotes. Otherwise // stores the quoting character that started it, for matching the end quote. char quoting = '\0'; while (1) { switch (*pos) { case '\\': pos++; if (*pos == '\0') { continue; } break; case '\"': case '\'': if (quoting != '\0') { if (quoting == *pos) { quoting = '\0'; pos++; continue; } else { break; } } else { quoting = *pos; pos++; continue; } case '\n': case '\r': case '\t': case ' ': if (quoting) { break; } // Fall through. case '\0': // End of token *argpos = '\0'; if (argbuf[0] != '\0') { args_add(args, argbuf); } argpos = argbuf; if (*pos == '\0') { goto out; } else { pos++; continue; } } *argpos = *pos; pos++; argpos++; } out: free(argbuf); free(argtext); return args; } struct args * args_copy(struct args *args) { return args_init(args->argc, args->argv); } // Insert all arguments in src into dest at position index. If replace is true, // the element at dest->argv[index] is replaced with the contents of src and // everything past it is shifted. Otherwise, dest->argv[index] is also shifted. // // src is consumed by this operation and should not be freed or used again by // the caller. void args_insert(struct args *dest, int index, struct args *src, bool replace) { // Adjustments made if we are replacing or shifting the element currently at // dest->argv[index]. int offset = replace ? 1 : 0; if (replace) { free(dest->argv[index]); } if (src->argc == 0) { if (replace) { // Have to shift everything down by 1 since we replaced with an empty // list. for (int i = index; i < dest->argc; i++) { dest->argv[i] = dest->argv[i + 1]; } dest->argc--; } args_free(src); return; } if (src->argc == 1 && replace) { // Trivial case; replace with 1 element. dest->argv[index] = src->argv[0]; src->argc = 0; args_free(src); return; } dest->argv = (char **)x_realloc( dest->argv, (src->argc + dest->argc + 1 - offset) * sizeof(char *)); // Shift arguments over. for (int i = dest->argc; i >= index + offset; i--) { dest->argv[i + src->argc - offset] = dest->argv[i]; } // Copy the new arguments into place. for (int i = 0; i < src->argc; i++) { dest->argv[i + index] = src->argv[i]; } dest->argc += src->argc - offset; src->argc = 0; args_free(src); } void args_free(struct args *args) { if (!args) { return; } for (int i = 0; i < args->argc; ++i) { if (args->argv[i]) { free(args->argv[i]); } } free(args->argv); free(args); } void args_add(struct args *args, const char *s) { args->argv = (char **)x_realloc(args->argv, (args->argc + 2) * sizeof(char *)); args->argv[args->argc] = x_strdup(s); args->argc++; args->argv[args->argc] = NULL; } // Add all arguments in to_append to args. void args_extend(struct args *args, struct args *to_append) { for (int i = 0; i < to_append->argc; i++) { args_add(args, to_append->argv[i]); } } // Pop the last element off the args list. void args_pop(struct args *args, int n) { while (n--) { args->argc--; free(args->argv[args->argc]); args->argv[args->argc] = NULL; } } // Set argument at given index. void args_set(struct args *args, int index, const char *value) { assert(index < args->argc); free(args->argv[index]); args->argv[index] = x_strdup(value); } // Remove the first element of the argument list. void args_remove_first(struct args *args) { free(args->argv[0]); memmove(&args->argv[0], &args->argv[1], args->argc * sizeof(args->argv[0])); args->argc--; } // Add an argument into the front of the argument list. void args_add_prefix(struct args *args, const char *s) { args->argv = (char **)x_realloc(args->argv, (args->argc + 2) * sizeof(char *)); memmove(&args->argv[1], &args->argv[0], (args->argc+1) * sizeof(args->argv[0])); args->argv[0] = x_strdup(s); args->argc++; } // Strip any arguments beginning with the specified prefix. void args_strip(struct args *args, const char *prefix) { for (int i = 0; i < args->argc; ) { if (str_startswith(args->argv[i], prefix)) { free(args->argv[i]); memmove(&args->argv[i], &args->argv[i+1], (args->argc - i) * sizeof(args->argv[i])); args->argc--; } else { i++; } } } // Format args to a space-separated string. Does not quote spaces. Caller // frees. char * args_to_string(struct args *args) { unsigned size = 0; for (char **p = args->argv; *p; p++) { size += strlen(*p) + 1; } char *result = x_malloc(size + 1); int pos = 0; for (char **p = args->argv; *p; p++) { pos += sprintf(&result[pos], "%s ", *p); } result[pos - 1] = '\0'; return result; } // Returns true if args1 equals args2, else false. bool args_equal(struct args *args1, struct args *args2) { if (args1->argc != args2->argc) { return false; } for (int i = 0; i < args1->argc; i++) { if (!str_eq(args1->argv[i], args2->argv[i])) { return false; } } return true; }