/* 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; }