version 1.90, 2007/09/17 11:14:37 |
version 1.91, 2007/09/17 11:32:25 |
|
|
static bool handle_for_loop(Buffer, const char *); |
static bool handle_for_loop(Buffer, const char *); |
static bool handle_undef(const char *); |
static bool handle_undef(const char *); |
#define ParseReadLoopLine(linebuf) Parse_ReadUnparsedLine(linebuf, "for loop") |
#define ParseReadLoopLine(linebuf) Parse_ReadUnparsedLine(linebuf, "for loop") |
static void ParseFinishDependency(void); |
|
static bool handle_bsd_command(Buffer, Buffer, const char *); |
static bool handle_bsd_command(Buffer, Buffer, const char *); |
static char *strip_comments(Buffer, const char *); |
static char *strip_comments(Buffer, const char *); |
static char *resolve_include_filename(const char *, bool); |
static char *resolve_include_filename(const char *, bool); |
|
|
static void lookup_sysv_style_include(const char *, const char *, bool); |
static void lookup_sysv_style_include(const char *, const char *, bool); |
static void lookup_sysv_include(const char *, const char *); |
static void lookup_sysv_include(const char *, const char *); |
static void lookup_conditional_include(const char *, const char *); |
static void lookup_conditional_include(const char *, const char *); |
|
static bool parse_as_special_line(Buffer, Buffer, const char *); |
|
static void parse_target_line(struct growableArray *, const char *, |
|
const char *); |
|
|
static void ParseDoCommands(const char *); |
|
|
|
/*- |
/*- |
*---------------------------------------------------------------------- |
*---------------------------------------------------------------------- |
* ParseFindKeyword -- |
* ParseFindKeyword -- |
|
|
|
|
|
|
|
|
|
/* Strip comments from line. Build a copy in buffer if necessary, */ |
|
static char * |
|
strip_comments(Buffer copy, const char *line) |
|
{ |
|
const char *comment; |
|
const char *p; |
|
|
|
comment = strchr(line, '#'); |
|
assert(comment != line); |
|
if (comment == NULL) |
|
return (char *)line; |
|
else { |
|
Buf_Reset(copy); |
|
|
|
for (p = line; *p != '\0'; p++) { |
|
if (*p == '\\') { |
|
if (p[1] == '#') { |
|
Buf_Addi(copy, line, p); |
|
Buf_AddChar(copy, '#'); |
|
line = p+2; |
|
} |
|
if (p[1] != '\0') |
|
p++; |
|
} else if (*p == '#') |
|
break; |
|
} |
|
Buf_Addi(copy, line, p); |
|
Buf_KillTrailingSpaces(copy); |
|
return Buf_Retrieve(copy); |
|
} |
|
} |
|
|
|
|
|
|
/*** |
/*** |
*** Support for various include constructs |
*** Support for various include constructs |
***/ |
***/ |
|
|
} |
} |
|
|
|
|
|
/*** |
|
*** BSD-specific . constructs |
|
*** They all follow the same pattern: |
|
*** if the syntax matches BSD stuff, then we're committed to handle |
|
*** them and report fatal errors (like, include file not existing) |
|
*** otherwise, we return false, and hope somebody else will handle it. |
|
***/ |
|
|
static bool |
static bool |
handle_poison(const char *line) |
handle_poison(const char *line) |
{ |
{ |
|
|
} |
} |
} |
} |
|
|
|
|
/* Strip comments from the line. Build a copy in buffer if necessary, */ |
|
static char * |
|
strip_comments(Buffer copy, const char *line) |
|
{ |
|
const char *comment; |
|
const char *p; |
|
|
|
comment = strchr(line, '#'); |
|
assert(comment != line); |
|
if (comment == NULL) |
|
return (char *)line; |
|
else { |
|
Buf_Reset(copy); |
|
|
|
for (p = line; *p != '\0'; p++) { |
|
if (*p == '\\') { |
|
if (p[1] == '#') { |
|
Buf_Addi(copy, line, p); |
|
Buf_AddChar(copy, '#'); |
|
line = p+2; |
|
} |
|
if (p[1] != '\0') |
|
p++; |
|
} else if (*p == '#') |
|
break; |
|
} |
|
Buf_Addi(copy, line, p); |
|
Buf_KillTrailingSpaces(copy); |
|
return Buf_Retrieve(copy); |
|
} |
|
} |
|
|
|
static bool |
static bool |
handle_for_loop(Buffer linebuf, const char *line) |
handle_for_loop(Buffer linebuf, const char *line) |
{ |
{ |
For *loop; |
For *loop; |
|
|
loop = For_Eval(line+3); |
loop = For_Eval(line); |
if (loop != NULL) { |
if (loop != NULL) { |
bool ok; |
bool ok; |
do { |
do { |
|
|
while (isspace(*line)) |
while (isspace(*line)) |
line++; |
line++; |
|
|
/* The line might be a conditional. Ask the conditional module |
/* delegate basic classification to the conditional module */ |
* about it and act accordingly. */ |
|
switch (Cond_Eval(line)) { |
switch (Cond_Eval(line)) { |
case COND_SKIP: |
case COND_SKIP: |
/* Skip to next conditional that evaluates to COND_PARSE. */ |
/* Skip to next conditional that evaluates to COND_PARSE. */ |
|
|
case COND_PARSE: |
case COND_PARSE: |
return true; |
return true; |
case COND_ISFOR: |
case COND_ISFOR: |
return handle_for_loop(linebuf, line); |
return handle_for_loop(linebuf, line + 3); |
case COND_ISINCLUDE: |
case COND_ISINCLUDE: |
return lookup_bsd_include(line + 7); |
return lookup_bsd_include(line + 7); |
case COND_ISPOISON: |
case COND_ISPOISON: |
return handle_poison(line+6); |
return handle_poison(line + 6); |
case COND_ISUNDEF: |
case COND_ISUNDEF: |
return handle_undef(line + 5); |
return handle_undef(line + 5); |
default: |
default: |
|
|
return false; |
return false; |
} |
} |
|
|
/*- |
/*** |
*----------------------------------------------------------------------- |
*** handle a group of commands |
* ParseFinishDependency -- |
***/ |
* Handle the end of a dependency group. |
|
* |
|
* Side Effects: |
|
* 'targets' list destroyed. |
|
* |
|
*----------------------------------------------------------------------- |
|
*/ |
|
static void |
static void |
ParseFinishDependency(void) |
finish_commands(struct growableArray *targets) |
{ |
{ |
Array_Every(>argets, Suff_EndTransform); |
Array_Every(targets, Suff_EndTransform); |
Array_Every(>argets, ParseHasCommands); |
Array_Every(targets, ParseHasCommands); |
Array_Reset(>argets); |
Array_Reset(targets); |
} |
} |
|
|
static void |
static void |
ParseDoCommands(const char *line) |
parse_commands(struct growableArray *targets, const char *line) |
{ |
{ |
/* add the command to the list of |
/* add the command to the list of |
* commands of all targets in the dependency spec */ |
* commands of all targets in the dependency spec */ |
char *cmd = estrdup(line); |
char *cmd = estrdup(line); |
|
|
Array_ForEach(>argets, ParseAddCmd, cmd); |
Array_ForEach(targets, ParseAddCmd, cmd); |
#ifdef CLEANUP |
#ifdef CLEANUP |
Lst_AtEnd(&targCmds, cmd); |
Lst_AtEnd(&targCmds, cmd); |
#endif |
#endif |
} |
} |
|
|
void |
static bool |
Parse_File( |
parse_as_special_line(Buffer buf, Buffer copy, const char *line) |
const char *name, /* the name of the file being read */ |
|
FILE *stream) /* Stream open to makefile to parse */ |
|
{ |
{ |
char *cp, /* pointer into the line */ |
if (*line == '.' && handle_bsd_command(buf, copy, line+1)) |
*line; /* the line we're working on */ |
return true; |
bool inDependency; /* true if currently in a dependency |
if (FEATURES(FEATURE_SYSVINCLUDE) && |
* line or its commands */ |
strncmp(line, "include", 7) == 0 && |
|
isspace(line[7]) && |
|
strchr(line, ':') == NULL) { |
|
/* It's an S3/S5-style "include". */ |
|
lookup_sysv_include(line + 7, "include"); |
|
return true; |
|
} |
|
if (FEATURES(FEATURE_CONDINCLUDE) && |
|
strncmp(line, "sinclude", 8) == 0 && |
|
isspace(line[8]) && |
|
strchr(line, ':') == NULL) { |
|
lookup_conditional_include(line+8, "sinclude"); |
|
return true; |
|
} |
|
if (FEATURES(FEATURE_CONDINCLUDE) && |
|
strncmp(line, "-include", 8) == 0 && |
|
isspace(line[8]) && |
|
strchr(line, ':') == NULL) { |
|
lookup_conditional_include(line+8, "-include"); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
BUFFER buf; |
static void |
BUFFER copy; |
parse_target_line(struct growableArray *targets, const char *line, |
|
const char *stripped) |
|
{ |
|
size_t pos; |
|
char *end; |
|
char *cp; |
|
char *dep; |
|
|
Buf_Init(&buf, MAKE_BSIZE); |
/* let's start a new set of commands */ |
Buf_Init(©, MAKE_BSIZE); |
Array_Reset(targets); |
inDependency = false; |
|
Parse_FromFile(name, stream); |
|
|
|
do { |
/* XXX this is a dirty heuristic to handle target: dep ; commands */ |
while ((line = Parse_ReadNormalLine(&buf)) != NULL) { |
dep = NULL; |
if (*line == '\t') { |
/* First we need to find eventual dependencies */ |
if (inDependency) |
pos = strcspn(stripped, ":!"); |
ParseDoCommands(line+1); |
/* go over :!, and find ; */ |
|
if (stripped[pos] != '\0' && |
|
(end = strchr(stripped+pos+1, ';')) != NULL) { |
|
if (line != stripped) |
|
/* find matching ; in original... The |
|
* original might be slightly longer. */ |
|
dep = strchr(line+(end-stripped), ';'); |
else |
else |
Parse_Error(PARSE_FATAL, |
dep = end; |
"Unassociated shell command \"%s\"", |
/* kill end of line. */ |
line); |
*end = '\0'; |
} else { |
} |
char *stripped; |
/* We now know it's a dependency line so it needs to |
stripped = strip_comments(©, line); |
* have all variables expanded before being parsed. |
if (*stripped == '.' && handle_bsd_command(&buf, ©, |
*/ |
stripped+1)) |
cp = Var_Subst(stripped, NULL, false); |
; |
ParseDoDependency(cp); |
else if (FEATURES(FEATURE_SYSVINCLUDE) && |
free(cp); |
strncmp(stripped, "include", 7) == 0 && |
|
isspace(stripped[7]) && |
|
strchr(stripped, ':') == NULL) { |
|
/* It's an S3/S5-style "include". */ |
|
lookup_sysv_include(stripped + 7, "include"); |
|
} else if (FEATURES(FEATURE_CONDINCLUDE) && |
|
strncmp(stripped, "sinclude", 8) == 0 && |
|
isspace(stripped[8]) && |
|
strchr(stripped, ':') == NULL) { |
|
lookup_conditional_include(stripped+8, "sinclude"); |
|
} else if (FEATURES(FEATURE_CONDINCLUDE) && |
|
strncmp(stripped, "-include", 8) == 0 && |
|
isspace(stripped[8]) && |
|
strchr(stripped, ':') == NULL) { |
|
lookup_conditional_include(stripped+8, "-include"); |
|
} else { |
|
char *dep; |
|
|
|
if (inDependency) |
/* Parse dependency if it's not empty. */ |
ParseFinishDependency(); |
if (dep != NULL) { |
if (Parse_As_Var_Assignment(stripped)) |
do { |
inDependency = false; |
dep++; |
else { |
} while (isspace(*dep)); |
size_t pos; |
if (*dep != '\0') |
char *end; |
parse_commands(targets, dep); |
|
} |
|
} |
|
|
/* Need a new list for the target nodes. */ |
void |
Array_Reset(>argets); |
Parse_File(const char *filename, FILE *stream) |
inDependency = true; |
{ |
|
char *line; |
|
bool expectingCommands = false; |
|
|
dep = NULL; |
/* somewhat permanent spaces to shave time */ |
/* First we need to find eventual dependencies */ |
BUFFER buf; |
pos = strcspn(stripped, ":!"); |
BUFFER copy; |
/* go over :!, and find ; */ |
|
if (stripped[pos] != '\0' && |
Buf_Init(&buf, MAKE_BSIZE); |
(end = strchr(stripped+pos+1, ';')) != NULL) { |
Buf_Init(©, MAKE_BSIZE); |
if (line != stripped) |
|
/* find matching ; in original... The |
Parse_FromFile(filename, stream); |
* original might be slightly longer. */ |
do { |
dep = strchr(line+(end-stripped), ';'); |
while ((line = Parse_ReadNormalLine(&buf)) != NULL) { |
|
if (*line == '\t') { |
|
if (expectingCommands) |
|
parse_commands(>argets, line+1); |
else |
else |
dep = end; |
Parse_Error(PARSE_FATAL, |
/* kill end of line. */ |
"Unassociated shell command \"%s\"", |
*end = '\0'; |
line); |
|
} else { |
|
const char *stripped = strip_comments(©, |
|
line); |
|
if (!parse_as_special_line(&buf, ©, |
|
stripped)) { |
|
if (expectingCommands) |
|
finish_commands(>argets); |
|
if (Parse_As_Var_Assignment(stripped)) |
|
expectingCommands = false; |
|
else { |
|
parse_target_line(>argets, |
|
line, stripped); |
|
expectingCommands = true; |
|
} |
|
} |
} |
} |
/* We now know it's a dependency line so it needs to |
|
* have all variables expanded before being parsed. |
|
* Tell the variable module to complain if some |
|
* variable is undefined... */ |
|
cp = Var_Subst(stripped, NULL, true); |
|
ParseDoDependency(cp); |
|
free(cp); |
|
|
|
/* Parse dependency if it's not empty. */ |
|
if (dep != NULL) { |
|
do { |
|
dep++; |
|
} while (isspace(*dep)); |
|
if (*dep != '\0') |
|
ParseDoCommands(dep); |
|
} |
|
} |
|
} |
} |
} |
} while (Parse_NextFile()); |
} |
|
} while (Parse_NextFile()); |
|
|
|
if (inDependency) |
if (expectingCommands) |
ParseFinishDependency(); |
finish_commands(>argets); |
/* Make sure conditionals are clean. */ |
/* Make sure conditionals are clean. */ |
Cond_End(); |
Cond_End(); |
|
|
Parse_ReportErrors(); |
Parse_ReportErrors(); |
Buf_Destroy(&buf); |
Buf_Destroy(&buf); |
Buf_Destroy(©); |
Buf_Destroy(©); |
} |
} |
|
|
void |
void |