summaryrefslogtreecommitdiff
path: root/expand.c
diff options
context:
space:
mode:
Diffstat (limited to 'expand.c')
-rw-r--r--expand.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/expand.c b/expand.c
new file mode 100644
index 0000000..81796d5
--- /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;
+}