Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

2363 lines
62 KiB
C

/*
* COMPONENT_NAME: BLDPROCESS
*
* FUNCTIONS: ParseAddCmd
* ParseAddDir
* ParseAddDirs
* ParseBlankLines
* ParseClearPath
* ParseDoDependency
* ParseDoInclude
* ParseDoOp
* ParseDoSources
* ParseDoSrc
* ParseDoVar
* ParseEOF
* ParseErrorCond
* ParseFindKeyword
* ParseFindMain
* ParseFinishLine
* ParseHasCommands1546
* ParseLinkSrc
* ParseReadLine
* ParseReadc
* ParseVarOrDep
* Parse_AddIncludeDir1569
* Parse_DoVar
* Parse_Error
* Parse_File
* Parse_Init
* Parse_MainName
*
* ORIGINS: 27,71
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
*
* (C) COPYRIGHT International Business Machines Corp. 1994
* All Rights Reserved
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
/*
* @OSF_FREE_COPYRIGHT@
* COPYRIGHT NOTICE
* Copyright (c) 1992, 1991, 1990
* Open Software Foundation, Inc.
*
* Permission is hereby granted to use, copy, modify and freely distribute
* the software in this file and its documentation for any purpose without
* fee, provided that the above copyright notice appears in all copies and
* that both the copyright notice and this permission notice appear in
* supporting documentation. Further, provided that the name of Open
* Software Foundation, Inc. ("OSF") not be used in advertising or
* publicity pertaining to distribution of the software without prior
* written permission from OSF. OSF makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
/*
* HISTORY
* $Log: parse.c,v $
* Revision 1.2.5.5 1992/12/03 19:07:09 damon
* ODE 2.2 CR 346. Expanded copyright
* [1992/12/03 18:36:25 damon]
*
* Revision 1.2.5.4 1992/11/09 21:50:30 damon
* CR 296. Cleaned up to remove warnings
* [1992/11/09 21:49:20 damon]
*
* Revision 1.2.5.3 1992/09/24 19:26:57 gm
* CR286: Major improvements to make internals.
* [1992/09/24 17:55:25 gm]
*
* Revision 1.2.5.2 1992/06/24 16:31:45 damon
* CR 181. Changed vfork to fork
* [1992/06/24 16:19:35 damon]
*
* Revision 1.2 1991/12/05 20:44:42 devrcs
* Changes for parallel make.
* [91/04/21 16:38:34 gm]
*
* Changes for Reno make
* [91/03/22 16:08:14 mckeen]
*
* $EndLog$
*/
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* Copyright (c) 1988, 1989 by Adam de Boor
* Copyright (c) 1989 by Berkeley Softworks
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Adam de Boor.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* Neither the name of the University nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char sccsid[] = "@(#)49 1.4 src/bldenv/make/parse.c, bldprocess, bos412, GOLDA411a 1/19/94 16:31:15";
#endif /* not lint */
#ifndef lint
static char rcsid[] = "@(#)parse.c 5.18 (Berkeley) 2/19/91";
#endif /* not lint */
/*-
* parse.c --
* Functions to parse a makefile.
*
* One function, Parse_Init, must be called before any functions
* in this module are used. After that, the function Parse_File is the
* main entry point and controls most of the other functions in this
* module.
*
* Most important structures are kept in Lsts. Directories for
* the #include "..." function are kept in the 'dirSearchPath' Lst, while
* those for the #include <...> are kept in the 'parseIncPath' and
* 'sysIncPath' Lst's. The targets currently being defined are kept
* in the 'targets' Lst.
*
* The variables 'fname' and 'lineno' are used to track the name
* of the current file and the line number in that file so that error
* messages can be more meaningful.
*
* Interface:
* Parse_Init Initialization function which must be
* called before anything else in this module
* is used.
*
* Parse_File Function used to parse a makefile. It must
* be given the name of the file, which should
* already have been opened, and a function
* to call to read a character from the file.
*
* Parse_DoVar Returns zero if the given line is a
* variable assignment. Used by MainParseArgs
* to determine if an argument is a target
* or a variable assignment. Used internally
* for pretty much the same thing...
*
* Parse_Error Function called when an error occurs in
* parsing. Used by the variable and
* conditional modules.
* Parse_MainName Returns a Lst of the main target to create.
*/
#include <stdarg.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/wait.h>
#include <errno.h>
#include "make.h"
#include "buf.h"
#include "hash.h"
#include "pathnames.h"
/*
* These values are returned by ParseEOF to tell Parse_File whether to
* CONTINUE parsing, i.e. it had only reached the end of an include file,
* or if it's DONE.
*/
#define CONTINUE 1
#define DONE 0
static int ParseEOF(int);
static Lst targets; /* targets we're working on */
static Boolean inLine; /* true if currently in a dependency
* line or its commands */
string_t fname; /* name of current file (for errors) */
static int lineno; /* line number in current file */
static FILE *curFILE; /* current makefile */
static Buffer lookaheadBuf;
static Boolean haveLookahead = FALSE;
static int makeincludecompat;
static int fatals = 0;
static GNode *mainNode; /* The main target to create. This is the
* first target on the first dependency
* line in the first makefile */
/*
* Definitions for handling #include specifications
*/
typedef struct IFile {
string_t fname; /* name of previous file */
int lineno; /* saved line number */
FILE * F; /* the open stream */
} IFile;
static Lst includes; /* stack of IFiles generated by
* #includes */
Lst parseIncPath; /* list of directories for <...> includes */
Lst sysIncPath; /* list of directories for <...> includes */
Lst specPaths; /* special paths */
/*-
* specType contains the SPECial TYPE of the current target. It is
* Not if the target is unspecial. If it *is* special, however, the children
* are linked as children of the parent but not vice versa. This variable is
* set in ParseDoDependency
*/
typedef enum {
Default, /* .DEFAULT */
Ignore, /* .IGNORE */
Includes, /* .INCLUDES */
Libs, /* .LIBS */
MFlags, /* .MFLAGS or .MAKEFLAGS */
Main, /* .MAIN and we don't have anything user-specified to
* make */
Not, /* Not special */
NotMain, /* .BEGIN, .END, .INTERRUPT, .ERROR or .EXIT */
NotParallel, /* .NOTPARALLEL */
Null, /* .NULL */
Order, /* .ORDER */
Path, /* .PATH */
Precious, /* .PRECIOUS */
Silent, /* .SILENT */
Suffixes, /* .SUFFIXES */
Attribute /* Generic attribute */
} ParseSpecial;
ParseSpecial specType;
/*
* Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER
* seen, then set to each successive source on the line.
*/
static GNode *predecessor;
/*
* The parseKeywords table is searched using binary search when deciding
* if a target or source is special. The 'spec' field is the ParseSpecial
* type of the keyword ("Not" if the keyword isn't special as a target) while
* the 'op' field is the operator to apply to the list of targets if the
* keyword is used as a source ("0" if the keyword isn't special as a source)
*/
static struct {
const char *name; /* Name of keyword */
ParseSpecial spec; /* Type when used as a target */
int op; /* Operator when used as a source */
} parseKeywords[] = {
{ ".BEGIN", NotMain, 0 },
{ ".DEFAULT", Default, 0 },
{ ".OPTIONAL", Attribute, OP_OPTIONAL },
{ ".END", NotMain, 0 },
{ ".ERROR", NotMain, 0 },
{ ".EXIT", NotMain, 0 },
{ ".EXEC", Attribute, OP_EXEC },
{ ".IGNORE", Ignore, OP_IGNORE },
{ ".INCLUDES", Includes, 0 },
{ ".INTERRUPT", NotMain, 0 },
{ ".INVISIBLE", Attribute, OP_INVISIBLE },
{ ".JOIN", Attribute, OP_JOIN },
{ ".LIBS", Libs, 0 },
{ ".LINKS", Attribute, OP_LINK },
{ ".MAIN", Main, 0 },
{ ".MAKE", Attribute, OP_MAKE },
{ ".MAKEFLAGS", MFlags, 0 },
{ ".MFLAGS", MFlags, 0 },
{ ".NOTMAIN", Attribute, OP_NOTMAIN },
{ ".NOTPARALLEL", NotParallel, 0 },
{ ".NULL", Null, 0 },
{ ".ORDER", Order, 0 },
{ ".PATH", Path, 0 },
{ ".PRECIOUS", Precious, OP_PRECIOUS },
{ ".RECURSIVE", Attribute, OP_MAKE },
{ ".SILENT", Silent, OP_SILENT },
{ ".SUFFIXES", Suffixes, 0 },
{ ".USE", Attribute, OP_USE },
};
int specOp;
char parseMeta[UCHAR_MAX+1];
/*-
*----------------------------------------------------------------------
* ParseFindKeyword --
* Look in the table of keywords for one matching the given string.
*
* Results:
* The index of the keyword, or -1 if it isn't there.
*
* Side Effects:
* None
*----------------------------------------------------------------------
*/
static int
ParseFindKeyword (const char *str) /* String to find */
{
register int start,
end,
cur;
register int diff;
start = 0;
end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;
do {
cur = start + ((end - start) / 2);
diff = strcmp (str, parseKeywords[cur].name);
if (diff == 0) {
return (cur);
} else if (diff < 0) {
end = cur - 1;
} else {
start = cur + 1;
}
} while (start <= end);
return (-1);
}
/*-
* Parse_Error --
* Error message abort function for parsing. Prints out the context
* of the error (line number and file) as well as the message with
* two optional arguments.
*
* Results:
* None
*
* Side Effects:
* "fatals" is incremented if the level is PARSE_FATAL.
*/
/* VARARGS */
void
Parse_Error(int type, const char *fmt, ...)
/* Error type (PARSE_WARNING, PARSE_FATAL) */
{
va_list ap;
(void)fflush(stdout);
(void)fprintf(stderr, "\"%s\", line %d: ", fname->data, lineno);
if (type == PARSE_WARNING)
(void)fprintf(stderr, "warning: ");
va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
(void)fflush(stderr);
if (type == PARSE_FATAL)
fatals += 1;
}
/*-
*---------------------------------------------------------------------
* ParseLinkSrc --
* Link the parent node to its new child. Used in a Lst_ForEach by
* ParseDoDependency. If the specType isn't 'Not', the parent
* isn't linked as a parent of the child.
*
* Results:
* Always = 0
*
* Side Effects:
* New elements are added to the parents list of cgn and the
* children list of cgn. the unmade field of pgn is updated
* to reflect the additional child.
*---------------------------------------------------------------------
*/
static int
ParseLinkSrc (
ClientData pgnCD, /* The parent node */
ClientData cgnCD) /* The child node */
{
GNode *pgn = (GNode *)pgnCD;
GNode *cgn = (GNode *)cgnCD;
Boolean new;
if (Lst_Length(pgn->children)) {
(void) Hash_CreateEntry(&pgn->childHT, cgn->name, &new);
if (!new)
return(0);
}
(void)Lst_AtEnd (pgn->children, (ClientData)cgn);
if (specType == Not)
(void)Lst_AtEnd (cgn->parents, (ClientData)pgn);
pgn->unmade += 1;
return (0);
}
/*-
*---------------------------------------------------------------------
* ParseDoOp --
* Apply the parsed operator to the given target node. Used in a
* Lst_ForEach call by ParseDoDependency once all targets have
* been found and their operator parsed. If the previous and new
* operators are incompatible, a major error is taken.
*
* Results:
* Always 0
*
* Side Effects:
* The type field of the node is altered to reflect any new bits in
* the op.
*---------------------------------------------------------------------
*/
static int
ParseDoOp (
ClientData gnCD, /* The node to which the operator is to be
* applied */
ClientData opCD) /* The operator to apply */
{
GNode *gn = (GNode *)gnCD;
int op = (int) opCD;
/*
* If the dependency mask of the operator and the node don't match and
* the node has actually had an operator applied to it before, and
* the operator actually has some dependency information in it, complain.
*/
if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) &&
!OP_NOP(gn->type) && !OP_NOP(op))
{
Parse_Error (PARSE_FATAL, "Inconsistent operator for %s", gn->name);
return (1);
}
if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) {
/*
* If the node was the object of a :: operator, we need to create a
* new instance of it for the children and commands on this dependency
* line. The new instance is placed on the 'cohorts' list of the
* initial one (note the initial one is not on its own cohorts list)
* and the new instance is linked to all parents of the initial
* instance.
*/
register GNode *cohort;
LstNode ln;
cohort = Targ_NewGN(gn->name);
/*
* Duplicate links to parents so graph traversal is simple. Perhaps
* some type bits should be duplicated?
*
* Make the cohort invisible as well to avoid duplicating it into
* other variables. True, parents of this target won't tend to do
* anything with their local variables, but better safe than
* sorry.
*/
Lst_ForEach(gn->parents, ParseLinkSrc, (ClientData)cohort);
cohort->type = OP_DOUBLEDEP|OP_INVISIBLE;
(void)Lst_AtEnd(gn->cohorts, (ClientData)cohort);
/*
* Replace the node in the targets list with the new copy
*/
ln = Lst_Member(targets, (ClientData)gn);
Lst_Replace(ln, (ClientData)cohort);
gn = cohort;
}
/*
* We don't want to nuke any previous flags (whatever they were) so we
* just OR the new operator into the old
*/
gn->type |= op;
return (0);
}
/*-
*---------------------------------------------------------------------
* ParseDoSrc --
* Given the name of a source, figure out if it is an attribute
* and apply it to the targets if it is. Else decide if there is
* some attribute which should be applied *to* the source because
* of some special target and apply it if so. Otherwise, make the
* source be a child of the targets in the list 'targets'
*
* Results:
* None
*
* Side Effects:
* Operator bits may be added to the list of targets or to the source.
* The targets may have a new source added to their lists of children.
*---------------------------------------------------------------------
*/
static void
ParseDoSrc (
string_t src) /* name of the source to handle */
{
int tOp; /* operator (if any) from special targets */
int op; /* operator (if any) from special source */
GNode *gn;
tOp = specOp;
op = 0;
if (*src->data == '.' && isupper (src->data[1])) {
int keywd = ParseFindKeyword(src->data);
if (keywd != -1) {
op = parseKeywords[keywd].op;
}
if (op != 0) {
Lst_ForEach (targets, ParseDoOp, (ClientData)op);
return;
}
}
if (specType == Main) {
/*
* If we have noted the existence of a .MAIN, it means we need
* to add the sources of said target to the list of things
* to create. The string 'src' is likely to be free, so we
* must make a new copy of it. Note that this will only be
* invoked if the user didn't specify a target on the command
* line. This is to allow #ifmake's to succeed, or something...
*/
(void) Lst_AtEnd (create, (ClientData)string_ref(src));
/*
* Add the name to the .TARGETS variable as well, so the user can
* employ that, if desired.
*/
Var_Append(s_TARGETS, src, VAR_GLOBAL);
return;
}
gn = Targ_FindNode(src, TARG_CREATE);
if (specType == Order) {
/*
* Create proper predecessor/successor links between the previous
* source and the current one.
*/
if (predecessor != NILGNODE) {
(void)Lst_AtEnd(predecessor->successors, (ClientData)gn);
(void)Lst_AtEnd(gn->preds, (ClientData)predecessor);
}
/*
* The current source now becomes the predecessor for the next one.
*/
predecessor = gn;
return;
}
/*
* If the source is not an attribute, we need to find/create
* a node for it. After that we can apply any operator to it
* from a special target or link it to its parents, as
* appropriate.
*
* In the case of a source that was the object of a :: operator,
* the attribute is applied to all of its instances (as kept in
* the 'cohorts' list of the node) or all the cohorts are linked
* to all the targets.
*/
if (tOp) {
gn->type |= tOp;
} else {
Lst_ForEach (targets, ParseLinkSrc, (ClientData)gn);
}
if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
register GNode *cohort;
register LstNode ln;
for (ln=Lst_First(gn->cohorts); ln != NILLNODE; ln = Lst_Succ(ln)){
cohort = (GNode *)Lst_Datum(ln);
if (tOp) {
cohort->type |= tOp;
} else {
Lst_ForEach(targets, ParseLinkSrc, (ClientData)cohort);
}
}
}
}
/*-
*-----------------------------------------------------------------------
* ParseFindMain --
* Find a real target in the list and set it to be the main one.
* Called by ParseDoDependency when a main target hasn't been found
* yet.
*
* Results:
* 0 if main not found yet, 1 if it is.
*
* Side Effects:
* mainNode is changed and Targ_SetMain is called.
*
*-----------------------------------------------------------------------
*/
static int
ParseFindMain(ClientData gnCD, ClientData unused)
{
GNode *gn = (GNode *)gnCD; /* Node to examine */
if ((gn->type & (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)) == 0) {
mainNode = gn;
Targ_SetMain(gn);
return (1);
} else {
return (0);
}
}
/*-
*-----------------------------------------------------------------------
* ParseAddDir --
* Front-end for Dir_AddDir to make sure Lst_ForEach keeps going
*
* Results:
* === 0
*
* Side Effects:
* See Dir_AddDir.
*
*-----------------------------------------------------------------------
*/
static int
ParseAddDir(ClientData path, ClientData name)
{
Dir_AddDir((Lst)path, (string_t)name);
return(0);
}
static void
ParseAddDirs(string_t name)
{
Lst_ForEach(specPaths, ParseAddDir, (ClientData)name);
}
/*-
*-----------------------------------------------------------------------
* ParseClearPath --
* Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going
*
* Results:
* === 0
*
* Side Effects:
* See Dir_ClearPath
*
*-----------------------------------------------------------------------
*/
static int
ParseClearPath(ClientData path, ClientData unused)
{
Dir_ClearPath((Lst)path);
return(0);
}
/*-
*---------------------------------------------------------------------
* ParseDoDependency --
* Parse the dependency line in line.
*
* Results:
* None
*
* Side Effects:
* The nodes of the sources are linked as children to the nodes of the
* targets. Some nodes may be created.
*
* We parse a dependency line by first extracting words from the line and
* finding nodes in the list of all targets with that name. This is done
* until a character is encountered which is an operator character. Currently
* these are only ! and :. At this point the operator is parsed and the
* pointer into the line advanced until the first source is encountered.
* The parsed operator is applied to each node in the 'targets' list,
* which is where the nodes found for the targets are kept, by means of
* the ParseDoOp function.
* The sources are read in much the same way as the targets were except
* that now they are expanded using the wildcarding scheme of the C-Shell
* and all instances of the resulting words in the list of all targets
* are found. Each of the resulting nodes is then linked to each of the
* targets as one of its children.
* Certain targets are handled specially. These are the ones detailed
* by the specType variable.
* The storing of transformation rules is also taken care of here.
* A target is recognized as a transformation rule by calling
* Suff_IsTransform. If it is a transformation rule, its node is gotten
* from the suffix module via Suff_AddTransform rather than the standard
* Targ_FindNode in the target module.
*---------------------------------------------------------------------
*/
static void
ParseDoSources(
Buffer buf,
char *dependents,
Boolean doArch,
void (*func)(string_t))
{
char *line; /* our current target/dependent */
register char *cp; /* our current position */
register int c; /* a place to save a character */
string_t sline;
/*
* Expand the sources - reuse targets buffer, we are done with it...
*/
Buf_Discard(buf, Buf_Size(buf));
cp = dependents;
for (;;) {
for (;;) {
if (!parseMeta[*(unsigned char *)cp]) {
Buf_AddByte(buf, (Byte) *cp++);
continue;
}
if (*cp == '\0')
goto out;
if (*cp == ' ' || *cp == '\t')
break;
if (*cp == '$') {
char *result;
int length;
Boolean freeIt;
result = Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt);
if (result == var_Error && !oldVars) {
Parse_Error(PARSE_FATAL, "Bad imbedded variable");
return;
}
Buf_AddBytes(buf, strlen(result), (Byte *)result);
if (freeIt)
free(result);
cp += length;
continue;
}
if (*cp == ';') {
char *cp2 = cp;
*cp2++ = '\0';
while (*cp2 == ' ' || *cp2 == '\t')
cp2++;
if (*cp2 == '\0')
break;
Buf_AddBytes(lookaheadBuf, strlen(cp2), (Byte *) cp2);
haveLookahead = TRUE;
goto out;
}
Buf_AddByte(buf, (Byte) *cp++);
}
cp++;
while (*cp == ' ' || *cp == '\t')
cp++;
if (*cp == '\0')
break;
Buf_AddByte(buf, (Byte) ' ');
}
out:
/*
* Get to the first source
*/
cp = (char *) Buf_GetBase(buf);
while (*cp == ' ' || *cp == '\t')
cp++;
if (DEBUG(SUFF)) {
printf("ParseDoDependency: dependents %s\n", cp);
}
/*
* Several special targets take different actions if present with no
* sources:
* a .SUFFIXES line with no sources clears out all old suffixes
* a .PRECIOUS line makes all targets precious
* a .IGNORE line ignores errors for all targets
* a .SILENT line creates silence when making all targets
* a .PATH removes all directories from the search path(s).
*/
if (*cp == '\0') {
switch (specType) {
case Suffixes:
Suff_ClearSuffixes ();
break;
case Precious:
allPrecious = TRUE;
break;
case Ignore:
ignoreErrors = TRUE;
break;
case Silent:
beSilent = TRUE;
break;
case NotParallel:
{
extern int maxJobs;
maxJobs = 1;
break;
}
case Path:
if (specPaths == (Lst)NULL)
break;
Lst_ForEach(specPaths, ParseClearPath, (ClientData)NULL);
Lst_Destroy(specPaths, NOFREE);
break;
default:
break;
}
return;
}
/*
* Handle sources that are not dependents
*/
if (specType == MFlags) {
/*
* Call on functions in main.c to deal with these arguments and
* set the initial character to a null-character so the loop to
* get sources won't get anything
*/
Main_ParseArgLine (cp);
return;
}
if (specType == NotParallel) {
extern int maxJobs;
/*
* Need to think of adding support for marking individual nodes
* not parallel...
*/
maxJobs = 1;
return;
}
/*
* NOW GO FOR THE SOURCES
*/
/*
* If the target was one that doesn't take files as its sources
* but takes something like suffixes, we take each
* space-separated word on the line as a something and deal
* with it accordingly.
*
* If the target was .SUFFIXES, we take each source as a
* suffix and add it to the list of suffixes maintained by the
* Suff module.
*
* If the target was a .PATH, we add the source as a directory
* to search on the search path.
*
* If it was .INCLUDES, the source is taken to be the suffix of
* files which will be #included and whose search path should
* be present in the .INCLUDES variable.
*
* If it was .LIBS, the source is taken to be the suffix of
* files which are considered libraries and whose search path
* should be present in the .LIBS variable.
*
* If it was .NULL, the source is the suffix to use when a file
* has no valid suffix.
*/
for (;;) {
line = cp;
for (;;) {
if (!parseMeta[c = *(unsigned char *)cp]) {
cp++;
continue;
}
if (c == '\0' || c == ' ' || c == '\t')
break;
if (doArch && c == LPAREN) {
Lst sources = Lst_Init ();
if (Arch_ParseArchive (&line, sources, VAR_CMD) != SUCCESS) {
Parse_Error (PARSE_FATAL,
"Error in source archive spec \"%s\"", line);
return;
}
while (!Lst_IsEmpty (sources)) {
GNode *gn = (GNode *) Lst_DeQueue (sources);
ParseDoSrc (gn->name);
}
Lst_Destroy (sources, NOFREE);
while (*line == ' ' || *line == '\t')
line++;
if (*line == '\0')
return;
cp = line;
continue;
}
if (c != '$') {
cp++;
continue;
}
/*
* Must be a dynamic source (would have been expanded
* otherwise), so call the Var module to parse the puppy
* so we can safely advance beyond it...There should be
* no errors in this, as they would have been discovered
* in the initial Var_Subst and we wouldn't be here.
*/
cp = Var_Skip(cp, VAR_CMD, TRUE);
}
*cp = '\0';
sline = string_create(line);
(*func)(sline);
string_deref(sline);
if (c == '\0')
return;
cp++;
while (*cp == ' ' || *cp == '\t')
cp++;
if (*cp == '\0')
return;
}
}
static void
ParseDoDependency (
Buffer buf, /* targets buffer - reused for dependents*/
int modifier, /* modifier for targets */
char *dependents) /* dependents */
{
char *targs; /* targets */
char *line; /* our current target/dependent */
register char *cp; /* our current position */
register GNode *gn; /* a general purpose temporary node */
register int op; /* the operator on the line */
register int c; /* a place to save a character */
Lst paths; /* List of search paths to alter when parsing
* a list of .PATH targets */
string_t sline;
targs = (char *) Buf_GetBase (buf);
specType = Not;
specOp = 0;
paths = (Lst)NULL;
line = targs;
if (DEBUG(SUFF)) {
printf("ParseDoDependency: targets %s\n", line);
}
while (*line) {
cp = line;
for (;;) {
if (!parseMeta[c = *(unsigned char *)cp]) {
cp++;
continue;
}
if (c == '\0' || c == ' ' || c == '\t')
break;
if (c == LPAREN) {
/*
* Archives must be handled specially to make sure the
* OP_ARCHV flag is set in their 'type' field, for one
* thing, and because things like "archive(file1.o
* file2.o file3.o)" are permissible. Arch_ParseArchive
* will set 'line' to be the first non-blank after the
* archive-spec. It creates/finds nodes for the members
* and places them on the given list, returning SUCCESS
* if all went well and FAILURE if there was an error in
* the specification. On error, line should remain untouched.
*/
if (Arch_ParseArchive (&line, targets, VAR_CMD) != SUCCESS) {
Parse_Error (PARSE_FATAL,
"Error in archive specification: \"%s\"",
line);
return;
}
while (*line == ' ' || *line == '\t')
line++;
cp = line;
if (*line == '\0')
goto out;
continue;
}
if (c != '$') {
cp++;
continue;
}
/*
* Must be a dynamic source (would have been expanded
* otherwise), so call the Var module to parse the puppy
* so we can safely advance beyond it...There should be
* no errors in this, as they would have been discovered
* in the initial Var_Subst and we wouldn't be here.
*/
cp = Var_Skip(cp, VAR_CMD, TRUE);
}
/*
* This assumes that line is in a buffer, not a string...
*/
*cp = '\0';
sline = string_create(line);
/*
* Have a word in line. See if it's a special target and set
* specType to match it.
*/
if (*line == '.' && isupper (line[1])) {
/*
* See if the target is a special target that must have it
* or its sources handled specially.
*/
int keywd = ParseFindKeyword(line);
if (keywd != -1) {
if (specType == Path && parseKeywords[keywd].spec != Path) {
Parse_Error(PARSE_FATAL, "Mismatched special targets");
return;
}
specType = parseKeywords[keywd].spec;
specOp = parseKeywords[keywd].op;
/*
* Certain special targets have special semantics:
* .PATH Have to set the dirSearchPath
* variable too
* .MAIN Its sources are only used if
* nothing has been specified to
* create.
* .DEFAULT Need to create a node to hang
* commands on, but we don't want
* it in the graph, nor do we want
* it to be the Main Target, so we
* create it, set OP_NOTMAIN and
* add it to the list, setting
* DEFAULT to the new node for
* later use. We claim the node is
* A transformation rule to make
* life easier later, when we'll
* use Make_HandleUse to actually
* apply the .DEFAULT commands.
* .BEGIN
* .END
* .ERROR
* .EXIT
* .INTERRUPT Are not to be considered the
* main target.
* .NOTPARALLEL Make only one target at a time.
* .ORDER Must set initial predecessor to NIL
*/
switch (specType) {
case Path:
if (paths == NULL) {
paths = Lst_Init();
}
(void)Lst_AtEnd(paths, (ClientData)dirSearchPath);
break;
case Main:
if (!Lst_IsEmpty(create)) {
specType = Not;
}
break;
case NotMain:
gn = Targ_FindNode(sline, TARG_CREATE);
gn->type |= OP_NOTMAIN;
(void)Lst_AtEnd(targets, (ClientData)gn);
break;
case Default:
gn = Targ_NewGN(s_DEFAULT);
gn->type |= (OP_NOTMAIN|OP_TRANSFORM);
(void)Lst_AtEnd(targets, (ClientData)gn);
DEFAULT = gn;
break;
case Order:
predecessor = NILGNODE;
break;
default:
break;
}
} else if (strncmp (line, ".PATH", 5) == 0) {
/*
* .PATH<suffix> has to be handled specially.
* Call on the suffix module to give us a path to
* modify.
*/
Lst path;
string_t pathSuff;
specType = Path;
pathSuff = string_create(&line[5]);
path = Suff_GetPath (pathSuff);
string_deref(pathSuff);
if (path == NILLST) {
Parse_Error (PARSE_FATAL,
"Suffix '%s' not defined (yet)",
pathSuff->data);
return;
}
if (paths == (Lst)NULL)
paths = Lst_Init();
(void)Lst_AtEnd(paths, (ClientData)path);
}
}
/*
* Have word in line. Get or create its node and stick it at
* the end of the targets list
*/
if (specType == Not && *line != '\0') {
if (DEBUG(SUFF))
printf("ParseDoDep: %s\n", line);
if (Suff_IsTransform (sline))
gn = Suff_AddTransform (sline);
else
gn = Targ_FindNode (sline, TARG_CREATE);
(void)Lst_AtEnd (targets, (ClientData)gn);
} else if (specType == Path && *line != '.' && *line != '\0')
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored",
sline->data);
*cp = c;
string_deref(sline);
/*
* If it is a special type and not .PATH, it's the only target we
* allow on this line...
*/
if (specType != Not && specType != Path) {
while (*cp && *cp != ' ' && *cp != '\t')
cp++;
if (*cp)
Parse_Error(PARSE_WARNING, "Extra target(s) ignored");
break;
}
while (*cp == ' ' || *cp == '\t')
cp++;
line = cp;
}
out:
switch(specType) {
case Default:
case NotMain:
/*
* These create nodes on which to hang commands, so
* targets shouldn't be empty...
*/
case Not:
/*
* Nothing special here -- targets can be empty if it wants.
*/
break;
default:
if (!Lst_IsEmpty(targets))
Parse_Error(PARSE_WARNING,
"Special and normal targets, normal ones ignored.");
break;
}
/*
* Have now parsed all the target names. Must parse the operator next. The
* result is left in op .
*/
if (modifier == '!')
op = OP_FORCE;
else if (modifier == ':')
op = OP_DOUBLEDEP;
else
op = OP_DEPENDS;
Lst_ForEach (targets, ParseDoOp, (ClientData)op);
switch (specType) {
case Suffixes:
ParseDoSources(buf, dependents, FALSE, Suff_AddSuffix);
break;
case Path:
specPaths = paths;
ParseDoSources(buf, dependents, FALSE, ParseAddDirs);
if (paths)
Lst_Destroy(paths, NOFREE);
break;
case Includes:
ParseDoSources(buf, dependents, FALSE, Suff_AddInclude);
break;
case Libs:
ParseDoSources(buf, dependents, FALSE, Suff_AddLib);
break;
case Null:
ParseDoSources(buf, dependents, FALSE, Suff_SetNull);
break;
default:
ParseDoSources(buf, dependents, TRUE, ParseDoSrc);
if (mainNode == NILGNODE) {
/*
* If we have yet to decide on a main target to make, in the
* absence of any user input, we want the first target on
* the first dependency line that is actually a real target
* (i.e. isn't a .USE or .EXEC rule) to be made.
*/
Lst_ForEach (targets, ParseFindMain, (ClientData)NULL);
}
}
}
/*-
*---------------------------------------------------------------------
* ParseDoVar --
* Take the variable assignment in the passed line and do it in the
* global context.
*
* Note: There is a lexical ambiguity with assignment modifier characters
* in variable names. This routine interprets the character before the =
* as a modifier. Therefore, an assignment like
* C++=/usr/bin/CC
* is interpreted as "C+ +=" instead of "C++ =".
*
* Results:
* none
*
* Side Effects:
* the variable structure of the given variable name is altered in the
* global context.
*---------------------------------------------------------------------
*/
static void
ParseDoVar (
char *name, /* the variable name */
int modifier, /* Modifer for variable assignment */
char *value, /* Value for variable */
GNode *ctxt) /* Context in which to do the assignment */
{
char result[BUFSIZ]; /* Result of command */
const char *args[4]; /* Args for invoking the shell */
int fds[2]; /* Pipe streams */
int cpid; /* Child PID */
int pid; /* PID from wait() */
Boolean freeCmd; /* TRUE if the command needs to be freed, i.e.
* if any variable expansion was performed */
string_t var, val;
var = string_create(name);
while (*value == ' ' || *value == '\t')
value++;
if (modifier == '+') {
val = string_create(value);
Var_Append (var, val, ctxt);
string_deref(val);
return;
}
if (modifier == '?') {
/*
* If the variable already has a value, we don't do anything.
*/
if (!Var_Exists(var, ctxt)) {
val = string_create(value);
Var_Set (var, val, ctxt);
string_deref(val);
}
return;
}
if (modifier == ':') {
/*
* Allow variables in the old value to be undefined, but leave their
* invocation alone -- this is done by forcing oldVars to be false.
* XXX: This can cause recursive variables, but that's not hard to do,
* and this allows someone to do something like
*
* CFLAGS = $(.INCLUDES)
* CFLAGS := -I.. $(CFLAGS)
*
* And not get an error.
*/
Boolean oldOldVars = oldVars;
oldVars = FALSE;
value = Var_Subst(value, ctxt, FALSE);
oldVars = oldOldVars;
val = string_create(value);
free(value);
Var_Set(var, val, ctxt);
string_deref(val);
return;
}
if (modifier != '!') {
/*
* Normal assignment -- just do it.
*/
val = string_create(value);
Var_Set (var, val, ctxt);
string_deref(val);
return;
}
/*
* Set up arguments for shell
*/
args[0] = "sh";
args[1] = "-c";
if (strchr(value, '$') != (char *)NULL) {
/*
* There's a dollar sign in the command, so perform variable
* expansion on the whole thing. The resulting string will need
* freeing when we're done, so set freeCmd to TRUE.
*/
args[2] = Var_Subst(value, VAR_CMD, TRUE);
freeCmd = TRUE;
} else {
args[2] = value;
freeCmd = FALSE;
}
args[3] = (char *)NULL;
/*
* Open a pipe for fetching its output
*/
pipe(fds);
/*
* Fork
*/
cpid = fork();
if (cpid == 0) {
/*
* Close input side of pipe
*/
close(fds[0]);
/*
* Duplicate the output stream to the shell's output, then
* shut the extra thing down. Note we don't fetch the error
* stream...why not? Why?
*/
dup2(fds[1], 1);
close(fds[1]);
execv("/bin/sh", (char * const *)args);
_exit(1);
} else if (cpid < 0) {
/*
* Couldn't fork -- tell the user and make the variable null
*/
Parse_Error(PARSE_FATAL, "Couldn't exec \"%s\"", value);
Var_Set(var, sNULL, ctxt);
} else {
int status;
int cc;
/*
* No need for the writing half
*/
close(fds[1]);
/*
* Wait for the process to exit.
*
* XXX: If the child writes more than a pipe's worth, we will
* deadlock.
*/
if ((pid = waitpid(cpid, &status, 0)) != cpid) {
Parse_Error(PARSE_FATAL, "unexpected error from waitpid: %s",
strerror(errno));
}
/*
* Read all the characters the child wrote.
*/
cc = read(fds[0], result, sizeof(result));
if (cc < 0) {
/*
* Couldn't read the child's output -- tell the user and
* set the variable to null
*/
Parse_Error(PARSE_FATAL, "Couldn't read shell's output");
cc = 0;
}
if (status) {
/*
* Child returned an error -- tell the user but still use
* the result.
*/
Parse_Error(PARSE_FATAL, "\"%s\" returned non-zero", value);
}
/*
* Null-terminate the result, convert newlines to spaces and
* install it in the variable.
*/
result[cc] = '\0';
value = &result[cc] - 1;
if (*value == '\n') {
/*
* A final newline is just stripped
*/
*value-- = '\0';
}
while (value >= result) {
if (*value == '\n') {
*value = ' ';
}
value--;
}
val = string_create(result);
Var_Set(var, val, ctxt);
string_deref(val);
/*
* Close the input side of the pipe.
*/
close(fds[0]);
}
if (freeCmd)
free((void *)args[2]);
}
/*-
*---------------------------------------------------------------------
* Parse_DoVar --
* Take the variable assignment in the passed line and do it in the
* global context.
*
* Note: There is a lexical ambiguity with assignment modifier characters
* in variable names. This routine interprets the character before the =
* as a modifier. Therefore, an assignment like
* C++=/usr/bin/CC
* is interpreted as "C+ +=" instead of "C++ =".
*
* Results:
* none
*
* Side Effects:
* the variable structure of the given variable name is altered in the
* global context.
*---------------------------------------------------------------------
*/
int
Parse_DoVar(char *line, GNode *ctxt)
{
register char *cp;
for (cp = line; *cp != '='; cp++)
if (*cp == '\0' || *cp == ' ' || *cp == '\t')
return(1);
*cp++ = '\0';
ParseDoVar(line, 0, cp, ctxt);
return(0);
}
/*-
*---------------------------------------------------------------------
* ParseVarOrDep --
* Take the variable assignment in the passed line and do it in the
* global context.
*
* Note: There is a lexical ambiguity with assignment modifier characters
* in variable names. This routine interprets the character before the =
* as a modifier. Therefore, an assignment like
* C++=/usr/bin/CC
* is interpreted as "C+ +=" instead of "C++ =".
*
* Results:
* none
*
* Side Effects:
* the variable structure of the given variable name is altered in the
* global context.
*---------------------------------------------------------------------
*/
int
ParseVarOrDep(Buffer buf, char **linePtr, int *modifierPtr)
{
register char *cp; /* pointer into line */
char *line = *linePtr;
Boolean oweSpace;
*modifierPtr = '\0';
/*
* Skip to operator character, nulling out whitespace as we go
*/
cp = line;
oweSpace = FALSE;
for (;;) {
while (*cp == ' ' || *cp == '\t')
cp++;
for (;;) {
if (*cp == '$') {
char *result;
int length;
Boolean freeIt;
result = Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt);
if (result == var_Error && !oldVars) {
Parse_Error(PARSE_FATAL, "Bad embedded variable");
return(1);
}
if (oweSpace) {
Buf_AddByte(buf, (Byte) ' ');
oweSpace = FALSE;
}
Buf_AddBytes(buf, strlen(result), (Byte *)result);
if (freeIt)
free(result);
cp += length;
continue;
}
if (*cp == '=')
break;
if (*cp == ' ' || *cp == '\t')
break;
if (*cp == '\0') {
Parse_Error(PARSE_FATAL, "\"%s\" unrecognized input", line);
return(1);
}
if (*(cp+1) == '=') {
if (*cp == '+' || *cp == '?' || *cp == ':' || *cp == '!')
*modifierPtr = (int) *cp++;
else
Buf_AddByte(buf, (Byte) *cp++);
break;
}
if (*cp == '!') {
*modifierPtr = (int) *cp;
*cp = ':';
break;
}
if (*cp == ':') {
if (*(cp+1) == ':')
*modifierPtr = (int) *cp++;
break;
}
if (oweSpace) {
Buf_AddByte(buf, (Byte) ' ');
oweSpace = FALSE;
}
Buf_AddByte(buf, (Byte) *cp++);
}
if (*cp == '=' || *cp == ':')
break;
oweSpace = TRUE;
cp++;
}
/*
* Handle empty lhs
*/
if (Buf_Size(buf) == 0 && *cp == '=') {
Parse_Error(PARSE_FATAL, "\"%s\" null variable assignment", line);
return(1);
}
*linePtr = cp;
return(0);
}
/*-
* ParseAddCmd --
* Lst_ForEach function to add a command line to all targets
*
* Results:
* Always 0
*
* Side Effects:
* A new element is added to the commands list of the node.
*/
static int
ParseAddCmd(
ClientData gnCD, /* the node to which the command is to be added */
ClientData cmd) /* the command to add */
{
GNode *gn = (GNode *)gnCD;
/* if target already supplied, ignore commands */
if ((gn->type & OP_HAS_COMMANDS) == 0)
(void)Lst_AtEnd(gn->commands, (ClientData)string_ref((string_t)cmd));
return(0);
}
/*-
*-----------------------------------------------------------------------
* ParseHasCommands --
* Callback procedure for Parse_File when destroying the list of
* targets on the last dependency line. Marks a target as already
* having commands if it does, to keep from having shell commands
* on multiple dependency lines.
*
* Results:
* Always 0.
*
* Side Effects:
* OP_HAS_COMMANDS may be set for the target.
*
*-----------------------------------------------------------------------
*/
static void
ParseHasCommands(ClientData gnCD)
{
GNode *gn = (GNode *)gnCD; /* Node to examine */
if (!Lst_IsEmpty(gn->commands))
gn->type |= OP_HAS_COMMANDS;
}
/*-
*-----------------------------------------------------------------------
* Parse_AddIncludeDir --
* Add a directory to the path searched for included makefiles
* bracketed by double-quotes. Used by functions in main.c
*
* Results:
* None.
*
* Side Effects:
* The directory is appended to the list.
*
*-----------------------------------------------------------------------
*/
void
Parse_AddIncludeDir (string_t dir) /* The name of the directory to add */
{
Dir_AddDir (parseIncPath, dir);
}
/*-
*---------------------------------------------------------------------
* ParseDoInclude --
* Push to another file.
*
* The input is the line minus the #include. A file spec is a string
* enclosed in <> or "". The former is looked for only in sysIncPath.
* The latter in . and the directories specified by -I command line
* options
*
* Results:
* None
*
* Side Effects:
* A structure is added to the includes Lst and readProc, lineno,
* fname and curFILE are altered for the new file
*---------------------------------------------------------------------
*/
static void
ParseDoInclude (char *file, /* file specification */
Boolean ignoreFailure)
{
string_t fullname; /* full pathname of file */
IFile *oldFile; /* state associated with current file */
char endc; /* the character which ends the file spec */
char *cp; /* current position in file spec */
Boolean isSystem; /* TRUE if makefile is a system makefile */
string_t sfile;
/*
* Skip to delimiter character so we know where to look
*/
while ((*file == ' ') || (*file == '\t')) {
file++;
}
if ((*file != '"') && (*file != '<')) {
Parse_Error (PARSE_FATAL,
".%sinclude filename must be delimited by '\"' or '<'",
ignoreFailure ? "try" : "");
return;
}
/*
* Set the search path on which to find the include file based on the
* characters which bracket its name. Angle-brackets imply it's
* a system Makefile while double-quotes imply it's a user makefile
*/
if (*file == '<') {
isSystem = TRUE;
endc = '>';
} else {
isSystem = FALSE;
endc = '"';
}
/*
* Skip to matching delimiter
*/
for (cp = ++file; *cp && *cp != endc; cp++) {
continue;
}
if (*cp != endc) {
Parse_Error (PARSE_FATAL,
"Unclosed .%sinclude filename. '%c' expected",
ignoreFailure ? "try" : "", endc);
return;
}
*cp = '\0';
/*
* Substitute for any variables in the file name before trying to
* find the thing.
*/
if (strchr(file, '$') != (char *) NULL)
file = Var_Subst (file, VAR_CMD, FALSE);
/*
* Now we know the file's name and its search path, we attempt to
* find the durn thing. A return of NULL indicates the file don't
* exist.
*/
sfile = string_create(file);
if (makeincludecompat) {
fullname = Dir_FindFile(sfile, dirSearchPath);
if (fullname == (string_t) NULL)
fullname = Dir_FindFile (sfile, parseIncPath);
if (fullname == (string_t) NULL)
fullname = Dir_FindFile(sfile, sysIncPath);
} else {
if (!isSystem) {
fullname = Dir_FindFile(sfile, dirSearchPath);
} else {
fullname = Dir_FindFile (sfile, parseIncPath);
if (fullname == (string_t) NULL)
fullname = Dir_FindFile(sfile, sysIncPath);
}
}
if (fullname == (string_t) NULL) {
if (!ignoreFailure)
Parse_Error (PARSE_FATAL, "Could not find %s", file);
return;
}
/*
* Once we find the absolute path to the file, we get to save all the
* state from the current file before we can start reading this
* include file. The state is stored in an IFile structure which
* is placed on a list with other IFile structures. The list makes
* a very nice stack to track how we got here...
*/
oldFile = (IFile *) emalloc (sizeof (IFile));
oldFile->fname = fname;
oldFile->F = curFILE;
oldFile->lineno = lineno;
(void) Lst_AtFront (includes, (ClientData)oldFile);
/*
* Once the previous state has been saved, we can get down to reading
* the new file. We set up the name of the file to be the absolute
* name of the include file so error messages refer to the right
* place. Naturally enough, we start reading at line number 0.
*/
fname = fullname;
lineno = 0;
curFILE = fopen (fullname->data, "r");
if (curFILE == (FILE * ) NULL) {
Parse_Error (PARSE_FATAL, "Cannot open %s", fullname->data);
/*
* Pop to previous file
*/
(void) ParseEOF(0);
}
if (DEBUG(SUFF)) {
printf("ParseDoInclude: now reading %s\n", fullname->data);
fflush(stdout);
}
}
/*-
*---------------------------------------------------------------------
* ParseEOF --
* Called when EOF is reached in the current file. If we were reading
* an include file, the includes stack is popped and things set up
* to go back to reading the previous file at the previous location.
*
* Results:
* CONTINUE if there's more to do. DONE if not.
*
* Side Effects:
* The old curFILE, is closed. The includes list is shortened.
* lineno, curFILE, and fname are changed if CONTINUE is returned.
*---------------------------------------------------------------------
*/
static int
ParseEOF (int opened)
{
IFile *ifile; /* the state on the top of the includes stack */
if (DEBUG(SUFF)) {
printf("ParseEOF: %s\n", fname->data);
}
if (Lst_IsEmpty (includes)) {
return (DONE);
}
ifile = (IFile *) Lst_DeQueue (includes);
string_deref (fname);
fname = ifile->fname;
lineno = ifile->lineno;
if (opened)
(void) fclose (curFILE);
curFILE = ifile->F;
free ((Address)ifile);
return (CONTINUE);
}
/*-
*---------------------------------------------------------------------
* ParseReadc --
* Read a character from the current file and update the line number
* counter as necessary
*
* Results:
* The character that was read
*
* Side Effects:
* The lineno counter is incremented if the character is a newline
*---------------------------------------------------------------------
*/
#define ParseReadc() (getc(curFILE))
/*-
*---------------------------------------------------------------------
* ParseReadLine --
* Read an entire line from the input file. Called only by Parse_File.
* To facilitate escaped newlines and what have you, a character is
* buffered in 'lastc', which is '\0' when no characters have been
* read. When we break out of the loop, c holds the terminating
* character and lastc holds a character that should be added to
* the line (unless we don't read anything but a terminator).
*
* Results:
* A line w/o its newline
*
* Side Effects:
* Only those associated with reading a character
*---------------------------------------------------------------------
*/
static Boolean
ParseBlankLines (int *cptr)
{
register int c;
/*
* Discard completely blank and comment lines.
*/
for (;;) {
c = ParseReadc();
if (c == EOF)
return(FALSE);
if (!parseMeta[c])
break;
if (c == '\n') {
lineno++;
continue;
}
if (c != '#')
break;
for (;;) {
int lastc;
lastc = c;
c = ParseReadc();
if (c == EOF)
return(FALSE);
if (c == '\n') {
lineno++;
if (lastc != '\\')
break;
}
}
}
*cptr = c;
return(TRUE);
}
static Boolean
ParseReadLine (register Buffer buf)
{
int c; /* the current character */
if (!ParseBlankLines(&c))
return(FALSE);
/*
* Handle special-characters at the beginning of the line. Either a
* leading tab (shell command) or dot (possible conditional)
* forces us to ignore comments and dependency operators and treat
* semi-colons as semi-colons (by leaving semiNL FALSE).
*/
if (c == '\t') {
Buf_AddByte (buf, (Byte)c);
while ((c = ParseReadc()) == ' ' || c == '\t')
;
while (c != EOF) {
/*
* Nothing special
*/
if (!parseMeta[c]) {
Buf_AddByte (buf, (Byte)c);
c = ParseReadc();
continue;
}
/*
* Unescaped newline - we are done
*/
if (c == '\n') {
lineno++;
return(TRUE);
}
/*
* Maybe escaped newline?
*/
if (c != '\\') {
Buf_AddByte (buf, (Byte)c);
c = ParseReadc();
continue;
}
c = ParseReadc();
if (c != '\n') {
Buf_AddByte (buf, (Byte)'\\');
Buf_AddByte (buf, (Byte)c);
c = ParseReadc();
continue;
}
/*
* Escaped newline: read characters until a non-space or an
* unescaped newline and replace them all by a single space.
* This is done by storing the space over the backslash and
* dropping through with the next nonspace. If it is a
* semi-colon and semiNL is TRUE, it will be recognized as a
* newline in the code below this...
*/
lineno++;
while ((c = ParseReadc()) == ' ' || c == '\t')
;
Buf_AddByte (buf, (Byte)' ');
}
return(FALSE);
}
Buf_AddByte (buf, (Byte)c);
c = ParseReadc();
while (c != EOF) {
/*
* Nothing special
*/
if (!parseMeta[c]) {
Buf_AddByte (buf, (Byte)c);
c = ParseReadc();
continue;
}
/*
* Unescaped newline - we are done
*/
if (c == '\n') {
lineno++;
return(TRUE);
}
/*
* Maybe escaped newline?
*/
if (c == '\\') {
c = ParseReadc();
if (c == '\n') {
/*
* Escaped newline: read characters until a non-space or an
* unescaped newline and replace them all by a single space.
* This is done by storing the space over the backslash and
* dropping through with the next nonspace. If it is a
* semi-colon and semiNL is TRUE, it will be recognized as a
* newline in the code below this...
*/
lineno++;
while ((c = ParseReadc()) == ' ' || c == '\t')
;
Buf_AddByte (buf, (Byte)' ');
continue;
}
Buf_AddByte (buf, (Byte)'\\');
}
/*
* Not a comment...
*/
if (c != '#') {
Buf_AddByte (buf, (Byte)c);
c = ParseReadc();
continue;
}
/*
* If the character is a hash mark, this is a comment.
* Skip to the end of the line.
*/
for (;;) {
c = ParseReadc();
if (c == EOF)
return(FALSE);
if (c == '\n') {
lineno++;
return(TRUE);
}
while (c == '\\') {
c = ParseReadc();
if (c == '\n') {
lineno++;
break;
}
if (c == EOF)
return(FALSE);
}
}
}
return(FALSE);
}
/*-
*-----------------------------------------------------------------------
* ParseFinishLine --
* Handle the end of a dependency group.
*
* Results:
* Nothing.
*
* Side Effects:
* inLine set FALSE. 'targets' list destroyed.
*
*-----------------------------------------------------------------------
*/
static void
ParseFinishLine(void)
{
if (inLine) {
Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL);
Lst_Destroy (targets, ParseHasCommands);
inLine = FALSE;
}
}
/*-
*---------------------------------------------------------------------
* Parse_File --
* Parse a file into its component parts, incorporating it into the
* current dependency graph. This is the main function and controls
* almost every other function in this module
*
* Results:
* None
*
* Side Effects:
* Loads. Nodes are added to the list of all targets, nodes and links
* are added to the dependency graph. etc. etc. etc.
*---------------------------------------------------------------------
*/
void
Parse_File(
string_t name, /* the name of the file being read */
FILE * stream) /* Stream open to makefile to parse */
{
register char *cp; /* pointer into the line */
char *line; /* the line we're working on */
Buffer buf; /* Buffer for current line */
Buffer parseBuf;
int lineLength; /* Length of result */
int modifier;
inLine = FALSE;
fname = name;
curFILE = stream;
lineno = 0;
fatals = 0;
buf = Buf_Init(0);
parseBuf = Buf_Init(0);
do {
while (ParseReadLine (buf)) {
line = (char *)Buf_GetBase (buf);
lineLength = Buf_Size (buf);
if (*line == '.' && !isupper(line[1])) {
/*
* Lines that begin with the special character are either
* include, undef or conditional directives.
*/
for (cp = line + 1; *cp == ' ' || *cp == '\t'; cp++) {
continue;
}
if (*cp && cp[1] == 'n' && strncmp(cp, "include", 7) == 0) {
ParseDoInclude (cp + 7, FALSE);
Buf_Discard (buf, lineLength);
continue;
}
if (*cp == 't' && strncmp(cp, "tryinclude", 10) == 0) {
ParseDoInclude (cp + 10, TRUE);
Buf_Discard (buf, lineLength);
continue;
}
#ifdef SNI_MIPSMAG
} else if (strncmp(cp, "MAKEFLAGS", 9) == 0) {
printf("This seems wrong - explain this to OSF !!!\n");
cp += 10;
Var_Append(".MAKEFLAGS", cp, VAR_GLOBAL);
goto nextLine;
#endif /* SNI_MIPSMAG */
if (*cp == 'u' && strncmp(cp, "undef", 5) == 0) {
char *cp2;
for (cp += 5; *cp == ' ' || *cp == '\t'; cp++)
continue;
for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
continue;
*cp2 = '\0';
Var_Delete(string_create(cp), VAR_GLOBAL);
Buf_Discard (buf, lineLength);
continue;
}
/*
* The line might be a conditional. Ask the conditional module
* about it and act accordingly
*/
switch (Cond_Eval (cp)) {
case 0:
break;
case 1:
Buf_Discard (buf, lineLength);
continue;
default:
goto next;
}
/*
* Skip to next conditional that evaluates to 1.
*/
do {
int c = '\0';
Buf_Discard (buf, lineLength);
for (;;) {
int lastc;
lastc = c;
c = ParseReadc();
/*
* Skip lines until get to one that begins with a
* special char (dot).
*/
if ((c == '.') || (c == EOF))
break;
while ((c != '\n' || lastc == '\\') && c != EOF) {
/*
* Advance to next unescaped newline
*/
if ((lastc = c) == '\n')
lineno++;
c = ParseReadc();
}
lineno++;
}
if (c == EOF || !ParseReadLine (buf)) {
Parse_Error (PARSE_FATAL, "Unclosed conditional");
goto eof;
}
line = (char *)Buf_GetBase(buf);
lineLength = Buf_Size (buf);
} while (Cond_Eval(line) != 1);
Buf_Discard (buf, lineLength);
continue;
}
if (*line == '\t')
{
/*
* If a line starts with a tab, it
* can only hope to be a creation command.
*/
cp = line + 1;
if (*cp) {
if (inLine) {
/*
* So long as it's not a blank line and we're actually
* in a dependency spec, add the command to the list of
* commands of all targets in the dependency spec
*/
Lst_ForEach (targets, ParseAddCmd,
(ClientData)string_create(cp));
} else {
Parse_Error (PARSE_FATAL,
"Unassociated shell command \"%.20s\"",
cp);
}
}
Buf_Discard (buf, lineLength);
continue;
}
for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
continue;
if (*cp == '\0') {
Buf_Discard (buf, lineLength);
continue;
}
next:
ParseFinishLine();
/*
* Parse the line as a variable assignment or dependency line.
* On success, parseBuf will either contain the variable name
* or the list of targets, each separated by a single space.
* line will be updated to point to either an equal-sign (=) or
* colon (:). modifier, if non-null, will contain either the
* variable or dependency modifier character.
*/
if (ParseVarOrDep (parseBuf, &line, &modifier) != 0) {
Buf_Discard (buf, lineLength);
if ((lineLength = Buf_Size (parseBuf)))
Buf_Discard (parseBuf, lineLength);
continue;
}
/*
* Handle the variable assignment
*/
if (*line == '=') {
cp = (char *) Buf_GetBase (parseBuf);
ParseDoVar (cp, modifier, ++line, VAR_GLOBAL);
Buf_Discard (buf, lineLength);
Buf_Discard (parseBuf, Buf_Size(parseBuf));
continue;
}
/*
* Need a non-circular list for the target nodes
*/
targets = Lst_Init();
inLine = TRUE;
for (cp = line + 1; *cp == ' ' || *cp == '\t'; cp++)
;
ParseDoDependency (parseBuf, modifier, cp);
if (haveLookahead) {
cp = (char *) Buf_GetBase (lookaheadBuf);
Lst_ForEach (targets, ParseAddCmd,
(ClientData)string_create(cp));
Buf_Discard(lookaheadBuf, Buf_Size(lookaheadBuf));
haveLookahead = FALSE;
}
Buf_Discard (buf, lineLength);
Buf_Discard (parseBuf, Buf_Size(parseBuf));
}
eof:;
/*
* Reached EOF, but it may be just EOF of an include file...
*/
} while (ParseEOF(1) == CONTINUE);
Buf_Destroy (buf);
/*
* Make sure conditionals are clean
*/
Cond_End();
if (fatals) {
fprintf (stderr, "Fatal errors encountered -- cannot continue\n");
exit (1);
}
}
/*-
*---------------------------------------------------------------------
* Parse_Init --
* initialize the parsing module
*
* Results:
* none
*
* Side Effects:
* the parseIncPath list is initialized...
*---------------------------------------------------------------------
*/
void
Parse_Init (void)
{
char *cp, *start;
char *syspath;
string_t start_str;
mainNode = NILGNODE;
parseIncPath = Lst_Init();
sysIncPath = Lst_Init();
includes = Lst_Init();
lookaheadBuf = Buf_Init(0);
for (cp = (char *)" \t\\\n#$;("; *cp; cp++)
parseMeta[*(unsigned char *)cp] = 1;
parseMeta['\0'] = 1;
makeincludecompat = (getenv("MAKEINCLUDECOMPAT") != NULL);
if ((syspath = getenv("MAKESYSPATH")) == NULL)
syspath = strdup(_PATH_DEFSYSPATH);
else
syspath = strdup(syspath);
if (syspath == NULL)
enomem();
/*
* Add the directories from the syspath (more than one may be given
* as dir1:...:dirn) to the system include path.
*/
for (start = syspath; *start != '\0'; start = cp) {
for (cp = start; *cp != '\0' && *cp != ':'; cp++) {
;
}
if (*cp != '\0')
*cp++ = '\0';
start_str = string_create(start);
Dir_AddDir(sysIncPath, start_str);
}
free(syspath);
}
/*-
*-----------------------------------------------------------------------
* Parse_MainName --
* Return a Lst of the main target to create for main()'s sake. If
* no such target exists, we Punt with an obnoxious error message.
*
* Results:
* A Lst of the single node to create.
*
* Side Effects:
* None.
*
*-----------------------------------------------------------------------
*/
Lst
Parse_MainName(void)
{
Lst main; /* result list */
main = Lst_Init();
if (mainNode == NILGNODE) {
Punt ("make: no target to make.\n");
/*NOTREACHED*/
} else if (mainNode->type & OP_DOUBLEDEP) {
Lst_Concat(main, mainNode->cohorts, LST_CONCNEW);
}
(void) Lst_AtEnd (main, (ClientData)mainNode);
return (main);
}
void
ParseErrorCond(const char *fmt, ...)
{
va_list ap;
(void)fflush(stdout);
(void)fprintf(stderr, "\"%s\", line %d: ", fname->data, lineno);
va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
(void)fflush(stderr);
fatals += 1;
}