diff --git a/args.c b/args.c index 13a3d37..068338c 100644 --- a/args.c +++ b/args.c @@ -51,12 +51,137 @@ args_init_from_string(const char *command) return args; } +struct args * +args_init_from_gcc_atfile(const char *filename) +{ + struct args *args; + char *pos, *argtext, *argpos, *argbuf; + char quoting; + + /* Used to track quoting state; if \0, we're not + * inside quotes. Otherwise stores the quoting character + * that started it, for matching the end quote */ + quoting = '\0'; + + if (!(argtext = read_text_file(filename, 0))) + return NULL; + + args = args_init(0, NULL); + pos = argtext; + argbuf = x_malloc(strlen(argtext) + 1); + argpos = argbuf; + + 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 '\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) +{ + int offset; + int j; + + /* Adjustments made if we are replacing or shifting the element + * currently at dest->argv[index] */ + 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 (j = index; j < dest->argc; j++) + dest->argv[j] = dest->argv[j + 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 (j = dest->argc; j >= index + offset; j--) + dest->argv[j + src->argc - offset] = dest->argv[j]; + + /* Copy the new arguments into place */ + for (j = 0; j < src->argc; j++) + dest->argv[j + index] = src->argv[j]; + + dest->argc += src->argc - offset; + src->argc = 0; + args_free(src); +} + void args_free(struct args *args) { diff --git a/ccache.c b/ccache.c index e5c2481..a547c0f 100644 --- a/ccache.c +++ b/ccache.c @@ -1455,9 +1455,28 @@ cc_process_args(struct args *orig_args, struct args **preprocessor_args, goto out; } + if (str_startswith(argv[i], "@")) { + char *argpath = argv[i] + 1; + struct args *file_args; + + file_args = args_init_from_gcc_atfile(argpath); + if (!file_args) { + cc_log("Coudln't read arg file %s", argpath); + stats_update(STATS_ARGS); + result = false; + goto out; + } + + args_insert(orig_args, i, file_args, true); + + argc = orig_args->argc; + argv = orig_args->argv; + i--; + continue; + } + /* These are always too hard. */ if (compopt_too_hard(argv[i]) - || str_startswith(argv[i], "@") || str_startswith(argv[i], "-fdump-")) { cc_log("Compiler option %s is unsupported", argv[i]); stats_update(STATS_UNSUPPORTED); diff --git a/ccache.h b/ccache.h index 7e25883..a0a82aa 100644 --- a/ccache.h +++ b/ccache.h @@ -70,11 +70,13 @@ struct args { struct args *args_init(int, char **); struct args *args_init_from_string(const char *); +struct args *args_init_from_gcc_atfile(const char *filename); struct args *args_copy(struct args *args); void args_free(struct args *args); void args_add(struct args *args, const char *s); void args_add_prefix(struct args *args, const char *s); void args_extend(struct args *args, struct args *to_append); +void args_insert(struct args *dest, int index, struct args *src, bool replace); void args_pop(struct args *args, int n); void args_set(struct args *args, int index, const char *value); void args_strip(struct args *args, const char *prefix); diff --git a/test/test_args.c b/test/test_args.c index 50608fc..8da83bd 100644 --- a/test/test_args.c +++ b/test/test_args.c @@ -59,6 +59,31 @@ TEST(args_init_from_string) args_free(args); } +TEST(args_init_from_gcc_atfile) +{ + int fd; + struct args *args; + const char *argtext = "first sec\\\tond\tthi\\\\rd\nfourth \tfif\\ th \"si'x\\\" th\" 'seve\nth'\\"; + + fd = open("gcc_atfile", O_CREAT | O_WRONLY, 0600); + CHECK(fd >= 0); + CHECK(write(fd, argtext, strlen(argtext)) == (ssize_t)strlen(argtext)); + close(fd); + + args = args_init_from_gcc_atfile("gcc_atfile"); + CHECK(args); + CHECK_INT_EQ(7, args->argc); + CHECK_STR_EQ("first", args->argv[0]); + CHECK_STR_EQ("sec\tond", args->argv[1]); + CHECK_STR_EQ("thi\\rd", args->argv[2]); + CHECK_STR_EQ("fourth", args->argv[3]); + CHECK_STR_EQ("fif th", args->argv[4]); + CHECK_STR_EQ("si'x\" th", args->argv[5]); + CHECK_STR_EQ("seve\nth", args->argv[6]); + CHECK(!args->argv[7]); + args_free(args); +} + TEST(args_copy) { struct args *args1 = args_init_from_string("foo"); @@ -144,4 +169,44 @@ TEST(args_to_string) args_free(args); } +TEST(args_insert) +{ + struct args *args = args_init_from_string("first second third fourth fifth"); + + struct args *src1 = args_init_from_string("alpha beta gamma"); + struct args *src2 = args_init_from_string("one"); + struct args *src3 = args_init_from_string(""); + struct args *src4 = args_init_from_string("alpha beta gamma"); + struct args *src5 = args_init_from_string("one"); + struct args *src6 = args_init_from_string(""); + + args_insert(args, 2, src1, true); + CHECK_STR_EQ_FREE2("first second alpha beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(7, args->argc); + args_insert(args, 2, src2, true); + CHECK_STR_EQ_FREE2("first second one beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(7, args->argc); + args_insert(args, 2, src3, true); + CHECK_STR_EQ_FREE2("first second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(6, args->argc); + + args_insert(args, 1, src4, false); + CHECK_STR_EQ_FREE2("first alpha beta gamma second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(9, args->argc); + args_insert(args, 1, src5, false); + CHECK_STR_EQ_FREE2("first one alpha beta gamma second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(10, args->argc); + args_insert(args, 1, src6, false); + CHECK_STR_EQ_FREE2("first one alpha beta gamma second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(10, args->argc); + + args_free(args); +} + TEST_SUITE_END