kconfig: stop supporting '.' and '/' in unquoted words
[carl9170fw.git] / config / preprocess.c
index f32a496626da0f5faa493c95a5922eef447ef6b8..b028a48b0e761f491f90e29df43b6975f5a0a757 100644 (file)
@@ -106,8 +106,92 @@ struct function {
        char *(*func)(int argc, char *argv[]);
 };
 
+static char *do_error_if(int argc, char *argv[])
+{
+       if (!strcmp(argv[0], "y"))
+               pperror("%s", argv[1]);
+
+       return NULL;
+}
+
+static char *do_filename(int argc, char *argv[])
+{
+       return xstrdup(current_file->name);
+}
+
+static char *do_info(int argc, char *argv[])
+{
+       printf("%s\n", argv[0]);
+
+       return xstrdup("");
+}
+
+static char *do_lineno(int argc, char *argv[])
+{
+       char buf[16];
+
+       sprintf(buf, "%d", yylineno);
+
+       return xstrdup(buf);
+}
+
+static char *do_shell(int argc, char *argv[])
+{
+       FILE *p;
+       char buf[256];
+       char *cmd;
+       size_t nread;
+       int i;
+
+       cmd = argv[0];
+
+       p = popen(cmd, "r");
+       if (!p) {
+               perror(cmd);
+               exit(1);
+       }
+
+       nread = fread(buf, 1, sizeof(buf), p);
+       if (nread == sizeof(buf))
+               nread--;
+
+       /* remove trailing new lines */
+       while (nread > 0 && buf[nread - 1] == '\n')
+               nread--;
+
+       buf[nread] = 0;
+
+       /* replace a new line with a space */
+       for (i = 0; i < nread; i++) {
+               if (buf[i] == '\n')
+                       buf[i] = ' ';
+       }
+
+       if (pclose(p) == -1) {
+               perror(cmd);
+               exit(1);
+       }
+
+       return xstrdup(buf);
+}
+
+static char *do_warning_if(int argc, char *argv[])
+{
+       if (!strcmp(argv[0], "y"))
+               fprintf(stderr, "%s:%d: %s\n",
+                       current_file->name, yylineno, argv[1]);
+
+       return xstrdup("");
+}
+
 static const struct function function_table[] = {
        /* Name         MIN     MAX     Function */
+       { "error-if",   2,      2,      do_error_if },
+       { "filename",   0,      0,      do_filename },
+       { "info",       1,      1,      do_info },
+       { "lineno",     0,      0,      do_lineno },
+       { "shell",      1,      1,      do_shell },
+       { "warning-if", 2,      2,      do_warning_if },
 };
 
 #define FUNCTION_MAX_ARGS              16
@@ -136,6 +220,120 @@ static char *function_expand(const char *name, int argc, char *argv[])
        return NULL;
 }
 
+/*
+ * Variables (and user-defined functions)
+ */
+static LIST_HEAD(variable_list);
+
+struct variable {
+       char *name;
+       char *value;
+       enum variable_flavor flavor;
+       int exp_count;
+       struct list_head node;
+};
+
+static struct variable *variable_lookup(const char *name)
+{
+       struct variable *v;
+
+       list_for_each_entry(v, &variable_list, node) {
+               if (!strcmp(name, v->name))
+                       return v;
+       }
+
+       return NULL;
+}
+
+static char *variable_expand(const char *name, int argc, char *argv[])
+{
+       struct variable *v;
+       char *res;
+
+       v = variable_lookup(name);
+       if (!v)
+               return NULL;
+
+       if (argc == 0 && v->exp_count)
+               pperror("Recursive variable '%s' references itself (eventually)",
+                       name);
+
+       if (v->exp_count > 1000)
+               pperror("Too deep recursive expansion");
+
+       v->exp_count++;
+
+       if (v->flavor == VAR_RECURSIVE)
+               res = expand_string_with_args(v->value, argc, argv);
+       else
+               res = xstrdup(v->value);
+
+       v->exp_count--;
+
+       return res;
+}
+
+void variable_add(const char *name, const char *value,
+                 enum variable_flavor flavor)
+{
+       struct variable *v;
+       char *new_value;
+       bool append = false;
+
+       v = variable_lookup(name);
+       if (v) {
+               /* For defined variables, += inherits the existing flavor */
+               if (flavor == VAR_APPEND) {
+                       flavor = v->flavor;
+                       append = true;
+               } else {
+                       free(v->value);
+               }
+       } else {
+               /* For undefined variables, += assumes the recursive flavor */
+               if (flavor == VAR_APPEND)
+                       flavor = VAR_RECURSIVE;
+
+               v = xmalloc(sizeof(*v));
+               v->name = xstrdup(name);
+               v->exp_count = 0;
+               list_add_tail(&v->node, &variable_list);
+       }
+
+       v->flavor = flavor;
+
+       if (flavor == VAR_SIMPLE)
+               new_value = expand_string(value);
+       else
+               new_value = xstrdup(value);
+
+       if (append) {
+               v->value = xrealloc(v->value,
+                                   strlen(v->value) + strlen(new_value) + 2);
+               strcat(v->value, " ");
+               strcat(v->value, new_value);
+               free(new_value);
+       } else {
+               v->value = new_value;
+       }
+}
+
+static void variable_del(struct variable *v)
+{
+       list_del(&v->node);
+       free(v->name);
+       free(v->value);
+       free(v);
+}
+
+void variable_all_del(void)
+{
+       struct variable *v, *tmp;
+
+       list_for_each_entry_safe(v, tmp, &variable_list, node)
+               variable_del(v);
+}
+
 /*
  * Evaluate a clause with arguments.  argc/argv are arguments from the upper
  * function call.
@@ -144,14 +342,26 @@ static char *function_expand(const char *name, int argc, char *argv[])
  */
 static char *eval_clause(const char *str, size_t len, int argc, char *argv[])
 {
-       char *tmp, *name, *res, *prev, *p;
+       char *tmp, *name, *res, *endptr, *prev, *p;
        int new_argc = 0;
        char *new_argv[FUNCTION_MAX_ARGS];
        int nest = 0;
        int i;
+       unsigned long n;
 
        tmp = xstrndup(str, len);
 
+       /*
+        * If variable name is '1', '2', etc.  It is generally an argument
+        * from a user-function call (i.e. local-scope variable).  If not
+        * available, then look-up global-scope variables.
+        */
+       n = strtoul(tmp, &endptr, 10);
+       if (!*endptr && n > 0 && n <= argc) {
+               res = xstrdup(argv[n - 1]);
+               goto free_tmp;
+       }
+
        prev = p = tmp;
 
        /*
@@ -197,6 +407,11 @@ static char *eval_clause(const char *str, size_t len, int argc, char *argv[])
                new_argv[i] = expand_string_with_args(new_argv[i + 1],
                                                      argc, argv);
 
+       /* Search for variables */
+       res = variable_expand(name, new_argc, new_argv);
+       if (res)
+               goto free;
+
        /* Look for built-in functions */
        res = function_expand(name, new_argc, new_argv);
        if (res)
@@ -214,6 +429,7 @@ free:
        for (i = 0; i < new_argc; i++)
                free(new_argv[i]);
        free(name);
+free_tmp:
        free(tmp);
 
        return res;
@@ -339,8 +555,7 @@ char *expand_string(const char *in)
 
 static bool is_end_of_token(char c)
 {
-       /* Why are '.' and '/' valid characters for symbols? */
-       return !(isalnum(c) || c == '_' || c == '-' || c == '.' || c == '/');
+       return !(isalnum(c) || c == '_' || c == '-');
 }
 
 /*