| /* Variable expansion functions for GNU Make. |
| Copyright (C) 1988, 89, 91, 92, 93, 95 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" |
| |
| /* The next two describe the variable output buffer. |
| This buffer is used to hold the variable-expansion of a line of the |
| makefile. It is made bigger with realloc whenever it is too small. |
| variable_buffer_length is the size currently allocated. |
| variable_buffer is the address of the buffer. */ |
| |
| static unsigned int variable_buffer_length; |
| static char *variable_buffer; |
| |
| /* Subroutine of variable_expand and friends: |
| The text to add is LENGTH chars starting at STRING to the variable_buffer. |
| The text is added to the buffer at PTR, and the updated pointer into |
| the buffer is returned as the value. Thus, the value returned by |
| each call to variable_buffer_output should be the first argument to |
| the following call. */ |
| |
| char * |
| variable_buffer_output (ptr, string, length) |
| char *ptr, *string; |
| unsigned int length; |
| { |
| register unsigned int newlen = length + (ptr - variable_buffer); |
| |
| if (newlen > variable_buffer_length) |
| { |
| unsigned int offset = ptr - variable_buffer; |
| variable_buffer_length = (newlen + 100 > 2 * variable_buffer_length |
| ? newlen + 100 |
| : 2 * variable_buffer_length); |
| variable_buffer = (char *) xrealloc (variable_buffer, |
| variable_buffer_length); |
| ptr = variable_buffer + offset; |
| } |
| |
| bcopy (string, ptr, length); |
| return ptr + length; |
| } |
| |
| /* Return a pointer to the beginning of the variable buffer. */ |
| |
| static char * |
| initialize_variable_output () |
| { |
| /* If we don't have a variable output buffer yet, get one. */ |
| |
| if (variable_buffer == 0) |
| { |
| variable_buffer_length = 200; |
| variable_buffer = (char *) xmalloc (variable_buffer_length); |
| variable_buffer[0] = '\0'; |
| } |
| |
| return variable_buffer; |
| } |
| |
| /* Recursively expand V. The returned string is malloc'd. */ |
| |
| 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; |
| } |
| |
| /* Warn that NAME is an undefined variable. */ |
| |
| #ifdef __GNUC__ |
| __inline |
| #endif |
| static void |
| warn_undefined (name, length) |
| char *name; |
| unsigned int length; |
| { |
| if (warn_undefined_variables_flag) |
| { |
| static const char warnmsg[] = "warning: undefined variable `%.*s'"; |
| if (reading_filename != 0) |
| makefile_error (reading_filename, *reading_lineno_ptr, |
| warnmsg, length, name); |
| else |
| error (warnmsg, length, name); |
| } |
| } |
| |
| /* Expand a simple reference to variable NAME, which is LENGTH chars long. */ |
| |
| #ifdef __GNUC__ |
| __inline |
| #endif |
| static char * |
| reference_variable (o, name, length) |
| char *o; |
| char *name; |
| unsigned int length; |
| { |
| register struct variable *v = lookup_variable (name, length); |
| |
| if (v == 0) |
| warn_undefined (name, length); |
| |
| 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); |
| } |
| |
| return o; |
| } |
| |
| /* 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; |
| int free_beg = 0; |
| char *op, *begp; |
| char *end, *colon; |
| |
| 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. */ |
| |
| end = index (beg, closeparen); |
| if (end == 0) |
| { |
| /* Unterminated variable reference. */ |
| if (reading_filename != 0) |
| makefile_fatal (reading_filename, *reading_lineno_ptr, |
| "unterminated variable reference"); |
| else |
| fatal ("unterminated variable reference"); |
| } |
| p1 = lindex (beg, end, '$'); |
| 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) |
| { |
| beg = expand_argument (beg, p); /* Expand the name. */ |
| free_beg = 1; /* Remember to free BEG when finished. */ |
| end = index (beg, '\0'); |
| } |
| } |
| else |
| /* Advance P to the end of this reference. After we are |
| finished expanding this one, P will be incremented to |
| continue the scan. */ |
| p = end; |
| |
| /* This is not a reference to a built-in function and |
| any variable references inside are now expanded. |
| Is the resultant text a substitution reference? */ |
| |
| colon = lindex (beg, end, ':'); |
| if (colon != 0) |
| { |
| /* This looks like a substitution reference: $(FOO:A=B). */ |
| char *subst_beg, *subst_end, *replace_beg, *replace_end; |
| |
| subst_beg = colon + 1; |
| subst_end = index (subst_beg, '='); |
| if (subst_end == 0) |
| /* There is no = in sight. Punt on the substitution |
| reference and treat this as a variable name containing |
| a colon, in the code below. */ |
| colon = 0; |
| else |
| { |
| replace_beg = subst_end + 1; |
| replace_end = end; |
| |
| /* Extract the variable name before the colon |
| and look up that variable. */ |
| v = lookup_variable (beg, colon - beg); |
| if (v == 0) |
| warn_undefined (beg, colon - beg); |
| |
| if (v != 0 && *v->value != '\0') |
| { |
| char *value = (v->recursive ? recursively_expand (v) |
| : v->value); |
| char *pattern, *percent; |
| if (free_beg) |
| { |
| *subst_end = '\0'; |
| pattern = subst_beg; |
| } |
| else |
| { |
| pattern = (char *) alloca (subst_end - subst_beg |
| + 1); |
| bcopy (subst_beg, pattern, subst_end - subst_beg); |
| pattern[subst_end - subst_beg] = '\0'; |
| } |
| percent = find_percent (pattern); |
| if (percent != 0) |
| { |
| char *replace; |
| if (free_beg) |
| { |
| *replace_end = '\0'; |
| replace = replace_beg; |
| } |
| else |
| { |
| replace = (char *) alloca (replace_end |
| - replace_beg |
| + 1); |
| bcopy (replace_beg, replace, |
| replace_end - replace_beg); |
| replace[replace_end - replace_beg] = '\0'; |
| } |
| |
| o = patsubst_expand (o, value, pattern, replace, |
| percent, (char *) 0); |
| } |
| else |
| o = subst_expand (o, value, |
| pattern, replace_beg, |
| strlen (pattern), |
| end - replace_beg, |
| 0, 1); |
| if (v->recursive) |
| free (value); |
| } |
| } |
| } |
| |
| if (colon == 0) |
| /* This is an ordinary variable reference. |
| Look up the value of the variable. */ |
| o = reference_variable (o, beg, end - beg); |
| |
| if (free_beg) |
| free (beg); |
| } |
| 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; |
| |
| if (*end == '\0') |
| tmp = str; |
| else |
| { |
| tmp = (char *) alloca (end - str + 1); |
| bcopy (str, tmp, end - str); |
| tmp[end - str] = '\0'; |
| } |
| |
| return allocated_variable_expand (tmp); |
| } |
| |
| /* 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_for_file, but the returned string is malloc'd. |
| This function is called a lot. It wants to be efficient. */ |
| |
| char * |
| allocated_variable_expand_for_file (line, file) |
| char *line; |
| struct file *file; |
| { |
| char *value; |
| |
| char *obuf = variable_buffer; |
| unsigned int olen = variable_buffer_length; |
| |
| variable_buffer = 0; |
| |
| value = variable_expand_for_file (line, file); |
| |
| #if 0 |
| /* Waste a little memory and save time. */ |
| value = xrealloc (value, strlen (value)) |
| #endif |
| |
| variable_buffer = obuf; |
| variable_buffer_length = olen; |
| |
| return value; |
| } |