968 lines
25 KiB
C
968 lines
25 KiB
C
#ident "@(#)macro.c 1.1 94/10/31 Copyright 1986,1987,1988 Sun Micro"
|
|
|
|
/*
|
|
* macro.c
|
|
*
|
|
* Handle expansion of make macros
|
|
*/
|
|
|
|
/*
|
|
* Included files
|
|
*/
|
|
#include "defs.h"
|
|
|
|
/*
|
|
* Defined macros
|
|
*/
|
|
|
|
/*
|
|
* typedefs & structs
|
|
*/
|
|
|
|
/*
|
|
* Static variables
|
|
*/
|
|
static Envvar envvar = NULL;
|
|
|
|
/*
|
|
* File table of contents
|
|
*/
|
|
extern Property setvar_daemon();
|
|
extern Name getvar();
|
|
extern void setvar_envvar();
|
|
extern void expand_value_with_daemon();
|
|
extern void expand_value();
|
|
extern void expand_macro();
|
|
|
|
/*
|
|
* setvar_daemon(name, value, append, daemon)
|
|
*
|
|
* Set a macro value, possibly supplying a daemon to be used
|
|
* when referencing the value.
|
|
*
|
|
* Return value:
|
|
* The property block with the new value
|
|
*
|
|
* Parameters:
|
|
* name Name of the macro to set
|
|
* value The value to set
|
|
* append Should we reset or append to the current value?
|
|
* daemon Special treatment when reading the value
|
|
*
|
|
* Static variables used:
|
|
* envvar A list of environment vars with $ in value
|
|
*
|
|
* Global variables used:
|
|
* debug_level Indicates how much tracing we should do
|
|
* makefile_type Used to check if we should enforce read only
|
|
* path_name The Name "PATH", compared against
|
|
* virtual_root The Name "VIRTUAL_ROOT", compared against
|
|
* vpath_defined Set if the macro VPATH is set
|
|
* vpath_name The Name "VPATH", compared against
|
|
*/
|
|
Property
|
|
setvar_daemon(name, value, append, daemon)
|
|
register Name name;
|
|
register Name value;
|
|
Boolean append;
|
|
Daemon daemon;
|
|
{
|
|
register Property macro = maybe_append_prop(name, macro_prop);
|
|
String_rec destination;
|
|
char buffer[STRING_BUFFER_LENGTH];
|
|
register Chain chain;
|
|
int length;
|
|
|
|
if ((makefile_type != reading_nothing) &&
|
|
macro->body.macro.read_only) {
|
|
return macro;
|
|
}
|
|
/* Strip spaces from the end of the value */
|
|
if (daemon == no_daemon) {
|
|
length = (value == NULL) || (value->string == NULL) ?
|
|
0 : strlen(value->string);
|
|
if ((length > 0) && isspace(value->string[length-1])) {
|
|
INIT_STRING_FROM_STACK(destination, buffer);
|
|
buffer[0] = 0;
|
|
append_string(value->string, &destination, length);
|
|
while ((length > 0) &&
|
|
isspace(destination.buffer.start[length-1])) {
|
|
destination.buffer.start[--length] = 0;
|
|
}
|
|
value = GETNAME(destination.buffer.start, FIND_LENGTH);
|
|
}
|
|
}
|
|
|
|
if (append) {
|
|
/* If we are appending we just tack the new value after */
|
|
/* the old one with a space in between */
|
|
INIT_STRING_FROM_STACK(destination, buffer);
|
|
buffer[0] = 0;
|
|
if ((macro != NULL) && (macro->body.macro.value != NULL)) {
|
|
append_string(macro->body.macro.value->string,
|
|
&destination,
|
|
(int) macro->body.macro.value->
|
|
hash.length);
|
|
if (value != NULL) {
|
|
append_char((int) space_char, &destination);
|
|
}
|
|
}
|
|
if (value != NULL) {
|
|
append_string(value->string,
|
|
&destination,
|
|
(int) value->hash.length);
|
|
}
|
|
value = GETNAME(destination.buffer.start, FIND_LENGTH);
|
|
if (destination.free_after_use) {
|
|
retmem(destination.buffer.start);
|
|
}
|
|
}
|
|
/* Debugging trace */
|
|
if (debug_level > 1) {
|
|
if (value != NULL) {
|
|
switch (daemon) {
|
|
case chain_daemon:
|
|
(void) printf("%s =", name->string);
|
|
for (chain = (Chain) value;
|
|
chain != NULL;
|
|
chain = chain->next) {
|
|
(void) printf(" %s",
|
|
chain->name->string);
|
|
}
|
|
(void) printf("\n");
|
|
break;
|
|
case no_daemon:
|
|
(void) printf("%s= %s\n",
|
|
name->string,
|
|
value->string);
|
|
break;
|
|
}
|
|
} else {
|
|
(void) printf("%s =\n", name->string);
|
|
}
|
|
}
|
|
/* Set the new values in the macro property block */
|
|
macro->body.macro.value = value;
|
|
macro->body.macro.daemon = daemon;
|
|
/* If the user changes the VIRTUAL_ROOT we need to flush */
|
|
/* the vroot package cache */
|
|
if (name == path_name) {
|
|
flush_path_cache();
|
|
}
|
|
if (name == virtual_root) {
|
|
flush_vroot_cache();
|
|
}
|
|
/* If this sets the VPATH we remember that */
|
|
if ((name == vpath_name) &&
|
|
(value != NULL) &&
|
|
(value->hash.length > 0)) {
|
|
vpath_defined = true;
|
|
}
|
|
/* For environment variables we also set the */
|
|
/* environment value each time */
|
|
if (macro->body.macro.exported) {
|
|
char *env;
|
|
|
|
if (!reading_environment && (value != NULL) && value->dollar) {
|
|
Envvar p;
|
|
|
|
for (p = envvar; p != NULL; p = p->next) {
|
|
if (p->name == name) {
|
|
p->value = value;
|
|
goto found_it;
|
|
}
|
|
}
|
|
|
|
p = ALLOC(Envvar);
|
|
p->name = name;
|
|
p->value = value;
|
|
p->next = envvar;
|
|
envvar = p;
|
|
found_it:;
|
|
} else {
|
|
length = 2 + strlen(name->string);
|
|
if (value != NULL) {
|
|
length += strlen(value->string);
|
|
}
|
|
env = getmem(length);
|
|
(void) sprintf(env,
|
|
"%s=%s",
|
|
name->string,
|
|
value == NULL ? "" : value->string);
|
|
(void) putenv(env);
|
|
}
|
|
}
|
|
if (name == target_arch) {
|
|
Name ha = getvar(host_arch);
|
|
Name ta = getvar(target_arch);
|
|
Name vr = getvar(virtual_root);
|
|
int length;
|
|
char *new_value;
|
|
char *old_vr;
|
|
|
|
length = 32 +
|
|
strlen(ha->string) +
|
|
strlen(ta->string) +
|
|
strlen(vr->string);
|
|
new_value = alloca(length);
|
|
old_vr = vr->string;
|
|
if (IS_EQUALN(old_vr,
|
|
"/usr/arch/",
|
|
strlen("/usr/arch/"))) {
|
|
old_vr = strchr(old_vr, (int) colon_char) + 1;
|
|
}
|
|
if (ha == ta) {
|
|
new_value = old_vr;
|
|
} else {
|
|
(void) sprintf(new_value,
|
|
"/usr/arch/%s/%s:%s",
|
|
ha->string + 1,
|
|
ta->string + 1,
|
|
old_vr);
|
|
}
|
|
if (new_value[0] != 0) {
|
|
(void) SETVAR(virtual_root,
|
|
GETNAME(new_value, FIND_LENGTH),
|
|
false);
|
|
}
|
|
}
|
|
return macro;
|
|
}
|
|
|
|
/*
|
|
* getvar(name)
|
|
*
|
|
* Return expanded value of macro.
|
|
*
|
|
* Return value:
|
|
* The expanded value of the macro
|
|
*
|
|
* Parameters:
|
|
* name The name of the macro we want the value for
|
|
*
|
|
* Global variables used:
|
|
*/
|
|
Name
|
|
getvar(name)
|
|
register Name name;
|
|
{
|
|
String_rec destination;
|
|
char buffer[STRING_BUFFER_LENGTH];
|
|
register Name result;
|
|
|
|
INIT_STRING_FROM_STACK(destination, buffer);
|
|
expand_value(maybe_append_prop(name, macro_prop)->body.macro.value,
|
|
&destination,
|
|
false);
|
|
result = GETNAME(destination.buffer.start, FIND_LENGTH);
|
|
if (destination.free_after_use) {
|
|
retmem(destination.buffer.start);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* setvar_envvar()
|
|
*
|
|
* This function scans the list of environment variables that have
|
|
* dynamic values and sets them.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Static variables used:
|
|
* envvar A list of environment vars with $ in value
|
|
*/
|
|
void
|
|
setvar_envvar()
|
|
{
|
|
Envvar p;
|
|
int length;
|
|
char *env;
|
|
String_rec value;
|
|
char buffer[STRING_BUFFER_LENGTH];
|
|
|
|
for (p = envvar; p != NULL; p = p->next) {
|
|
INIT_STRING_FROM_STACK(value, buffer);
|
|
expand_value(p->value, &value, false);
|
|
length = 2 + strlen(p->name->string);
|
|
length += strlen(value.buffer.start);
|
|
env = getmem(length);
|
|
(void) sprintf(env,
|
|
"%s=%s",
|
|
p->name->string,
|
|
value.buffer.start);
|
|
(void) putenv(env);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* expand_value_with_daemon(macro, destination, cmd)
|
|
*
|
|
* Checks for daemons and then maybe calls expand_value().
|
|
*
|
|
* Parameters:
|
|
* macro The property block with the value to expand
|
|
* destination Where the result should be deposited
|
|
* cmd If we are evaluating a command line we
|
|
* turn \ quoting off
|
|
*
|
|
* Global variables used:
|
|
*/
|
|
static void
|
|
expand_value_with_daemon(macro, destination, cmd)
|
|
register Property macro;
|
|
register String destination;
|
|
Boolean cmd;
|
|
{
|
|
register Chain chain;
|
|
|
|
switch (macro->body.macro.daemon) {
|
|
case no_daemon:
|
|
expand_value(macro->body.macro.value, destination, cmd);
|
|
return;
|
|
case chain_daemon:
|
|
/* If this is a $? value we call the daemon to translate the */
|
|
/* list of names to a string */
|
|
for (chain = (Chain) macro->body.macro.value;
|
|
chain != NULL;
|
|
chain = chain->next) {
|
|
append_string(chain->name->string,
|
|
destination,
|
|
(int) chain->name->hash.length);
|
|
if (chain->next != NULL) {
|
|
append_char((int) space_char, destination);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* expand_value(value, destination, cmd)
|
|
*
|
|
* Recursively expands all macros in the string value.
|
|
* destination is where the expanded value should be appended.
|
|
*
|
|
* Parameters:
|
|
* value The value we are expanding
|
|
* destination Where to deposit the expansion
|
|
* cmd If we are evaluating a command line we
|
|
* turn \ quoting off
|
|
*
|
|
* Global variables used:
|
|
*/
|
|
void
|
|
expand_value(value, destination, cmd)
|
|
Name value;
|
|
register String destination;
|
|
Boolean cmd;
|
|
{
|
|
Source_rec sourceb;
|
|
register Source source = &sourceb;
|
|
register char *source_p;
|
|
register char *source_end;
|
|
char *block_start;
|
|
int quote_seen;
|
|
|
|
if (value == NULL) {
|
|
/* Make sure to get a string allocated even if it */
|
|
/* will be empty */
|
|
append_string("", destination, FIND_LENGTH);
|
|
destination->text.end = destination->text.p;
|
|
return;
|
|
}
|
|
if (!value->dollar) {
|
|
/* If the value we are expanding does not contain */
|
|
/* any $ we dont have to parse it */
|
|
append_string(value->string,
|
|
destination,
|
|
(int) value->hash.length);
|
|
destination->text.end = destination->text.p;
|
|
return;
|
|
}
|
|
|
|
if (value->being_expanded) {
|
|
fatal_reader("Loop detected when expanding macro value `%s'",
|
|
value->string);
|
|
}
|
|
value->being_expanded = true;
|
|
/* Setup the structure we read from */
|
|
sourceb.string.text.p = sourceb.string.buffer.start = value->string;
|
|
sourceb.string.text.end =
|
|
sourceb.string.buffer.end =
|
|
sourceb.string.text.p + value->hash.length;
|
|
sourceb.string.free_after_use = false;
|
|
sourceb.previous = NULL;
|
|
sourceb.fd = -1;
|
|
/* Lift some pointers from the struct to local register variables */
|
|
CACHE_SOURCE(0);
|
|
/* We parse the string in segments */
|
|
/* We read chars until we find a $, then we append what we have read so far */
|
|
/* (since last $ processing) to the destination. When we find a $ we call */
|
|
/* expand_macro() and let it expand that particular $ reference into dest */
|
|
block_start = source_p;
|
|
quote_seen = 0;
|
|
for (; 1; source_p++) {
|
|
switch (GET_CHAR()) {
|
|
case backslash_char:
|
|
/* Quote $ in macro value */
|
|
if (!cmd) {
|
|
quote_seen = ~quote_seen;
|
|
}
|
|
continue;
|
|
case dollar_char:
|
|
/* Save the plain string we found since */
|
|
/* start of string or previous $ */
|
|
if (quote_seen) {
|
|
append_string(block_start,
|
|
destination,
|
|
source_p - block_start - 1);
|
|
block_start = source_p;
|
|
break;
|
|
}
|
|
append_string(block_start,
|
|
destination,
|
|
source_p - block_start);
|
|
source->string.text.p = ++source_p;
|
|
UNCACHE_SOURCE();
|
|
/* Go expand the macro reference */
|
|
expand_macro(source, destination, value->string, cmd);
|
|
CACHE_SOURCE(1);
|
|
block_start = source_p + 1;
|
|
break;
|
|
case nul_char:
|
|
/* The string ran out. Get some more */
|
|
append_string(block_start,
|
|
destination,
|
|
source_p - block_start);
|
|
GET_NEXT_BLOCK(source);
|
|
if (source == NULL) {
|
|
destination->text.end = destination->text.p;
|
|
value->being_expanded = false;
|
|
return;
|
|
}
|
|
block_start = source_p;
|
|
source_p--;
|
|
continue;
|
|
}
|
|
quote_seen = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* expand_macro(source, destination, current_string, cmd)
|
|
*
|
|
* Should be called with source->string.text.p pointing to
|
|
* the first char after the $ that starts a macro reference.
|
|
* source->string.text.p is returned pointing to the first char after
|
|
* the macro name.
|
|
* It will read the macro name, expanding any macros in it,
|
|
* and get the value. The value is then expanded.
|
|
* destination is a String that is filled in with the expanded macro.
|
|
* It may be passed in referencing a buffer to expand the macro into.
|
|
*
|
|
* Parameters:
|
|
* source The source block that references the string
|
|
* to expand
|
|
* destination Where to put the result
|
|
* current_string The string we are expanding, for error msg
|
|
* cmd If we are evaluating a command line we
|
|
* turn \ quoting off
|
|
*
|
|
* Global variables used:
|
|
* funny Vector of semantic tags for characters
|
|
* is_conditional Set if a conditional macro is refd
|
|
* make_word_mentioned Set if the word "MAKE" is mentioned
|
|
* makefile_type We deliver extra msg when reading makefiles
|
|
* query The Name "?", compared against
|
|
* query_mentioned Set if the word "?" is mentioned
|
|
*/
|
|
void
|
|
expand_macro(source, destination, current_string, cmd)
|
|
register Source source;
|
|
register String destination;
|
|
char *current_string;
|
|
Boolean cmd;
|
|
{
|
|
register char *source_p = source->string.text.p;
|
|
register char *source_end = source->string.text.end;
|
|
Property macro = NULL;
|
|
Name name;
|
|
char *block_start;
|
|
char *colon;
|
|
char *eq = NULL;
|
|
char *percent;
|
|
char *p;
|
|
register int closer;
|
|
register int closer_level = 1;
|
|
String_rec string;
|
|
char buffer[STRING_BUFFER_LENGTH];
|
|
String_rec extracted;
|
|
char extracted_string[MAXPATHLEN];
|
|
char *left_head;
|
|
char *left_tail;
|
|
char *right_tail;
|
|
int left_head_len;
|
|
int left_tail_len;
|
|
char *right_hand[128];
|
|
int quote_seen;
|
|
int i;
|
|
enum {
|
|
no_replace,
|
|
suffix_replace,
|
|
pattern_replace,
|
|
sh_replace,
|
|
} replacement = no_replace;
|
|
enum {
|
|
no_extract,
|
|
dir_extract,
|
|
file_extract,
|
|
} extraction = no_extract;
|
|
static Name make;
|
|
|
|
if (make == NULL) {
|
|
make = GETNAME("MAKE", FIND_LENGTH);
|
|
}
|
|
|
|
/* First copy the (macro-expanded) macro name into string */
|
|
INIT_STRING_FROM_STACK(string, buffer);
|
|
recheck_first_char:
|
|
/* Check the first char of the macro name to figure out what to do */
|
|
switch (GET_CHAR()) {
|
|
case nul_char:
|
|
GET_NEXT_BLOCK(source);
|
|
if (source == NULL) {
|
|
fatal_reader("'$' at end of string `%s'",
|
|
current_string);
|
|
}
|
|
goto recheck_first_char;
|
|
case parenleft_char:
|
|
/* Multi char name */
|
|
closer = (int) parenright_char;
|
|
break;
|
|
case braceleft_char:
|
|
/* Multi char name */
|
|
closer = (int) braceright_char;
|
|
break;
|
|
case newline_char:
|
|
fatal_reader("'$' at end of line");
|
|
default:
|
|
/* Single char macro name. Just suck it up */
|
|
append_char(*source_p, &string);
|
|
source->string.text.p = source_p + 1;
|
|
goto get_macro_value;
|
|
}
|
|
|
|
/* Handle multi-char macro names */
|
|
block_start = ++source_p;
|
|
quote_seen = 0;
|
|
for (; 1; source_p++) {
|
|
switch (GET_CHAR()) {
|
|
case nul_char:
|
|
append_string(block_start,
|
|
&string,
|
|
source_p - block_start);
|
|
GET_NEXT_BLOCK(source);
|
|
if (source == NULL) {
|
|
if (current_string != NULL) {
|
|
fatal_reader("Unmatched `%c' in string `%s'",
|
|
closer ==
|
|
(int) braceright_char ?
|
|
(int) braceleft_char :
|
|
(int) parenleft_char,
|
|
current_string);
|
|
} else {
|
|
fatal_reader("Premature EOF");
|
|
}
|
|
}
|
|
block_start = source_p;
|
|
source_p--;
|
|
continue;
|
|
case newline_char:
|
|
fatal_reader("Unmatched `%c' on line",
|
|
closer == (int) braceright_char ?
|
|
(int) braceleft_char :
|
|
(int) parenleft_char);
|
|
case backslash_char:
|
|
/* Quote dollar in macro value */
|
|
if (!cmd) {
|
|
quote_seen = ~quote_seen;
|
|
}
|
|
continue;
|
|
case dollar_char:
|
|
/* Macro names may reference macros. This expands the*/
|
|
/* value of such macros into the macro name string */
|
|
if (quote_seen) {
|
|
append_string(block_start,
|
|
&string,
|
|
source_p - block_start - 1);
|
|
block_start = source_p;
|
|
break;
|
|
}
|
|
append_string(block_start,
|
|
&string,
|
|
source_p - block_start);
|
|
source->string.text.p = ++source_p;
|
|
UNCACHE_SOURCE();
|
|
expand_macro(source, &string, current_string, cmd);
|
|
CACHE_SOURCE(0);
|
|
block_start = source_p;
|
|
source_p--;
|
|
break;
|
|
case parenleft_char:
|
|
/* Allow nested pairs of () in the macro name */
|
|
if (closer == (int) parenright_char) {
|
|
closer_level++;
|
|
}
|
|
break;
|
|
case braceleft_char:
|
|
/* Allow nested pairs of {} in the macro name */
|
|
if (closer == (int) braceright_char) {
|
|
closer_level++;
|
|
}
|
|
break;
|
|
case parenright_char:
|
|
case braceright_char:
|
|
/* End of the name. Save the string in the macro name*/
|
|
/* string */
|
|
if ((*source_p == closer) && (--closer_level <= 0)) {
|
|
source->string.text.p = source_p + 1;
|
|
append_string(block_start,
|
|
&string,
|
|
source_p - block_start);
|
|
goto get_macro_value;
|
|
}
|
|
break;
|
|
}
|
|
quote_seen = 0;
|
|
}
|
|
/* We got the macro name. We now inspect it to see if it */
|
|
/* specifies any translations of the value */
|
|
get_macro_value:
|
|
name = NULL;
|
|
/* First check if we have a $(@D) type translation */
|
|
if ((char_semantics[string.buffer.start[0]] &
|
|
(int) special_macro_sem) &&
|
|
(string.text.p - string.buffer.start >= 2) &&
|
|
((string.buffer.start[1] == 'D') ||
|
|
(string.buffer.start[1] == 'F'))) {
|
|
switch (string.buffer.start[1]) {
|
|
case 'D':
|
|
extraction = dir_extract;
|
|
break;
|
|
case 'F':
|
|
extraction = file_extract;
|
|
break;
|
|
default:
|
|
fatal_reader("Illegal macro reference `%s'",
|
|
string.buffer.start);
|
|
}
|
|
/* Internalize the macro name using the first char only */
|
|
name = GETNAME(string.buffer.start, 1);
|
|
(void) strcpy(string.buffer.start, string.buffer.start+2);
|
|
}
|
|
/* Check for other kinds of translations */
|
|
if ((colon = strchr(string.buffer.start, (int) colon_char)) != NULL) {
|
|
/* We have a $(FOO:.c=.o) type translation. Get the */
|
|
/* name of the macro proper */
|
|
if (name == NULL) {
|
|
name = GETNAME(string.buffer.start,
|
|
colon - string.buffer.start);
|
|
}
|
|
/* Pickup all the translations */
|
|
if ((colon[1] == 's') &&
|
|
(colon[2] == 'h') &&
|
|
(colon[3] == (int) nul_char)){
|
|
replacement = sh_replace;
|
|
} else if ((percent = strchr(colon + 1, (int) percent_char))
|
|
== NULL) {
|
|
while (colon != NULL) {
|
|
if ((eq = strchr(colon + 1,
|
|
(int) equal_char)) == NULL) {
|
|
fatal("= missing from := transformation");
|
|
}
|
|
left_tail_len = eq - colon - 1;
|
|
left_tail = alloca(left_tail_len + 1);
|
|
(void) strncpy(left_tail,
|
|
colon + 1,
|
|
eq - colon - 1);
|
|
left_tail[eq - colon - 1] = (int) nul_char;
|
|
replacement = suffix_replace;
|
|
if ((colon = strchr(eq + 1,
|
|
(int) colon_char)) !=
|
|
NULL) {
|
|
right_tail = alloca(colon - eq);
|
|
(void) strncpy(right_tail,
|
|
eq + 1,
|
|
colon - eq - 1);
|
|
right_tail[colon - eq - 1] =
|
|
(int) nul_char;
|
|
} else {
|
|
right_tail = alloca(strlen(eq) + 1);
|
|
(void) strcpy(right_tail, eq + 1);
|
|
}
|
|
}
|
|
} else {
|
|
if ((eq = strchr(colon + 1, (int) equal_char)) ==
|
|
NULL) {
|
|
fatal("= missing from := transformation");
|
|
}
|
|
if ((percent = strchr(colon + 1,
|
|
(int) percent_char)) == NULL) {
|
|
fatal("%% missing from := transformation");
|
|
}
|
|
if (eq < percent) {
|
|
fatal("%% missing from pattern of := transformation");
|
|
}
|
|
|
|
if (percent > colon+1) {
|
|
left_head = alloca(percent-colon);
|
|
(void) strncpy(left_head,
|
|
colon+1,
|
|
percent-colon-1);
|
|
left_head[percent-colon-1] = (int) nul_char;
|
|
left_head_len = percent-colon-1;
|
|
} else {
|
|
left_head = NULL;
|
|
left_head_len = 0;
|
|
}
|
|
|
|
if (eq > percent+1) {
|
|
left_tail = alloca(eq-percent);
|
|
(void) strncpy(left_tail,
|
|
percent+1,
|
|
eq-percent-1);
|
|
left_tail[eq-percent-1] = (int) nul_char;
|
|
left_tail_len = eq-percent-1;
|
|
} else {
|
|
left_tail = NULL;
|
|
left_tail_len = 0;
|
|
}
|
|
|
|
if ((percent = strchr(++eq,
|
|
(int) percent_char)) == NULL) {
|
|
|
|
right_hand[0] = alloca(strlen(eq)+1);
|
|
right_hand[1] = NULL;
|
|
(void) strcpy(right_hand[0], eq);
|
|
} else {
|
|
i = 0;
|
|
do {
|
|
right_hand[i] = alloca(percent-eq+1);
|
|
(void) strncpy(right_hand[i],
|
|
eq,
|
|
percent-eq);
|
|
right_hand[i][percent-eq] =
|
|
(int) nul_char;
|
|
if (i++ >= VSIZEOF(right_hand)) {
|
|
fatal("Too many %% in pattern");
|
|
}
|
|
eq = percent + 1;
|
|
if (eq[0] == (int) nul_char) {
|
|
right_hand[i] = "";
|
|
i++;
|
|
break;
|
|
}
|
|
} while ((percent =
|
|
strchr(eq,
|
|
(int) percent_char)) != NULL);
|
|
if (eq[0] != (int) nul_char) {
|
|
right_hand[i] = alloca(strlen(eq)+1);
|
|
(void) strcpy(right_hand[i], eq);
|
|
i++;
|
|
}
|
|
right_hand[i] = NULL;
|
|
}
|
|
replacement = pattern_replace;
|
|
}
|
|
}
|
|
if (name == NULL) {
|
|
/* No translations found. Use the whole string as */
|
|
/* the macro name */
|
|
name = GETNAME(string.buffer.start,
|
|
string.text.p - string.buffer.start);
|
|
}
|
|
if (string.free_after_use) {
|
|
retmem(string.buffer.start);
|
|
}
|
|
if (name == make) {
|
|
make_word_mentioned = true;
|
|
}
|
|
if (name == query) {
|
|
query_mentioned = true;
|
|
}
|
|
/* Get the macro value */
|
|
macro = get_prop(name->prop, macro_prop);
|
|
if ((macro != NULL) && macro->body.macro.is_conditional) {
|
|
conditional_macro_used = true;
|
|
if (makefile_type == reading_makefile) {
|
|
warning("Possibly conditional macro `%s' referenced when reading makefile",
|
|
name->string);
|
|
}
|
|
}
|
|
/* Macro name read and parsed. Expand the value. */
|
|
if ((macro == NULL) || (macro->body.macro.value == NULL)) {
|
|
/* If the value is empty we just get out of here */
|
|
goto exit;
|
|
}
|
|
if (replacement == sh_replace) {
|
|
/* If we should do a :sh transform we expand the command */
|
|
/* and process it */
|
|
INIT_STRING_FROM_STACK(string, buffer);
|
|
/* Expand the value into a local string buffer and run cmd */
|
|
expand_value_with_daemon(macro, &string, cmd);
|
|
sh_command2string(&string, destination);
|
|
} else if ((replacement != no_replace) || (extraction != no_extract)) {
|
|
/* If there were any transforms specified in the macro */
|
|
/* name we deal with them here */
|
|
INIT_STRING_FROM_STACK(string, buffer);
|
|
/* Expand the value into a local string buffer */
|
|
expand_value_with_daemon(macro, &string, cmd);
|
|
/* Scan the expanded string */
|
|
p = string.buffer.start;
|
|
while (*p != (int) nul_char) {
|
|
char chr;
|
|
|
|
/* First skip over any white space and append */
|
|
/* that to the destination string */
|
|
block_start = p;
|
|
while ((*p != (int) nul_char) && isspace(*p)) {
|
|
p++;
|
|
}
|
|
append_string(block_start,
|
|
destination,
|
|
p - block_start);
|
|
/* Then find the end of the next word */
|
|
block_start = p;
|
|
while ((*p != (int) nul_char) && !isspace(*p)) {
|
|
p++;
|
|
}
|
|
/* If we cant find another word we are done */
|
|
if (block_start == p) {
|
|
break;
|
|
}
|
|
/* Then apply the transforms to the word */
|
|
INIT_STRING_FROM_STACK(extracted, extracted_string);
|
|
switch (extraction) {
|
|
case dir_extract:
|
|
/* $(@D) type transform. Extract the */
|
|
/* path from the word. Deliver "." if */
|
|
/* none is found */
|
|
if (p != NULL) {
|
|
chr = *p;
|
|
*p = (int) nul_char;
|
|
}
|
|
eq = strrchr(block_start, (int) slash_char);
|
|
if (p != NULL) {
|
|
*p = chr;
|
|
}
|
|
if ((eq == NULL) || (eq > p)) {
|
|
append_string(".", &extracted, 1);
|
|
} else {
|
|
append_string(block_start,
|
|
&extracted,
|
|
eq - block_start);
|
|
}
|
|
break;
|
|
case file_extract:
|
|
/* $(@F) type transform. Remove the path */
|
|
/* from the word if any */
|
|
if (p != NULL) {
|
|
chr = *p;
|
|
*p = (int) nul_char;
|
|
}
|
|
eq = strrchr(block_start, (int) slash_char);
|
|
if (p != NULL) {
|
|
*p = chr;
|
|
}
|
|
if ((eq == NULL) || (eq > p)) {
|
|
append_string(block_start,
|
|
&extracted,
|
|
p - block_start);
|
|
} else {
|
|
append_string(eq + 1,
|
|
&extracted,
|
|
p - eq - 1);
|
|
}
|
|
break;
|
|
case no_extract:
|
|
append_string(block_start,
|
|
&extracted,
|
|
p - block_start);
|
|
break;
|
|
}
|
|
switch (replacement) {
|
|
case suffix_replace:
|
|
/* $(FOO:.o=.c) type transform. Maybe */
|
|
/* replace the tail of the word */
|
|
if (((extracted.text.p -
|
|
extracted.buffer.start) >=
|
|
left_tail_len) &&
|
|
IS_EQUALN(extracted.text.p - left_tail_len,
|
|
left_tail,
|
|
left_tail_len)) {
|
|
append_string(extracted.buffer.start,
|
|
destination,
|
|
(extracted.text.p -
|
|
extracted.buffer.start)
|
|
- left_tail_len);
|
|
append_string(right_tail,
|
|
destination,
|
|
FIND_LENGTH);
|
|
} else {
|
|
append_string(extracted.buffer.start,
|
|
destination,
|
|
FIND_LENGTH);
|
|
}
|
|
break;
|
|
case pattern_replace:
|
|
/* $(X:a%b=c%d) type transform */
|
|
if (((extracted.text.p -
|
|
extracted.buffer.start) >=
|
|
left_head_len+left_tail_len) &&
|
|
IS_EQUALN(left_head,
|
|
extracted.buffer.start,
|
|
left_head_len) &&
|
|
IS_EQUALN(left_tail,
|
|
extracted.text.p - left_tail_len,
|
|
left_tail_len)) {
|
|
i = 0;
|
|
while (right_hand[i] != NULL) {
|
|
append_string(right_hand[i],
|
|
destination,
|
|
FIND_LENGTH);
|
|
i++;
|
|
if (right_hand[i] != NULL) {
|
|
append_string(extracted.buffer.
|
|
start +
|
|
left_head_len,
|
|
destination,
|
|
(extracted.text.p - extracted.buffer.start)-left_head_len-left_tail_len);
|
|
}
|
|
}
|
|
} else {
|
|
append_string(extracted.buffer.start,
|
|
destination,
|
|
FIND_LENGTH);
|
|
}
|
|
break;
|
|
case no_replace:
|
|
append_string(extracted.buffer.start,
|
|
destination,
|
|
FIND_LENGTH);
|
|
break;
|
|
}
|
|
}
|
|
if (string.free_after_use) {
|
|
retmem(string.buffer.start);
|
|
}
|
|
} else {
|
|
/* This is for the case when the macro name did not */
|
|
/* specify transforms */
|
|
expand_value_with_daemon(macro, destination, cmd);
|
|
}
|
|
exit:
|
|
*destination->text.p = (int) nul_char;
|
|
destination->text.end = destination->text.p;
|
|
}
|