diff --git a/expand.c b/expand.c new file mode 100644 index 00000000..81796d57 --- /dev/null +++ b/expand.c @@ -0,0 +1,342 @@ +/* Variable expansion functions for GNU Make. +Copyright (C) 1988, 1989, 1991 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make 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 2, or (at your option) +any later version. + +GNU Make 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 GNU Make; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "make.h" +#include "commands.h" +#include "file.h" +#include "variable.h" + + +/* Recursively expand V. The returned string is malloc'd. */ + +static char * +recursively_expand (v) + register struct variable *v; +{ + char *value; + + if (v->expanding) + { + /* Expanding V causes infinite recursion. Lose. */ + if (reading_filename == 0) + fatal ("Recursive variable `%s' references itself (eventually)", + v->name); + else + makefile_fatal + (reading_filename, *reading_lineno_ptr, + "Recursive variable `%s' references itself (eventually)", + v->name); + } + + v->expanding = 1; + value = allocated_variable_expand (v->value); + v->expanding = 0; + + return value; +} + +/* Scan LINE for variable references and expansion-function calls. + Build in `variable_buffer' the result of expanding the references and calls. + Return the address of the resulting string, which is null-terminated + and is valid only until the next time this function is called. */ + +char * +variable_expand (line) + register char *line; +{ + register struct variable *v; + register char *p, *o, *p1; + + p = line; + o = initialize_variable_output (); + + while (1) + { + /* Copy all following uninteresting chars all at once to the + variable output buffer, and skip them. Uninteresting chars end + at the next $ or the end of the input. */ + + p1 = index (p, '$'); + + o = variable_buffer_output (o, p, p1 != 0 ? p1 - p : strlen (p) + 1); + + if (p1 == 0) + break; + p = p1 + 1; + + /* Dispatch on the char that follows the $. */ + + switch (*p) + { + case '$': + /* $$ seen means output one $ to the variable output buffer. */ + o = variable_buffer_output (o, p, 1); + break; + + case '(': + case '{': + /* $(...) or ${...} is the general case of substitution. */ + { + char openparen = *p; + char closeparen = (openparen == '(') ? ')' : '}'; + register char *beg = p + 1; + char *op, *begp; + char *end; + + op = o; + begp = p; + if (handle_function (&op, &begp)) + { + o = op; + p = begp; + break; + } + + /* Is there a variable reference inside the parens or braces? + If so, expand it before expanding the entire reference. */ + + p1 = index (beg, closeparen); + if (p1 != 0) + p1 = lindex (beg, p1, '$'); + if (p1 != 0) + { + /* BEG now points past the opening paren or brace. + Count parens or braces until it is matched. */ + int count = 0; + for (p = beg; *p != '\0'; ++p) + { + if (*p == openparen) + ++count; + else if (*p == closeparen && --count < 0) + break; + } + /* If count is >= 0, there were unmatched opening parens + or braces, so we go to the simple case of a variable name + such as `$($(a)'. */ + if (count < 0) + { + char *name = expand_argument (beg, p); + static char start[3] = { '$', }, end[2]; + start[1] = openparen; + end[0] = closeparen; + p1 = concat (start, name, end); + free (name); + name = allocated_variable_expand (p1); + o = variable_buffer_output (o, name, strlen (name)); + free (name); + break; + } + } + + /* This is not a reference to a built-in function and + it does not contain any variable references inside. + There are several things it could be. */ + + p = index (beg, ':'); + if (p != 0 && lindex (beg, p, closeparen) == 0) + { + /* This is a substitution reference: $(FOO:A=B). */ + int count; + char *subst_beg, *replace_beg; + unsigned int subst_len, replace_len; + + v = lookup_variable (beg, p - beg); + + subst_beg = p + 1; + count = 0; + for (p = subst_beg; *p != '\0'; ++p) + { + if (*p == openparen) + ++count; + else if (*p == closeparen) + --count; + else if (*p == '=' && count <= 0) + break; + } + if (count > 0) + /* There were unmatched opening parens. */ + return initialize_variable_output (); + subst_len = p - subst_beg; + + replace_beg = p + 1; + count = 0; + for (p = replace_beg; *p != '\0'; ++p) + { + if (*p == openparen) + ++count; + else if (*p == closeparen && --count < 0) + break; + } + if (count > 0) + /* There were unmatched opening parens. */ + return initialize_variable_output (); + end = p; + replace_len = p - replace_beg; + + if (v != 0 && *v->value != '\0') + { + char *value = (v->recursive ? recursively_expand (v) + : v->value); + if (lindex (subst_beg, subst_beg + subst_len, '%') != 0) + { + p = savestring (subst_beg, subst_len); + p1 = savestring (replace_beg, replace_len); + o = patsubst_expand (o, value, p, p1, + index (p, '%'), index (p1, '%')); + free (p); + free (p1); + } + else + o = subst_expand (o, value, subst_beg, replace_beg, + subst_len, replace_len, 0, 1); + if (v->recursive) + free (value); + } + } + + /* No, this must be an ordinary variable reference. */ + else + { + /* Look up the value of the variable. */ + end = index (beg, closeparen); + if (end == 0) + return initialize_variable_output (); + v = lookup_variable (beg, end - beg); + + if (v != 0 && *v->value != '\0') + { + char *value = (v->recursive ? recursively_expand (v) + : v->value); + o = variable_buffer_output (o, value, strlen (value)); + if (v->recursive) + free (value); + } + } + + /* Advance p past the variable reference to resume scan. */ + p = end; + } + break; + + case '\0': + break; + + default: + if (isblank (p[-1])) + break; + + /* A $ followed by a random char is a variable reference: + $a is equivalent to $(a). */ + { + /* We could do the expanding here, but this way + avoids code repetition at a small performance cost. */ + char name[5]; + name[0] = '$'; + name[1] = '('; + name[2] = *p; + name[3] = ')'; + name[4] = '\0'; + p1 = allocated_variable_expand (name); + o = variable_buffer_output (o, p1, strlen (p1)); + free (p1); + } + + break; + } + + if (*p == '\0') + break; + else + ++p; + } + + (void) variable_buffer_output (o, "", 1); + return initialize_variable_output (); +} + +/* Expand an argument for an expansion function. + The text starting at STR and ending at END is variable-expanded + into a null-terminated string that is returned as the value. + This is done without clobbering `variable_buffer' or the current + variable-expansion that is in progress. */ + +char * +expand_argument (str, end) + char *str, *end; +{ + char *tmp = savestring (str, end - str); + char *value = allocated_variable_expand (tmp); + + free (tmp); + + return value; +} + +/* Expand LINE for FILE. Error messages refer to the file and line where + FILE's commands were found. Expansion uses FILE's variable set list. */ + +char * +variable_expand_for_file (line, file) + char *line; + register struct file *file; +{ + char *result; + struct variable_set_list *save; + + if (file == 0) + return variable_expand (line); + + save = current_variable_set_list; + current_variable_set_list = file->variables; + reading_filename = file->cmds->filename; + reading_lineno_ptr = &file->cmds->lineno; + result = variable_expand (line); + current_variable_set_list = save; + reading_filename = 0; + reading_lineno_ptr = 0; + + return result; +} + +/* Like variable_expand, but the returned string is malloc'd. */ +char * +allocated_variable_expand (line) + char *line; +{ + return allocated_variable_expand_for_file (line, (struct file *) 0); +} + +/* Like variable_expand_for_file, but the returned string is malloc'd. */ + +char * +allocated_variable_expand_for_file (line, file) + char *line; + struct file *file; +{ + char *save; + char *value; + + save = save_variable_output (); + + value = variable_expand_for_file (line, file); + value = savestring (value, strlen (value)); + + restore_variable_output (save); + + return value; +}