Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
0004 
0005 #include <ctype.h>
0006 #include <stdarg.h>
0007 #include <stdbool.h>
0008 #include <stdio.h>
0009 #include <stdlib.h>
0010 #include <string.h>
0011 
0012 #include "list.h"
0013 #include "lkc.h"
0014 
0015 #define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]))
0016 
0017 static char *expand_string_with_args(const char *in, int argc, char *argv[]);
0018 static char *expand_string(const char *in);
0019 
0020 static void __attribute__((noreturn)) pperror(const char *format, ...)
0021 {
0022     va_list ap;
0023 
0024     fprintf(stderr, "%s:%d: ", current_file->name, yylineno);
0025     va_start(ap, format);
0026     vfprintf(stderr, format, ap);
0027     va_end(ap);
0028     fprintf(stderr, "\n");
0029 
0030     exit(1);
0031 }
0032 
0033 /*
0034  * Environment variables
0035  */
0036 static LIST_HEAD(env_list);
0037 
0038 struct env {
0039     char *name;
0040     char *value;
0041     struct list_head node;
0042 };
0043 
0044 static void env_add(const char *name, const char *value)
0045 {
0046     struct env *e;
0047 
0048     e = xmalloc(sizeof(*e));
0049     e->name = xstrdup(name);
0050     e->value = xstrdup(value);
0051 
0052     list_add_tail(&e->node, &env_list);
0053 }
0054 
0055 static void env_del(struct env *e)
0056 {
0057     list_del(&e->node);
0058     free(e->name);
0059     free(e->value);
0060     free(e);
0061 }
0062 
0063 /* The returned pointer must be freed when done */
0064 static char *env_expand(const char *name)
0065 {
0066     struct env *e;
0067     const char *value;
0068 
0069     if (!*name)
0070         return NULL;
0071 
0072     list_for_each_entry(e, &env_list, node) {
0073         if (!strcmp(name, e->name))
0074             return xstrdup(e->value);
0075     }
0076 
0077     value = getenv(name);
0078     if (!value)
0079         return NULL;
0080 
0081     /*
0082      * We need to remember all referenced environment variables.
0083      * They will be written out to include/config/auto.conf.cmd
0084      */
0085     env_add(name, value);
0086 
0087     return xstrdup(value);
0088 }
0089 
0090 void env_write_dep(FILE *f, const char *autoconfig_name)
0091 {
0092     struct env *e, *tmp;
0093 
0094     list_for_each_entry_safe(e, tmp, &env_list, node) {
0095         fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value);
0096         fprintf(f, "%s: FORCE\n", autoconfig_name);
0097         fprintf(f, "endif\n");
0098         env_del(e);
0099     }
0100 }
0101 
0102 /*
0103  * Built-in functions
0104  */
0105 struct function {
0106     const char *name;
0107     unsigned int min_args;
0108     unsigned int max_args;
0109     char *(*func)(int argc, char *argv[]);
0110 };
0111 
0112 static char *do_error_if(int argc, char *argv[])
0113 {
0114     if (!strcmp(argv[0], "y"))
0115         pperror("%s", argv[1]);
0116 
0117     return xstrdup("");
0118 }
0119 
0120 static char *do_filename(int argc, char *argv[])
0121 {
0122     return xstrdup(current_file->name);
0123 }
0124 
0125 static char *do_info(int argc, char *argv[])
0126 {
0127     printf("%s\n", argv[0]);
0128 
0129     return xstrdup("");
0130 }
0131 
0132 static char *do_lineno(int argc, char *argv[])
0133 {
0134     char buf[16];
0135 
0136     sprintf(buf, "%d", yylineno);
0137 
0138     return xstrdup(buf);
0139 }
0140 
0141 static char *do_shell(int argc, char *argv[])
0142 {
0143     FILE *p;
0144     char buf[4096];
0145     char *cmd;
0146     size_t nread;
0147     int i;
0148 
0149     cmd = argv[0];
0150 
0151     p = popen(cmd, "r");
0152     if (!p) {
0153         perror(cmd);
0154         exit(1);
0155     }
0156 
0157     nread = fread(buf, 1, sizeof(buf), p);
0158     if (nread == sizeof(buf))
0159         nread--;
0160 
0161     /* remove trailing new lines */
0162     while (nread > 0 && buf[nread - 1] == '\n')
0163         nread--;
0164 
0165     buf[nread] = 0;
0166 
0167     /* replace a new line with a space */
0168     for (i = 0; i < nread; i++) {
0169         if (buf[i] == '\n')
0170             buf[i] = ' ';
0171     }
0172 
0173     if (pclose(p) == -1) {
0174         perror(cmd);
0175         exit(1);
0176     }
0177 
0178     return xstrdup(buf);
0179 }
0180 
0181 static char *do_warning_if(int argc, char *argv[])
0182 {
0183     if (!strcmp(argv[0], "y"))
0184         fprintf(stderr, "%s:%d: %s\n",
0185             current_file->name, yylineno, argv[1]);
0186 
0187     return xstrdup("");
0188 }
0189 
0190 static const struct function function_table[] = {
0191     /* Name     MIN MAX Function */
0192     { "error-if",   2,  2,  do_error_if },
0193     { "filename",   0,  0,  do_filename },
0194     { "info",   1,  1,  do_info },
0195     { "lineno", 0,  0,  do_lineno },
0196     { "shell",  1,  1,  do_shell },
0197     { "warning-if", 2,  2,  do_warning_if },
0198 };
0199 
0200 #define FUNCTION_MAX_ARGS       16
0201 
0202 static char *function_expand(const char *name, int argc, char *argv[])
0203 {
0204     const struct function *f;
0205     int i;
0206 
0207     for (i = 0; i < ARRAY_SIZE(function_table); i++) {
0208         f = &function_table[i];
0209         if (strcmp(f->name, name))
0210             continue;
0211 
0212         if (argc < f->min_args)
0213             pperror("too few function arguments passed to '%s'",
0214                 name);
0215 
0216         if (argc > f->max_args)
0217             pperror("too many function arguments passed to '%s'",
0218                 name);
0219 
0220         return f->func(argc, argv);
0221     }
0222 
0223     return NULL;
0224 }
0225 
0226 /*
0227  * Variables (and user-defined functions)
0228  */
0229 static LIST_HEAD(variable_list);
0230 
0231 struct variable {
0232     char *name;
0233     char *value;
0234     enum variable_flavor flavor;
0235     int exp_count;
0236     struct list_head node;
0237 };
0238 
0239 static struct variable *variable_lookup(const char *name)
0240 {
0241     struct variable *v;
0242 
0243     list_for_each_entry(v, &variable_list, node) {
0244         if (!strcmp(name, v->name))
0245             return v;
0246     }
0247 
0248     return NULL;
0249 }
0250 
0251 static char *variable_expand(const char *name, int argc, char *argv[])
0252 {
0253     struct variable *v;
0254     char *res;
0255 
0256     v = variable_lookup(name);
0257     if (!v)
0258         return NULL;
0259 
0260     if (argc == 0 && v->exp_count)
0261         pperror("Recursive variable '%s' references itself (eventually)",
0262             name);
0263 
0264     if (v->exp_count > 1000)
0265         pperror("Too deep recursive expansion");
0266 
0267     v->exp_count++;
0268 
0269     if (v->flavor == VAR_RECURSIVE)
0270         res = expand_string_with_args(v->value, argc, argv);
0271     else
0272         res = xstrdup(v->value);
0273 
0274     v->exp_count--;
0275 
0276     return res;
0277 }
0278 
0279 void variable_add(const char *name, const char *value,
0280           enum variable_flavor flavor)
0281 {
0282     struct variable *v;
0283     char *new_value;
0284     bool append = false;
0285 
0286     v = variable_lookup(name);
0287     if (v) {
0288         /* For defined variables, += inherits the existing flavor */
0289         if (flavor == VAR_APPEND) {
0290             flavor = v->flavor;
0291             append = true;
0292         } else {
0293             free(v->value);
0294         }
0295     } else {
0296         /* For undefined variables, += assumes the recursive flavor */
0297         if (flavor == VAR_APPEND)
0298             flavor = VAR_RECURSIVE;
0299 
0300         v = xmalloc(sizeof(*v));
0301         v->name = xstrdup(name);
0302         v->exp_count = 0;
0303         list_add_tail(&v->node, &variable_list);
0304     }
0305 
0306     v->flavor = flavor;
0307 
0308     if (flavor == VAR_SIMPLE)
0309         new_value = expand_string(value);
0310     else
0311         new_value = xstrdup(value);
0312 
0313     if (append) {
0314         v->value = xrealloc(v->value,
0315                     strlen(v->value) + strlen(new_value) + 2);
0316         strcat(v->value, " ");
0317         strcat(v->value, new_value);
0318         free(new_value);
0319     } else {
0320         v->value = new_value;
0321     }
0322 }
0323 
0324 static void variable_del(struct variable *v)
0325 {
0326     list_del(&v->node);
0327     free(v->name);
0328     free(v->value);
0329     free(v);
0330 }
0331 
0332 void variable_all_del(void)
0333 {
0334     struct variable *v, *tmp;
0335 
0336     list_for_each_entry_safe(v, tmp, &variable_list, node)
0337         variable_del(v);
0338 }
0339 
0340 /*
0341  * Evaluate a clause with arguments.  argc/argv are arguments from the upper
0342  * function call.
0343  *
0344  * Returned string must be freed when done
0345  */
0346 static char *eval_clause(const char *str, size_t len, int argc, char *argv[])
0347 {
0348     char *tmp, *name, *res, *endptr, *prev, *p;
0349     int new_argc = 0;
0350     char *new_argv[FUNCTION_MAX_ARGS];
0351     int nest = 0;
0352     int i;
0353     unsigned long n;
0354 
0355     tmp = xstrndup(str, len);
0356 
0357     /*
0358      * If variable name is '1', '2', etc.  It is generally an argument
0359      * from a user-function call (i.e. local-scope variable).  If not
0360      * available, then look-up global-scope variables.
0361      */
0362     n = strtoul(tmp, &endptr, 10);
0363     if (!*endptr && n > 0 && n <= argc) {
0364         res = xstrdup(argv[n - 1]);
0365         goto free_tmp;
0366     }
0367 
0368     prev = p = tmp;
0369 
0370     /*
0371      * Split into tokens
0372      * The function name and arguments are separated by a comma.
0373      * For example, if the function call is like this:
0374      *   $(foo,$(x),$(y))
0375      *
0376      * The input string for this helper should be:
0377      *   foo,$(x),$(y)
0378      *
0379      * and split into:
0380      *   new_argv[0] = 'foo'
0381      *   new_argv[1] = '$(x)'
0382      *   new_argv[2] = '$(y)'
0383      */
0384     while (*p) {
0385         if (nest == 0 && *p == ',') {
0386             *p = 0;
0387             if (new_argc >= FUNCTION_MAX_ARGS)
0388                 pperror("too many function arguments");
0389             new_argv[new_argc++] = prev;
0390             prev = p + 1;
0391         } else if (*p == '(') {
0392             nest++;
0393         } else if (*p == ')') {
0394             nest--;
0395         }
0396 
0397         p++;
0398     }
0399     new_argv[new_argc++] = prev;
0400 
0401     /*
0402      * Shift arguments
0403      * new_argv[0] represents a function name or a variable name.  Put it
0404      * into 'name', then shift the rest of the arguments.  This simplifies
0405      * 'const' handling.
0406      */
0407     name = expand_string_with_args(new_argv[0], argc, argv);
0408     new_argc--;
0409     for (i = 0; i < new_argc; i++)
0410         new_argv[i] = expand_string_with_args(new_argv[i + 1],
0411                               argc, argv);
0412 
0413     /* Search for variables */
0414     res = variable_expand(name, new_argc, new_argv);
0415     if (res)
0416         goto free;
0417 
0418     /* Look for built-in functions */
0419     res = function_expand(name, new_argc, new_argv);
0420     if (res)
0421         goto free;
0422 
0423     /* Last, try environment variable */
0424     if (new_argc == 0) {
0425         res = env_expand(name);
0426         if (res)
0427             goto free;
0428     }
0429 
0430     res = xstrdup("");
0431 free:
0432     for (i = 0; i < new_argc; i++)
0433         free(new_argv[i]);
0434     free(name);
0435 free_tmp:
0436     free(tmp);
0437 
0438     return res;
0439 }
0440 
0441 /*
0442  * Expand a string that follows '$'
0443  *
0444  * For example, if the input string is
0445  *     ($(FOO)$($(BAR)))$(BAZ)
0446  * this helper evaluates
0447  *     $($(FOO)$($(BAR)))
0448  * and returns a new string containing the expansion (note that the string is
0449  * recursively expanded), also advancing 'str' to point to the next character
0450  * after the corresponding closing parenthesis, in this case, *str will be
0451  *     $(BAR)
0452  */
0453 static char *expand_dollar_with_args(const char **str, int argc, char *argv[])
0454 {
0455     const char *p = *str;
0456     const char *q;
0457     int nest = 0;
0458 
0459     /*
0460      * In Kconfig, variable/function references always start with "$(".
0461      * Neither single-letter variables as in $A nor curly braces as in ${CC}
0462      * are supported.  '$' not followed by '(' loses its special meaning.
0463      */
0464     if (*p != '(') {
0465         *str = p;
0466         return xstrdup("$");
0467     }
0468 
0469     p++;
0470     q = p;
0471     while (*q) {
0472         if (*q == '(') {
0473             nest++;
0474         } else if (*q == ')') {
0475             if (nest-- == 0)
0476                 break;
0477         }
0478         q++;
0479     }
0480 
0481     if (!*q)
0482         pperror("unterminated reference to '%s': missing ')'", p);
0483 
0484     /* Advance 'str' to after the expanded initial portion of the string */
0485     *str = q + 1;
0486 
0487     return eval_clause(p, q - p, argc, argv);
0488 }
0489 
0490 char *expand_dollar(const char **str)
0491 {
0492     return expand_dollar_with_args(str, 0, NULL);
0493 }
0494 
0495 static char *__expand_string(const char **str, bool (*is_end)(char c),
0496                  int argc, char *argv[])
0497 {
0498     const char *in, *p;
0499     char *expansion, *out;
0500     size_t in_len, out_len;
0501 
0502     out = xmalloc(1);
0503     *out = 0;
0504     out_len = 1;
0505 
0506     p = in = *str;
0507 
0508     while (1) {
0509         if (*p == '$') {
0510             in_len = p - in;
0511             p++;
0512             expansion = expand_dollar_with_args(&p, argc, argv);
0513             out_len += in_len + strlen(expansion);
0514             out = xrealloc(out, out_len);
0515             strncat(out, in, in_len);
0516             strcat(out, expansion);
0517             free(expansion);
0518             in = p;
0519             continue;
0520         }
0521 
0522         if (is_end(*p))
0523             break;
0524 
0525         p++;
0526     }
0527 
0528     in_len = p - in;
0529     out_len += in_len;
0530     out = xrealloc(out, out_len);
0531     strncat(out, in, in_len);
0532 
0533     /* Advance 'str' to the end character */
0534     *str = p;
0535 
0536     return out;
0537 }
0538 
0539 static bool is_end_of_str(char c)
0540 {
0541     return !c;
0542 }
0543 
0544 /*
0545  * Expand variables and functions in the given string.  Undefined variables
0546  * expand to an empty string.
0547  * The returned string must be freed when done.
0548  */
0549 static char *expand_string_with_args(const char *in, int argc, char *argv[])
0550 {
0551     return __expand_string(&in, is_end_of_str, argc, argv);
0552 }
0553 
0554 static char *expand_string(const char *in)
0555 {
0556     return expand_string_with_args(in, 0, NULL);
0557 }
0558 
0559 static bool is_end_of_token(char c)
0560 {
0561     return !(isalnum(c) || c == '_' || c == '-');
0562 }
0563 
0564 /*
0565  * Expand variables in a token.  The parsing stops when a token separater
0566  * (in most cases, it is a whitespace) is encountered.  'str' is updated to
0567  * point to the next character.
0568  *
0569  * The returned string must be freed when done.
0570  */
0571 char *expand_one_token(const char **str)
0572 {
0573     return __expand_string(str, is_end_of_token, 0, NULL);
0574 }