From: "Boie, Andrew P" <andrew.p.b...@intel.com>

These indicate to the compiler that additional command line options
should be read from a text file. If encountered, read the file,
tokenize any arguments, and if any are found, do an in-place replacement
of the '@' parameter with the arguments within the file.

args_insert() added to insert a set of arguments into a position within
another set of arguments.

args_init_from_string() has been improved so that any character may be
included by prefixing that character with a backslash, and support for
quoted arguments which pass special characters within the quotation marks
unmodified.

Signed-off-by: Andrew Boie <andrew.p.b...@intel.com>
---
 args.c           |  108 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 ccache.c         |   25 ++++++++++++-
 ccache.h         |    1 +
 test/test_args.c |   47 +++++++++++++++++++++---
 4 files changed, 166 insertions(+), 15 deletions(-)

diff --git a/args.c b/args.c
index 13a3d37..ee84951 100644
--- a/args.c
+++ b/args.c
@@ -37,17 +37,57 @@ struct args *
 args_init_from_string(const char *command)
 {
        struct args *args;
-       char *p = x_strdup(command);
-       char *q = p;
-       char *word, *saveptr = NULL;
-
+       const char *pos = command;
+       char *argbuf = x_malloc(strlen(command) + 1);
+       char *argpos = argbuf;
        args = args_init(0, NULL);
-       while ((word = strtok_r(q, " \t\r\n", &saveptr))) {
-               args_add(args, word);
-               q = NULL;
-       }
+       argpos = argbuf;
+       char quoting = '\0';
+
+       while (1) {
+               switch (*pos) {
+               case '\\':
+                       pos++;
+                       if (*pos == '\0')
+                               continue;
+                       break;
 
-       free(p);
+               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);
        return args;
 }
 
@@ -57,6 +97,56 @@ 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.
+ *
+ * This operation modifies dest but leaves src untouched. */
+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];
+               }
+               return;
+       }
+
+       if (src->argc == 1 && replace) {
+               /* Trivial case; replace with 1 element */
+               dest->argv[index] = x_strdup(src->argv[0]);
+               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] = x_strdup(src->argv[j]);
+
+       dest->argc += src->argc - offset;
+}
+
 void
 args_free(struct args *args)
 {
diff --git a/ccache.c b/ccache.c
index 8e36bdd..a3d27ab 100644
--- a/ccache.c
+++ b/ccache.c
@@ -1445,9 +1445,32 @@ 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;
+                       char *argdata;
+
+                       if (!(argdata = read_text_file(argpath, 0))) {
+                               cc_log("Coudln't read arg file %s", argpath);
+                               stats_update(STATS_ARGS);
+                               result = false;
+                               goto out;
+                       }
+
+                       file_args = args_init_from_string(argdata);
+                       free(argdata);
+
+                       args_insert(orig_args, i, file_args, true);
+
+                       args_free(file_args);
+                       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..ed04c04 100644
--- a/ccache.h
+++ b/ccache.h
@@ -75,6 +75,7 @@ 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..3a595be 100644
--- a/test/test_args.c
+++ b/test/test_args.c
@@ -48,14 +48,17 @@ TEST(args_init_populated)
 
 TEST(args_init_from_string)
 {
-       struct args *args = args_init_from_string("first 
second\tthird\nfourth");
+       struct args *args = args_init_from_string("first 
sec\\\tond\tthi\\\\rd\nfourth  \tfif\\ th \"si'x\\\" th\" 'seve\nth'\\");
        CHECK(args);
-       CHECK_INT_EQ(4, args->argc);
+       CHECK_INT_EQ(7, args->argc);
        CHECK_STR_EQ("first", args->argv[0]);
-       CHECK_STR_EQ("second", args->argv[1]);
-       CHECK_STR_EQ("third", args->argv[2]);
+       CHECK_STR_EQ("sec\tond", args->argv[1]);
+       CHECK_STR_EQ("thi\\rd", args->argv[2]);
        CHECK_STR_EQ("fourth", args->argv[3]);
-       CHECK(!args->argv[4]);
+       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);
 }
 
@@ -144,4 +147,38 @@ 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("");
+
+       args_insert(args, 2, src1, true);
+       CHECK_STR_EQ(args_to_string(args),
+               "first second alpha beta gamma fourth fifth");
+       args_insert(args, 2, src2, true);
+       CHECK_STR_EQ(args_to_string(args),
+               "first second one beta gamma fourth fifth");
+       args_insert(args, 2, src3, true);
+       CHECK_STR_EQ(args_to_string(args),
+               "first second beta gamma fourth fifth");
+
+       args_insert(args, 1, src1, false);
+       CHECK_STR_EQ(args_to_string(args),
+               "first alpha beta gamma second beta gamma fourth fifth");
+       args_insert(args, 1, src2, false);
+       CHECK_STR_EQ(args_to_string(args),
+               "first one alpha beta gamma second beta gamma fourth fifth");
+       args_insert(args, 1, src3, false);
+       CHECK_STR_EQ(args_to_string(args),
+               "first one alpha beta gamma second beta gamma fourth fifth");
+
+       args_free(args);
+       args_free(src1);
+       args_free(src2);
+       args_free(src3);
+}
+
 TEST_SUITE_END
-- 
1.7.9.5

_______________________________________________
ccache mailing list
ccache@lists.samba.org
https://lists.samba.org/mailman/listinfo/ccache

Reply via email to