xref: /minix3/usr.bin/make/parse.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: parse.c,v 1.204 2014/09/18 08:06:13 dholland Exp $	*/
22e2caf59SThomas Veerman 
32e2caf59SThomas Veerman /*
42e2caf59SThomas Veerman  * Copyright (c) 1988, 1989, 1990, 1993
52e2caf59SThomas Veerman  *	The Regents of the University of California.  All rights reserved.
62e2caf59SThomas Veerman  *
72e2caf59SThomas Veerman  * This code is derived from software contributed to Berkeley by
82e2caf59SThomas Veerman  * Adam de Boor.
92e2caf59SThomas Veerman  *
102e2caf59SThomas Veerman  * Redistribution and use in source and binary forms, with or without
112e2caf59SThomas Veerman  * modification, are permitted provided that the following conditions
122e2caf59SThomas Veerman  * are met:
132e2caf59SThomas Veerman  * 1. Redistributions of source code must retain the above copyright
142e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer.
152e2caf59SThomas Veerman  * 2. Redistributions in binary form must reproduce the above copyright
162e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer in the
172e2caf59SThomas Veerman  *    documentation and/or other materials provided with the distribution.
182e2caf59SThomas Veerman  * 3. Neither the name of the University nor the names of its contributors
192e2caf59SThomas Veerman  *    may be used to endorse or promote products derived from this software
202e2caf59SThomas Veerman  *    without specific prior written permission.
212e2caf59SThomas Veerman  *
222e2caf59SThomas Veerman  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
232e2caf59SThomas Veerman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
242e2caf59SThomas Veerman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
252e2caf59SThomas Veerman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
262e2caf59SThomas Veerman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
272e2caf59SThomas Veerman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
282e2caf59SThomas Veerman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
292e2caf59SThomas Veerman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
302e2caf59SThomas Veerman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
312e2caf59SThomas Veerman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322e2caf59SThomas Veerman  * SUCH DAMAGE.
332e2caf59SThomas Veerman  */
342e2caf59SThomas Veerman 
352e2caf59SThomas Veerman /*
362e2caf59SThomas Veerman  * Copyright (c) 1989 by Berkeley Softworks
372e2caf59SThomas Veerman  * All rights reserved.
382e2caf59SThomas Veerman  *
392e2caf59SThomas Veerman  * This code is derived from software contributed to Berkeley by
402e2caf59SThomas Veerman  * Adam de Boor.
412e2caf59SThomas Veerman  *
422e2caf59SThomas Veerman  * Redistribution and use in source and binary forms, with or without
432e2caf59SThomas Veerman  * modification, are permitted provided that the following conditions
442e2caf59SThomas Veerman  * are met:
452e2caf59SThomas Veerman  * 1. Redistributions of source code must retain the above copyright
462e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer.
472e2caf59SThomas Veerman  * 2. Redistributions in binary form must reproduce the above copyright
482e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer in the
492e2caf59SThomas Veerman  *    documentation and/or other materials provided with the distribution.
502e2caf59SThomas Veerman  * 3. All advertising materials mentioning features or use of this software
512e2caf59SThomas Veerman  *    must display the following acknowledgement:
522e2caf59SThomas Veerman  *	This product includes software developed by the University of
532e2caf59SThomas Veerman  *	California, Berkeley and its contributors.
542e2caf59SThomas Veerman  * 4. Neither the name of the University nor the names of its contributors
552e2caf59SThomas Veerman  *    may be used to endorse or promote products derived from this software
562e2caf59SThomas Veerman  *    without specific prior written permission.
572e2caf59SThomas Veerman  *
582e2caf59SThomas Veerman  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
592e2caf59SThomas Veerman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
602e2caf59SThomas Veerman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
612e2caf59SThomas Veerman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
622e2caf59SThomas Veerman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
632e2caf59SThomas Veerman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
642e2caf59SThomas Veerman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
652e2caf59SThomas Veerman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
662e2caf59SThomas Veerman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
672e2caf59SThomas Veerman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
682e2caf59SThomas Veerman  * SUCH DAMAGE.
692e2caf59SThomas Veerman  */
702e2caf59SThomas Veerman 
712e2caf59SThomas Veerman #ifndef MAKE_NATIVE
72*0a6a1f1dSLionel Sambuc static char rcsid[] = "$NetBSD: parse.c,v 1.204 2014/09/18 08:06:13 dholland Exp $";
732e2caf59SThomas Veerman #else
742e2caf59SThomas Veerman #include <sys/cdefs.h>
752e2caf59SThomas Veerman #ifndef lint
762e2caf59SThomas Veerman #if 0
772e2caf59SThomas Veerman static char sccsid[] = "@(#)parse.c	8.3 (Berkeley) 3/19/94";
782e2caf59SThomas Veerman #else
79*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: parse.c,v 1.204 2014/09/18 08:06:13 dholland Exp $");
802e2caf59SThomas Veerman #endif
812e2caf59SThomas Veerman #endif /* not lint */
822e2caf59SThomas Veerman #endif
832e2caf59SThomas Veerman 
842e2caf59SThomas Veerman /*-
852e2caf59SThomas Veerman  * parse.c --
862e2caf59SThomas Veerman  *	Functions to parse a makefile.
872e2caf59SThomas Veerman  *
882e2caf59SThomas Veerman  *	One function, Parse_Init, must be called before any functions
892e2caf59SThomas Veerman  *	in this module are used. After that, the function Parse_File is the
902e2caf59SThomas Veerman  *	main entry point and controls most of the other functions in this
912e2caf59SThomas Veerman  *	module.
922e2caf59SThomas Veerman  *
932e2caf59SThomas Veerman  *	Most important structures are kept in Lsts. Directories for
942e2caf59SThomas Veerman  *	the .include "..." function are kept in the 'parseIncPath' Lst, while
952e2caf59SThomas Veerman  *	those for the .include <...> are kept in the 'sysIncPath' Lst. The
962e2caf59SThomas Veerman  *	targets currently being defined are kept in the 'targets' Lst.
972e2caf59SThomas Veerman  *
982e2caf59SThomas Veerman  *	The variables 'fname' and 'lineno' are used to track the name
992e2caf59SThomas Veerman  *	of the current file and the line number in that file so that error
1002e2caf59SThomas Veerman  *	messages can be more meaningful.
1012e2caf59SThomas Veerman  *
1022e2caf59SThomas Veerman  * Interface:
1032e2caf59SThomas Veerman  *	Parse_Init	    	    Initialization function which must be
1042e2caf59SThomas Veerman  *	    	  	    	    called before anything else in this module
1052e2caf59SThomas Veerman  *	    	  	    	    is used.
1062e2caf59SThomas Veerman  *
1072e2caf59SThomas Veerman  *	Parse_End		    Cleanup the module
1082e2caf59SThomas Veerman  *
1092e2caf59SThomas Veerman  *	Parse_File	    	    Function used to parse a makefile. It must
1102e2caf59SThomas Veerman  *	    	  	    	    be given the name of the file, which should
1112e2caf59SThomas Veerman  *	    	  	    	    already have been opened, and a function
1122e2caf59SThomas Veerman  *	    	  	    	    to call to read a character from the file.
1132e2caf59SThomas Veerman  *
1142e2caf59SThomas Veerman  *	Parse_IsVar	    	    Returns TRUE if the given line is a
1152e2caf59SThomas Veerman  *	    	  	    	    variable assignment. Used by MainParseArgs
1162e2caf59SThomas Veerman  *	    	  	    	    to determine if an argument is a target
1172e2caf59SThomas Veerman  *	    	  	    	    or a variable assignment. Used internally
1182e2caf59SThomas Veerman  *	    	  	    	    for pretty much the same thing...
1192e2caf59SThomas Veerman  *
1202e2caf59SThomas Veerman  *	Parse_Error	    	    Function called when an error occurs in
1212e2caf59SThomas Veerman  *	    	  	    	    parsing. Used by the variable and
1222e2caf59SThomas Veerman  *	    	  	    	    conditional modules.
1232e2caf59SThomas Veerman  *	Parse_MainName	    	    Returns a Lst of the main target to create.
1242e2caf59SThomas Veerman  */
1252e2caf59SThomas Veerman 
1262e2caf59SThomas Veerman #include <sys/types.h>
1272e2caf59SThomas Veerman #include <sys/mman.h>
1282e2caf59SThomas Veerman #include <sys/stat.h>
1292e2caf59SThomas Veerman #include <assert.h>
1302e2caf59SThomas Veerman #include <ctype.h>
1312e2caf59SThomas Veerman #include <errno.h>
1322e2caf59SThomas Veerman #include <fcntl.h>
1332e2caf59SThomas Veerman #include <stdarg.h>
1342e2caf59SThomas Veerman #include <stdio.h>
1352e2caf59SThomas Veerman 
13684d9c625SLionel Sambuc #ifndef MAP_FILE
13784d9c625SLionel Sambuc #define MAP_FILE 0
13884d9c625SLionel Sambuc #endif
1392e2caf59SThomas Veerman #ifndef MAP_COPY
1402e2caf59SThomas Veerman #define MAP_COPY MAP_PRIVATE
1412e2caf59SThomas Veerman #endif
1422e2caf59SThomas Veerman 
1432e2caf59SThomas Veerman #include "make.h"
1442e2caf59SThomas Veerman #include "hash.h"
1452e2caf59SThomas Veerman #include "dir.h"
1462e2caf59SThomas Veerman #include "job.h"
1472e2caf59SThomas Veerman #include "buf.h"
1482e2caf59SThomas Veerman #include "pathnames.h"
1492e2caf59SThomas Veerman 
1502e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
1512e2caf59SThomas Veerman // types and constants
1522e2caf59SThomas Veerman 
1532e2caf59SThomas Veerman /*
1542e2caf59SThomas Veerman  * Structure for a file being read ("included file")
1552e2caf59SThomas Veerman  */
1562e2caf59SThomas Veerman typedef struct IFile {
15784d9c625SLionel Sambuc     char      	    *fname;         /* name of file */
1582e2caf59SThomas Veerman     int             lineno;         /* current line number in file */
1592e2caf59SThomas Veerman     int             first_lineno;   /* line number of start of text */
1602e2caf59SThomas Veerman     int             cond_depth;     /* 'if' nesting when file opened */
1612e2caf59SThomas Veerman     char            *P_str;         /* point to base of string buffer */
1622e2caf59SThomas Veerman     char            *P_ptr;         /* point to next char of string buffer */
1632e2caf59SThomas Veerman     char            *P_end;         /* point to the end of string buffer */
1642e2caf59SThomas Veerman     char            *(*nextbuf)(void *, size_t *); /* Function to get more data */
1652e2caf59SThomas Veerman     void            *nextbuf_arg;   /* Opaque arg for nextbuf() */
1662e2caf59SThomas Veerman     struct loadedfile *lf;          /* loadedfile object, if any */
1672e2caf59SThomas Veerman } IFile;
1682e2caf59SThomas Veerman 
1692e2caf59SThomas Veerman 
1702e2caf59SThomas Veerman /*
1712e2caf59SThomas Veerman  * These values are returned by ParseEOF to tell Parse_File whether to
1722e2caf59SThomas Veerman  * CONTINUE parsing, i.e. it had only reached the end of an include file,
1732e2caf59SThomas Veerman  * or if it's DONE.
1742e2caf59SThomas Veerman  */
1752e2caf59SThomas Veerman #define CONTINUE	1
1762e2caf59SThomas Veerman #define DONE		0
1772e2caf59SThomas Veerman 
1782e2caf59SThomas Veerman /*
1792e2caf59SThomas Veerman  * Tokens for target attributes
1802e2caf59SThomas Veerman  */
1812e2caf59SThomas Veerman typedef enum {
1822e2caf59SThomas Veerman     Begin,  	    /* .BEGIN */
1832e2caf59SThomas Veerman     Default,	    /* .DEFAULT */
1842e2caf59SThomas Veerman     End,    	    /* .END */
1852e2caf59SThomas Veerman     dotError,	    /* .ERROR */
1862e2caf59SThomas Veerman     Ignore,	    /* .IGNORE */
1872e2caf59SThomas Veerman     Includes,	    /* .INCLUDES */
1882e2caf59SThomas Veerman     Interrupt,	    /* .INTERRUPT */
1892e2caf59SThomas Veerman     Libs,	    /* .LIBS */
1902e2caf59SThomas Veerman     Meta,	    /* .META */
1912e2caf59SThomas Veerman     MFlags,	    /* .MFLAGS or .MAKEFLAGS */
1922e2caf59SThomas Veerman     Main,	    /* .MAIN and we don't have anything user-specified to
1932e2caf59SThomas Veerman 		     * make */
1942e2caf59SThomas Veerman     NoExport,	    /* .NOEXPORT */
1952e2caf59SThomas Veerman     NoMeta,	    /* .NOMETA */
1962e2caf59SThomas Veerman     NoMetaCmp,	    /* .NOMETA_CMP */
1972e2caf59SThomas Veerman     NoPath,	    /* .NOPATH */
1982e2caf59SThomas Veerman     Not,	    /* Not special */
1992e2caf59SThomas Veerman     NotParallel,    /* .NOTPARALLEL */
2002e2caf59SThomas Veerman     Null,   	    /* .NULL */
2012e2caf59SThomas Veerman     ExObjdir,	    /* .OBJDIR */
2022e2caf59SThomas Veerman     Order,  	    /* .ORDER */
2032e2caf59SThomas Veerman     Parallel,	    /* .PARALLEL */
2042e2caf59SThomas Veerman     ExPath,	    /* .PATH */
2052e2caf59SThomas Veerman     Phony,	    /* .PHONY */
2062e2caf59SThomas Veerman #ifdef POSIX
2072e2caf59SThomas Veerman     Posix,	    /* .POSIX */
2082e2caf59SThomas Veerman #endif
2092e2caf59SThomas Veerman     Precious,	    /* .PRECIOUS */
2102e2caf59SThomas Veerman     ExShell,	    /* .SHELL */
2112e2caf59SThomas Veerman     Silent,	    /* .SILENT */
2122e2caf59SThomas Veerman     SingleShell,    /* .SINGLESHELL */
21384d9c625SLionel Sambuc     Stale,	    /* .STALE */
2142e2caf59SThomas Veerman     Suffixes,	    /* .SUFFIXES */
2152e2caf59SThomas Veerman     Wait,	    /* .WAIT */
2162e2caf59SThomas Veerman     Attribute	    /* Generic attribute */
2172e2caf59SThomas Veerman } ParseSpecial;
2182e2caf59SThomas Veerman 
2192e2caf59SThomas Veerman /*
2202e2caf59SThomas Veerman  * Other tokens
2212e2caf59SThomas Veerman  */
2222e2caf59SThomas Veerman #define LPAREN	'('
2232e2caf59SThomas Veerman #define RPAREN	')'
2242e2caf59SThomas Veerman 
2252e2caf59SThomas Veerman 
2262e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
2272e2caf59SThomas Veerman // result data
2282e2caf59SThomas Veerman 
2292e2caf59SThomas Veerman /*
2302e2caf59SThomas Veerman  * The main target to create. This is the first target on the first
2312e2caf59SThomas Veerman  * dependency line in the first makefile.
2322e2caf59SThomas Veerman  */
2332e2caf59SThomas Veerman static GNode *mainNode;
2342e2caf59SThomas Veerman 
2352e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
2362e2caf59SThomas Veerman // eval state
2372e2caf59SThomas Veerman 
2382e2caf59SThomas Veerman /* targets we're working on */
2392e2caf59SThomas Veerman static Lst targets;
2402e2caf59SThomas Veerman 
2412e2caf59SThomas Veerman #ifdef CLEANUP
2422e2caf59SThomas Veerman /* command lines for targets */
2432e2caf59SThomas Veerman static Lst targCmds;
2442e2caf59SThomas Veerman #endif
2452e2caf59SThomas Veerman 
2462e2caf59SThomas Veerman /*
2472e2caf59SThomas Veerman  * specType contains the SPECial TYPE of the current target. It is
2482e2caf59SThomas Veerman  * Not if the target is unspecial. If it *is* special, however, the children
2492e2caf59SThomas Veerman  * are linked as children of the parent but not vice versa. This variable is
2502e2caf59SThomas Veerman  * set in ParseDoDependency
2512e2caf59SThomas Veerman  */
2522e2caf59SThomas Veerman static ParseSpecial specType;
2532e2caf59SThomas Veerman 
2542e2caf59SThomas Veerman /*
2552e2caf59SThomas Veerman  * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER
2562e2caf59SThomas Veerman  * seen, then set to each successive source on the line.
2572e2caf59SThomas Veerman  */
2582e2caf59SThomas Veerman static GNode	*predecessor;
2592e2caf59SThomas Veerman 
2602e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
2612e2caf59SThomas Veerman // parser state
2622e2caf59SThomas Veerman 
2632e2caf59SThomas Veerman /* true if currently in a dependency line or its commands */
2642e2caf59SThomas Veerman static Boolean inLine;
2652e2caf59SThomas Veerman 
2662e2caf59SThomas Veerman /* number of fatal errors */
2672e2caf59SThomas Veerman static int fatals = 0;
2682e2caf59SThomas Veerman 
2692e2caf59SThomas Veerman /*
2702e2caf59SThomas Veerman  * Variables for doing includes
2712e2caf59SThomas Veerman  */
2722e2caf59SThomas Veerman 
2732e2caf59SThomas Veerman /* current file being read */
2742e2caf59SThomas Veerman static IFile *curFile;
2752e2caf59SThomas Veerman 
2762e2caf59SThomas Veerman /* stack of IFiles generated by .includes */
2772e2caf59SThomas Veerman static Lst includes;
2782e2caf59SThomas Veerman 
2792e2caf59SThomas Veerman /* include paths (lists of directories) */
2802e2caf59SThomas Veerman Lst parseIncPath;	/* dirs for "..." includes */
2812e2caf59SThomas Veerman Lst sysIncPath;		/* dirs for <...> includes */
2822e2caf59SThomas Veerman Lst defIncPath;		/* default for sysIncPath */
2832e2caf59SThomas Veerman 
2842e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
2852e2caf59SThomas Veerman // parser tables
2862e2caf59SThomas Veerman 
2872e2caf59SThomas Veerman /*
2882e2caf59SThomas Veerman  * The parseKeywords table is searched using binary search when deciding
2892e2caf59SThomas Veerman  * if a target or source is special. The 'spec' field is the ParseSpecial
2902e2caf59SThomas Veerman  * type of the keyword ("Not" if the keyword isn't special as a target) while
2912e2caf59SThomas Veerman  * the 'op' field is the operator to apply to the list of targets if the
2922e2caf59SThomas Veerman  * keyword is used as a source ("0" if the keyword isn't special as a source)
2932e2caf59SThomas Veerman  */
2942e2caf59SThomas Veerman static const struct {
2952e2caf59SThomas Veerman     const char   *name;    	/* Name of keyword */
2962e2caf59SThomas Veerman     ParseSpecial  spec;	    	/* Type when used as a target */
2972e2caf59SThomas Veerman     int	    	  op;	    	/* Operator when used as a source */
2982e2caf59SThomas Veerman } parseKeywords[] = {
2992e2caf59SThomas Veerman { ".BEGIN", 	  Begin,    	0 },
3002e2caf59SThomas Veerman { ".DEFAULT",	  Default,  	0 },
3012e2caf59SThomas Veerman { ".END",   	  End,	    	0 },
3022e2caf59SThomas Veerman { ".ERROR",   	  dotError,    	0 },
3032e2caf59SThomas Veerman { ".EXEC",	  Attribute,   	OP_EXEC },
3042e2caf59SThomas Veerman { ".IGNORE",	  Ignore,   	OP_IGNORE },
3052e2caf59SThomas Veerman { ".INCLUDES",	  Includes, 	0 },
3062e2caf59SThomas Veerman { ".INTERRUPT",	  Interrupt,	0 },
3072e2caf59SThomas Veerman { ".INVISIBLE",	  Attribute,   	OP_INVISIBLE },
3082e2caf59SThomas Veerman { ".JOIN",  	  Attribute,   	OP_JOIN },
3092e2caf59SThomas Veerman { ".LIBS",  	  Libs,	    	0 },
3102e2caf59SThomas Veerman { ".MADE",	  Attribute,	OP_MADE },
3112e2caf59SThomas Veerman { ".MAIN",	  Main,		0 },
3122e2caf59SThomas Veerman { ".MAKE",  	  Attribute,   	OP_MAKE },
3132e2caf59SThomas Veerman { ".MAKEFLAGS",	  MFlags,   	0 },
3142e2caf59SThomas Veerman { ".META",	  Meta,		OP_META },
3152e2caf59SThomas Veerman { ".MFLAGS",	  MFlags,   	0 },
3162e2caf59SThomas Veerman { ".NOMETA",	  NoMeta,	OP_NOMETA },
3172e2caf59SThomas Veerman { ".NOMETA_CMP",  NoMetaCmp,	OP_NOMETA_CMP },
3182e2caf59SThomas Veerman { ".NOPATH",	  NoPath,	OP_NOPATH },
3192e2caf59SThomas Veerman { ".NOTMAIN",	  Attribute,   	OP_NOTMAIN },
3202e2caf59SThomas Veerman { ".NOTPARALLEL", NotParallel,	0 },
3212e2caf59SThomas Veerman { ".NO_PARALLEL", NotParallel,	0 },
3222e2caf59SThomas Veerman { ".NULL",  	  Null,	    	0 },
3232e2caf59SThomas Veerman { ".OBJDIR",	  ExObjdir,	0 },
3242e2caf59SThomas Veerman { ".OPTIONAL",	  Attribute,   	OP_OPTIONAL },
3252e2caf59SThomas Veerman { ".ORDER", 	  Order,    	0 },
3262e2caf59SThomas Veerman { ".PARALLEL",	  Parallel,	0 },
3272e2caf59SThomas Veerman { ".PATH",	  ExPath,	0 },
3282e2caf59SThomas Veerman { ".PHONY",	  Phony,	OP_PHONY },
3292e2caf59SThomas Veerman #ifdef POSIX
3302e2caf59SThomas Veerman { ".POSIX",	  Posix,	0 },
3312e2caf59SThomas Veerman #endif
3322e2caf59SThomas Veerman { ".PRECIOUS",	  Precious, 	OP_PRECIOUS },
3332e2caf59SThomas Veerman { ".RECURSIVE",	  Attribute,	OP_MAKE },
3342e2caf59SThomas Veerman { ".SHELL", 	  ExShell,    	0 },
3352e2caf59SThomas Veerman { ".SILENT",	  Silent,   	OP_SILENT },
3362e2caf59SThomas Veerman { ".SINGLESHELL", SingleShell,	0 },
33784d9c625SLionel Sambuc { ".STALE",	  Stale,	0 },
3382e2caf59SThomas Veerman { ".SUFFIXES",	  Suffixes, 	0 },
3392e2caf59SThomas Veerman { ".USE",   	  Attribute,   	OP_USE },
3402e2caf59SThomas Veerman { ".USEBEFORE",   Attribute,   	OP_USEBEFORE },
3412e2caf59SThomas Veerman { ".WAIT",	  Wait, 	0 },
3422e2caf59SThomas Veerman };
3432e2caf59SThomas Veerman 
3442e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
3452e2caf59SThomas Veerman // local functions
3462e2caf59SThomas Veerman 
3472e2caf59SThomas Veerman static int ParseIsEscaped(const char *, const char *);
3482e2caf59SThomas Veerman static void ParseErrorInternal(const char *, size_t, int, const char *, ...)
3492bc7c627SLionel Sambuc     MAKE_ATTR_PRINTFLIKE(4,5);
3502e2caf59SThomas Veerman static void ParseVErrorInternal(FILE *, const char *, size_t, int, const char *, va_list)
3512bc7c627SLionel Sambuc     MAKE_ATTR_PRINTFLIKE(5, 0);
3522e2caf59SThomas Veerman static int ParseFindKeyword(const char *);
3532e2caf59SThomas Veerman static int ParseLinkSrc(void *, void *);
3542e2caf59SThomas Veerman static int ParseDoOp(void *, void *);
3552e2caf59SThomas Veerman static void ParseDoSrc(int, const char *);
3562e2caf59SThomas Veerman static int ParseFindMain(void *, void *);
3572e2caf59SThomas Veerman static int ParseAddDir(void *, void *);
3582e2caf59SThomas Veerman static int ParseClearPath(void *, void *);
3592e2caf59SThomas Veerman static void ParseDoDependency(char *);
3602e2caf59SThomas Veerman static int ParseAddCmd(void *, void *);
3612e2caf59SThomas Veerman static void ParseHasCommands(void *);
3622e2caf59SThomas Veerman static void ParseDoInclude(char *);
3632e2caf59SThomas Veerman static void ParseSetParseFile(const char *);
364*0a6a1f1dSLionel Sambuc static void ParseSetIncludedFile(void);
3652e2caf59SThomas Veerman #ifdef SYSVINCLUDE
3662e2caf59SThomas Veerman static void ParseTraditionalInclude(char *);
3672e2caf59SThomas Veerman #endif
3682e2caf59SThomas Veerman #ifdef GMAKEEXPORT
3692e2caf59SThomas Veerman static void ParseGmakeExport(char *);
3702e2caf59SThomas Veerman #endif
3712e2caf59SThomas Veerman static int ParseEOF(void);
3722e2caf59SThomas Veerman static char *ParseReadLine(void);
3732e2caf59SThomas Veerman static void ParseFinishLine(void);
3742e2caf59SThomas Veerman static void ParseMark(GNode *);
3752e2caf59SThomas Veerman 
3762e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
3772e2caf59SThomas Veerman // file loader
3782e2caf59SThomas Veerman 
3792e2caf59SThomas Veerman struct loadedfile {
3802e2caf59SThomas Veerman 	const char *path;		/* name, for error reports */
3812e2caf59SThomas Veerman 	char *buf;			/* contents buffer */
3822e2caf59SThomas Veerman 	size_t len;			/* length of contents */
3832e2caf59SThomas Veerman 	size_t maplen;			/* length of mmap area, or 0 */
3842e2caf59SThomas Veerman 	Boolean used;			/* XXX: have we used the data yet */
3852e2caf59SThomas Veerman };
3862e2caf59SThomas Veerman 
3872e2caf59SThomas Veerman /*
3882e2caf59SThomas Veerman  * Constructor/destructor for loadedfile
3892e2caf59SThomas Veerman  */
3902e2caf59SThomas Veerman static struct loadedfile *
loadedfile_create(const char * path)3912e2caf59SThomas Veerman loadedfile_create(const char *path)
3922e2caf59SThomas Veerman {
3932e2caf59SThomas Veerman 	struct loadedfile *lf;
3942e2caf59SThomas Veerman 
3952e2caf59SThomas Veerman 	lf = bmake_malloc(sizeof(*lf));
3962e2caf59SThomas Veerman 	lf->path = (path == NULL ? "(stdin)" : path);
3972e2caf59SThomas Veerman 	lf->buf = NULL;
3982e2caf59SThomas Veerman 	lf->len = 0;
3992e2caf59SThomas Veerman 	lf->maplen = 0;
4002e2caf59SThomas Veerman 	lf->used = FALSE;
4012e2caf59SThomas Veerman 	return lf;
4022e2caf59SThomas Veerman }
4032e2caf59SThomas Veerman 
4042e2caf59SThomas Veerman static void
loadedfile_destroy(struct loadedfile * lf)4052e2caf59SThomas Veerman loadedfile_destroy(struct loadedfile *lf)
4062e2caf59SThomas Veerman {
4072e2caf59SThomas Veerman 	if (lf->buf != NULL) {
4082e2caf59SThomas Veerman 		if (lf->maplen > 0) {
4092e2caf59SThomas Veerman 			munmap(lf->buf, lf->maplen);
4102e2caf59SThomas Veerman 		} else {
4112e2caf59SThomas Veerman 			free(lf->buf);
4122e2caf59SThomas Veerman 		}
4132e2caf59SThomas Veerman 	}
4142e2caf59SThomas Veerman 	free(lf);
4152e2caf59SThomas Veerman }
4162e2caf59SThomas Veerman 
4172e2caf59SThomas Veerman /*
4182e2caf59SThomas Veerman  * nextbuf() operation for loadedfile, as needed by the weird and twisted
4192e2caf59SThomas Veerman  * logic below. Once that's cleaned up, we can get rid of lf->used...
4202e2caf59SThomas Veerman  */
4212e2caf59SThomas Veerman static char *
loadedfile_nextbuf(void * x,size_t * len)4222e2caf59SThomas Veerman loadedfile_nextbuf(void *x, size_t *len)
4232e2caf59SThomas Veerman {
4242e2caf59SThomas Veerman 	struct loadedfile *lf = x;
4252e2caf59SThomas Veerman 
4262e2caf59SThomas Veerman 	if (lf->used) {
4272e2caf59SThomas Veerman 		return NULL;
4282e2caf59SThomas Veerman 	}
4292e2caf59SThomas Veerman 	lf->used = TRUE;
4302e2caf59SThomas Veerman 	*len = lf->len;
4312e2caf59SThomas Veerman 	return lf->buf;
4322e2caf59SThomas Veerman }
4332e2caf59SThomas Veerman 
4342e2caf59SThomas Veerman /*
4352e2caf59SThomas Veerman  * Try to get the size of a file.
4362e2caf59SThomas Veerman  */
4372e2caf59SThomas Veerman static ReturnStatus
load_getsize(int fd,size_t * ret)4382e2caf59SThomas Veerman load_getsize(int fd, size_t *ret)
4392e2caf59SThomas Veerman {
4402e2caf59SThomas Veerman 	struct stat st;
4412e2caf59SThomas Veerman 
4422e2caf59SThomas Veerman 	if (fstat(fd, &st) < 0) {
4432e2caf59SThomas Veerman 		return FAILURE;
4442e2caf59SThomas Veerman 	}
4452e2caf59SThomas Veerman 
4462e2caf59SThomas Veerman 	if (!S_ISREG(st.st_mode)) {
4472e2caf59SThomas Veerman 		return FAILURE;
4482e2caf59SThomas Veerman 	}
4492e2caf59SThomas Veerman 
4502e2caf59SThomas Veerman 	/*
4512e2caf59SThomas Veerman 	 * st_size is an off_t, which is 64 bits signed; *ret is
4522e2caf59SThomas Veerman 	 * size_t, which might be 32 bits unsigned or 64 bits
4532e2caf59SThomas Veerman 	 * unsigned. Rather than being elaborate, just punt on
4542e2caf59SThomas Veerman 	 * files that are more than 2^31 bytes. We should never
4552e2caf59SThomas Veerman 	 * see a makefile that size in practice...
4562e2caf59SThomas Veerman 	 *
4572e2caf59SThomas Veerman 	 * While we're at it reject negative sizes too, just in case.
4582e2caf59SThomas Veerman 	 */
4592e2caf59SThomas Veerman 	if (st.st_size < 0 || st.st_size > 0x7fffffff) {
4602e2caf59SThomas Veerman 		return FAILURE;
4612e2caf59SThomas Veerman 	}
4622e2caf59SThomas Veerman 
4632e2caf59SThomas Veerman 	*ret = (size_t) st.st_size;
4642e2caf59SThomas Veerman 	return SUCCESS;
4652e2caf59SThomas Veerman }
4662e2caf59SThomas Veerman 
4672e2caf59SThomas Veerman /*
4682e2caf59SThomas Veerman  * Read in a file.
4692e2caf59SThomas Veerman  *
4702e2caf59SThomas Veerman  * Until the path search logic can be moved under here instead of
4712e2caf59SThomas Veerman  * being in the caller in another source file, we need to have the fd
4722e2caf59SThomas Veerman  * passed in already open. Bleh.
4732e2caf59SThomas Veerman  *
4742e2caf59SThomas Veerman  * If the path is NULL use stdin and (to insure against fd leaks)
4752e2caf59SThomas Veerman  * assert that the caller passed in -1.
4762e2caf59SThomas Veerman  */
4772e2caf59SThomas Veerman static struct loadedfile *
loadfile(const char * path,int fd)4782e2caf59SThomas Veerman loadfile(const char *path, int fd)
4792e2caf59SThomas Veerman {
4802e2caf59SThomas Veerman 	struct loadedfile *lf;
4812e2caf59SThomas Veerman 	long pagesize;
4822e2caf59SThomas Veerman 	ssize_t result;
4832e2caf59SThomas Veerman 	size_t bufpos;
4842e2caf59SThomas Veerman 
4852e2caf59SThomas Veerman 	lf = loadedfile_create(path);
4862e2caf59SThomas Veerman 
4872e2caf59SThomas Veerman 	if (path == NULL) {
4882e2caf59SThomas Veerman 		assert(fd == -1);
4892e2caf59SThomas Veerman 		fd = STDIN_FILENO;
4902e2caf59SThomas Veerman 	} else {
4912e2caf59SThomas Veerman #if 0 /* notyet */
4922e2caf59SThomas Veerman 		fd = open(path, O_RDONLY);
4932e2caf59SThomas Veerman 		if (fd < 0) {
4942e2caf59SThomas Veerman 			...
4952e2caf59SThomas Veerman 			Error("%s: %s", path, strerror(errno));
4962e2caf59SThomas Veerman 			exit(1);
4972e2caf59SThomas Veerman 		}
4982e2caf59SThomas Veerman #endif
4992e2caf59SThomas Veerman 	}
5002e2caf59SThomas Veerman 
5012e2caf59SThomas Veerman 	if (load_getsize(fd, &lf->len) == SUCCESS) {
5022e2caf59SThomas Veerman 		/* found a size, try mmap */
5032e2caf59SThomas Veerman 		pagesize = sysconf(_SC_PAGESIZE);
5042e2caf59SThomas Veerman 		if (pagesize <= 0) {
5052e2caf59SThomas Veerman 			pagesize = 0x1000;
5062e2caf59SThomas Veerman 		}
5072e2caf59SThomas Veerman 		/* round size up to a page */
5082e2caf59SThomas Veerman 		lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize);
5092e2caf59SThomas Veerman 
5102e2caf59SThomas Veerman 		/*
5112e2caf59SThomas Veerman 		 * XXX hack for dealing with empty files; remove when
5122e2caf59SThomas Veerman 		 * we're no longer limited by interfacing to the old
5132e2caf59SThomas Veerman 		 * logic elsewhere in this file.
5142e2caf59SThomas Veerman 		 */
5152e2caf59SThomas Veerman 		if (lf->maplen == 0) {
5162e2caf59SThomas Veerman 			lf->maplen = pagesize;
5172e2caf59SThomas Veerman 		}
5182e2caf59SThomas Veerman 
5192e2caf59SThomas Veerman 		/*
5202e2caf59SThomas Veerman 		 * FUTURE: remove PROT_WRITE when the parser no longer
5212e2caf59SThomas Veerman 		 * needs to scribble on the input.
5222e2caf59SThomas Veerman 		 */
5232e2caf59SThomas Veerman 		lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE,
5242e2caf59SThomas Veerman 			       MAP_FILE|MAP_COPY, fd, 0);
5252e2caf59SThomas Veerman 		if (lf->buf != MAP_FAILED) {
5262e2caf59SThomas Veerman 			/* succeeded */
5272e2caf59SThomas Veerman 			if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') {
5282e2caf59SThomas Veerman 				char *b = malloc(lf->len + 1);
5292e2caf59SThomas Veerman 				b[lf->len] = '\n';
5302e2caf59SThomas Veerman 				memcpy(b, lf->buf, lf->len++);
5312e2caf59SThomas Veerman 				munmap(lf->buf, lf->maplen);
5322e2caf59SThomas Veerman 				lf->maplen = 0;
5332e2caf59SThomas Veerman 				lf->buf = b;
5342e2caf59SThomas Veerman 			}
5352e2caf59SThomas Veerman 			goto done;
5362e2caf59SThomas Veerman 		}
5372e2caf59SThomas Veerman 	}
5382bc7c627SLionel Sambuc 
5392e2caf59SThomas Veerman 	/* cannot mmap; load the traditional way */
5402e2caf59SThomas Veerman 
5412e2caf59SThomas Veerman 	lf->maplen = 0;
5422e2caf59SThomas Veerman 	lf->len = 1024;
5432e2caf59SThomas Veerman 	lf->buf = bmake_malloc(lf->len);
5442e2caf59SThomas Veerman 
5452e2caf59SThomas Veerman 	bufpos = 0;
5462e2caf59SThomas Veerman 	while (1) {
5472e2caf59SThomas Veerman 		assert(bufpos <= lf->len);
5482e2caf59SThomas Veerman 		if (bufpos == lf->len) {
5492e2caf59SThomas Veerman 			lf->len *= 2;
5502e2caf59SThomas Veerman 			lf->buf = bmake_realloc(lf->buf, lf->len);
5512e2caf59SThomas Veerman 		}
5522e2caf59SThomas Veerman 		result = read(fd, lf->buf + bufpos, lf->len - bufpos);
5532e2caf59SThomas Veerman 		if (result < 0) {
5542e2caf59SThomas Veerman 			Error("%s: read error: %s", path, strerror(errno));
5552e2caf59SThomas Veerman 			exit(1);
5562e2caf59SThomas Veerman 		}
5572e2caf59SThomas Veerman 		if (result == 0) {
5582e2caf59SThomas Veerman 			break;
5592e2caf59SThomas Veerman 		}
5602e2caf59SThomas Veerman 		bufpos += result;
5612e2caf59SThomas Veerman 	}
5622e2caf59SThomas Veerman 	assert(bufpos <= lf->len);
5632e2caf59SThomas Veerman 	lf->len = bufpos;
5642e2caf59SThomas Veerman 
5652e2caf59SThomas Veerman 	/* truncate malloc region to actual length (maybe not useful) */
5662e2caf59SThomas Veerman 	if (lf->len > 0) {
5672e2caf59SThomas Veerman 		lf->buf = bmake_realloc(lf->buf, lf->len);
5682e2caf59SThomas Veerman 	}
5692e2caf59SThomas Veerman 
5702e2caf59SThomas Veerman done:
5712e2caf59SThomas Veerman 	if (path != NULL) {
5722e2caf59SThomas Veerman 		close(fd);
5732e2caf59SThomas Veerman 	}
5742e2caf59SThomas Veerman 	return lf;
5752e2caf59SThomas Veerman }
5762e2caf59SThomas Veerman 
5772e2caf59SThomas Veerman ////////////////////////////////////////////////////////////
5782e2caf59SThomas Veerman // old code
5792e2caf59SThomas Veerman 
5802e2caf59SThomas Veerman /*-
5812e2caf59SThomas Veerman  *----------------------------------------------------------------------
5822e2caf59SThomas Veerman  * ParseIsEscaped --
5832e2caf59SThomas Veerman  *	Check if the current character is escaped on the current line
5842e2caf59SThomas Veerman  *
5852e2caf59SThomas Veerman  * Results:
5862e2caf59SThomas Veerman  *	0 if the character is not backslash escaped, 1 otherwise
5872e2caf59SThomas Veerman  *
5882e2caf59SThomas Veerman  * Side Effects:
5892e2caf59SThomas Veerman  *	None
5902e2caf59SThomas Veerman  *----------------------------------------------------------------------
5912e2caf59SThomas Veerman  */
5922e2caf59SThomas Veerman static int
ParseIsEscaped(const char * line,const char * c)5932e2caf59SThomas Veerman ParseIsEscaped(const char *line, const char *c)
5942e2caf59SThomas Veerman {
5952e2caf59SThomas Veerman     int active = 0;
5962e2caf59SThomas Veerman     for (;;) {
5972e2caf59SThomas Veerman 	if (line == c)
5982e2caf59SThomas Veerman 	    return active;
5992e2caf59SThomas Veerman 	if (*--c != '\\')
6002e2caf59SThomas Veerman 	    return active;
6012e2caf59SThomas Veerman 	active = !active;
6022e2caf59SThomas Veerman     }
6032e2caf59SThomas Veerman }
6042e2caf59SThomas Veerman 
6052e2caf59SThomas Veerman /*-
6062e2caf59SThomas Veerman  *----------------------------------------------------------------------
6072e2caf59SThomas Veerman  * ParseFindKeyword --
6082e2caf59SThomas Veerman  *	Look in the table of keywords for one matching the given string.
6092e2caf59SThomas Veerman  *
6102e2caf59SThomas Veerman  * Input:
6112e2caf59SThomas Veerman  *	str		String to find
6122e2caf59SThomas Veerman  *
6132e2caf59SThomas Veerman  * Results:
6142e2caf59SThomas Veerman  *	The index of the keyword, or -1 if it isn't there.
6152e2caf59SThomas Veerman  *
6162e2caf59SThomas Veerman  * Side Effects:
6172e2caf59SThomas Veerman  *	None
6182e2caf59SThomas Veerman  *----------------------------------------------------------------------
6192e2caf59SThomas Veerman  */
6202e2caf59SThomas Veerman static int
ParseFindKeyword(const char * str)6212e2caf59SThomas Veerman ParseFindKeyword(const char *str)
6222e2caf59SThomas Veerman {
6232e2caf59SThomas Veerman     int    start, end, cur;
6242e2caf59SThomas Veerman     int    diff;
6252e2caf59SThomas Veerman 
6262e2caf59SThomas Veerman     start = 0;
6272e2caf59SThomas Veerman     end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;
6282e2caf59SThomas Veerman 
6292e2caf59SThomas Veerman     do {
6302e2caf59SThomas Veerman 	cur = start + ((end - start) / 2);
6312e2caf59SThomas Veerman 	diff = strcmp(str, parseKeywords[cur].name);
6322e2caf59SThomas Veerman 
6332e2caf59SThomas Veerman 	if (diff == 0) {
6342e2caf59SThomas Veerman 	    return (cur);
6352e2caf59SThomas Veerman 	} else if (diff < 0) {
6362e2caf59SThomas Veerman 	    end = cur - 1;
6372e2caf59SThomas Veerman 	} else {
6382e2caf59SThomas Veerman 	    start = cur + 1;
6392e2caf59SThomas Veerman 	}
6402e2caf59SThomas Veerman     } while (start <= end);
6412e2caf59SThomas Veerman     return (-1);
6422e2caf59SThomas Veerman }
6432e2caf59SThomas Veerman 
6442e2caf59SThomas Veerman /*-
6452e2caf59SThomas Veerman  * ParseVErrorInternal  --
6462e2caf59SThomas Veerman  *	Error message abort function for parsing. Prints out the context
6472e2caf59SThomas Veerman  *	of the error (line number and file) as well as the message with
6482e2caf59SThomas Veerman  *	two optional arguments.
6492e2caf59SThomas Veerman  *
6502e2caf59SThomas Veerman  * Results:
6512e2caf59SThomas Veerman  *	None
6522e2caf59SThomas Veerman  *
6532e2caf59SThomas Veerman  * Side Effects:
6542e2caf59SThomas Veerman  *	"fatals" is incremented if the level is PARSE_FATAL.
6552e2caf59SThomas Veerman  */
6562e2caf59SThomas Veerman /* VARARGS */
6572e2caf59SThomas Veerman static void
ParseVErrorInternal(FILE * f,const char * cfname,size_t clineno,int type,const char * fmt,va_list ap)6582e2caf59SThomas Veerman ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type,
6592e2caf59SThomas Veerman     const char *fmt, va_list ap)
6602e2caf59SThomas Veerman {
6612e2caf59SThomas Veerman 	static Boolean fatal_warning_error_printed = FALSE;
6622e2caf59SThomas Veerman 
6632e2caf59SThomas Veerman 	(void)fprintf(f, "%s: ", progname);
6642e2caf59SThomas Veerman 
6652e2caf59SThomas Veerman 	if (cfname != NULL) {
6662e2caf59SThomas Veerman 		(void)fprintf(f, "\"");
6672e2caf59SThomas Veerman 		if (*cfname != '/' && strcmp(cfname, "(stdin)") != 0) {
6682e2caf59SThomas Veerman 			char *cp;
6692e2caf59SThomas Veerman 			const char *dir;
6702e2caf59SThomas Veerman 
6712e2caf59SThomas Veerman 			/*
6722e2caf59SThomas Veerman 			 * Nothing is more annoying than not knowing
6732e2caf59SThomas Veerman 			 * which Makefile is the culprit.
6742e2caf59SThomas Veerman 			 */
6752e2caf59SThomas Veerman 			dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp);
6762e2caf59SThomas Veerman 			if (dir == NULL || *dir == '\0' ||
6772e2caf59SThomas Veerman 			    (*dir == '.' && dir[1] == '\0'))
6782e2caf59SThomas Veerman 				dir = Var_Value(".CURDIR", VAR_GLOBAL, &cp);
6792e2caf59SThomas Veerman 			if (dir == NULL)
6802e2caf59SThomas Veerman 				dir = ".";
6812e2caf59SThomas Veerman 
6822e2caf59SThomas Veerman 			(void)fprintf(f, "%s/%s", dir, cfname);
6832e2caf59SThomas Veerman 		} else
6842e2caf59SThomas Veerman 			(void)fprintf(f, "%s", cfname);
6852e2caf59SThomas Veerman 
6862e2caf59SThomas Veerman 		(void)fprintf(f, "\" line %d: ", (int)clineno);
6872e2caf59SThomas Veerman 	}
6882e2caf59SThomas Veerman 	if (type == PARSE_WARNING)
6892e2caf59SThomas Veerman 		(void)fprintf(f, "warning: ");
6902e2caf59SThomas Veerman 	(void)vfprintf(f, fmt, ap);
6912e2caf59SThomas Veerman 	(void)fprintf(f, "\n");
6922e2caf59SThomas Veerman 	(void)fflush(f);
6932e2caf59SThomas Veerman 	if (type == PARSE_FATAL || parseWarnFatal)
6942e2caf59SThomas Veerman 		fatals += 1;
6952e2caf59SThomas Veerman 	if (parseWarnFatal && !fatal_warning_error_printed) {
6962e2caf59SThomas Veerman 		Error("parsing warnings being treated as errors");
6972e2caf59SThomas Veerman 		fatal_warning_error_printed = TRUE;
6982e2caf59SThomas Veerman 	}
6992e2caf59SThomas Veerman }
7002e2caf59SThomas Veerman 
7012e2caf59SThomas Veerman /*-
7022e2caf59SThomas Veerman  * ParseErrorInternal  --
7032e2caf59SThomas Veerman  *	Error function
7042e2caf59SThomas Veerman  *
7052e2caf59SThomas Veerman  * Results:
7062e2caf59SThomas Veerman  *	None
7072e2caf59SThomas Veerman  *
7082e2caf59SThomas Veerman  * Side Effects:
7092e2caf59SThomas Veerman  *	None
7102e2caf59SThomas Veerman  */
7112e2caf59SThomas Veerman /* VARARGS */
7122e2caf59SThomas Veerman static void
ParseErrorInternal(const char * cfname,size_t clineno,int type,const char * fmt,...)7132e2caf59SThomas Veerman ParseErrorInternal(const char *cfname, size_t clineno, int type,
7142e2caf59SThomas Veerman     const char *fmt, ...)
7152e2caf59SThomas Veerman {
7162e2caf59SThomas Veerman 	va_list ap;
7172e2caf59SThomas Veerman 
7182e2caf59SThomas Veerman 	va_start(ap, fmt);
7192e2caf59SThomas Veerman 	(void)fflush(stdout);
7202e2caf59SThomas Veerman 	ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap);
7212e2caf59SThomas Veerman 	va_end(ap);
7222e2caf59SThomas Veerman 
7232e2caf59SThomas Veerman 	if (debug_file != stderr && debug_file != stdout) {
7242e2caf59SThomas Veerman 		va_start(ap, fmt);
7252e2caf59SThomas Veerman 		ParseVErrorInternal(debug_file, cfname, clineno, type, fmt, ap);
7262e2caf59SThomas Veerman 		va_end(ap);
7272e2caf59SThomas Veerman 	}
7282e2caf59SThomas Veerman }
7292e2caf59SThomas Veerman 
7302e2caf59SThomas Veerman /*-
7312e2caf59SThomas Veerman  * Parse_Error  --
7322e2caf59SThomas Veerman  *	External interface to ParseErrorInternal; uses the default filename
7332e2caf59SThomas Veerman  *	Line number.
7342e2caf59SThomas Veerman  *
7352e2caf59SThomas Veerman  * Results:
7362e2caf59SThomas Veerman  *	None
7372e2caf59SThomas Veerman  *
7382e2caf59SThomas Veerman  * Side Effects:
7392e2caf59SThomas Veerman  *	None
7402e2caf59SThomas Veerman  */
7412e2caf59SThomas Veerman /* VARARGS */
7422e2caf59SThomas Veerman void
Parse_Error(int type,const char * fmt,...)7432e2caf59SThomas Veerman Parse_Error(int type, const char *fmt, ...)
7442e2caf59SThomas Veerman {
7452e2caf59SThomas Veerman 	va_list ap;
7462e2caf59SThomas Veerman 	const char *fname;
7472e2caf59SThomas Veerman 	size_t lineno;
7482e2caf59SThomas Veerman 
7492e2caf59SThomas Veerman 	if (curFile == NULL) {
7502e2caf59SThomas Veerman 		fname = NULL;
7512e2caf59SThomas Veerman 		lineno = 0;
7522e2caf59SThomas Veerman 	} else {
7532e2caf59SThomas Veerman 		fname = curFile->fname;
7542e2caf59SThomas Veerman 		lineno = curFile->lineno;
7552e2caf59SThomas Veerman 	}
7562e2caf59SThomas Veerman 
7572e2caf59SThomas Veerman 	va_start(ap, fmt);
7582e2caf59SThomas Veerman 	(void)fflush(stdout);
7592e2caf59SThomas Veerman 	ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap);
7602e2caf59SThomas Veerman 	va_end(ap);
7612e2caf59SThomas Veerman 
7622e2caf59SThomas Veerman 	if (debug_file != stderr && debug_file != stdout) {
7632e2caf59SThomas Veerman 		va_start(ap, fmt);
7642e2caf59SThomas Veerman 		ParseVErrorInternal(debug_file, fname, lineno, type, fmt, ap);
7652e2caf59SThomas Veerman 		va_end(ap);
7662e2caf59SThomas Veerman 	}
7672e2caf59SThomas Veerman }
7682e2caf59SThomas Veerman 
7692e2caf59SThomas Veerman 
7702e2caf59SThomas Veerman /*
7712e2caf59SThomas Veerman  * ParseMessage
7722e2caf59SThomas Veerman  *	Parse a .info .warning or .error directive
7732e2caf59SThomas Veerman  *
7742e2caf59SThomas Veerman  *	The input is the line minus the ".".  We substitute
7752e2caf59SThomas Veerman  *	variables, print the message and exit(1) (for .error) or just print
7762e2caf59SThomas Veerman  *	a warning if the directive is malformed.
7772e2caf59SThomas Veerman  */
7782e2caf59SThomas Veerman static Boolean
ParseMessage(char * line)7792e2caf59SThomas Veerman ParseMessage(char *line)
7802e2caf59SThomas Veerman {
7812e2caf59SThomas Veerman     int mtype;
7822e2caf59SThomas Veerman 
7832e2caf59SThomas Veerman     switch(*line) {
7842e2caf59SThomas Veerman     case 'i':
7852e2caf59SThomas Veerman 	mtype = 0;
7862e2caf59SThomas Veerman 	break;
7872e2caf59SThomas Veerman     case 'w':
7882e2caf59SThomas Veerman 	mtype = PARSE_WARNING;
7892e2caf59SThomas Veerman 	break;
7902e2caf59SThomas Veerman     case 'e':
7912e2caf59SThomas Veerman 	mtype = PARSE_FATAL;
7922e2caf59SThomas Veerman 	break;
7932e2caf59SThomas Veerman     default:
7942e2caf59SThomas Veerman 	Parse_Error(PARSE_WARNING, "invalid syntax: \".%s\"", line);
7952e2caf59SThomas Veerman 	return FALSE;
7962e2caf59SThomas Veerman     }
7972e2caf59SThomas Veerman 
7982e2caf59SThomas Veerman     while (isalpha((u_char)*line))
7992e2caf59SThomas Veerman 	line++;
8002e2caf59SThomas Veerman     if (!isspace((u_char)*line))
8012e2caf59SThomas Veerman 	return FALSE;			/* not for us */
8022e2caf59SThomas Veerman     while (isspace((u_char)*line))
8032e2caf59SThomas Veerman 	line++;
8042e2caf59SThomas Veerman 
8052e2caf59SThomas Veerman     line = Var_Subst(NULL, line, VAR_CMD, 0);
8062e2caf59SThomas Veerman     Parse_Error(mtype, "%s", line);
8072e2caf59SThomas Veerman     free(line);
8082e2caf59SThomas Veerman 
8092e2caf59SThomas Veerman     if (mtype == PARSE_FATAL) {
8102e2caf59SThomas Veerman 	/* Terminate immediately. */
8112e2caf59SThomas Veerman 	exit(1);
8122e2caf59SThomas Veerman     }
8132e2caf59SThomas Veerman     return TRUE;
8142e2caf59SThomas Veerman }
8152e2caf59SThomas Veerman 
8162e2caf59SThomas Veerman /*-
8172e2caf59SThomas Veerman  *---------------------------------------------------------------------
8182e2caf59SThomas Veerman  * ParseLinkSrc  --
8192e2caf59SThomas Veerman  *	Link the parent node to its new child. Used in a Lst_ForEach by
8202e2caf59SThomas Veerman  *	ParseDoDependency. If the specType isn't 'Not', the parent
8212e2caf59SThomas Veerman  *	isn't linked as a parent of the child.
8222e2caf59SThomas Veerman  *
8232e2caf59SThomas Veerman  * Input:
8242e2caf59SThomas Veerman  *	pgnp		The parent node
8252e2caf59SThomas Veerman  *	cgpn		The child node
8262e2caf59SThomas Veerman  *
8272e2caf59SThomas Veerman  * Results:
8282e2caf59SThomas Veerman  *	Always = 0
8292e2caf59SThomas Veerman  *
8302e2caf59SThomas Veerman  * Side Effects:
8312e2caf59SThomas Veerman  *	New elements are added to the parents list of cgn and the
8322e2caf59SThomas Veerman  *	children list of cgn. the unmade field of pgn is updated
8332e2caf59SThomas Veerman  *	to reflect the additional child.
8342e2caf59SThomas Veerman  *---------------------------------------------------------------------
8352e2caf59SThomas Veerman  */
8362e2caf59SThomas Veerman static int
ParseLinkSrc(void * pgnp,void * cgnp)8372e2caf59SThomas Veerman ParseLinkSrc(void *pgnp, void *cgnp)
8382e2caf59SThomas Veerman {
8392e2caf59SThomas Veerman     GNode          *pgn = (GNode *)pgnp;
8402e2caf59SThomas Veerman     GNode          *cgn = (GNode *)cgnp;
8412e2caf59SThomas Veerman 
8422e2caf59SThomas Veerman     if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts))
8432e2caf59SThomas Veerman 	pgn = (GNode *)Lst_Datum(Lst_Last(pgn->cohorts));
8442e2caf59SThomas Veerman     (void)Lst_AtEnd(pgn->children, cgn);
8452e2caf59SThomas Veerman     if (specType == Not)
8462e2caf59SThomas Veerman 	    (void)Lst_AtEnd(cgn->parents, pgn);
8472e2caf59SThomas Veerman     pgn->unmade += 1;
8482e2caf59SThomas Veerman     if (DEBUG(PARSE)) {
849*0a6a1f1dSLionel Sambuc 	fprintf(debug_file, "# %s: added child %s - %s\n", __func__,
850*0a6a1f1dSLionel Sambuc 	    pgn->name, cgn->name);
8512e2caf59SThomas Veerman 	Targ_PrintNode(pgn, 0);
8522e2caf59SThomas Veerman 	Targ_PrintNode(cgn, 0);
8532e2caf59SThomas Veerman     }
8542e2caf59SThomas Veerman     return (0);
8552e2caf59SThomas Veerman }
8562e2caf59SThomas Veerman 
8572e2caf59SThomas Veerman /*-
8582e2caf59SThomas Veerman  *---------------------------------------------------------------------
8592e2caf59SThomas Veerman  * ParseDoOp  --
8602e2caf59SThomas Veerman  *	Apply the parsed operator to the given target node. Used in a
8612e2caf59SThomas Veerman  *	Lst_ForEach call by ParseDoDependency once all targets have
8622e2caf59SThomas Veerman  *	been found and their operator parsed. If the previous and new
8632e2caf59SThomas Veerman  *	operators are incompatible, a major error is taken.
8642e2caf59SThomas Veerman  *
8652e2caf59SThomas Veerman  * Input:
8662e2caf59SThomas Veerman  *	gnp		The node to which the operator is to be applied
8672e2caf59SThomas Veerman  *	opp		The operator to apply
8682e2caf59SThomas Veerman  *
8692e2caf59SThomas Veerman  * Results:
8702e2caf59SThomas Veerman  *	Always 0
8712e2caf59SThomas Veerman  *
8722e2caf59SThomas Veerman  * Side Effects:
8732e2caf59SThomas Veerman  *	The type field of the node is altered to reflect any new bits in
8742e2caf59SThomas Veerman  *	the op.
8752e2caf59SThomas Veerman  *---------------------------------------------------------------------
8762e2caf59SThomas Veerman  */
8772e2caf59SThomas Veerman static int
ParseDoOp(void * gnp,void * opp)8782e2caf59SThomas Veerman ParseDoOp(void *gnp, void *opp)
8792e2caf59SThomas Veerman {
8802e2caf59SThomas Veerman     GNode          *gn = (GNode *)gnp;
8812e2caf59SThomas Veerman     int             op = *(int *)opp;
8822e2caf59SThomas Veerman     /*
8832e2caf59SThomas Veerman      * If the dependency mask of the operator and the node don't match and
8842e2caf59SThomas Veerman      * the node has actually had an operator applied to it before, and
8852e2caf59SThomas Veerman      * the operator actually has some dependency information in it, complain.
8862e2caf59SThomas Veerman      */
8872e2caf59SThomas Veerman     if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) &&
8882e2caf59SThomas Veerman 	!OP_NOP(gn->type) && !OP_NOP(op))
8892e2caf59SThomas Veerman     {
8902e2caf59SThomas Veerman 	Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name);
8912e2caf59SThomas Veerman 	return (1);
8922e2caf59SThomas Veerman     }
8932e2caf59SThomas Veerman 
8942e2caf59SThomas Veerman     if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) {
8952e2caf59SThomas Veerman 	/*
8962e2caf59SThomas Veerman 	 * If the node was the object of a :: operator, we need to create a
8972e2caf59SThomas Veerman 	 * new instance of it for the children and commands on this dependency
8982e2caf59SThomas Veerman 	 * line. The new instance is placed on the 'cohorts' list of the
8992e2caf59SThomas Veerman 	 * initial one (note the initial one is not on its own cohorts list)
9002e2caf59SThomas Veerman 	 * and the new instance is linked to all parents of the initial
9012e2caf59SThomas Veerman 	 * instance.
9022e2caf59SThomas Veerman 	 */
9032e2caf59SThomas Veerman 	GNode	*cohort;
9042e2caf59SThomas Veerman 
9052e2caf59SThomas Veerman 	/*
9062e2caf59SThomas Veerman 	 * Propagate copied bits to the initial node.  They'll be propagated
9072e2caf59SThomas Veerman 	 * back to the rest of the cohorts later.
9082e2caf59SThomas Veerman 	 */
9092e2caf59SThomas Veerman 	gn->type |= op & ~OP_OPMASK;
9102e2caf59SThomas Veerman 
9112e2caf59SThomas Veerman 	cohort = Targ_FindNode(gn->name, TARG_NOHASH);
91284d9c625SLionel Sambuc 	if (doing_depend)
91384d9c625SLionel Sambuc 	    ParseMark(cohort);
9142e2caf59SThomas Veerman 	/*
9152e2caf59SThomas Veerman 	 * Make the cohort invisible as well to avoid duplicating it into
9162e2caf59SThomas Veerman 	 * other variables. True, parents of this target won't tend to do
9172e2caf59SThomas Veerman 	 * anything with their local variables, but better safe than
9182e2caf59SThomas Veerman 	 * sorry. (I think this is pointless now, since the relevant list
9192e2caf59SThomas Veerman 	 * traversals will no longer see this node anyway. -mycroft)
9202e2caf59SThomas Veerman 	 */
9212e2caf59SThomas Veerman 	cohort->type = op | OP_INVISIBLE;
9222e2caf59SThomas Veerman 	(void)Lst_AtEnd(gn->cohorts, cohort);
9232e2caf59SThomas Veerman 	cohort->centurion = gn;
9242e2caf59SThomas Veerman 	gn->unmade_cohorts += 1;
9252e2caf59SThomas Veerman 	snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
9262e2caf59SThomas Veerman 		gn->unmade_cohorts);
9272e2caf59SThomas Veerman     } else {
9282e2caf59SThomas Veerman 	/*
9292e2caf59SThomas Veerman 	 * We don't want to nuke any previous flags (whatever they were) so we
9302e2caf59SThomas Veerman 	 * just OR the new operator into the old
9312e2caf59SThomas Veerman 	 */
9322e2caf59SThomas Veerman 	gn->type |= op;
9332e2caf59SThomas Veerman     }
9342e2caf59SThomas Veerman 
9352e2caf59SThomas Veerman     return (0);
9362e2caf59SThomas Veerman }
9372e2caf59SThomas Veerman 
9382e2caf59SThomas Veerman /*-
9392e2caf59SThomas Veerman  *---------------------------------------------------------------------
9402e2caf59SThomas Veerman  * ParseDoSrc  --
9412e2caf59SThomas Veerman  *	Given the name of a source, figure out if it is an attribute
9422e2caf59SThomas Veerman  *	and apply it to the targets if it is. Else decide if there is
9432e2caf59SThomas Veerman  *	some attribute which should be applied *to* the source because
9442e2caf59SThomas Veerman  *	of some special target and apply it if so. Otherwise, make the
9452e2caf59SThomas Veerman  *	source be a child of the targets in the list 'targets'
9462e2caf59SThomas Veerman  *
9472e2caf59SThomas Veerman  * Input:
9482e2caf59SThomas Veerman  *	tOp		operator (if any) from special targets
9492e2caf59SThomas Veerman  *	src		name of the source to handle
9502e2caf59SThomas Veerman  *
9512e2caf59SThomas Veerman  * Results:
9522e2caf59SThomas Veerman  *	None
9532e2caf59SThomas Veerman  *
9542e2caf59SThomas Veerman  * Side Effects:
9552e2caf59SThomas Veerman  *	Operator bits may be added to the list of targets or to the source.
9562e2caf59SThomas Veerman  *	The targets may have a new source added to their lists of children.
9572e2caf59SThomas Veerman  *---------------------------------------------------------------------
9582e2caf59SThomas Veerman  */
9592e2caf59SThomas Veerman static void
ParseDoSrc(int tOp,const char * src)9602e2caf59SThomas Veerman ParseDoSrc(int tOp, const char *src)
9612e2caf59SThomas Veerman {
9622e2caf59SThomas Veerman     GNode	*gn = NULL;
9632e2caf59SThomas Veerman     static int wait_number = 0;
9642e2caf59SThomas Veerman     char wait_src[16];
9652e2caf59SThomas Veerman 
9662e2caf59SThomas Veerman     if (*src == '.' && isupper ((unsigned char)src[1])) {
9672e2caf59SThomas Veerman 	int keywd = ParseFindKeyword(src);
9682e2caf59SThomas Veerman 	if (keywd != -1) {
9692e2caf59SThomas Veerman 	    int op = parseKeywords[keywd].op;
9702e2caf59SThomas Veerman 	    if (op != 0) {
9712e2caf59SThomas Veerman 		Lst_ForEach(targets, ParseDoOp, &op);
9722e2caf59SThomas Veerman 		return;
9732e2caf59SThomas Veerman 	    }
9742e2caf59SThomas Veerman 	    if (parseKeywords[keywd].spec == Wait) {
9752e2caf59SThomas Veerman 		/*
9762e2caf59SThomas Veerman 		 * We add a .WAIT node in the dependency list.
9772e2caf59SThomas Veerman 		 * After any dynamic dependencies (and filename globbing)
9782e2caf59SThomas Veerman 		 * have happened, it is given a dependency on the each
9792e2caf59SThomas Veerman 		 * previous child back to and previous .WAIT node.
9802e2caf59SThomas Veerman 		 * The next child won't be scheduled until the .WAIT node
9812e2caf59SThomas Veerman 		 * is built.
9822e2caf59SThomas Veerman 		 * We give each .WAIT node a unique name (mainly for diag).
9832e2caf59SThomas Veerman 		 */
9842e2caf59SThomas Veerman 		snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number);
9852e2caf59SThomas Veerman 		gn = Targ_FindNode(wait_src, TARG_NOHASH);
98684d9c625SLionel Sambuc 		if (doing_depend)
98784d9c625SLionel Sambuc 		    ParseMark(gn);
9882e2caf59SThomas Veerman 		gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
9892e2caf59SThomas Veerman 		Lst_ForEach(targets, ParseLinkSrc, gn);
9902e2caf59SThomas Veerman 		return;
9912e2caf59SThomas Veerman 	    }
9922e2caf59SThomas Veerman 	}
9932e2caf59SThomas Veerman     }
9942e2caf59SThomas Veerman 
9952e2caf59SThomas Veerman     switch (specType) {
9962e2caf59SThomas Veerman     case Main:
9972e2caf59SThomas Veerman 	/*
9982e2caf59SThomas Veerman 	 * If we have noted the existence of a .MAIN, it means we need
9992e2caf59SThomas Veerman 	 * to add the sources of said target to the list of things
10002e2caf59SThomas Veerman 	 * to create. The string 'src' is likely to be free, so we
10012e2caf59SThomas Veerman 	 * must make a new copy of it. Note that this will only be
10022e2caf59SThomas Veerman 	 * invoked if the user didn't specify a target on the command
10032e2caf59SThomas Veerman 	 * line. This is to allow #ifmake's to succeed, or something...
10042e2caf59SThomas Veerman 	 */
10052e2caf59SThomas Veerman 	(void)Lst_AtEnd(create, bmake_strdup(src));
10062e2caf59SThomas Veerman 	/*
10072e2caf59SThomas Veerman 	 * Add the name to the .TARGETS variable as well, so the user can
10082e2caf59SThomas Veerman 	 * employ that, if desired.
10092e2caf59SThomas Veerman 	 */
10102e2caf59SThomas Veerman 	Var_Append(".TARGETS", src, VAR_GLOBAL);
10112e2caf59SThomas Veerman 	return;
10122e2caf59SThomas Veerman 
10132e2caf59SThomas Veerman     case Order:
10142e2caf59SThomas Veerman 	/*
10152e2caf59SThomas Veerman 	 * Create proper predecessor/successor links between the previous
10162e2caf59SThomas Veerman 	 * source and the current one.
10172e2caf59SThomas Veerman 	 */
10182e2caf59SThomas Veerman 	gn = Targ_FindNode(src, TARG_CREATE);
101984d9c625SLionel Sambuc 	if (doing_depend)
102084d9c625SLionel Sambuc 	    ParseMark(gn);
10212e2caf59SThomas Veerman 	if (predecessor != NULL) {
10222e2caf59SThomas Veerman 	    (void)Lst_AtEnd(predecessor->order_succ, gn);
10232e2caf59SThomas Veerman 	    (void)Lst_AtEnd(gn->order_pred, predecessor);
10242e2caf59SThomas Veerman 	    if (DEBUG(PARSE)) {
1025*0a6a1f1dSLionel Sambuc 		fprintf(debug_file, "# %s: added Order dependency %s - %s\n",
1026*0a6a1f1dSLionel Sambuc 		    __func__, predecessor->name, gn->name);
10272e2caf59SThomas Veerman 		Targ_PrintNode(predecessor, 0);
10282e2caf59SThomas Veerman 		Targ_PrintNode(gn, 0);
10292e2caf59SThomas Veerman 	    }
10302e2caf59SThomas Veerman 	}
10312e2caf59SThomas Veerman 	/*
10322e2caf59SThomas Veerman 	 * The current source now becomes the predecessor for the next one.
10332e2caf59SThomas Veerman 	 */
10342e2caf59SThomas Veerman 	predecessor = gn;
10352e2caf59SThomas Veerman 	break;
10362e2caf59SThomas Veerman 
10372e2caf59SThomas Veerman     default:
10382e2caf59SThomas Veerman 	/*
10392e2caf59SThomas Veerman 	 * If the source is not an attribute, we need to find/create
10402e2caf59SThomas Veerman 	 * a node for it. After that we can apply any operator to it
10412e2caf59SThomas Veerman 	 * from a special target or link it to its parents, as
10422e2caf59SThomas Veerman 	 * appropriate.
10432e2caf59SThomas Veerman 	 *
10442e2caf59SThomas Veerman 	 * In the case of a source that was the object of a :: operator,
10452e2caf59SThomas Veerman 	 * the attribute is applied to all of its instances (as kept in
10462e2caf59SThomas Veerman 	 * the 'cohorts' list of the node) or all the cohorts are linked
10472e2caf59SThomas Veerman 	 * to all the targets.
10482e2caf59SThomas Veerman 	 */
10492e2caf59SThomas Veerman 
10502e2caf59SThomas Veerman 	/* Find/create the 'src' node and attach to all targets */
10512e2caf59SThomas Veerman 	gn = Targ_FindNode(src, TARG_CREATE);
105284d9c625SLionel Sambuc 	if (doing_depend)
105384d9c625SLionel Sambuc 	    ParseMark(gn);
10542e2caf59SThomas Veerman 	if (tOp) {
10552e2caf59SThomas Veerman 	    gn->type |= tOp;
10562e2caf59SThomas Veerman 	} else {
10572e2caf59SThomas Veerman 	    Lst_ForEach(targets, ParseLinkSrc, gn);
10582e2caf59SThomas Veerman 	}
10592e2caf59SThomas Veerman 	break;
10602e2caf59SThomas Veerman     }
10612e2caf59SThomas Veerman }
10622e2caf59SThomas Veerman 
10632e2caf59SThomas Veerman /*-
10642e2caf59SThomas Veerman  *-----------------------------------------------------------------------
10652e2caf59SThomas Veerman  * ParseFindMain --
10662e2caf59SThomas Veerman  *	Find a real target in the list and set it to be the main one.
10672e2caf59SThomas Veerman  *	Called by ParseDoDependency when a main target hasn't been found
10682e2caf59SThomas Veerman  *	yet.
10692e2caf59SThomas Veerman  *
10702e2caf59SThomas Veerman  * Input:
10712e2caf59SThomas Veerman  *	gnp		Node to examine
10722e2caf59SThomas Veerman  *
10732e2caf59SThomas Veerman  * Results:
10742e2caf59SThomas Veerman  *	0 if main not found yet, 1 if it is.
10752e2caf59SThomas Veerman  *
10762e2caf59SThomas Veerman  * Side Effects:
10772e2caf59SThomas Veerman  *	mainNode is changed and Targ_SetMain is called.
10782e2caf59SThomas Veerman  *
10792e2caf59SThomas Veerman  *-----------------------------------------------------------------------
10802e2caf59SThomas Veerman  */
10812e2caf59SThomas Veerman static int
ParseFindMain(void * gnp,void * dummy)10822e2caf59SThomas Veerman ParseFindMain(void *gnp, void *dummy)
10832e2caf59SThomas Veerman {
10842e2caf59SThomas Veerman     GNode   	  *gn = (GNode *)gnp;
10852e2caf59SThomas Veerman     if ((gn->type & OP_NOTARGET) == 0) {
10862e2caf59SThomas Veerman 	mainNode = gn;
10872e2caf59SThomas Veerman 	Targ_SetMain(gn);
10882e2caf59SThomas Veerman 	return (dummy ? 1 : 1);
10892e2caf59SThomas Veerman     } else {
10902e2caf59SThomas Veerman 	return (dummy ? 0 : 0);
10912e2caf59SThomas Veerman     }
10922e2caf59SThomas Veerman }
10932e2caf59SThomas Veerman 
10942e2caf59SThomas Veerman /*-
10952e2caf59SThomas Veerman  *-----------------------------------------------------------------------
10962e2caf59SThomas Veerman  * ParseAddDir --
10972e2caf59SThomas Veerman  *	Front-end for Dir_AddDir to make sure Lst_ForEach keeps going
10982e2caf59SThomas Veerman  *
10992e2caf59SThomas Veerman  * Results:
11002e2caf59SThomas Veerman  *	=== 0
11012e2caf59SThomas Veerman  *
11022e2caf59SThomas Veerman  * Side Effects:
11032e2caf59SThomas Veerman  *	See Dir_AddDir.
11042e2caf59SThomas Veerman  *
11052e2caf59SThomas Veerman  *-----------------------------------------------------------------------
11062e2caf59SThomas Veerman  */
11072e2caf59SThomas Veerman static int
ParseAddDir(void * path,void * name)11082e2caf59SThomas Veerman ParseAddDir(void *path, void *name)
11092e2caf59SThomas Veerman {
11102e2caf59SThomas Veerman     (void)Dir_AddDir((Lst) path, (char *)name);
11112e2caf59SThomas Veerman     return(0);
11122e2caf59SThomas Veerman }
11132e2caf59SThomas Veerman 
11142e2caf59SThomas Veerman /*-
11152e2caf59SThomas Veerman  *-----------------------------------------------------------------------
11162e2caf59SThomas Veerman  * ParseClearPath --
11172e2caf59SThomas Veerman  *	Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going
11182e2caf59SThomas Veerman  *
11192e2caf59SThomas Veerman  * Results:
11202e2caf59SThomas Veerman  *	=== 0
11212e2caf59SThomas Veerman  *
11222e2caf59SThomas Veerman  * Side Effects:
11232e2caf59SThomas Veerman  *	See Dir_ClearPath
11242e2caf59SThomas Veerman  *
11252e2caf59SThomas Veerman  *-----------------------------------------------------------------------
11262e2caf59SThomas Veerman  */
11272e2caf59SThomas Veerman static int
ParseClearPath(void * path,void * dummy)11282e2caf59SThomas Veerman ParseClearPath(void *path, void *dummy)
11292e2caf59SThomas Veerman {
11302e2caf59SThomas Veerman     Dir_ClearPath((Lst) path);
11312e2caf59SThomas Veerman     return(dummy ? 0 : 0);
11322e2caf59SThomas Veerman }
11332e2caf59SThomas Veerman 
11342e2caf59SThomas Veerman /*-
11352e2caf59SThomas Veerman  *---------------------------------------------------------------------
11362e2caf59SThomas Veerman  * ParseDoDependency  --
11372e2caf59SThomas Veerman  *	Parse the dependency line in line.
11382e2caf59SThomas Veerman  *
11392e2caf59SThomas Veerman  * Input:
11402e2caf59SThomas Veerman  *	line		the line to parse
11412e2caf59SThomas Veerman  *
11422e2caf59SThomas Veerman  * Results:
11432e2caf59SThomas Veerman  *	None
11442e2caf59SThomas Veerman  *
11452e2caf59SThomas Veerman  * Side Effects:
11462e2caf59SThomas Veerman  *	The nodes of the sources are linked as children to the nodes of the
11472e2caf59SThomas Veerman  *	targets. Some nodes may be created.
11482e2caf59SThomas Veerman  *
11492e2caf59SThomas Veerman  *	We parse a dependency line by first extracting words from the line and
11502e2caf59SThomas Veerman  * finding nodes in the list of all targets with that name. This is done
11512e2caf59SThomas Veerman  * until a character is encountered which is an operator character. Currently
11522e2caf59SThomas Veerman  * these are only ! and :. At this point the operator is parsed and the
11532e2caf59SThomas Veerman  * pointer into the line advanced until the first source is encountered.
11542e2caf59SThomas Veerman  * 	The parsed operator is applied to each node in the 'targets' list,
11552e2caf59SThomas Veerman  * which is where the nodes found for the targets are kept, by means of
11562e2caf59SThomas Veerman  * the ParseDoOp function.
11572e2caf59SThomas Veerman  *	The sources are read in much the same way as the targets were except
11582e2caf59SThomas Veerman  * that now they are expanded using the wildcarding scheme of the C-Shell
11592e2caf59SThomas Veerman  * and all instances of the resulting words in the list of all targets
11602e2caf59SThomas Veerman  * are found. Each of the resulting nodes is then linked to each of the
11612e2caf59SThomas Veerman  * targets as one of its children.
11622e2caf59SThomas Veerman  *	Certain targets are handled specially. These are the ones detailed
11632e2caf59SThomas Veerman  * by the specType variable.
11642e2caf59SThomas Veerman  *	The storing of transformation rules is also taken care of here.
11652e2caf59SThomas Veerman  * A target is recognized as a transformation rule by calling
11662e2caf59SThomas Veerman  * Suff_IsTransform. If it is a transformation rule, its node is gotten
11672e2caf59SThomas Veerman  * from the suffix module via Suff_AddTransform rather than the standard
11682e2caf59SThomas Veerman  * Targ_FindNode in the target module.
11692e2caf59SThomas Veerman  *---------------------------------------------------------------------
11702e2caf59SThomas Veerman  */
11712e2caf59SThomas Veerman static void
ParseDoDependency(char * line)11722e2caf59SThomas Veerman ParseDoDependency(char *line)
11732e2caf59SThomas Veerman {
11742e2caf59SThomas Veerman     char  	   *cp;		/* our current position */
11752e2caf59SThomas Veerman     GNode 	   *gn = NULL;	/* a general purpose temporary node */
11762e2caf59SThomas Veerman     int             op;		/* the operator on the line */
11772e2caf59SThomas Veerman     char            savec;	/* a place to save a character */
11782e2caf59SThomas Veerman     Lst    	    paths;   	/* List of search paths to alter when parsing
11792e2caf59SThomas Veerman 				 * a list of .PATH targets */
11802e2caf59SThomas Veerman     int	    	    tOp;    	/* operator from special target */
11812e2caf59SThomas Veerman     Lst	    	    sources;	/* list of archive source names after
11822e2caf59SThomas Veerman 				 * expansion */
11832e2caf59SThomas Veerman     Lst 	    curTargs;	/* list of target names to be found and added
11842e2caf59SThomas Veerman 				 * to the targets list */
11852e2caf59SThomas Veerman     char	   *lstart = line;
11862e2caf59SThomas Veerman 
11872e2caf59SThomas Veerman     if (DEBUG(PARSE))
11882e2caf59SThomas Veerman 	fprintf(debug_file, "ParseDoDependency(%s)\n", line);
11892e2caf59SThomas Veerman     tOp = 0;
11902e2caf59SThomas Veerman 
11912e2caf59SThomas Veerman     specType = Not;
11922e2caf59SThomas Veerman     paths = NULL;
11932e2caf59SThomas Veerman 
11942e2caf59SThomas Veerman     curTargs = Lst_Init(FALSE);
11952e2caf59SThomas Veerman 
1196*0a6a1f1dSLionel Sambuc     /*
1197*0a6a1f1dSLionel Sambuc      * First, grind through the targets.
1198*0a6a1f1dSLionel Sambuc      */
1199*0a6a1f1dSLionel Sambuc 
12002e2caf59SThomas Veerman     do {
1201*0a6a1f1dSLionel Sambuc 	/*
1202*0a6a1f1dSLionel Sambuc 	 * Here LINE points to the beginning of the next word, and
1203*0a6a1f1dSLionel Sambuc 	 * LSTART points to the actual beginning of the line.
1204*0a6a1f1dSLionel Sambuc 	 */
1205*0a6a1f1dSLionel Sambuc 
1206*0a6a1f1dSLionel Sambuc 	/* Find the end of the next word. */
12072e2caf59SThomas Veerman 	for (cp = line; *cp && (ParseIsEscaped(lstart, cp) ||
12082e2caf59SThomas Veerman 		     !(isspace((unsigned char)*cp) ||
12092e2caf59SThomas Veerman 			 *cp == '!' || *cp == ':' || *cp == LPAREN));
12102e2caf59SThomas Veerman 		 cp++) {
12112e2caf59SThomas Veerman 	    if (*cp == '$') {
12122e2caf59SThomas Veerman 		/*
12132e2caf59SThomas Veerman 		 * Must be a dynamic source (would have been expanded
12142e2caf59SThomas Veerman 		 * otherwise), so call the Var module to parse the puppy
12152e2caf59SThomas Veerman 		 * so we can safely advance beyond it...There should be
12162e2caf59SThomas Veerman 		 * no errors in this, as they would have been discovered
12172e2caf59SThomas Veerman 		 * in the initial Var_Subst and we wouldn't be here.
12182e2caf59SThomas Veerman 		 */
12192e2caf59SThomas Veerman 		int 	length;
12202e2caf59SThomas Veerman 		void    *freeIt;
12212e2caf59SThomas Veerman 
122284d9c625SLionel Sambuc 		(void)Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt);
12232e2caf59SThomas Veerman 		if (freeIt)
12242e2caf59SThomas Veerman 		    free(freeIt);
12252e2caf59SThomas Veerman 		cp += length-1;
12262e2caf59SThomas Veerman 	    }
12272e2caf59SThomas Veerman 	}
12282e2caf59SThomas Veerman 
1229*0a6a1f1dSLionel Sambuc 	/*
1230*0a6a1f1dSLionel Sambuc 	 * If the word is followed by a left parenthesis, it's the
1231*0a6a1f1dSLionel Sambuc 	 * name of an object file inside an archive (ar file).
1232*0a6a1f1dSLionel Sambuc 	 */
12332e2caf59SThomas Veerman 	if (!ParseIsEscaped(lstart, cp) && *cp == LPAREN) {
12342e2caf59SThomas Veerman 	    /*
12352e2caf59SThomas Veerman 	     * Archives must be handled specially to make sure the OP_ARCHV
12362e2caf59SThomas Veerman 	     * flag is set in their 'type' field, for one thing, and because
12372e2caf59SThomas Veerman 	     * things like "archive(file1.o file2.o file3.o)" are permissible.
12382e2caf59SThomas Veerman 	     * Arch_ParseArchive will set 'line' to be the first non-blank
12392e2caf59SThomas Veerman 	     * after the archive-spec. It creates/finds nodes for the members
12402e2caf59SThomas Veerman 	     * and places them on the given list, returning SUCCESS if all
12412e2caf59SThomas Veerman 	     * went well and FAILURE if there was an error in the
12422e2caf59SThomas Veerman 	     * specification. On error, line should remain untouched.
12432e2caf59SThomas Veerman 	     */
12442e2caf59SThomas Veerman 	    if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) {
12452e2caf59SThomas Veerman 		Parse_Error(PARSE_FATAL,
12462e2caf59SThomas Veerman 			     "Error in archive specification: \"%s\"", line);
12472e2caf59SThomas Veerman 		goto out;
12482e2caf59SThomas Veerman 	    } else {
1249*0a6a1f1dSLionel Sambuc 		/* Done with this word; on to the next. */
12502e2caf59SThomas Veerman 		continue;
12512e2caf59SThomas Veerman 	    }
12522e2caf59SThomas Veerman 	}
12532e2caf59SThomas Veerman 
12542e2caf59SThomas Veerman 	if (!*cp) {
12552e2caf59SThomas Veerman 	    /*
1256*0a6a1f1dSLionel Sambuc 	     * We got to the end of the line while we were still
1257*0a6a1f1dSLionel Sambuc 	     * looking at targets.
1258*0a6a1f1dSLionel Sambuc 	     *
12592e2caf59SThomas Veerman 	     * Ending a dependency line without an operator is a Bozo
12602e2caf59SThomas Veerman 	     * no-no.  As a heuristic, this is also often triggered by
12612e2caf59SThomas Veerman 	     * undetected conflicts from cvs/rcs merges.
12622e2caf59SThomas Veerman 	     */
12632e2caf59SThomas Veerman 	    if ((strncmp(line, "<<<<<<", 6) == 0) ||
12642e2caf59SThomas Veerman 		(strncmp(line, "======", 6) == 0) ||
12652e2caf59SThomas Veerman 		(strncmp(line, ">>>>>>", 6) == 0))
12662e2caf59SThomas Veerman 		Parse_Error(PARSE_FATAL,
12672e2caf59SThomas Veerman 		    "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts");
12682e2caf59SThomas Veerman 	    else
12692e2caf59SThomas Veerman 		Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive"
12702e2caf59SThomas Veerman 				     : "Need an operator");
12712e2caf59SThomas Veerman 	    goto out;
12722e2caf59SThomas Veerman 	}
1273*0a6a1f1dSLionel Sambuc 
1274*0a6a1f1dSLionel Sambuc 	/* Insert a null terminator. */
1275*0a6a1f1dSLionel Sambuc 	savec = *cp;
12762e2caf59SThomas Veerman 	*cp = '\0';
12772e2caf59SThomas Veerman 
12782e2caf59SThomas Veerman 	/*
1279*0a6a1f1dSLionel Sambuc 	 * Got the word. See if it's a special target and if so set
12802e2caf59SThomas Veerman 	 * specType to match it.
12812e2caf59SThomas Veerman 	 */
12822e2caf59SThomas Veerman 	if (*line == '.' && isupper ((unsigned char)line[1])) {
12832e2caf59SThomas Veerman 	    /*
12842e2caf59SThomas Veerman 	     * See if the target is a special target that must have it
12852e2caf59SThomas Veerman 	     * or its sources handled specially.
12862e2caf59SThomas Veerman 	     */
12872e2caf59SThomas Veerman 	    int keywd = ParseFindKeyword(line);
12882e2caf59SThomas Veerman 	    if (keywd != -1) {
12892e2caf59SThomas Veerman 		if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {
12902e2caf59SThomas Veerman 		    Parse_Error(PARSE_FATAL, "Mismatched special targets");
12912e2caf59SThomas Veerman 		    goto out;
12922e2caf59SThomas Veerman 		}
12932e2caf59SThomas Veerman 
12942e2caf59SThomas Veerman 		specType = parseKeywords[keywd].spec;
12952e2caf59SThomas Veerman 		tOp = parseKeywords[keywd].op;
12962e2caf59SThomas Veerman 
12972e2caf59SThomas Veerman 		/*
12982e2caf59SThomas Veerman 		 * Certain special targets have special semantics:
12992e2caf59SThomas Veerman 		 *	.PATH		Have to set the dirSearchPath
13002e2caf59SThomas Veerman 		 *			variable too
13012e2caf59SThomas Veerman 		 *	.MAIN		Its sources are only used if
13022e2caf59SThomas Veerman 		 *			nothing has been specified to
13032e2caf59SThomas Veerman 		 *			create.
13042e2caf59SThomas Veerman 		 *	.DEFAULT    	Need to create a node to hang
13052e2caf59SThomas Veerman 		 *			commands on, but we don't want
13062e2caf59SThomas Veerman 		 *			it in the graph, nor do we want
13072e2caf59SThomas Veerman 		 *			it to be the Main Target, so we
13082e2caf59SThomas Veerman 		 *			create it, set OP_NOTMAIN and
13092e2caf59SThomas Veerman 		 *			add it to the list, setting
13102e2caf59SThomas Veerman 		 *			DEFAULT to the new node for
13112e2caf59SThomas Veerman 		 *			later use. We claim the node is
13122e2caf59SThomas Veerman 		 *	    	    	A transformation rule to make
13132e2caf59SThomas Veerman 		 *	    	    	life easier later, when we'll
13142e2caf59SThomas Veerman 		 *	    	    	use Make_HandleUse to actually
13152e2caf59SThomas Veerman 		 *	    	    	apply the .DEFAULT commands.
13162e2caf59SThomas Veerman 		 *	.PHONY		The list of targets
13172e2caf59SThomas Veerman 		 *	.NOPATH		Don't search for file in the path
131884d9c625SLionel Sambuc 		 *	.STALE
13192e2caf59SThomas Veerman 		 *	.BEGIN
13202e2caf59SThomas Veerman 		 *	.END
13212e2caf59SThomas Veerman 		 *	.ERROR
13222e2caf59SThomas Veerman 		 *	.INTERRUPT  	Are not to be considered the
13232e2caf59SThomas Veerman 		 *			main target.
13242e2caf59SThomas Veerman 		 *  	.NOTPARALLEL	Make only one target at a time.
13252e2caf59SThomas Veerman 		 *  	.SINGLESHELL	Create a shell for each command.
13262e2caf59SThomas Veerman 		 *  	.ORDER	    	Must set initial predecessor to NULL
13272e2caf59SThomas Veerman 		 */
13282e2caf59SThomas Veerman 		switch (specType) {
13292e2caf59SThomas Veerman 		case ExPath:
13302e2caf59SThomas Veerman 		    if (paths == NULL) {
13312e2caf59SThomas Veerman 			paths = Lst_Init(FALSE);
13322e2caf59SThomas Veerman 		    }
13332e2caf59SThomas Veerman 		    (void)Lst_AtEnd(paths, dirSearchPath);
13342e2caf59SThomas Veerman 		    break;
13352e2caf59SThomas Veerman 		case Main:
13362e2caf59SThomas Veerman 		    if (!Lst_IsEmpty(create)) {
13372e2caf59SThomas Veerman 			specType = Not;
13382e2caf59SThomas Veerman 		    }
13392e2caf59SThomas Veerman 		    break;
13402e2caf59SThomas Veerman 		case Begin:
13412e2caf59SThomas Veerman 		case End:
134284d9c625SLionel Sambuc 		case Stale:
13432e2caf59SThomas Veerman 		case dotError:
13442e2caf59SThomas Veerman 		case Interrupt:
13452e2caf59SThomas Veerman 		    gn = Targ_FindNode(line, TARG_CREATE);
134684d9c625SLionel Sambuc 		    if (doing_depend)
134784d9c625SLionel Sambuc 			ParseMark(gn);
13482e2caf59SThomas Veerman 		    gn->type |= OP_NOTMAIN|OP_SPECIAL;
13492e2caf59SThomas Veerman 		    (void)Lst_AtEnd(targets, gn);
13502e2caf59SThomas Veerman 		    break;
13512e2caf59SThomas Veerman 		case Default:
13522e2caf59SThomas Veerman 		    gn = Targ_NewGN(".DEFAULT");
13532e2caf59SThomas Veerman 		    gn->type |= (OP_NOTMAIN|OP_TRANSFORM);
13542e2caf59SThomas Veerman 		    (void)Lst_AtEnd(targets, gn);
13552e2caf59SThomas Veerman 		    DEFAULT = gn;
13562e2caf59SThomas Veerman 		    break;
13572e2caf59SThomas Veerman 		case NotParallel:
13582e2caf59SThomas Veerman 		    maxJobs = 1;
13592e2caf59SThomas Veerman 		    break;
13602e2caf59SThomas Veerman 		case SingleShell:
13612e2caf59SThomas Veerman 		    compatMake = TRUE;
13622e2caf59SThomas Veerman 		    break;
13632e2caf59SThomas Veerman 		case Order:
13642e2caf59SThomas Veerman 		    predecessor = NULL;
13652e2caf59SThomas Veerman 		    break;
13662e2caf59SThomas Veerman 		default:
13672e2caf59SThomas Veerman 		    break;
13682e2caf59SThomas Veerman 		}
13692e2caf59SThomas Veerman 	    } else if (strncmp(line, ".PATH", 5) == 0) {
13702e2caf59SThomas Veerman 		/*
13712e2caf59SThomas Veerman 		 * .PATH<suffix> has to be handled specially.
13722e2caf59SThomas Veerman 		 * Call on the suffix module to give us a path to
13732e2caf59SThomas Veerman 		 * modify.
13742e2caf59SThomas Veerman 		 */
13752e2caf59SThomas Veerman 		Lst 	path;
13762e2caf59SThomas Veerman 
13772e2caf59SThomas Veerman 		specType = ExPath;
13782e2caf59SThomas Veerman 		path = Suff_GetPath(&line[5]);
13792e2caf59SThomas Veerman 		if (path == NULL) {
13802e2caf59SThomas Veerman 		    Parse_Error(PARSE_FATAL,
13812e2caf59SThomas Veerman 				 "Suffix '%s' not defined (yet)",
13822e2caf59SThomas Veerman 				 &line[5]);
13832e2caf59SThomas Veerman 		    goto out;
13842e2caf59SThomas Veerman 		} else {
13852e2caf59SThomas Veerman 		    if (paths == NULL) {
13862e2caf59SThomas Veerman 			paths = Lst_Init(FALSE);
13872e2caf59SThomas Veerman 		    }
13882e2caf59SThomas Veerman 		    (void)Lst_AtEnd(paths, path);
13892e2caf59SThomas Veerman 		}
13902e2caf59SThomas Veerman 	    }
13912e2caf59SThomas Veerman 	}
13922e2caf59SThomas Veerman 
13932e2caf59SThomas Veerman 	/*
13942e2caf59SThomas Veerman 	 * Have word in line. Get or create its node and stick it at
13952e2caf59SThomas Veerman 	 * the end of the targets list
13962e2caf59SThomas Veerman 	 */
13972e2caf59SThomas Veerman 	if ((specType == Not) && (*line != '\0')) {
13982e2caf59SThomas Veerman 	    if (Dir_HasWildcards(line)) {
13992e2caf59SThomas Veerman 		/*
14002e2caf59SThomas Veerman 		 * Targets are to be sought only in the current directory,
14012e2caf59SThomas Veerman 		 * so create an empty path for the thing. Note we need to
14022e2caf59SThomas Veerman 		 * use Dir_Destroy in the destruction of the path as the
14032e2caf59SThomas Veerman 		 * Dir module could have added a directory to the path...
14042e2caf59SThomas Veerman 		 */
14052e2caf59SThomas Veerman 		Lst	    emptyPath = Lst_Init(FALSE);
14062e2caf59SThomas Veerman 
14072e2caf59SThomas Veerman 		Dir_Expand(line, emptyPath, curTargs);
14082e2caf59SThomas Veerman 
14092e2caf59SThomas Veerman 		Lst_Destroy(emptyPath, Dir_Destroy);
14102e2caf59SThomas Veerman 	    } else {
14112e2caf59SThomas Veerman 		/*
14122e2caf59SThomas Veerman 		 * No wildcards, but we want to avoid code duplication,
14132e2caf59SThomas Veerman 		 * so create a list with the word on it.
14142e2caf59SThomas Veerman 		 */
14152e2caf59SThomas Veerman 		(void)Lst_AtEnd(curTargs, line);
14162e2caf59SThomas Veerman 	    }
14172e2caf59SThomas Veerman 
1418*0a6a1f1dSLionel Sambuc 	    /* Apply the targets. */
1419*0a6a1f1dSLionel Sambuc 
14202e2caf59SThomas Veerman 	    while(!Lst_IsEmpty(curTargs)) {
14212e2caf59SThomas Veerman 		char	*targName = (char *)Lst_DeQueue(curTargs);
14222e2caf59SThomas Veerman 
14232e2caf59SThomas Veerman 		if (!Suff_IsTransform (targName)) {
14242e2caf59SThomas Veerman 		    gn = Targ_FindNode(targName, TARG_CREATE);
14252e2caf59SThomas Veerman 		} else {
14262e2caf59SThomas Veerman 		    gn = Suff_AddTransform(targName);
14272e2caf59SThomas Veerman 		}
142884d9c625SLionel Sambuc 		if (doing_depend)
142984d9c625SLionel Sambuc 		    ParseMark(gn);
14302e2caf59SThomas Veerman 
14312e2caf59SThomas Veerman 		(void)Lst_AtEnd(targets, gn);
14322e2caf59SThomas Veerman 	    }
14332e2caf59SThomas Veerman 	} else if (specType == ExPath && *line != '.' && *line != '\0') {
14342e2caf59SThomas Veerman 	    Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
14352e2caf59SThomas Veerman 	}
14362e2caf59SThomas Veerman 
1437*0a6a1f1dSLionel Sambuc 	/* Don't need the inserted null terminator any more. */
14382e2caf59SThomas Veerman 	*cp = savec;
1439*0a6a1f1dSLionel Sambuc 
14402e2caf59SThomas Veerman 	/*
14412e2caf59SThomas Veerman 	 * If it is a special type and not .PATH, it's the only target we
14422e2caf59SThomas Veerman 	 * allow on this line...
14432e2caf59SThomas Veerman 	 */
14442e2caf59SThomas Veerman 	if (specType != Not && specType != ExPath) {
14452e2caf59SThomas Veerman 	    Boolean warning = FALSE;
14462e2caf59SThomas Veerman 
14472e2caf59SThomas Veerman 	    while (*cp && (ParseIsEscaped(lstart, cp) ||
14482e2caf59SThomas Veerman 		((*cp != '!') && (*cp != ':')))) {
14492e2caf59SThomas Veerman 		if (ParseIsEscaped(lstart, cp) ||
14502e2caf59SThomas Veerman 		    (*cp != ' ' && *cp != '\t')) {
14512e2caf59SThomas Veerman 		    warning = TRUE;
14522e2caf59SThomas Veerman 		}
14532e2caf59SThomas Veerman 		cp++;
14542e2caf59SThomas Veerman 	    }
14552e2caf59SThomas Veerman 	    if (warning) {
14562e2caf59SThomas Veerman 		Parse_Error(PARSE_WARNING, "Extra target ignored");
14572e2caf59SThomas Veerman 	    }
14582e2caf59SThomas Veerman 	} else {
14592e2caf59SThomas Veerman 	    while (*cp && isspace ((unsigned char)*cp)) {
14602e2caf59SThomas Veerman 		cp++;
14612e2caf59SThomas Veerman 	    }
14622e2caf59SThomas Veerman 	}
14632e2caf59SThomas Veerman 	line = cp;
14642e2caf59SThomas Veerman     } while (*line && (ParseIsEscaped(lstart, line) ||
14652e2caf59SThomas Veerman 	((*line != '!') && (*line != ':'))));
14662e2caf59SThomas Veerman 
14672e2caf59SThomas Veerman     /*
14682e2caf59SThomas Veerman      * Don't need the list of target names anymore...
14692e2caf59SThomas Veerman      */
14702e2caf59SThomas Veerman     Lst_Destroy(curTargs, NULL);
14712e2caf59SThomas Veerman     curTargs = NULL;
14722e2caf59SThomas Veerman 
14732e2caf59SThomas Veerman     if (!Lst_IsEmpty(targets)) {
14742e2caf59SThomas Veerman 	switch(specType) {
14752e2caf59SThomas Veerman 	    default:
14762e2caf59SThomas Veerman 		Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");
14772e2caf59SThomas Veerman 		break;
14782e2caf59SThomas Veerman 	    case Default:
147984d9c625SLionel Sambuc 	    case Stale:
14802e2caf59SThomas Veerman 	    case Begin:
14812e2caf59SThomas Veerman 	    case End:
14822e2caf59SThomas Veerman 	    case dotError:
14832e2caf59SThomas Veerman 	    case Interrupt:
14842e2caf59SThomas Veerman 		/*
14852e2caf59SThomas Veerman 		 * These four create nodes on which to hang commands, so
14862e2caf59SThomas Veerman 		 * targets shouldn't be empty...
14872e2caf59SThomas Veerman 		 */
14882e2caf59SThomas Veerman 	    case Not:
14892e2caf59SThomas Veerman 		/*
14902e2caf59SThomas Veerman 		 * Nothing special here -- targets can be empty if it wants.
14912e2caf59SThomas Veerman 		 */
14922e2caf59SThomas Veerman 		break;
14932e2caf59SThomas Veerman 	}
14942e2caf59SThomas Veerman     }
14952e2caf59SThomas Veerman 
14962e2caf59SThomas Veerman     /*
14972e2caf59SThomas Veerman      * Have now parsed all the target names. Must parse the operator next. The
14982e2caf59SThomas Veerman      * result is left in  op .
14992e2caf59SThomas Veerman      */
15002e2caf59SThomas Veerman     if (*cp == '!') {
15012e2caf59SThomas Veerman 	op = OP_FORCE;
15022e2caf59SThomas Veerman     } else if (*cp == ':') {
15032e2caf59SThomas Veerman 	if (cp[1] == ':') {
15042e2caf59SThomas Veerman 	    op = OP_DOUBLEDEP;
15052e2caf59SThomas Veerman 	    cp++;
15062e2caf59SThomas Veerman 	} else {
15072e2caf59SThomas Veerman 	    op = OP_DEPENDS;
15082e2caf59SThomas Veerman 	}
15092e2caf59SThomas Veerman     } else {
15102e2caf59SThomas Veerman 	Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive"
15112e2caf59SThomas Veerman 		    : "Missing dependency operator");
15122e2caf59SThomas Veerman 	goto out;
15132e2caf59SThomas Veerman     }
15142e2caf59SThomas Veerman 
1515*0a6a1f1dSLionel Sambuc     /* Advance beyond the operator */
1516*0a6a1f1dSLionel Sambuc     cp++;
15172e2caf59SThomas Veerman 
1518*0a6a1f1dSLionel Sambuc     /*
1519*0a6a1f1dSLionel Sambuc      * Apply the operator to the target. This is how we remember which
1520*0a6a1f1dSLionel Sambuc      * operator a target was defined with. It fails if the operator
1521*0a6a1f1dSLionel Sambuc      * used isn't consistent across all references.
1522*0a6a1f1dSLionel Sambuc      */
15232e2caf59SThomas Veerman     Lst_ForEach(targets, ParseDoOp, &op);
15242e2caf59SThomas Veerman 
15252e2caf59SThomas Veerman     /*
1526*0a6a1f1dSLionel Sambuc      * Onward to the sources.
1527*0a6a1f1dSLionel Sambuc      *
1528*0a6a1f1dSLionel Sambuc      * LINE will now point to the first source word, if any, or the
1529*0a6a1f1dSLionel Sambuc      * end of the string if not.
15302e2caf59SThomas Veerman      */
15312e2caf59SThomas Veerman     while (*cp && isspace ((unsigned char)*cp)) {
15322e2caf59SThomas Veerman 	cp++;
15332e2caf59SThomas Veerman     }
15342e2caf59SThomas Veerman     line = cp;
15352e2caf59SThomas Veerman 
15362e2caf59SThomas Veerman     /*
15372e2caf59SThomas Veerman      * Several special targets take different actions if present with no
15382e2caf59SThomas Veerman      * sources:
15392e2caf59SThomas Veerman      *	a .SUFFIXES line with no sources clears out all old suffixes
15402e2caf59SThomas Veerman      *	a .PRECIOUS line makes all targets precious
15412e2caf59SThomas Veerman      *	a .IGNORE line ignores errors for all targets
15422e2caf59SThomas Veerman      *	a .SILENT line creates silence when making all targets
15432e2caf59SThomas Veerman      *	a .PATH removes all directories from the search path(s).
15442e2caf59SThomas Veerman      */
15452e2caf59SThomas Veerman     if (!*line) {
15462e2caf59SThomas Veerman 	switch (specType) {
15472e2caf59SThomas Veerman 	    case Suffixes:
15482e2caf59SThomas Veerman 		Suff_ClearSuffixes();
15492e2caf59SThomas Veerman 		break;
15502e2caf59SThomas Veerman 	    case Precious:
15512e2caf59SThomas Veerman 		allPrecious = TRUE;
15522e2caf59SThomas Veerman 		break;
15532e2caf59SThomas Veerman 	    case Ignore:
15542e2caf59SThomas Veerman 		ignoreErrors = TRUE;
15552e2caf59SThomas Veerman 		break;
15562e2caf59SThomas Veerman 	    case Silent:
15572e2caf59SThomas Veerman 		beSilent = TRUE;
15582e2caf59SThomas Veerman 		break;
15592e2caf59SThomas Veerman 	    case ExPath:
15602e2caf59SThomas Veerman 		Lst_ForEach(paths, ParseClearPath, NULL);
15612e2caf59SThomas Veerman 		Dir_SetPATH();
15622e2caf59SThomas Veerman 		break;
15632e2caf59SThomas Veerman #ifdef POSIX
15642e2caf59SThomas Veerman             case Posix:
15652e2caf59SThomas Veerman                 Var_Set("%POSIX", "1003.2", VAR_GLOBAL, 0);
15662e2caf59SThomas Veerman                 break;
15672e2caf59SThomas Veerman #endif
15682e2caf59SThomas Veerman 	    default:
15692e2caf59SThomas Veerman 		break;
15702e2caf59SThomas Veerman 	}
15712e2caf59SThomas Veerman     } else if (specType == MFlags) {
15722e2caf59SThomas Veerman 	/*
15732e2caf59SThomas Veerman 	 * Call on functions in main.c to deal with these arguments and
15742e2caf59SThomas Veerman 	 * set the initial character to a null-character so the loop to
15752e2caf59SThomas Veerman 	 * get sources won't get anything
15762e2caf59SThomas Veerman 	 */
15772e2caf59SThomas Veerman 	Main_ParseArgLine(line);
15782e2caf59SThomas Veerman 	*line = '\0';
15792e2caf59SThomas Veerman     } else if (specType == ExShell) {
15802e2caf59SThomas Veerman 	if (Job_ParseShell(line) != SUCCESS) {
15812e2caf59SThomas Veerman 	    Parse_Error(PARSE_FATAL, "improper shell specification");
15822e2caf59SThomas Veerman 	    goto out;
15832e2caf59SThomas Veerman 	}
15842e2caf59SThomas Veerman 	*line = '\0';
15852e2caf59SThomas Veerman     } else if ((specType == NotParallel) || (specType == SingleShell)) {
15862e2caf59SThomas Veerman 	*line = '\0';
15872e2caf59SThomas Veerman     }
15882e2caf59SThomas Veerman 
15892e2caf59SThomas Veerman     /*
15902e2caf59SThomas Veerman      * NOW GO FOR THE SOURCES
15912e2caf59SThomas Veerman      */
15922e2caf59SThomas Veerman     if ((specType == Suffixes) || (specType == ExPath) ||
15932e2caf59SThomas Veerman 	(specType == Includes) || (specType == Libs) ||
15942e2caf59SThomas Veerman 	(specType == Null) || (specType == ExObjdir))
15952e2caf59SThomas Veerman     {
15962e2caf59SThomas Veerman 	while (*line) {
15972e2caf59SThomas Veerman 	    /*
15982e2caf59SThomas Veerman 	     * If the target was one that doesn't take files as its sources
15992e2caf59SThomas Veerman 	     * but takes something like suffixes, we take each
16002e2caf59SThomas Veerman 	     * space-separated word on the line as a something and deal
16012e2caf59SThomas Veerman 	     * with it accordingly.
16022e2caf59SThomas Veerman 	     *
16032e2caf59SThomas Veerman 	     * If the target was .SUFFIXES, we take each source as a
16042e2caf59SThomas Veerman 	     * suffix and add it to the list of suffixes maintained by the
16052e2caf59SThomas Veerman 	     * Suff module.
16062e2caf59SThomas Veerman 	     *
16072e2caf59SThomas Veerman 	     * If the target was a .PATH, we add the source as a directory
16082e2caf59SThomas Veerman 	     * to search on the search path.
16092e2caf59SThomas Veerman 	     *
16102e2caf59SThomas Veerman 	     * If it was .INCLUDES, the source is taken to be the suffix of
16112e2caf59SThomas Veerman 	     * files which will be #included and whose search path should
16122e2caf59SThomas Veerman 	     * be present in the .INCLUDES variable.
16132e2caf59SThomas Veerman 	     *
16142e2caf59SThomas Veerman 	     * If it was .LIBS, the source is taken to be the suffix of
16152e2caf59SThomas Veerman 	     * files which are considered libraries and whose search path
16162e2caf59SThomas Veerman 	     * should be present in the .LIBS variable.
16172e2caf59SThomas Veerman 	     *
16182e2caf59SThomas Veerman 	     * If it was .NULL, the source is the suffix to use when a file
16192e2caf59SThomas Veerman 	     * has no valid suffix.
16202e2caf59SThomas Veerman 	     *
16212e2caf59SThomas Veerman 	     * If it was .OBJDIR, the source is a new definition for .OBJDIR,
16222e2caf59SThomas Veerman 	     * and will cause make to do a new chdir to that path.
16232e2caf59SThomas Veerman 	     */
16242e2caf59SThomas Veerman 	    while (*cp && !isspace ((unsigned char)*cp)) {
16252e2caf59SThomas Veerman 		cp++;
16262e2caf59SThomas Veerman 	    }
16272e2caf59SThomas Veerman 	    savec = *cp;
16282e2caf59SThomas Veerman 	    *cp = '\0';
16292e2caf59SThomas Veerman 	    switch (specType) {
16302e2caf59SThomas Veerman 		case Suffixes:
16312e2caf59SThomas Veerman 		    Suff_AddSuffix(line, &mainNode);
16322e2caf59SThomas Veerman 		    break;
16332e2caf59SThomas Veerman 		case ExPath:
16342e2caf59SThomas Veerman 		    Lst_ForEach(paths, ParseAddDir, line);
16352e2caf59SThomas Veerman 		    break;
16362e2caf59SThomas Veerman 		case Includes:
16372e2caf59SThomas Veerman 		    Suff_AddInclude(line);
16382e2caf59SThomas Veerman 		    break;
16392e2caf59SThomas Veerman 		case Libs:
16402e2caf59SThomas Veerman 		    Suff_AddLib(line);
16412e2caf59SThomas Veerman 		    break;
16422e2caf59SThomas Veerman 		case Null:
16432e2caf59SThomas Veerman 		    Suff_SetNull(line);
16442e2caf59SThomas Veerman 		    break;
16452e2caf59SThomas Veerman 		case ExObjdir:
16462e2caf59SThomas Veerman 		    Main_SetObjdir(line);
16472e2caf59SThomas Veerman 		    break;
16482e2caf59SThomas Veerman 		default:
16492e2caf59SThomas Veerman 		    break;
16502e2caf59SThomas Veerman 	    }
16512e2caf59SThomas Veerman 	    *cp = savec;
16522e2caf59SThomas Veerman 	    if (savec != '\0') {
16532e2caf59SThomas Veerman 		cp++;
16542e2caf59SThomas Veerman 	    }
16552e2caf59SThomas Veerman 	    while (*cp && isspace ((unsigned char)*cp)) {
16562e2caf59SThomas Veerman 		cp++;
16572e2caf59SThomas Veerman 	    }
16582e2caf59SThomas Veerman 	    line = cp;
16592e2caf59SThomas Veerman 	}
16602e2caf59SThomas Veerman 	if (paths) {
16612e2caf59SThomas Veerman 	    Lst_Destroy(paths, NULL);
16622e2caf59SThomas Veerman 	}
16632e2caf59SThomas Veerman 	if (specType == ExPath)
16642e2caf59SThomas Veerman 	    Dir_SetPATH();
16652e2caf59SThomas Veerman     } else {
16662e2caf59SThomas Veerman 	while (*line) {
16672e2caf59SThomas Veerman 	    /*
16682e2caf59SThomas Veerman 	     * The targets take real sources, so we must beware of archive
16692e2caf59SThomas Veerman 	     * specifications (i.e. things with left parentheses in them)
16702e2caf59SThomas Veerman 	     * and handle them accordingly.
16712e2caf59SThomas Veerman 	     */
16722e2caf59SThomas Veerman 	    for (; *cp && !isspace ((unsigned char)*cp); cp++) {
16732e2caf59SThomas Veerman 		if ((*cp == LPAREN) && (cp > line) && (cp[-1] != '$')) {
16742e2caf59SThomas Veerman 		    /*
16752e2caf59SThomas Veerman 		     * Only stop for a left parenthesis if it isn't at the
16762e2caf59SThomas Veerman 		     * start of a word (that'll be for variable changes
16772e2caf59SThomas Veerman 		     * later) and isn't preceded by a dollar sign (a dynamic
16782e2caf59SThomas Veerman 		     * source).
16792e2caf59SThomas Veerman 		     */
16802e2caf59SThomas Veerman 		    break;
16812e2caf59SThomas Veerman 		}
16822e2caf59SThomas Veerman 	    }
16832e2caf59SThomas Veerman 
16842e2caf59SThomas Veerman 	    if (*cp == LPAREN) {
16852e2caf59SThomas Veerman 		sources = Lst_Init(FALSE);
16862e2caf59SThomas Veerman 		if (Arch_ParseArchive(&line, sources, VAR_CMD) != SUCCESS) {
16872e2caf59SThomas Veerman 		    Parse_Error(PARSE_FATAL,
16882e2caf59SThomas Veerman 				 "Error in source archive spec \"%s\"", line);
16892e2caf59SThomas Veerman 		    goto out;
16902e2caf59SThomas Veerman 		}
16912e2caf59SThomas Veerman 
16922e2caf59SThomas Veerman 		while (!Lst_IsEmpty (sources)) {
16932e2caf59SThomas Veerman 		    gn = (GNode *)Lst_DeQueue(sources);
16942e2caf59SThomas Veerman 		    ParseDoSrc(tOp, gn->name);
16952e2caf59SThomas Veerman 		}
16962e2caf59SThomas Veerman 		Lst_Destroy(sources, NULL);
16972e2caf59SThomas Veerman 		cp = line;
16982e2caf59SThomas Veerman 	    } else {
16992e2caf59SThomas Veerman 		if (*cp) {
17002e2caf59SThomas Veerman 		    *cp = '\0';
17012e2caf59SThomas Veerman 		    cp += 1;
17022e2caf59SThomas Veerman 		}
17032e2caf59SThomas Veerman 
17042e2caf59SThomas Veerman 		ParseDoSrc(tOp, line);
17052e2caf59SThomas Veerman 	    }
17062e2caf59SThomas Veerman 	    while (*cp && isspace ((unsigned char)*cp)) {
17072e2caf59SThomas Veerman 		cp++;
17082e2caf59SThomas Veerman 	    }
17092e2caf59SThomas Veerman 	    line = cp;
17102e2caf59SThomas Veerman 	}
17112e2caf59SThomas Veerman     }
17122e2caf59SThomas Veerman 
17132e2caf59SThomas Veerman     if (mainNode == NULL) {
17142e2caf59SThomas Veerman 	/*
17152e2caf59SThomas Veerman 	 * If we have yet to decide on a main target to make, in the
17162e2caf59SThomas Veerman 	 * absence of any user input, we want the first target on
17172e2caf59SThomas Veerman 	 * the first dependency line that is actually a real target
17182e2caf59SThomas Veerman 	 * (i.e. isn't a .USE or .EXEC rule) to be made.
17192e2caf59SThomas Veerman 	 */
17202e2caf59SThomas Veerman 	Lst_ForEach(targets, ParseFindMain, NULL);
17212e2caf59SThomas Veerman     }
17222e2caf59SThomas Veerman 
17232e2caf59SThomas Veerman out:
17242e2caf59SThomas Veerman     if (curTargs)
17252e2caf59SThomas Veerman 	    Lst_Destroy(curTargs, NULL);
17262e2caf59SThomas Veerman }
17272e2caf59SThomas Veerman 
17282e2caf59SThomas Veerman /*-
17292e2caf59SThomas Veerman  *---------------------------------------------------------------------
17302e2caf59SThomas Veerman  * Parse_IsVar  --
17312e2caf59SThomas Veerman  *	Return TRUE if the passed line is a variable assignment. A variable
17322e2caf59SThomas Veerman  *	assignment consists of a single word followed by optional whitespace
17332e2caf59SThomas Veerman  *	followed by either a += or an = operator.
17342e2caf59SThomas Veerman  *	This function is used both by the Parse_File function and main when
17352e2caf59SThomas Veerman  *	parsing the command-line arguments.
17362e2caf59SThomas Veerman  *
17372e2caf59SThomas Veerman  * Input:
17382e2caf59SThomas Veerman  *	line		the line to check
17392e2caf59SThomas Veerman  *
17402e2caf59SThomas Veerman  * Results:
17412e2caf59SThomas Veerman  *	TRUE if it is. FALSE if it ain't
17422e2caf59SThomas Veerman  *
17432e2caf59SThomas Veerman  * Side Effects:
17442e2caf59SThomas Veerman  *	none
17452e2caf59SThomas Veerman  *---------------------------------------------------------------------
17462e2caf59SThomas Veerman  */
17472e2caf59SThomas Veerman Boolean
Parse_IsVar(char * line)17482e2caf59SThomas Veerman Parse_IsVar(char *line)
17492e2caf59SThomas Veerman {
17502e2caf59SThomas Veerman     Boolean wasSpace = FALSE;	/* set TRUE if found a space */
17512e2caf59SThomas Veerman     char ch;
17522e2caf59SThomas Veerman     int level = 0;
17532e2caf59SThomas Veerman #define ISEQOPERATOR(c) \
17542e2caf59SThomas Veerman 	(((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!'))
17552e2caf59SThomas Veerman 
17562e2caf59SThomas Veerman     /*
17572e2caf59SThomas Veerman      * Skip to variable name
17582e2caf59SThomas Veerman      */
17592e2caf59SThomas Veerman     for (;(*line == ' ') || (*line == '\t'); line++)
17602e2caf59SThomas Veerman 	continue;
17612e2caf59SThomas Veerman 
17622e2caf59SThomas Veerman     /* Scan for one of the assignment operators outside a variable expansion */
17632e2caf59SThomas Veerman     while ((ch = *line++) != 0) {
17642e2caf59SThomas Veerman 	if (ch == '(' || ch == '{') {
17652e2caf59SThomas Veerman 	    level++;
17662e2caf59SThomas Veerman 	    continue;
17672e2caf59SThomas Veerman 	}
17682e2caf59SThomas Veerman 	if (ch == ')' || ch == '}') {
17692e2caf59SThomas Veerman 	    level--;
17702e2caf59SThomas Veerman 	    continue;
17712e2caf59SThomas Veerman 	}
17722e2caf59SThomas Veerman 	if (level != 0)
17732e2caf59SThomas Veerman 	    continue;
17742e2caf59SThomas Veerman 	while (ch == ' ' || ch == '\t') {
17752e2caf59SThomas Veerman 	    ch = *line++;
17762e2caf59SThomas Veerman 	    wasSpace = TRUE;
17772e2caf59SThomas Veerman 	}
177884d9c625SLionel Sambuc #ifdef SUNSHCMD
177984d9c625SLionel Sambuc 	if (ch == ':' && strncmp(line, "sh", 2) == 0) {
178084d9c625SLionel Sambuc 	    line += 2;
178184d9c625SLionel Sambuc 	    continue;
178284d9c625SLionel Sambuc 	}
178384d9c625SLionel Sambuc #endif
17842e2caf59SThomas Veerman 	if (ch == '=')
17852e2caf59SThomas Veerman 	    return TRUE;
17862e2caf59SThomas Veerman 	if (*line == '=' && ISEQOPERATOR(ch))
17872e2caf59SThomas Veerman 	    return TRUE;
17882e2caf59SThomas Veerman 	if (wasSpace)
17892e2caf59SThomas Veerman 	    return FALSE;
17902e2caf59SThomas Veerman     }
17912e2caf59SThomas Veerman 
17922e2caf59SThomas Veerman     return FALSE;
17932e2caf59SThomas Veerman }
17942e2caf59SThomas Veerman 
17952e2caf59SThomas Veerman /*-
17962e2caf59SThomas Veerman  *---------------------------------------------------------------------
17972e2caf59SThomas Veerman  * Parse_DoVar  --
17982e2caf59SThomas Veerman  *	Take the variable assignment in the passed line and do it in the
17992e2caf59SThomas Veerman  *	global context.
18002e2caf59SThomas Veerman  *
18012e2caf59SThomas Veerman  *	Note: There is a lexical ambiguity with assignment modifier characters
18022e2caf59SThomas Veerman  *	in variable names. This routine interprets the character before the =
18032e2caf59SThomas Veerman  *	as a modifier. Therefore, an assignment like
18042e2caf59SThomas Veerman  *	    C++=/usr/bin/CC
18052e2caf59SThomas Veerman  *	is interpreted as "C+ +=" instead of "C++ =".
18062e2caf59SThomas Veerman  *
18072e2caf59SThomas Veerman  * Input:
18082e2caf59SThomas Veerman  *	line		a line guaranteed to be a variable assignment.
18092e2caf59SThomas Veerman  *			This reduces error checks
18102e2caf59SThomas Veerman  *	ctxt		Context in which to do the assignment
18112e2caf59SThomas Veerman  *
18122e2caf59SThomas Veerman  * Results:
18132e2caf59SThomas Veerman  *	none
18142e2caf59SThomas Veerman  *
18152e2caf59SThomas Veerman  * Side Effects:
18162e2caf59SThomas Veerman  *	the variable structure of the given variable name is altered in the
18172e2caf59SThomas Veerman  *	global context.
18182e2caf59SThomas Veerman  *---------------------------------------------------------------------
18192e2caf59SThomas Veerman  */
18202e2caf59SThomas Veerman void
Parse_DoVar(char * line,GNode * ctxt)18212e2caf59SThomas Veerman Parse_DoVar(char *line, GNode *ctxt)
18222e2caf59SThomas Veerman {
18232e2caf59SThomas Veerman     char	   *cp;	/* pointer into line */
18242e2caf59SThomas Veerman     enum {
18252e2caf59SThomas Veerman 	VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL
18262e2caf59SThomas Veerman     }	    	    type;   	/* Type of assignment */
18272e2caf59SThomas Veerman     char            *opc;	/* ptr to operator character to
18282e2caf59SThomas Veerman 				 * null-terminate the variable name */
18292e2caf59SThomas Veerman     Boolean	   freeCp = FALSE; /* TRUE if cp needs to be freed,
18302e2caf59SThomas Veerman 				    * i.e. if any variable expansion was
18312e2caf59SThomas Veerman 				    * performed */
18322e2caf59SThomas Veerman     int depth;
18332e2caf59SThomas Veerman 
18342e2caf59SThomas Veerman     /*
18352e2caf59SThomas Veerman      * Skip to variable name
18362e2caf59SThomas Veerman      */
18372e2caf59SThomas Veerman     while ((*line == ' ') || (*line == '\t')) {
18382e2caf59SThomas Veerman 	line++;
18392e2caf59SThomas Veerman     }
18402e2caf59SThomas Veerman 
18412e2caf59SThomas Veerman     /*
18422e2caf59SThomas Veerman      * Skip to operator character, nulling out whitespace as we go
18432e2caf59SThomas Veerman      * XXX Rather than counting () and {} we should look for $ and
18442e2caf59SThomas Veerman      * then expand the variable.
18452e2caf59SThomas Veerman      */
18462e2caf59SThomas Veerman     for (depth = 0, cp = line + 1; depth != 0 || *cp != '='; cp++) {
18472e2caf59SThomas Veerman 	if (*cp == '(' || *cp == '{') {
18482e2caf59SThomas Veerman 	    depth++;
18492e2caf59SThomas Veerman 	    continue;
18502e2caf59SThomas Veerman 	}
18512e2caf59SThomas Veerman 	if (*cp == ')' || *cp == '}') {
18522e2caf59SThomas Veerman 	    depth--;
18532e2caf59SThomas Veerman 	    continue;
18542e2caf59SThomas Veerman 	}
18552e2caf59SThomas Veerman 	if (depth == 0 && isspace ((unsigned char)*cp)) {
18562e2caf59SThomas Veerman 	    *cp = '\0';
18572e2caf59SThomas Veerman 	}
18582e2caf59SThomas Veerman     }
18592e2caf59SThomas Veerman     opc = cp-1;		/* operator is the previous character */
18602e2caf59SThomas Veerman     *cp++ = '\0';	/* nuke the = */
18612e2caf59SThomas Veerman 
18622e2caf59SThomas Veerman     /*
18632e2caf59SThomas Veerman      * Check operator type
18642e2caf59SThomas Veerman      */
18652e2caf59SThomas Veerman     switch (*opc) {
18662e2caf59SThomas Veerman 	case '+':
18672e2caf59SThomas Veerman 	    type = VAR_APPEND;
18682e2caf59SThomas Veerman 	    *opc = '\0';
18692e2caf59SThomas Veerman 	    break;
18702e2caf59SThomas Veerman 
18712e2caf59SThomas Veerman 	case '?':
18722e2caf59SThomas Veerman 	    /*
18732e2caf59SThomas Veerman 	     * If the variable already has a value, we don't do anything.
18742e2caf59SThomas Veerman 	     */
18752e2caf59SThomas Veerman 	    *opc = '\0';
18762e2caf59SThomas Veerman 	    if (Var_Exists(line, ctxt)) {
18772e2caf59SThomas Veerman 		return;
18782e2caf59SThomas Veerman 	    } else {
18792e2caf59SThomas Veerman 		type = VAR_NORMAL;
18802e2caf59SThomas Veerman 	    }
18812e2caf59SThomas Veerman 	    break;
18822e2caf59SThomas Veerman 
18832e2caf59SThomas Veerman 	case ':':
18842e2caf59SThomas Veerman 	    type = VAR_SUBST;
18852e2caf59SThomas Veerman 	    *opc = '\0';
18862e2caf59SThomas Veerman 	    break;
18872e2caf59SThomas Veerman 
18882e2caf59SThomas Veerman 	case '!':
18892e2caf59SThomas Veerman 	    type = VAR_SHELL;
18902e2caf59SThomas Veerman 	    *opc = '\0';
18912e2caf59SThomas Veerman 	    break;
18922e2caf59SThomas Veerman 
18932e2caf59SThomas Veerman 	default:
18942e2caf59SThomas Veerman #ifdef SUNSHCMD
18952e2caf59SThomas Veerman 	    while (opc > line && *opc != ':')
18962e2caf59SThomas Veerman 		opc--;
18972e2caf59SThomas Veerman 
18982e2caf59SThomas Veerman 	    if (strncmp(opc, ":sh", 3) == 0) {
18992e2caf59SThomas Veerman 		type = VAR_SHELL;
19002e2caf59SThomas Veerman 		*opc = '\0';
19012e2caf59SThomas Veerman 		break;
19022e2caf59SThomas Veerman 	    }
19032e2caf59SThomas Veerman #endif
19042e2caf59SThomas Veerman 	    type = VAR_NORMAL;
19052e2caf59SThomas Veerman 	    break;
19062e2caf59SThomas Veerman     }
19072e2caf59SThomas Veerman 
19082e2caf59SThomas Veerman     while (isspace ((unsigned char)*cp)) {
19092e2caf59SThomas Veerman 	cp++;
19102e2caf59SThomas Veerman     }
19112e2caf59SThomas Veerman 
19122e2caf59SThomas Veerman     if (type == VAR_APPEND) {
19132e2caf59SThomas Veerman 	Var_Append(line, cp, ctxt);
19142e2caf59SThomas Veerman     } else if (type == VAR_SUBST) {
19152e2caf59SThomas Veerman 	/*
19162e2caf59SThomas Veerman 	 * Allow variables in the old value to be undefined, but leave their
19172e2caf59SThomas Veerman 	 * invocation alone -- this is done by forcing oldVars to be false.
19182e2caf59SThomas Veerman 	 * XXX: This can cause recursive variables, but that's not hard to do,
19192e2caf59SThomas Veerman 	 * and this allows someone to do something like
19202e2caf59SThomas Veerman 	 *
19212e2caf59SThomas Veerman 	 *  CFLAGS = $(.INCLUDES)
19222e2caf59SThomas Veerman 	 *  CFLAGS := -I.. $(CFLAGS)
19232e2caf59SThomas Veerman 	 *
19242e2caf59SThomas Veerman 	 * And not get an error.
19252e2caf59SThomas Veerman 	 */
19262e2caf59SThomas Veerman 	Boolean	  oldOldVars = oldVars;
19272e2caf59SThomas Veerman 
19282e2caf59SThomas Veerman 	oldVars = FALSE;
19292e2caf59SThomas Veerman 
19302e2caf59SThomas Veerman 	/*
19312e2caf59SThomas Veerman 	 * make sure that we set the variable the first time to nothing
19322e2caf59SThomas Veerman 	 * so that it gets substituted!
19332e2caf59SThomas Veerman 	 */
19342e2caf59SThomas Veerman 	if (!Var_Exists(line, ctxt))
19352e2caf59SThomas Veerman 	    Var_Set(line, "", ctxt, 0);
19362e2caf59SThomas Veerman 
19372e2caf59SThomas Veerman 	cp = Var_Subst(NULL, cp, ctxt, FALSE);
19382e2caf59SThomas Veerman 	oldVars = oldOldVars;
19392e2caf59SThomas Veerman 	freeCp = TRUE;
19402e2caf59SThomas Veerman 
19412e2caf59SThomas Veerman 	Var_Set(line, cp, ctxt, 0);
19422e2caf59SThomas Veerman     } else if (type == VAR_SHELL) {
19432e2caf59SThomas Veerman 	char *res;
19442e2caf59SThomas Veerman 	const char *error;
19452e2caf59SThomas Veerman 
19462e2caf59SThomas Veerman 	if (strchr(cp, '$') != NULL) {
19472e2caf59SThomas Veerman 	    /*
19482e2caf59SThomas Veerman 	     * There's a dollar sign in the command, so perform variable
19492e2caf59SThomas Veerman 	     * expansion on the whole thing. The resulting string will need
19502e2caf59SThomas Veerman 	     * freeing when we're done, so set freeCmd to TRUE.
19512e2caf59SThomas Veerman 	     */
19522e2caf59SThomas Veerman 	    cp = Var_Subst(NULL, cp, VAR_CMD, TRUE);
19532e2caf59SThomas Veerman 	    freeCp = TRUE;
19542e2caf59SThomas Veerman 	}
19552e2caf59SThomas Veerman 
19562e2caf59SThomas Veerman 	res = Cmd_Exec(cp, &error);
19572e2caf59SThomas Veerman 	Var_Set(line, res, ctxt, 0);
19582e2caf59SThomas Veerman 	free(res);
19592e2caf59SThomas Veerman 
19602e2caf59SThomas Veerman 	if (error)
19612e2caf59SThomas Veerman 	    Parse_Error(PARSE_WARNING, error, cp);
19622e2caf59SThomas Veerman     } else {
19632e2caf59SThomas Veerman 	/*
19642e2caf59SThomas Veerman 	 * Normal assignment -- just do it.
19652e2caf59SThomas Veerman 	 */
19662e2caf59SThomas Veerman 	Var_Set(line, cp, ctxt, 0);
19672e2caf59SThomas Veerman     }
19682e2caf59SThomas Veerman     if (strcmp(line, MAKEOVERRIDES) == 0)
19692e2caf59SThomas Veerman 	Main_ExportMAKEFLAGS(FALSE);	/* re-export MAKEFLAGS */
19702e2caf59SThomas Veerman     else if (strcmp(line, ".CURDIR") == 0) {
19712e2caf59SThomas Veerman 	/*
19722e2caf59SThomas Veerman 	 * Somone is being (too?) clever...
19732e2caf59SThomas Veerman 	 * Let's pretend they know what they are doing and
19742e2caf59SThomas Veerman 	 * re-initialize the 'cur' Path.
19752e2caf59SThomas Veerman 	 */
19762e2caf59SThomas Veerman 	Dir_InitCur(cp);
19772e2caf59SThomas Veerman 	Dir_SetPATH();
19782e2caf59SThomas Veerman     } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) {
19792e2caf59SThomas Veerman 	Job_SetPrefix();
19802e2caf59SThomas Veerman     } else if (strcmp(line, MAKE_EXPORTED) == 0) {
19812e2caf59SThomas Veerman 	Var_Export(cp, 0);
19822e2caf59SThomas Veerman     }
19832e2caf59SThomas Veerman     if (freeCp)
19842e2caf59SThomas Veerman 	free(cp);
19852e2caf59SThomas Veerman }
19862e2caf59SThomas Veerman 
19872e2caf59SThomas Veerman 
1988*0a6a1f1dSLionel Sambuc /*
1989*0a6a1f1dSLionel Sambuc  * ParseMaybeSubMake --
1990*0a6a1f1dSLionel Sambuc  * 	Scan the command string to see if it a possible submake node
1991*0a6a1f1dSLionel Sambuc  * Input:
1992*0a6a1f1dSLionel Sambuc  *	cmd		the command to scan
1993*0a6a1f1dSLionel Sambuc  * Results:
1994*0a6a1f1dSLionel Sambuc  *	TRUE if the command is possibly a submake, FALSE if not.
1995*0a6a1f1dSLionel Sambuc  */
1996*0a6a1f1dSLionel Sambuc static Boolean
ParseMaybeSubMake(const char * cmd)1997*0a6a1f1dSLionel Sambuc ParseMaybeSubMake(const char *cmd)
1998*0a6a1f1dSLionel Sambuc {
1999*0a6a1f1dSLionel Sambuc     size_t i;
2000*0a6a1f1dSLionel Sambuc     static struct {
2001*0a6a1f1dSLionel Sambuc 	const char *name;
2002*0a6a1f1dSLionel Sambuc 	size_t len;
2003*0a6a1f1dSLionel Sambuc     } vals[] = {
2004*0a6a1f1dSLionel Sambuc #define MKV(A)	{	A, sizeof(A) - 1	}
2005*0a6a1f1dSLionel Sambuc 	MKV("${MAKE}"),
2006*0a6a1f1dSLionel Sambuc 	MKV("${.MAKE}"),
2007*0a6a1f1dSLionel Sambuc 	MKV("$(MAKE)"),
2008*0a6a1f1dSLionel Sambuc 	MKV("$(.MAKE)"),
2009*0a6a1f1dSLionel Sambuc 	MKV("make"),
2010*0a6a1f1dSLionel Sambuc     };
2011*0a6a1f1dSLionel Sambuc     for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
2012*0a6a1f1dSLionel Sambuc 	char *ptr;
2013*0a6a1f1dSLionel Sambuc 	if ((ptr = strstr(cmd, vals[i].name)) == NULL)
2014*0a6a1f1dSLionel Sambuc 	    continue;
2015*0a6a1f1dSLionel Sambuc 	if ((ptr == cmd || !isalnum((unsigned char)ptr[-1]))
2016*0a6a1f1dSLionel Sambuc 	    && !isalnum((unsigned char)ptr[vals[i].len]))
2017*0a6a1f1dSLionel Sambuc 	    return TRUE;
2018*0a6a1f1dSLionel Sambuc     }
2019*0a6a1f1dSLionel Sambuc     return FALSE;
2020*0a6a1f1dSLionel Sambuc }
2021*0a6a1f1dSLionel Sambuc 
20222e2caf59SThomas Veerman /*-
20232e2caf59SThomas Veerman  * ParseAddCmd  --
20242e2caf59SThomas Veerman  *	Lst_ForEach function to add a command line to all targets
20252e2caf59SThomas Veerman  *
20262e2caf59SThomas Veerman  * Input:
20272e2caf59SThomas Veerman  *	gnp		the node to which the command is to be added
20282e2caf59SThomas Veerman  *	cmd		the command to add
20292e2caf59SThomas Veerman  *
20302e2caf59SThomas Veerman  * Results:
20312e2caf59SThomas Veerman  *	Always 0
20322e2caf59SThomas Veerman  *
20332e2caf59SThomas Veerman  * Side Effects:
2034*0a6a1f1dSLionel Sambuc  *	A new element is added to the commands list of the node,
2035*0a6a1f1dSLionel Sambuc  *	and the node can be marked as a submake node if the command is
2036*0a6a1f1dSLionel Sambuc  *	determined to be that.
20372e2caf59SThomas Veerman  */
20382e2caf59SThomas Veerman static int
ParseAddCmd(void * gnp,void * cmd)20392e2caf59SThomas Veerman ParseAddCmd(void *gnp, void *cmd)
20402e2caf59SThomas Veerman {
20412e2caf59SThomas Veerman     GNode *gn = (GNode *)gnp;
20422e2caf59SThomas Veerman 
20432e2caf59SThomas Veerman     /* Add to last (ie current) cohort for :: targets */
20442e2caf59SThomas Veerman     if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts))
20452e2caf59SThomas Veerman 	gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts));
20462e2caf59SThomas Veerman 
20472e2caf59SThomas Veerman     /* if target already supplied, ignore commands */
20482e2caf59SThomas Veerman     if (!(gn->type & OP_HAS_COMMANDS)) {
20492e2caf59SThomas Veerman 	(void)Lst_AtEnd(gn->commands, cmd);
2050*0a6a1f1dSLionel Sambuc 	if (ParseMaybeSubMake(cmd))
2051*0a6a1f1dSLionel Sambuc 	    gn->type |= OP_SUBMAKE;
20522e2caf59SThomas Veerman 	ParseMark(gn);
20532e2caf59SThomas Veerman     } else {
20542e2caf59SThomas Veerman #ifdef notyet
20552e2caf59SThomas Veerman 	/* XXX: We cannot do this until we fix the tree */
20562e2caf59SThomas Veerman 	(void)Lst_AtEnd(gn->commands, cmd);
20572e2caf59SThomas Veerman 	Parse_Error(PARSE_WARNING,
20582e2caf59SThomas Veerman 		     "overriding commands for target \"%s\"; "
20592e2caf59SThomas Veerman 		     "previous commands defined at %s: %d ignored",
20602e2caf59SThomas Veerman 		     gn->name, gn->fname, gn->lineno);
20612e2caf59SThomas Veerman #else
20622e2caf59SThomas Veerman 	Parse_Error(PARSE_WARNING,
20632e2caf59SThomas Veerman 		     "duplicate script for target \"%s\" ignored",
20642e2caf59SThomas Veerman 		     gn->name);
20652e2caf59SThomas Veerman 	ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING,
20662e2caf59SThomas Veerman 			    "using previous script for \"%s\" defined here",
20672e2caf59SThomas Veerman 			    gn->name);
20682e2caf59SThomas Veerman #endif
20692e2caf59SThomas Veerman     }
20702e2caf59SThomas Veerman     return(0);
20712e2caf59SThomas Veerman }
20722e2caf59SThomas Veerman 
20732e2caf59SThomas Veerman /*-
20742e2caf59SThomas Veerman  *-----------------------------------------------------------------------
20752e2caf59SThomas Veerman  * ParseHasCommands --
20762e2caf59SThomas Veerman  *	Callback procedure for Parse_File when destroying the list of
20772e2caf59SThomas Veerman  *	targets on the last dependency line. Marks a target as already
20782e2caf59SThomas Veerman  *	having commands if it does, to keep from having shell commands
20792e2caf59SThomas Veerman  *	on multiple dependency lines.
20802e2caf59SThomas Veerman  *
20812e2caf59SThomas Veerman  * Input:
20822e2caf59SThomas Veerman  *	gnp		Node to examine
20832e2caf59SThomas Veerman  *
20842e2caf59SThomas Veerman  * Results:
20852e2caf59SThomas Veerman  *	None
20862e2caf59SThomas Veerman  *
20872e2caf59SThomas Veerman  * Side Effects:
20882e2caf59SThomas Veerman  *	OP_HAS_COMMANDS may be set for the target.
20892e2caf59SThomas Veerman  *
20902e2caf59SThomas Veerman  *-----------------------------------------------------------------------
20912e2caf59SThomas Veerman  */
20922e2caf59SThomas Veerman static void
ParseHasCommands(void * gnp)20932e2caf59SThomas Veerman ParseHasCommands(void *gnp)
20942e2caf59SThomas Veerman {
20952e2caf59SThomas Veerman     GNode *gn = (GNode *)gnp;
20962e2caf59SThomas Veerman     if (!Lst_IsEmpty(gn->commands)) {
20972e2caf59SThomas Veerman 	gn->type |= OP_HAS_COMMANDS;
20982e2caf59SThomas Veerman     }
20992e2caf59SThomas Veerman }
21002e2caf59SThomas Veerman 
21012e2caf59SThomas Veerman /*-
21022e2caf59SThomas Veerman  *-----------------------------------------------------------------------
21032e2caf59SThomas Veerman  * Parse_AddIncludeDir --
21042e2caf59SThomas Veerman  *	Add a directory to the path searched for included makefiles
21052e2caf59SThomas Veerman  *	bracketed by double-quotes. Used by functions in main.c
21062e2caf59SThomas Veerman  *
21072e2caf59SThomas Veerman  * Input:
21082e2caf59SThomas Veerman  *	dir		The name of the directory to add
21092e2caf59SThomas Veerman  *
21102e2caf59SThomas Veerman  * Results:
21112e2caf59SThomas Veerman  *	None.
21122e2caf59SThomas Veerman  *
21132e2caf59SThomas Veerman  * Side Effects:
21142e2caf59SThomas Veerman  *	The directory is appended to the list.
21152e2caf59SThomas Veerman  *
21162e2caf59SThomas Veerman  *-----------------------------------------------------------------------
21172e2caf59SThomas Veerman  */
21182e2caf59SThomas Veerman void
Parse_AddIncludeDir(char * dir)21192e2caf59SThomas Veerman Parse_AddIncludeDir(char *dir)
21202e2caf59SThomas Veerman {
21212e2caf59SThomas Veerman     (void)Dir_AddDir(parseIncPath, dir);
21222e2caf59SThomas Veerman }
21232e2caf59SThomas Veerman 
21242e2caf59SThomas Veerman /*-
21252e2caf59SThomas Veerman  *---------------------------------------------------------------------
21262e2caf59SThomas Veerman  * ParseDoInclude  --
21272e2caf59SThomas Veerman  *	Push to another file.
21282e2caf59SThomas Veerman  *
21292e2caf59SThomas Veerman  *	The input is the line minus the `.'. A file spec is a string
21302e2caf59SThomas Veerman  *	enclosed in <> or "". The former is looked for only in sysIncPath.
21312e2caf59SThomas Veerman  *	The latter in . and the directories specified by -I command line
21322e2caf59SThomas Veerman  *	options
21332e2caf59SThomas Veerman  *
21342e2caf59SThomas Veerman  * Results:
21352e2caf59SThomas Veerman  *	None
21362e2caf59SThomas Veerman  *
21372e2caf59SThomas Veerman  * Side Effects:
21382e2caf59SThomas Veerman  *	A structure is added to the includes Lst and readProc, lineno,
21392e2caf59SThomas Veerman  *	fname and curFILE are altered for the new file
21402e2caf59SThomas Veerman  *---------------------------------------------------------------------
21412e2caf59SThomas Veerman  */
21422e2caf59SThomas Veerman 
21432e2caf59SThomas Veerman static void
Parse_include_file(char * file,Boolean isSystem,int silent)21442e2caf59SThomas Veerman Parse_include_file(char *file, Boolean isSystem, int silent)
21452e2caf59SThomas Veerman {
21462e2caf59SThomas Veerman     struct loadedfile *lf;
21472e2caf59SThomas Veerman     char          *fullname;	/* full pathname of file */
21482e2caf59SThomas Veerman     char          *newName;
21492e2caf59SThomas Veerman     char          *prefEnd, *incdir;
21502e2caf59SThomas Veerman     int           fd;
21512e2caf59SThomas Veerman     int           i;
21522e2caf59SThomas Veerman 
21532e2caf59SThomas Veerman     /*
21542e2caf59SThomas Veerman      * Now we know the file's name and its search path, we attempt to
21552e2caf59SThomas Veerman      * find the durn thing. A return of NULL indicates the file don't
21562e2caf59SThomas Veerman      * exist.
21572e2caf59SThomas Veerman      */
21582e2caf59SThomas Veerman     fullname = file[0] == '/' ? bmake_strdup(file) : NULL;
21592e2caf59SThomas Veerman 
21602e2caf59SThomas Veerman     if (fullname == NULL && !isSystem) {
21612e2caf59SThomas Veerman 	/*
21622e2caf59SThomas Veerman 	 * Include files contained in double-quotes are first searched for
21632e2caf59SThomas Veerman 	 * relative to the including file's location. We don't want to
21642e2caf59SThomas Veerman 	 * cd there, of course, so we just tack on the old file's
21652e2caf59SThomas Veerman 	 * leading path components and call Dir_FindFile to see if
21662e2caf59SThomas Veerman 	 * we can locate the beast.
21672e2caf59SThomas Veerman 	 */
21682e2caf59SThomas Veerman 
21692e2caf59SThomas Veerman 	incdir = bmake_strdup(curFile->fname);
21702e2caf59SThomas Veerman 	prefEnd = strrchr(incdir, '/');
21712e2caf59SThomas Veerman 	if (prefEnd != NULL) {
21722e2caf59SThomas Veerman 	    *prefEnd = '\0';
21732e2caf59SThomas Veerman 	    /* Now do lexical processing of leading "../" on the filename */
21742e2caf59SThomas Veerman 	    for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) {
21752e2caf59SThomas Veerman 		prefEnd = strrchr(incdir + 1, '/');
21762e2caf59SThomas Veerman 		if (prefEnd == NULL || strcmp(prefEnd, "/..") == 0)
21772e2caf59SThomas Veerman 		    break;
21782e2caf59SThomas Veerman 		*prefEnd = '\0';
21792e2caf59SThomas Veerman 	    }
21802e2caf59SThomas Veerman 	    newName = str_concat(incdir, file + i, STR_ADDSLASH);
21812e2caf59SThomas Veerman 	    fullname = Dir_FindFile(newName, parseIncPath);
21822e2caf59SThomas Veerman 	    if (fullname == NULL)
21832e2caf59SThomas Veerman 		fullname = Dir_FindFile(newName, dirSearchPath);
21842e2caf59SThomas Veerman 	    free(newName);
21852e2caf59SThomas Veerman 	}
21862e2caf59SThomas Veerman 	free(incdir);
21872e2caf59SThomas Veerman 
21882e2caf59SThomas Veerman 	if (fullname == NULL) {
21892e2caf59SThomas Veerman 	    /*
21902e2caf59SThomas Veerman     	     * Makefile wasn't found in same directory as included makefile.
21912e2caf59SThomas Veerman 	     * Search for it first on the -I search path,
21922e2caf59SThomas Veerman 	     * then on the .PATH search path, if not found in a -I directory.
21932e2caf59SThomas Veerman 	     * If we have a suffix specific path we should use that.
21942e2caf59SThomas Veerman 	     */
21952e2caf59SThomas Veerman 	    char *suff;
21962e2caf59SThomas Veerman 	    Lst	suffPath = NULL;
21972e2caf59SThomas Veerman 
21982e2caf59SThomas Veerman 	    if ((suff = strrchr(file, '.'))) {
21992e2caf59SThomas Veerman 		suffPath = Suff_GetPath(suff);
22002e2caf59SThomas Veerman 		if (suffPath != NULL) {
22012e2caf59SThomas Veerman 		    fullname = Dir_FindFile(file, suffPath);
22022e2caf59SThomas Veerman 		}
22032e2caf59SThomas Veerman 	    }
22042e2caf59SThomas Veerman 	    if (fullname == NULL) {
22052e2caf59SThomas Veerman 		fullname = Dir_FindFile(file, parseIncPath);
22062e2caf59SThomas Veerman 		if (fullname == NULL) {
22072e2caf59SThomas Veerman 		    fullname = Dir_FindFile(file, dirSearchPath);
22082e2caf59SThomas Veerman 		}
22092e2caf59SThomas Veerman 	    }
22102e2caf59SThomas Veerman 	}
22112e2caf59SThomas Veerman     }
22122e2caf59SThomas Veerman 
22132e2caf59SThomas Veerman     /* Looking for a system file or file still not found */
22142e2caf59SThomas Veerman     if (fullname == NULL) {
22152e2caf59SThomas Veerman 	/*
22162e2caf59SThomas Veerman 	 * Look for it on the system path
22172e2caf59SThomas Veerman 	 */
22182e2caf59SThomas Veerman 	fullname = Dir_FindFile(file,
22192e2caf59SThomas Veerman 		    Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath);
22202e2caf59SThomas Veerman     }
22212e2caf59SThomas Veerman 
22222e2caf59SThomas Veerman     if (fullname == NULL) {
22232e2caf59SThomas Veerman 	if (!silent)
22242e2caf59SThomas Veerman 	    Parse_Error(PARSE_FATAL, "Could not find %s", file);
22252e2caf59SThomas Veerman 	return;
22262e2caf59SThomas Veerman     }
22272e2caf59SThomas Veerman 
22282e2caf59SThomas Veerman     /* Actually open the file... */
22292e2caf59SThomas Veerman     fd = open(fullname, O_RDONLY);
22302e2caf59SThomas Veerman     if (fd == -1) {
22312e2caf59SThomas Veerman 	if (!silent)
22322e2caf59SThomas Veerman 	    Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);
22332e2caf59SThomas Veerman 	free(fullname);
22342e2caf59SThomas Veerman 	return;
22352e2caf59SThomas Veerman     }
22362e2caf59SThomas Veerman 
22372e2caf59SThomas Veerman     /* load it */
22382e2caf59SThomas Veerman     lf = loadfile(fullname, fd);
22392e2caf59SThomas Veerman 
2240*0a6a1f1dSLionel Sambuc     ParseSetIncludedFile();
22412e2caf59SThomas Veerman     /* Start reading from this file next */
22422e2caf59SThomas Veerman     Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf);
22432e2caf59SThomas Veerman     curFile->lf = lf;
22442e2caf59SThomas Veerman }
22452e2caf59SThomas Veerman 
22462e2caf59SThomas Veerman static void
ParseDoInclude(char * line)22472e2caf59SThomas Veerman ParseDoInclude(char *line)
22482e2caf59SThomas Veerman {
22492e2caf59SThomas Veerman     char          endc;	    	/* the character which ends the file spec */
22502e2caf59SThomas Veerman     char          *cp;		/* current position in file spec */
22512e2caf59SThomas Veerman     int		  silent = (*line != 'i') ? 1 : 0;
22522e2caf59SThomas Veerman     char	  *file = &line[7 + silent];
22532e2caf59SThomas Veerman 
22542e2caf59SThomas Veerman     /* Skip to delimiter character so we know where to look */
22552e2caf59SThomas Veerman     while (*file == ' ' || *file == '\t')
22562e2caf59SThomas Veerman 	file++;
22572e2caf59SThomas Veerman 
22582e2caf59SThomas Veerman     if (*file != '"' && *file != '<') {
22592e2caf59SThomas Veerman 	Parse_Error(PARSE_FATAL,
22602e2caf59SThomas Veerman 	    ".include filename must be delimited by '\"' or '<'");
22612e2caf59SThomas Veerman 	return;
22622e2caf59SThomas Veerman     }
22632e2caf59SThomas Veerman 
22642e2caf59SThomas Veerman     /*
22652e2caf59SThomas Veerman      * Set the search path on which to find the include file based on the
22662e2caf59SThomas Veerman      * characters which bracket its name. Angle-brackets imply it's
22672e2caf59SThomas Veerman      * a system Makefile while double-quotes imply it's a user makefile
22682e2caf59SThomas Veerman      */
22692e2caf59SThomas Veerman     if (*file == '<') {
22702e2caf59SThomas Veerman 	endc = '>';
22712e2caf59SThomas Veerman     } else {
22722e2caf59SThomas Veerman 	endc = '"';
22732e2caf59SThomas Veerman     }
22742e2caf59SThomas Veerman 
22752e2caf59SThomas Veerman     /* Skip to matching delimiter */
22762e2caf59SThomas Veerman     for (cp = ++file; *cp && *cp != endc; cp++)
22772e2caf59SThomas Veerman 	continue;
22782e2caf59SThomas Veerman 
22792e2caf59SThomas Veerman     if (*cp != endc) {
22802e2caf59SThomas Veerman 	Parse_Error(PARSE_FATAL,
22812e2caf59SThomas Veerman 		     "Unclosed %cinclude filename. '%c' expected",
22822e2caf59SThomas Veerman 		     '.', endc);
22832e2caf59SThomas Veerman 	return;
22842e2caf59SThomas Veerman     }
22852e2caf59SThomas Veerman     *cp = '\0';
22862e2caf59SThomas Veerman 
22872e2caf59SThomas Veerman     /*
22882e2caf59SThomas Veerman      * Substitute for any variables in the file name before trying to
22892e2caf59SThomas Veerman      * find the thing.
22902e2caf59SThomas Veerman      */
22912e2caf59SThomas Veerman     file = Var_Subst(NULL, file, VAR_CMD, FALSE);
22922e2caf59SThomas Veerman 
22932e2caf59SThomas Veerman     Parse_include_file(file, endc == '>', silent);
22942e2caf59SThomas Veerman     free(file);
22952e2caf59SThomas Veerman }
22962e2caf59SThomas Veerman 
22972e2caf59SThomas Veerman 
22982e2caf59SThomas Veerman /*-
22992e2caf59SThomas Veerman  *---------------------------------------------------------------------
2300*0a6a1f1dSLionel Sambuc  * ParseSetIncludedFile  --
2301*0a6a1f1dSLionel Sambuc  *	Set the .INCLUDEDFROMFILE variable to the contents of .PARSEFILE
2302*0a6a1f1dSLionel Sambuc  *	and the .INCLUDEDFROMDIR variable to the contents of .PARSEDIR
2303*0a6a1f1dSLionel Sambuc  *
2304*0a6a1f1dSLionel Sambuc  * Results:
2305*0a6a1f1dSLionel Sambuc  *	None
2306*0a6a1f1dSLionel Sambuc  *
2307*0a6a1f1dSLionel Sambuc  * Side Effects:
2308*0a6a1f1dSLionel Sambuc  *	The .INCLUDEDFROMFILE variable is overwritten by the contents
2309*0a6a1f1dSLionel Sambuc  *	of .PARSEFILE and the .INCLUDEDFROMDIR variable is overwriten
2310*0a6a1f1dSLionel Sambuc  *	by the contents of .PARSEDIR
2311*0a6a1f1dSLionel Sambuc  *---------------------------------------------------------------------
2312*0a6a1f1dSLionel Sambuc  */
2313*0a6a1f1dSLionel Sambuc static void
ParseSetIncludedFile(void)2314*0a6a1f1dSLionel Sambuc ParseSetIncludedFile(void)
2315*0a6a1f1dSLionel Sambuc {
2316*0a6a1f1dSLionel Sambuc     char *pf, *fp = NULL;
2317*0a6a1f1dSLionel Sambuc     char *pd, *dp = NULL;
2318*0a6a1f1dSLionel Sambuc 
2319*0a6a1f1dSLionel Sambuc     pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &fp);
2320*0a6a1f1dSLionel Sambuc     Var_Set(".INCLUDEDFROMFILE", pf, VAR_GLOBAL, 0);
2321*0a6a1f1dSLionel Sambuc     pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &dp);
2322*0a6a1f1dSLionel Sambuc     Var_Set(".INCLUDEDFROMDIR", pd, VAR_GLOBAL, 0);
2323*0a6a1f1dSLionel Sambuc 
2324*0a6a1f1dSLionel Sambuc     if (DEBUG(PARSE))
2325*0a6a1f1dSLionel Sambuc 	fprintf(debug_file, "%s: ${.INCLUDEDFROMDIR} = `%s' "
2326*0a6a1f1dSLionel Sambuc 	    "${.INCLUDEDFROMFILE} = `%s'\n", __func__, pd, pf);
2327*0a6a1f1dSLionel Sambuc 
2328*0a6a1f1dSLionel Sambuc     if (fp)
2329*0a6a1f1dSLionel Sambuc 	free(fp);
2330*0a6a1f1dSLionel Sambuc     if (dp)
2331*0a6a1f1dSLionel Sambuc 	free(dp);
2332*0a6a1f1dSLionel Sambuc }
2333*0a6a1f1dSLionel Sambuc /*-
2334*0a6a1f1dSLionel Sambuc  *---------------------------------------------------------------------
23352e2caf59SThomas Veerman  * ParseSetParseFile  --
23362e2caf59SThomas Veerman  *	Set the .PARSEDIR and .PARSEFILE variables to the dirname and
23372e2caf59SThomas Veerman  *	basename of the given filename
23382e2caf59SThomas Veerman  *
23392e2caf59SThomas Veerman  * Results:
23402e2caf59SThomas Veerman  *	None
23412e2caf59SThomas Veerman  *
23422e2caf59SThomas Veerman  * Side Effects:
23432e2caf59SThomas Veerman  *	The .PARSEDIR and .PARSEFILE variables are overwritten by the
23442e2caf59SThomas Veerman  *	dirname and basename of the given filename.
23452e2caf59SThomas Veerman  *---------------------------------------------------------------------
23462e2caf59SThomas Veerman  */
23472e2caf59SThomas Veerman static void
ParseSetParseFile(const char * filename)23482e2caf59SThomas Veerman ParseSetParseFile(const char *filename)
23492e2caf59SThomas Veerman {
23502e2caf59SThomas Veerman     char *slash, *dirname;
23512e2caf59SThomas Veerman     const char *pd, *pf;
23522e2caf59SThomas Veerman     int len;
23532e2caf59SThomas Veerman 
23542e2caf59SThomas Veerman     slash = strrchr(filename, '/');
23552e2caf59SThomas Veerman     if (slash == NULL) {
23562e2caf59SThomas Veerman 	Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL, 0);
23572e2caf59SThomas Veerman 	Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL, 0);
23582e2caf59SThomas Veerman 	dirname= NULL;
23592e2caf59SThomas Veerman     } else {
23602e2caf59SThomas Veerman 	len = slash - filename;
23612e2caf59SThomas Veerman 	dirname = bmake_malloc(len + 1);
23622e2caf59SThomas Veerman 	memcpy(dirname, filename, len);
23632e2caf59SThomas Veerman 	dirname[len] = '\0';
23642e2caf59SThomas Veerman 	Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL, 0);
23652e2caf59SThomas Veerman 	Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL, 0);
23662e2caf59SThomas Veerman     }
23672e2caf59SThomas Veerman     if (DEBUG(PARSE))
2368*0a6a1f1dSLionel Sambuc 	fprintf(debug_file, "%s: ${.PARSEDIR} = `%s' ${.PARSEFILE} = `%s'\n",
2369*0a6a1f1dSLionel Sambuc 	    __func__, pd, pf);
23702e2caf59SThomas Veerman     free(dirname);
23712e2caf59SThomas Veerman }
23722e2caf59SThomas Veerman 
23732e2caf59SThomas Veerman /*
23742e2caf59SThomas Veerman  * Track the makefiles we read - so makefiles can
23752e2caf59SThomas Veerman  * set dependencies on them.
23762e2caf59SThomas Veerman  * Avoid adding anything more than once.
23772e2caf59SThomas Veerman  */
23782e2caf59SThomas Veerman 
23792e2caf59SThomas Veerman static void
ParseTrackInput(const char * name)23802e2caf59SThomas Veerman ParseTrackInput(const char *name)
23812e2caf59SThomas Veerman {
23822e2caf59SThomas Veerman     char *old;
23832e2caf59SThomas Veerman     char *fp = NULL;
23842e2caf59SThomas Veerman     size_t name_len = strlen(name);
23852e2caf59SThomas Veerman 
23862e2caf59SThomas Veerman     old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp);
23872e2caf59SThomas Veerman     if (old) {
23882e2caf59SThomas Veerman 	/* does it contain name? */
23892e2caf59SThomas Veerman 	for (; old != NULL; old = strchr(old, ' ')) {
23902e2caf59SThomas Veerman 	    if (*old == ' ')
23912e2caf59SThomas Veerman 		old++;
23922e2caf59SThomas Veerman 	    if (memcmp(old, name, name_len) == 0
23932e2caf59SThomas Veerman 		    && (old[name_len] == 0 || old[name_len] == ' '))
23942e2caf59SThomas Veerman 		goto cleanup;
23952e2caf59SThomas Veerman 	}
23962e2caf59SThomas Veerman     }
23972e2caf59SThomas Veerman     Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL);
23982e2caf59SThomas Veerman  cleanup:
23992e2caf59SThomas Veerman     if (fp) {
24002e2caf59SThomas Veerman 	free(fp);
24012e2caf59SThomas Veerman     }
24022e2caf59SThomas Veerman }
24032e2caf59SThomas Veerman 
24042e2caf59SThomas Veerman 
24052e2caf59SThomas Veerman /*-
24062e2caf59SThomas Veerman  *---------------------------------------------------------------------
24072e2caf59SThomas Veerman  * Parse_setInput  --
24082e2caf59SThomas Veerman  *	Start Parsing from the given source
24092e2caf59SThomas Veerman  *
24102e2caf59SThomas Veerman  * Results:
24112e2caf59SThomas Veerman  *	None
24122e2caf59SThomas Veerman  *
24132e2caf59SThomas Veerman  * Side Effects:
24142e2caf59SThomas Veerman  *	A structure is added to the includes Lst and readProc, lineno,
24152e2caf59SThomas Veerman  *	fname and curFile are altered for the new file
24162e2caf59SThomas Veerman  *---------------------------------------------------------------------
24172e2caf59SThomas Veerman  */
24182e2caf59SThomas Veerman void
Parse_SetInput(const char * name,int line,int fd,char * (* nextbuf)(void *,size_t *),void * arg)24192e2caf59SThomas Veerman Parse_SetInput(const char *name, int line, int fd,
24202e2caf59SThomas Veerman 	char *(*nextbuf)(void *, size_t *), void *arg)
24212e2caf59SThomas Veerman {
24222e2caf59SThomas Veerman     char *buf;
24232e2caf59SThomas Veerman     size_t len;
24242e2caf59SThomas Veerman 
24252e2caf59SThomas Veerman     if (name == NULL)
24262e2caf59SThomas Veerman 	name = curFile->fname;
24272e2caf59SThomas Veerman     else
24282e2caf59SThomas Veerman 	ParseTrackInput(name);
24292e2caf59SThomas Veerman 
24302e2caf59SThomas Veerman     if (DEBUG(PARSE))
2431*0a6a1f1dSLionel Sambuc 	fprintf(debug_file, "%s: file %s, line %d, fd %d, nextbuf %p, arg %p\n",
2432*0a6a1f1dSLionel Sambuc 	    __func__, name, line, fd, nextbuf, arg);
24332e2caf59SThomas Veerman 
24342e2caf59SThomas Veerman     if (fd == -1 && nextbuf == NULL)
24352e2caf59SThomas Veerman 	/* sanity */
24362e2caf59SThomas Veerman 	return;
24372e2caf59SThomas Veerman 
24382e2caf59SThomas Veerman     if (curFile != NULL)
24392e2caf59SThomas Veerman 	/* Save exiting file info */
24402e2caf59SThomas Veerman 	Lst_AtFront(includes, curFile);
24412e2caf59SThomas Veerman 
24422e2caf59SThomas Veerman     /* Allocate and fill in new structure */
24432e2caf59SThomas Veerman     curFile = bmake_malloc(sizeof *curFile);
24442e2caf59SThomas Veerman 
24452e2caf59SThomas Veerman     /*
24462e2caf59SThomas Veerman      * Once the previous state has been saved, we can get down to reading
24472e2caf59SThomas Veerman      * the new file. We set up the name of the file to be the absolute
24482e2caf59SThomas Veerman      * name of the include file so error messages refer to the right
24492e2caf59SThomas Veerman      * place.
24502e2caf59SThomas Veerman      */
245184d9c625SLionel Sambuc     curFile->fname = bmake_strdup(name);
24522e2caf59SThomas Veerman     curFile->lineno = line;
24532e2caf59SThomas Veerman     curFile->first_lineno = line;
24542e2caf59SThomas Veerman     curFile->nextbuf = nextbuf;
24552e2caf59SThomas Veerman     curFile->nextbuf_arg = arg;
24562e2caf59SThomas Veerman     curFile->lf = NULL;
24572e2caf59SThomas Veerman 
24582e2caf59SThomas Veerman     assert(nextbuf != NULL);
24592e2caf59SThomas Veerman 
24602e2caf59SThomas Veerman     /* Get first block of input data */
24612e2caf59SThomas Veerman     buf = curFile->nextbuf(curFile->nextbuf_arg, &len);
24622e2caf59SThomas Veerman     if (buf == NULL) {
24632e2caf59SThomas Veerman         /* Was all a waste of time ... */
246484d9c625SLionel Sambuc 	if (curFile->fname)
246584d9c625SLionel Sambuc 	    free(curFile->fname);
24662e2caf59SThomas Veerman 	free(curFile);
24672e2caf59SThomas Veerman 	return;
24682e2caf59SThomas Veerman     }
24692e2caf59SThomas Veerman     curFile->P_str = buf;
24702e2caf59SThomas Veerman     curFile->P_ptr = buf;
24712e2caf59SThomas Veerman     curFile->P_end = buf+len;
24722e2caf59SThomas Veerman 
24732e2caf59SThomas Veerman     curFile->cond_depth = Cond_save_depth();
24742e2caf59SThomas Veerman     ParseSetParseFile(name);
24752e2caf59SThomas Veerman }
24762e2caf59SThomas Veerman 
24772e2caf59SThomas Veerman #ifdef SYSVINCLUDE
24782e2caf59SThomas Veerman /*-
24792e2caf59SThomas Veerman  *---------------------------------------------------------------------
24802e2caf59SThomas Veerman  * ParseTraditionalInclude  --
24812e2caf59SThomas Veerman  *	Push to another file.
24822e2caf59SThomas Veerman  *
24832e2caf59SThomas Veerman  *	The input is the current line. The file name(s) are
24842e2caf59SThomas Veerman  *	following the "include".
24852e2caf59SThomas Veerman  *
24862e2caf59SThomas Veerman  * Results:
24872e2caf59SThomas Veerman  *	None
24882e2caf59SThomas Veerman  *
24892e2caf59SThomas Veerman  * Side Effects:
24902e2caf59SThomas Veerman  *	A structure is added to the includes Lst and readProc, lineno,
24912e2caf59SThomas Veerman  *	fname and curFILE are altered for the new file
24922e2caf59SThomas Veerman  *---------------------------------------------------------------------
24932e2caf59SThomas Veerman  */
24942e2caf59SThomas Veerman static void
ParseTraditionalInclude(char * line)24952e2caf59SThomas Veerman ParseTraditionalInclude(char *line)
24962e2caf59SThomas Veerman {
24972e2caf59SThomas Veerman     char          *cp;		/* current position in file spec */
24982e2caf59SThomas Veerman     int		   done = 0;
24992e2caf59SThomas Veerman     int		   silent = (line[0] != 'i') ? 1 : 0;
25002e2caf59SThomas Veerman     char	  *file = &line[silent + 7];
25012e2caf59SThomas Veerman     char	  *all_files;
25022e2caf59SThomas Veerman 
25032e2caf59SThomas Veerman     if (DEBUG(PARSE)) {
2504*0a6a1f1dSLionel Sambuc 	    fprintf(debug_file, "%s: %s\n", __func__, file);
25052e2caf59SThomas Veerman     }
25062e2caf59SThomas Veerman 
25072e2caf59SThomas Veerman     /*
25082e2caf59SThomas Veerman      * Skip over whitespace
25092e2caf59SThomas Veerman      */
25102e2caf59SThomas Veerman     while (isspace((unsigned char)*file))
25112e2caf59SThomas Veerman 	file++;
25122e2caf59SThomas Veerman 
25132e2caf59SThomas Veerman     /*
25142e2caf59SThomas Veerman      * Substitute for any variables in the file name before trying to
25152e2caf59SThomas Veerman      * find the thing.
25162e2caf59SThomas Veerman      */
25172e2caf59SThomas Veerman     all_files = Var_Subst(NULL, file, VAR_CMD, FALSE);
25182e2caf59SThomas Veerman 
25192e2caf59SThomas Veerman     if (*file == '\0') {
25202e2caf59SThomas Veerman 	Parse_Error(PARSE_FATAL,
25212e2caf59SThomas Veerman 		     "Filename missing from \"include\"");
25222e2caf59SThomas Veerman 	return;
25232e2caf59SThomas Veerman     }
25242e2caf59SThomas Veerman 
25252e2caf59SThomas Veerman     for (file = all_files; !done; file = cp + 1) {
25262e2caf59SThomas Veerman 	/* Skip to end of line or next whitespace */
25272e2caf59SThomas Veerman 	for (cp = file; *cp && !isspace((unsigned char) *cp); cp++)
25282e2caf59SThomas Veerman 	    continue;
25292e2caf59SThomas Veerman 
25302e2caf59SThomas Veerman 	if (*cp)
25312e2caf59SThomas Veerman 	    *cp = '\0';
25322e2caf59SThomas Veerman 	else
25332e2caf59SThomas Veerman 	    done = 1;
25342e2caf59SThomas Veerman 
25352e2caf59SThomas Veerman 	Parse_include_file(file, FALSE, silent);
25362e2caf59SThomas Veerman     }
25372e2caf59SThomas Veerman     free(all_files);
25382e2caf59SThomas Veerman }
25392e2caf59SThomas Veerman #endif
25402e2caf59SThomas Veerman 
25412bc7c627SLionel Sambuc #ifdef GMAKEEXPORT
25422e2caf59SThomas Veerman /*-
25432e2caf59SThomas Veerman  *---------------------------------------------------------------------
25442e2caf59SThomas Veerman  * ParseGmakeExport  --
25452e2caf59SThomas Veerman  *	Parse export <variable>=<value>
25462e2caf59SThomas Veerman  *
25472e2caf59SThomas Veerman  *	And set the environment with it.
25482e2caf59SThomas Veerman  *
25492e2caf59SThomas Veerman  * Results:
25502e2caf59SThomas Veerman  *	None
25512e2caf59SThomas Veerman  *
25522e2caf59SThomas Veerman  * Side Effects:
25532e2caf59SThomas Veerman  *	None
25542e2caf59SThomas Veerman  *---------------------------------------------------------------------
25552e2caf59SThomas Veerman  */
25562e2caf59SThomas Veerman static void
ParseGmakeExport(char * line)25572e2caf59SThomas Veerman ParseGmakeExport(char *line)
25582e2caf59SThomas Veerman {
25592e2caf59SThomas Veerman     char	  *variable = &line[6];
25602e2caf59SThomas Veerman     char	  *value;
25612e2caf59SThomas Veerman 
25622e2caf59SThomas Veerman     if (DEBUG(PARSE)) {
2563*0a6a1f1dSLionel Sambuc 	    fprintf(debug_file, "%s: %s\n", __func__, variable);
25642e2caf59SThomas Veerman     }
25652e2caf59SThomas Veerman 
25662e2caf59SThomas Veerman     /*
25672e2caf59SThomas Veerman      * Skip over whitespace
25682e2caf59SThomas Veerman      */
25692e2caf59SThomas Veerman     while (isspace((unsigned char)*variable))
25702e2caf59SThomas Veerman 	variable++;
25712e2caf59SThomas Veerman 
25722e2caf59SThomas Veerman     for (value = variable; *value && *value != '='; value++)
25732e2caf59SThomas Veerman 	continue;
25742e2caf59SThomas Veerman 
25752e2caf59SThomas Veerman     if (*value != '=') {
25762e2caf59SThomas Veerman 	Parse_Error(PARSE_FATAL,
25772bc7c627SLionel Sambuc 		     "Variable/Value missing from \"export\"");
25782e2caf59SThomas Veerman 	return;
25792e2caf59SThomas Veerman     }
258084d9c625SLionel Sambuc     *value++ = '\0';			/* terminate variable */
25812e2caf59SThomas Veerman 
25822e2caf59SThomas Veerman     /*
25832bc7c627SLionel Sambuc      * Expand the value before putting it in the environment.
25842e2caf59SThomas Veerman      */
25852e2caf59SThomas Veerman     value = Var_Subst(NULL, value, VAR_CMD, FALSE);
25862e2caf59SThomas Veerman     setenv(variable, value, 1);
25872e2caf59SThomas Veerman }
25882e2caf59SThomas Veerman #endif
25892e2caf59SThomas Veerman 
25902e2caf59SThomas Veerman /*-
25912e2caf59SThomas Veerman  *---------------------------------------------------------------------
25922e2caf59SThomas Veerman  * ParseEOF  --
25932e2caf59SThomas Veerman  *	Called when EOF is reached in the current file. If we were reading
25942e2caf59SThomas Veerman  *	an include file, the includes stack is popped and things set up
25952e2caf59SThomas Veerman  *	to go back to reading the previous file at the previous location.
25962e2caf59SThomas Veerman  *
25972e2caf59SThomas Veerman  * Results:
25982e2caf59SThomas Veerman  *	CONTINUE if there's more to do. DONE if not.
25992e2caf59SThomas Veerman  *
26002e2caf59SThomas Veerman  * Side Effects:
26012e2caf59SThomas Veerman  *	The old curFILE, is closed. The includes list is shortened.
26022e2caf59SThomas Veerman  *	lineno, curFILE, and fname are changed if CONTINUE is returned.
26032e2caf59SThomas Veerman  *---------------------------------------------------------------------
26042e2caf59SThomas Veerman  */
26052e2caf59SThomas Veerman static int
ParseEOF(void)26062e2caf59SThomas Veerman ParseEOF(void)
26072e2caf59SThomas Veerman {
26082e2caf59SThomas Veerman     char *ptr;
26092e2caf59SThomas Veerman     size_t len;
26102e2caf59SThomas Veerman 
26112e2caf59SThomas Veerman     assert(curFile->nextbuf != NULL);
26122e2caf59SThomas Veerman 
26132e2caf59SThomas Veerman     /* get next input buffer, if any */
26142e2caf59SThomas Veerman     ptr = curFile->nextbuf(curFile->nextbuf_arg, &len);
26152e2caf59SThomas Veerman     curFile->P_ptr = ptr;
26162e2caf59SThomas Veerman     curFile->P_str = ptr;
26172e2caf59SThomas Veerman     curFile->P_end = ptr + len;
26182e2caf59SThomas Veerman     curFile->lineno = curFile->first_lineno;
26192e2caf59SThomas Veerman     if (ptr != NULL) {
26202e2caf59SThomas Veerman 	/* Iterate again */
26212e2caf59SThomas Veerman 	return CONTINUE;
26222e2caf59SThomas Veerman     }
26232e2caf59SThomas Veerman 
26242e2caf59SThomas Veerman     /* Ensure the makefile (or loop) didn't have mismatched conditionals */
26252e2caf59SThomas Veerman     Cond_restore_depth(curFile->cond_depth);
26262e2caf59SThomas Veerman 
26272e2caf59SThomas Veerman     if (curFile->lf != NULL) {
26282e2caf59SThomas Veerman 	    loadedfile_destroy(curFile->lf);
26292e2caf59SThomas Veerman 	    curFile->lf = NULL;
26302e2caf59SThomas Veerman     }
26312e2caf59SThomas Veerman 
26322e2caf59SThomas Veerman     /* Dispose of curFile info */
26332e2caf59SThomas Veerman     /* Leak curFile->fname because all the gnodes have pointers to it */
26342e2caf59SThomas Veerman     free(curFile->P_str);
26352e2caf59SThomas Veerman     free(curFile);
26362e2caf59SThomas Veerman 
26372e2caf59SThomas Veerman     curFile = Lst_DeQueue(includes);
26382e2caf59SThomas Veerman 
26392e2caf59SThomas Veerman     if (curFile == NULL) {
26402e2caf59SThomas Veerman 	/* We've run out of input */
26412e2caf59SThomas Veerman 	Var_Delete(".PARSEDIR", VAR_GLOBAL);
26422e2caf59SThomas Veerman 	Var_Delete(".PARSEFILE", VAR_GLOBAL);
2643*0a6a1f1dSLionel Sambuc 	Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL);
2644*0a6a1f1dSLionel Sambuc 	Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL);
26452e2caf59SThomas Veerman 	return DONE;
26462e2caf59SThomas Veerman     }
26472e2caf59SThomas Veerman 
26482e2caf59SThomas Veerman     if (DEBUG(PARSE))
26492e2caf59SThomas Veerman 	fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n",
26502e2caf59SThomas Veerman 	    curFile->fname, curFile->lineno);
26512e2caf59SThomas Veerman 
26522e2caf59SThomas Veerman     /* Restore the PARSEDIR/PARSEFILE variables */
26532e2caf59SThomas Veerman     ParseSetParseFile(curFile->fname);
26542e2caf59SThomas Veerman     return (CONTINUE);
26552e2caf59SThomas Veerman }
26562e2caf59SThomas Veerman 
26572e2caf59SThomas Veerman #define PARSE_RAW 1
26582e2caf59SThomas Veerman #define PARSE_SKIP 2
26592e2caf59SThomas Veerman 
26602e2caf59SThomas Veerman static char *
ParseGetLine(int flags,int * length)26612e2caf59SThomas Veerman ParseGetLine(int flags, int *length)
26622e2caf59SThomas Veerman {
26632e2caf59SThomas Veerman     IFile *cf = curFile;
26642e2caf59SThomas Veerman     char *ptr;
26652e2caf59SThomas Veerman     char ch;
26662e2caf59SThomas Veerman     char *line;
26672e2caf59SThomas Veerman     char *line_end;
26682e2caf59SThomas Veerman     char *escaped;
26692e2caf59SThomas Veerman     char *comment;
26702e2caf59SThomas Veerman     char *tp;
26712e2caf59SThomas Veerman 
26722e2caf59SThomas Veerman     /* Loop through blank lines and comment lines */
26732e2caf59SThomas Veerman     for (;;) {
26742e2caf59SThomas Veerman 	cf->lineno++;
26752e2caf59SThomas Veerman 	line = cf->P_ptr;
26762e2caf59SThomas Veerman 	ptr = line;
26772e2caf59SThomas Veerman 	line_end = line;
26782e2caf59SThomas Veerman 	escaped = NULL;
26792e2caf59SThomas Veerman 	comment = NULL;
26802e2caf59SThomas Veerman 	for (;;) {
26812e2caf59SThomas Veerman 	    if (cf->P_end != NULL && ptr == cf->P_end) {
26822e2caf59SThomas Veerman 		/* end of buffer */
26832e2caf59SThomas Veerman 		ch = 0;
26842e2caf59SThomas Veerman 		break;
26852e2caf59SThomas Veerman 	    }
26862e2caf59SThomas Veerman 	    ch = *ptr;
26872e2caf59SThomas Veerman 	    if (ch == 0 || (ch == '\\' && ptr[1] == 0)) {
26882e2caf59SThomas Veerman 		if (cf->P_end == NULL)
26892e2caf59SThomas Veerman 		    /* End of string (aka for loop) data */
26902e2caf59SThomas Veerman 		    break;
269184d9c625SLionel Sambuc 		/* see if there is more we can parse */
269284d9c625SLionel Sambuc 		while (ptr++ < cf->P_end) {
269384d9c625SLionel Sambuc 		    if ((ch = *ptr) == '\n') {
269484d9c625SLionel Sambuc 			if (ptr > line && ptr[-1] == '\\')
269584d9c625SLionel Sambuc 			    continue;
269684d9c625SLionel Sambuc 			Parse_Error(PARSE_WARNING,
269784d9c625SLionel Sambuc 			    "Zero byte read from file, skipping rest of line.");
269884d9c625SLionel Sambuc 			break;
269984d9c625SLionel Sambuc 		    }
270084d9c625SLionel Sambuc 		}
27012e2caf59SThomas Veerman 		if (cf->nextbuf != NULL) {
27022e2caf59SThomas Veerman 		    /*
27032e2caf59SThomas Veerman 		     * End of this buffer; return EOF and outer logic
27042e2caf59SThomas Veerman 		     * will get the next one. (eww)
27052e2caf59SThomas Veerman 		     */
27062e2caf59SThomas Veerman 		    break;
27072e2caf59SThomas Veerman 		}
27082e2caf59SThomas Veerman 		Parse_Error(PARSE_FATAL, "Zero byte read from file");
27092e2caf59SThomas Veerman 		return NULL;
27102e2caf59SThomas Veerman 	    }
27112e2caf59SThomas Veerman 
27122e2caf59SThomas Veerman 	    if (ch == '\\') {
27132e2caf59SThomas Veerman 		/* Don't treat next character as special, remember first one */
27142e2caf59SThomas Veerman 		if (escaped == NULL)
27152e2caf59SThomas Veerman 		    escaped = ptr;
27162e2caf59SThomas Veerman 		if (ptr[1] == '\n')
27172e2caf59SThomas Veerman 		    cf->lineno++;
27182e2caf59SThomas Veerman 		ptr += 2;
27192e2caf59SThomas Veerman 		line_end = ptr;
27202e2caf59SThomas Veerman 		continue;
27212e2caf59SThomas Veerman 	    }
27222e2caf59SThomas Veerman 	    if (ch == '#' && comment == NULL) {
27232e2caf59SThomas Veerman 		/* Remember first '#' for comment stripping */
27242e2caf59SThomas Veerman 		/* Unless previous char was '[', as in modifier :[#] */
27252e2caf59SThomas Veerman 		if (!(ptr > line && ptr[-1] == '['))
27262e2caf59SThomas Veerman 		    comment = line_end;
27272e2caf59SThomas Veerman 	    }
27282e2caf59SThomas Veerman 	    ptr++;
27292e2caf59SThomas Veerman 	    if (ch == '\n')
27302e2caf59SThomas Veerman 		break;
27312e2caf59SThomas Veerman 	    if (!isspace((unsigned char)ch))
27322e2caf59SThomas Veerman 		/* We are not interested in trailing whitespace */
27332e2caf59SThomas Veerman 		line_end = ptr;
27342e2caf59SThomas Veerman 	}
27352e2caf59SThomas Veerman 
27362e2caf59SThomas Veerman 	/* Save next 'to be processed' location */
27372e2caf59SThomas Veerman 	cf->P_ptr = ptr;
27382e2caf59SThomas Veerman 
27392e2caf59SThomas Veerman 	/* Check we have a non-comment, non-blank line */
27402e2caf59SThomas Veerman 	if (line_end == line || comment == line) {
27412e2caf59SThomas Veerman 	    if (ch == 0)
27422e2caf59SThomas Veerman 		/* At end of file */
27432e2caf59SThomas Veerman 		return NULL;
27442e2caf59SThomas Veerman 	    /* Parse another line */
27452e2caf59SThomas Veerman 	    continue;
27462e2caf59SThomas Veerman 	}
27472e2caf59SThomas Veerman 
27482e2caf59SThomas Veerman 	/* We now have a line of data */
27492e2caf59SThomas Veerman 	*line_end = 0;
27502e2caf59SThomas Veerman 
27512e2caf59SThomas Veerman 	if (flags & PARSE_RAW) {
27522e2caf59SThomas Veerman 	    /* Leave '\' (etc) in line buffer (eg 'for' lines) */
27532e2caf59SThomas Veerman 	    *length = line_end - line;
27542e2caf59SThomas Veerman 	    return line;
27552e2caf59SThomas Veerman 	}
27562e2caf59SThomas Veerman 
27572e2caf59SThomas Veerman 	if (flags & PARSE_SKIP) {
27582e2caf59SThomas Veerman 	    /* Completely ignore non-directives */
27592e2caf59SThomas Veerman 	    if (line[0] != '.')
27602e2caf59SThomas Veerman 		continue;
27612e2caf59SThomas Veerman 	    /* We could do more of the .else/.elif/.endif checks here */
27622e2caf59SThomas Veerman 	}
27632e2caf59SThomas Veerman 	break;
27642e2caf59SThomas Veerman     }
27652e2caf59SThomas Veerman 
27662e2caf59SThomas Veerman     /* Brutally ignore anything after a non-escaped '#' in non-commands */
27672e2caf59SThomas Veerman     if (comment != NULL && line[0] != '\t') {
27682e2caf59SThomas Veerman 	line_end = comment;
27692e2caf59SThomas Veerman 	*line_end = 0;
27702e2caf59SThomas Veerman     }
27712e2caf59SThomas Veerman 
27722e2caf59SThomas Veerman     /* If we didn't see a '\\' then the in-situ data is fine */
27732e2caf59SThomas Veerman     if (escaped == NULL) {
27742e2caf59SThomas Veerman 	*length = line_end - line;
27752e2caf59SThomas Veerman 	return line;
27762e2caf59SThomas Veerman     }
27772e2caf59SThomas Veerman 
27782e2caf59SThomas Veerman     /* Remove escapes from '\n' and '#' */
27792e2caf59SThomas Veerman     tp = ptr = escaped;
27802e2caf59SThomas Veerman     escaped = line;
27812e2caf59SThomas Veerman     for (; ; *tp++ = ch) {
27822e2caf59SThomas Veerman 	ch = *ptr++;
27832e2caf59SThomas Veerman 	if (ch != '\\') {
27842e2caf59SThomas Veerman 	    if (ch == 0)
27852e2caf59SThomas Veerman 		break;
27862e2caf59SThomas Veerman 	    continue;
27872e2caf59SThomas Veerman 	}
27882e2caf59SThomas Veerman 
27892e2caf59SThomas Veerman 	ch = *ptr++;
27902e2caf59SThomas Veerman 	if (ch == 0) {
27912e2caf59SThomas Veerman 	    /* Delete '\\' at end of buffer */
27922e2caf59SThomas Veerman 	    tp--;
27932e2caf59SThomas Veerman 	    break;
27942e2caf59SThomas Veerman 	}
27952e2caf59SThomas Veerman 
27962e2caf59SThomas Veerman 	if (ch == '#' && line[0] != '\t')
27972e2caf59SThomas Veerman 	    /* Delete '\\' from before '#' on non-command lines */
27982e2caf59SThomas Veerman 	    continue;
27992e2caf59SThomas Veerman 
28002e2caf59SThomas Veerman 	if (ch != '\n') {
28012e2caf59SThomas Veerman 	    /* Leave '\\' in buffer for later */
28022e2caf59SThomas Veerman 	    *tp++ = '\\';
28032e2caf59SThomas Veerman 	    /* Make sure we don't delete an escaped ' ' from the line end */
28042e2caf59SThomas Veerman 	    escaped = tp + 1;
28052e2caf59SThomas Veerman 	    continue;
28062e2caf59SThomas Veerman 	}
28072e2caf59SThomas Veerman 
28082e2caf59SThomas Veerman 	/* Escaped '\n' replace following whitespace with a single ' ' */
28092e2caf59SThomas Veerman 	while (ptr[0] == ' ' || ptr[0] == '\t')
28102e2caf59SThomas Veerman 	    ptr++;
28112e2caf59SThomas Veerman 	ch = ' ';
28122e2caf59SThomas Veerman     }
28132e2caf59SThomas Veerman 
28142e2caf59SThomas Veerman     /* Delete any trailing spaces - eg from empty continuations */
28152e2caf59SThomas Veerman     while (tp > escaped && isspace((unsigned char)tp[-1]))
28162e2caf59SThomas Veerman 	tp--;
28172e2caf59SThomas Veerman 
28182e2caf59SThomas Veerman     *tp = 0;
28192e2caf59SThomas Veerman     *length = tp - line;
28202e2caf59SThomas Veerman     return line;
28212e2caf59SThomas Veerman }
28222e2caf59SThomas Veerman 
28232e2caf59SThomas Veerman /*-
28242e2caf59SThomas Veerman  *---------------------------------------------------------------------
28252e2caf59SThomas Veerman  * ParseReadLine --
28262e2caf59SThomas Veerman  *	Read an entire line from the input file. Called only by Parse_File.
28272e2caf59SThomas Veerman  *
28282e2caf59SThomas Veerman  * Results:
28292e2caf59SThomas Veerman  *	A line w/o its newline
28302e2caf59SThomas Veerman  *
28312e2caf59SThomas Veerman  * Side Effects:
28322e2caf59SThomas Veerman  *	Only those associated with reading a character
28332e2caf59SThomas Veerman  *---------------------------------------------------------------------
28342e2caf59SThomas Veerman  */
28352e2caf59SThomas Veerman static char *
ParseReadLine(void)28362e2caf59SThomas Veerman ParseReadLine(void)
28372e2caf59SThomas Veerman {
28382e2caf59SThomas Veerman     char 	  *line;    	/* Result */
28392e2caf59SThomas Veerman     int	    	  lineLength;	/* Length of result */
28402e2caf59SThomas Veerman     int	    	  lineno;	/* Saved line # */
28412e2caf59SThomas Veerman     int	    	  rval;
28422e2caf59SThomas Veerman 
28432e2caf59SThomas Veerman     for (;;) {
28442e2caf59SThomas Veerman 	line = ParseGetLine(0, &lineLength);
28452e2caf59SThomas Veerman 	if (line == NULL)
28462e2caf59SThomas Veerman 	    return NULL;
28472e2caf59SThomas Veerman 
28482e2caf59SThomas Veerman 	if (line[0] != '.')
28492e2caf59SThomas Veerman 	    return line;
28502e2caf59SThomas Veerman 
28512e2caf59SThomas Veerman 	/*
28522e2caf59SThomas Veerman 	 * The line might be a conditional. Ask the conditional module
28532e2caf59SThomas Veerman 	 * about it and act accordingly
28542e2caf59SThomas Veerman 	 */
28552e2caf59SThomas Veerman 	switch (Cond_Eval(line)) {
28562e2caf59SThomas Veerman 	case COND_SKIP:
28572e2caf59SThomas Veerman 	    /* Skip to next conditional that evaluates to COND_PARSE.  */
28582e2caf59SThomas Veerman 	    do {
28592e2caf59SThomas Veerman 		line = ParseGetLine(PARSE_SKIP, &lineLength);
28602e2caf59SThomas Veerman 	    } while (line && Cond_Eval(line) != COND_PARSE);
28612e2caf59SThomas Veerman 	    if (line == NULL)
28622e2caf59SThomas Veerman 		break;
28632e2caf59SThomas Veerman 	    continue;
28642e2caf59SThomas Veerman 	case COND_PARSE:
28652e2caf59SThomas Veerman 	    continue;
28662e2caf59SThomas Veerman 	case COND_INVALID:    /* Not a conditional line */
28672e2caf59SThomas Veerman 	    /* Check for .for loops */
28682e2caf59SThomas Veerman 	    rval = For_Eval(line);
28692e2caf59SThomas Veerman 	    if (rval == 0)
28702e2caf59SThomas Veerman 		/* Not a .for line */
28712e2caf59SThomas Veerman 		break;
28722e2caf59SThomas Veerman 	    if (rval < 0)
28732e2caf59SThomas Veerman 		/* Syntax error - error printed, ignore line */
28742e2caf59SThomas Veerman 		continue;
28752e2caf59SThomas Veerman 	    /* Start of a .for loop */
28762e2caf59SThomas Veerman 	    lineno = curFile->lineno;
28772e2caf59SThomas Veerman 	    /* Accumulate loop lines until matching .endfor */
28782e2caf59SThomas Veerman 	    do {
28792e2caf59SThomas Veerman 		line = ParseGetLine(PARSE_RAW, &lineLength);
28802e2caf59SThomas Veerman 		if (line == NULL) {
28812e2caf59SThomas Veerman 		    Parse_Error(PARSE_FATAL,
28822e2caf59SThomas Veerman 			     "Unexpected end of file in for loop.");
28832e2caf59SThomas Veerman 		    break;
28842e2caf59SThomas Veerman 		}
28852e2caf59SThomas Veerman 	    } while (For_Accum(line));
28862e2caf59SThomas Veerman 	    /* Stash each iteration as a new 'input file' */
28872e2caf59SThomas Veerman 	    For_Run(lineno);
28882e2caf59SThomas Veerman 	    /* Read next line from for-loop buffer */
28892e2caf59SThomas Veerman 	    continue;
28902e2caf59SThomas Veerman 	}
28912e2caf59SThomas Veerman 	return (line);
28922e2caf59SThomas Veerman     }
28932e2caf59SThomas Veerman }
28942e2caf59SThomas Veerman 
28952e2caf59SThomas Veerman /*-
28962e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28972e2caf59SThomas Veerman  * ParseFinishLine --
28982e2caf59SThomas Veerman  *	Handle the end of a dependency group.
28992e2caf59SThomas Veerman  *
29002e2caf59SThomas Veerman  * Results:
29012e2caf59SThomas Veerman  *	Nothing.
29022e2caf59SThomas Veerman  *
29032e2caf59SThomas Veerman  * Side Effects:
29042e2caf59SThomas Veerman  *	inLine set FALSE. 'targets' list destroyed.
29052e2caf59SThomas Veerman  *
29062e2caf59SThomas Veerman  *-----------------------------------------------------------------------
29072e2caf59SThomas Veerman  */
29082e2caf59SThomas Veerman static void
ParseFinishLine(void)29092e2caf59SThomas Veerman ParseFinishLine(void)
29102e2caf59SThomas Veerman {
29112e2caf59SThomas Veerman     if (inLine) {
29122e2caf59SThomas Veerman 	Lst_ForEach(targets, Suff_EndTransform, NULL);
29132e2caf59SThomas Veerman 	Lst_Destroy(targets, ParseHasCommands);
29142e2caf59SThomas Veerman 	targets = NULL;
29152e2caf59SThomas Veerman 	inLine = FALSE;
29162e2caf59SThomas Veerman     }
29172e2caf59SThomas Veerman }
29182e2caf59SThomas Veerman 
29192e2caf59SThomas Veerman 
29202e2caf59SThomas Veerman /*-
29212e2caf59SThomas Veerman  *---------------------------------------------------------------------
29222e2caf59SThomas Veerman  * Parse_File --
29232e2caf59SThomas Veerman  *	Parse a file into its component parts, incorporating it into the
29242e2caf59SThomas Veerman  *	current dependency graph. This is the main function and controls
29252e2caf59SThomas Veerman  *	almost every other function in this module
29262e2caf59SThomas Veerman  *
29272e2caf59SThomas Veerman  * Input:
29282e2caf59SThomas Veerman  *	name		the name of the file being read
29292e2caf59SThomas Veerman  *	fd		Open file to makefile to parse
29302e2caf59SThomas Veerman  *
29312e2caf59SThomas Veerman  * Results:
29322e2caf59SThomas Veerman  *	None
29332e2caf59SThomas Veerman  *
29342e2caf59SThomas Veerman  * Side Effects:
29352e2caf59SThomas Veerman  *	closes fd.
29362e2caf59SThomas Veerman  *	Loads. Nodes are added to the list of all targets, nodes and links
29372e2caf59SThomas Veerman  *	are added to the dependency graph. etc. etc. etc.
29382e2caf59SThomas Veerman  *---------------------------------------------------------------------
29392e2caf59SThomas Veerman  */
29402e2caf59SThomas Veerman void
Parse_File(const char * name,int fd)29412e2caf59SThomas Veerman Parse_File(const char *name, int fd)
29422e2caf59SThomas Veerman {
29432e2caf59SThomas Veerman     char	  *cp;		/* pointer into the line */
29442e2caf59SThomas Veerman     char          *line;	/* the line we're working on */
29452e2caf59SThomas Veerman     struct loadedfile *lf;
29462e2caf59SThomas Veerman 
29472e2caf59SThomas Veerman     lf = loadfile(name, fd);
29482e2caf59SThomas Veerman 
29492e2caf59SThomas Veerman     inLine = FALSE;
29502e2caf59SThomas Veerman     fatals = 0;
29512e2caf59SThomas Veerman 
29522e2caf59SThomas Veerman     if (name == NULL) {
29532e2caf59SThomas Veerman 	    name = "(stdin)";
29542e2caf59SThomas Veerman     }
29552e2caf59SThomas Veerman 
29562e2caf59SThomas Veerman     Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf);
29572e2caf59SThomas Veerman     curFile->lf = lf;
29582e2caf59SThomas Veerman 
29592e2caf59SThomas Veerman     do {
29602e2caf59SThomas Veerman 	for (; (line = ParseReadLine()) != NULL; ) {
29612e2caf59SThomas Veerman 	    if (DEBUG(PARSE))
29622e2caf59SThomas Veerman 		fprintf(debug_file, "ParseReadLine (%d): '%s'\n",
29632e2caf59SThomas Veerman 			curFile->lineno, line);
29642e2caf59SThomas Veerman 	    if (*line == '.') {
29652e2caf59SThomas Veerman 		/*
29662e2caf59SThomas Veerman 		 * Lines that begin with the special character may be
29672e2caf59SThomas Veerman 		 * include or undef directives.
29682e2caf59SThomas Veerman 		 * On the other hand they can be suffix rules (.c.o: ...)
29692e2caf59SThomas Veerman 		 * or just dependencies for filenames that start '.'.
29702e2caf59SThomas Veerman 		 */
29712e2caf59SThomas Veerman 		for (cp = line + 1; isspace((unsigned char)*cp); cp++) {
29722e2caf59SThomas Veerman 		    continue;
29732e2caf59SThomas Veerman 		}
29742e2caf59SThomas Veerman 		if (strncmp(cp, "include", 7) == 0 ||
29752e2caf59SThomas Veerman 			((cp[0] == 's' || cp[0] == '-') &&
29762e2caf59SThomas Veerman 			    strncmp(&cp[1], "include", 7) == 0)) {
29772e2caf59SThomas Veerman 		    ParseDoInclude(cp);
29782e2caf59SThomas Veerman 		    continue;
29792e2caf59SThomas Veerman 		}
29802e2caf59SThomas Veerman 		if (strncmp(cp, "undef", 5) == 0) {
29812e2caf59SThomas Veerman 		    char *cp2;
29822e2caf59SThomas Veerman 		    for (cp += 5; isspace((unsigned char) *cp); cp++)
29832e2caf59SThomas Veerman 			continue;
29842e2caf59SThomas Veerman 		    for (cp2 = cp; !isspace((unsigned char) *cp2) &&
29852e2caf59SThomas Veerman 				   (*cp2 != '\0'); cp2++)
29862e2caf59SThomas Veerman 			continue;
29872e2caf59SThomas Veerman 		    *cp2 = '\0';
29882e2caf59SThomas Veerman 		    Var_Delete(cp, VAR_GLOBAL);
29892e2caf59SThomas Veerman 		    continue;
29902e2caf59SThomas Veerman 		} else if (strncmp(cp, "export", 6) == 0) {
29912e2caf59SThomas Veerman 		    for (cp += 6; isspace((unsigned char) *cp); cp++)
29922e2caf59SThomas Veerman 			continue;
29932e2caf59SThomas Veerman 		    Var_Export(cp, 1);
29942e2caf59SThomas Veerman 		    continue;
29952e2caf59SThomas Veerman 		} else if (strncmp(cp, "unexport", 8) == 0) {
29962e2caf59SThomas Veerman 		    Var_UnExport(cp);
29972e2caf59SThomas Veerman 		    continue;
29982e2caf59SThomas Veerman 		} else if (strncmp(cp, "info", 4) == 0 ||
29992e2caf59SThomas Veerman 			   strncmp(cp, "error", 5) == 0 ||
30002e2caf59SThomas Veerman 			   strncmp(cp, "warning", 7) == 0) {
30012e2caf59SThomas Veerman 		    if (ParseMessage(cp))
30022e2caf59SThomas Veerman 			continue;
30032e2caf59SThomas Veerman 		}
30042e2caf59SThomas Veerman 	    }
30052e2caf59SThomas Veerman 
30062e2caf59SThomas Veerman 	    if (*line == '\t') {
30072e2caf59SThomas Veerman 		/*
30082e2caf59SThomas Veerman 		 * If a line starts with a tab, it can only hope to be
30092e2caf59SThomas Veerman 		 * a creation command.
30102e2caf59SThomas Veerman 		 */
30112e2caf59SThomas Veerman 		cp = line + 1;
30122e2caf59SThomas Veerman 	      shellCommand:
30132e2caf59SThomas Veerman 		for (; isspace ((unsigned char)*cp); cp++) {
30142e2caf59SThomas Veerman 		    continue;
30152e2caf59SThomas Veerman 		}
30162e2caf59SThomas Veerman 		if (*cp) {
30172e2caf59SThomas Veerman 		    if (!inLine)
30182e2caf59SThomas Veerman 			Parse_Error(PARSE_FATAL,
30192e2caf59SThomas Veerman 				     "Unassociated shell command \"%s\"",
30202e2caf59SThomas Veerman 				     cp);
30212e2caf59SThomas Veerman 		    /*
30222e2caf59SThomas Veerman 		     * So long as it's not a blank line and we're actually
30232e2caf59SThomas Veerman 		     * in a dependency spec, add the command to the list of
30242e2caf59SThomas Veerman 		     * commands of all targets in the dependency spec
30252e2caf59SThomas Veerman 		     */
30262e2caf59SThomas Veerman 		    if (targets) {
30272e2caf59SThomas Veerman 			cp = bmake_strdup(cp);
30282e2caf59SThomas Veerman 			Lst_ForEach(targets, ParseAddCmd, cp);
30292e2caf59SThomas Veerman #ifdef CLEANUP
30302e2caf59SThomas Veerman 			Lst_AtEnd(targCmds, cp);
30312e2caf59SThomas Veerman #endif
30322e2caf59SThomas Veerman 		    }
30332e2caf59SThomas Veerman 		}
30342e2caf59SThomas Veerman 		continue;
30352e2caf59SThomas Veerman 	    }
30362e2caf59SThomas Veerman 
30372e2caf59SThomas Veerman #ifdef SYSVINCLUDE
30382e2caf59SThomas Veerman 	    if (((strncmp(line, "include", 7) == 0 &&
30392e2caf59SThomas Veerman 		    isspace((unsigned char) line[7])) ||
30402e2caf59SThomas Veerman 			((line[0] == 's' || line[0] == '-') &&
30412e2caf59SThomas Veerman 			    strncmp(&line[1], "include", 7) == 0 &&
30422e2caf59SThomas Veerman 			    isspace((unsigned char) line[8]))) &&
30432e2caf59SThomas Veerman 		    strchr(line, ':') == NULL) {
30442e2caf59SThomas Veerman 		/*
30452e2caf59SThomas Veerman 		 * It's an S3/S5-style "include".
30462e2caf59SThomas Veerman 		 */
30472e2caf59SThomas Veerman 		ParseTraditionalInclude(line);
30482e2caf59SThomas Veerman 		continue;
30492e2caf59SThomas Veerman 	    }
30502e2caf59SThomas Veerman #endif
30512e2caf59SThomas Veerman #ifdef GMAKEEXPORT
30522e2caf59SThomas Veerman 	    if (strncmp(line, "export", 6) == 0 &&
30532e2caf59SThomas Veerman 		isspace((unsigned char) line[6]) &&
30542e2caf59SThomas Veerman 		strchr(line, ':') == NULL) {
30552e2caf59SThomas Veerman 		/*
30562bc7c627SLionel Sambuc 		 * It's a Gmake "export".
30572e2caf59SThomas Veerman 		 */
30582e2caf59SThomas Veerman 		ParseGmakeExport(line);
30592e2caf59SThomas Veerman 		continue;
30602e2caf59SThomas Veerman 	    }
30612e2caf59SThomas Veerman #endif
30622e2caf59SThomas Veerman 	    if (Parse_IsVar(line)) {
30632e2caf59SThomas Veerman 		ParseFinishLine();
30642e2caf59SThomas Veerman 		Parse_DoVar(line, VAR_GLOBAL);
30652e2caf59SThomas Veerman 		continue;
30662e2caf59SThomas Veerman 	    }
30672e2caf59SThomas Veerman 
30682e2caf59SThomas Veerman #ifndef POSIX
30692e2caf59SThomas Veerman 	    /*
30702e2caf59SThomas Veerman 	     * To make life easier on novices, if the line is indented we
30712e2caf59SThomas Veerman 	     * first make sure the line has a dependency operator in it.
30722e2caf59SThomas Veerman 	     * If it doesn't have an operator and we're in a dependency
30732e2caf59SThomas Veerman 	     * line's script, we assume it's actually a shell command
30742e2caf59SThomas Veerman 	     * and add it to the current list of targets.
30752e2caf59SThomas Veerman 	     */
30762e2caf59SThomas Veerman 	    cp = line;
30772e2caf59SThomas Veerman 	    if (isspace((unsigned char) line[0])) {
30782e2caf59SThomas Veerman 		while ((*cp != '\0') && isspace((unsigned char) *cp))
30792e2caf59SThomas Veerman 		    cp++;
30802e2caf59SThomas Veerman 		while (*cp && (ParseIsEscaped(line, cp) ||
30812e2caf59SThomas Veerman 			(*cp != ':') && (*cp != '!'))) {
30822e2caf59SThomas Veerman 		    cp++;
30832e2caf59SThomas Veerman 		}
30842e2caf59SThomas Veerman 		if (*cp == '\0') {
30852e2caf59SThomas Veerman 		    if (inLine) {
30862e2caf59SThomas Veerman 			Parse_Error(PARSE_WARNING,
30872e2caf59SThomas Veerman 				     "Shell command needs a leading tab");
30882e2caf59SThomas Veerman 			goto shellCommand;
30892e2caf59SThomas Veerman 		    }
30902e2caf59SThomas Veerman 		}
30912e2caf59SThomas Veerman 	    }
30922e2caf59SThomas Veerman #endif
30932e2caf59SThomas Veerman 	    ParseFinishLine();
30942e2caf59SThomas Veerman 
30952e2caf59SThomas Veerman 	    /*
30962e2caf59SThomas Veerman 	     * For some reason - probably to make the parser impossible -
30972e2caf59SThomas Veerman 	     * a ';' can be used to separate commands from dependencies.
30982e2caf59SThomas Veerman 	     * Attempt to avoid ';' inside substitution patterns.
30992e2caf59SThomas Veerman 	     */
31002e2caf59SThomas Veerman 	    {
31012e2caf59SThomas Veerman 		int level = 0;
31022e2caf59SThomas Veerman 
31032e2caf59SThomas Veerman 		for (cp = line; *cp != 0; cp++) {
31042e2caf59SThomas Veerman 		    if (*cp == '\\' && cp[1] != 0) {
31052e2caf59SThomas Veerman 			cp++;
31062e2caf59SThomas Veerman 			continue;
31072e2caf59SThomas Veerman 		    }
31082e2caf59SThomas Veerman 		    if (*cp == '$' &&
31092e2caf59SThomas Veerman 			(cp[1] == '(' || cp[1] == '{')) {
31102e2caf59SThomas Veerman 			level++;
31112e2caf59SThomas Veerman 			continue;
31122e2caf59SThomas Veerman 		    }
31132e2caf59SThomas Veerman 		    if (level > 0) {
31142e2caf59SThomas Veerman 			if (*cp == ')' || *cp == '}') {
31152e2caf59SThomas Veerman 			    level--;
31162e2caf59SThomas Veerman 			    continue;
31172e2caf59SThomas Veerman 			}
31182e2caf59SThomas Veerman 		    } else if (*cp == ';') {
31192e2caf59SThomas Veerman 			break;
31202e2caf59SThomas Veerman 		    }
31212e2caf59SThomas Veerman 		}
31222e2caf59SThomas Veerman 	    }
31232e2caf59SThomas Veerman 	    if (*cp != 0)
31242e2caf59SThomas Veerman 		/* Terminate the dependency list at the ';' */
31252e2caf59SThomas Veerman 		*cp++ = 0;
31262e2caf59SThomas Veerman 	    else
31272e2caf59SThomas Veerman 		cp = NULL;
31282e2caf59SThomas Veerman 
31292e2caf59SThomas Veerman 	    /*
31302e2caf59SThomas Veerman 	     * We now know it's a dependency line so it needs to have all
31312e2caf59SThomas Veerman 	     * variables expanded before being parsed. Tell the variable
31322e2caf59SThomas Veerman 	     * module to complain if some variable is undefined...
31332e2caf59SThomas Veerman 	     */
31342e2caf59SThomas Veerman 	    line = Var_Subst(NULL, line, VAR_CMD, TRUE);
31352e2caf59SThomas Veerman 
31362e2caf59SThomas Veerman 	    /*
31372e2caf59SThomas Veerman 	     * Need a non-circular list for the target nodes
31382e2caf59SThomas Veerman 	     */
31392e2caf59SThomas Veerman 	    if (targets)
31402e2caf59SThomas Veerman 		Lst_Destroy(targets, NULL);
31412e2caf59SThomas Veerman 
31422e2caf59SThomas Veerman 	    targets = Lst_Init(FALSE);
31432e2caf59SThomas Veerman 	    inLine = TRUE;
31442e2caf59SThomas Veerman 
31452e2caf59SThomas Veerman 	    ParseDoDependency(line);
31462e2caf59SThomas Veerman 	    free(line);
31472e2caf59SThomas Veerman 
31482e2caf59SThomas Veerman 	    /* If there were commands after a ';', add them now */
31492e2caf59SThomas Veerman 	    if (cp != NULL) {
31502e2caf59SThomas Veerman 		goto shellCommand;
31512e2caf59SThomas Veerman 	    }
31522e2caf59SThomas Veerman 	}
31532e2caf59SThomas Veerman 	/*
31542e2caf59SThomas Veerman 	 * Reached EOF, but it may be just EOF of an include file...
31552e2caf59SThomas Veerman 	 */
31562e2caf59SThomas Veerman     } while (ParseEOF() == CONTINUE);
31572e2caf59SThomas Veerman 
31582e2caf59SThomas Veerman     if (fatals) {
31592e2caf59SThomas Veerman 	(void)fflush(stdout);
31602e2caf59SThomas Veerman 	(void)fprintf(stderr,
31612e2caf59SThomas Veerman 	    "%s: Fatal errors encountered -- cannot continue",
31622e2caf59SThomas Veerman 	    progname);
31632e2caf59SThomas Veerman 	PrintOnError(NULL, NULL);
31642e2caf59SThomas Veerman 	exit(1);
31652e2caf59SThomas Veerman     }
31662e2caf59SThomas Veerman }
31672e2caf59SThomas Veerman 
31682e2caf59SThomas Veerman /*-
31692e2caf59SThomas Veerman  *---------------------------------------------------------------------
31702e2caf59SThomas Veerman  * Parse_Init --
31712e2caf59SThomas Veerman  *	initialize the parsing module
31722e2caf59SThomas Veerman  *
31732e2caf59SThomas Veerman  * Results:
31742e2caf59SThomas Veerman  *	none
31752e2caf59SThomas Veerman  *
31762e2caf59SThomas Veerman  * Side Effects:
31772e2caf59SThomas Veerman  *	the parseIncPath list is initialized...
31782e2caf59SThomas Veerman  *---------------------------------------------------------------------
31792e2caf59SThomas Veerman  */
31802e2caf59SThomas Veerman void
Parse_Init(void)31812e2caf59SThomas Veerman Parse_Init(void)
31822e2caf59SThomas Veerman {
31832e2caf59SThomas Veerman     mainNode = NULL;
31842e2caf59SThomas Veerman     parseIncPath = Lst_Init(FALSE);
31852e2caf59SThomas Veerman     sysIncPath = Lst_Init(FALSE);
31862e2caf59SThomas Veerman     defIncPath = Lst_Init(FALSE);
31872e2caf59SThomas Veerman     includes = Lst_Init(FALSE);
31882e2caf59SThomas Veerman #ifdef CLEANUP
31892e2caf59SThomas Veerman     targCmds = Lst_Init(FALSE);
31902e2caf59SThomas Veerman #endif
31912e2caf59SThomas Veerman }
31922e2caf59SThomas Veerman 
31932e2caf59SThomas Veerman void
Parse_End(void)31942e2caf59SThomas Veerman Parse_End(void)
31952e2caf59SThomas Veerman {
31962e2caf59SThomas Veerman #ifdef CLEANUP
31972e2caf59SThomas Veerman     Lst_Destroy(targCmds, (FreeProc *)free);
31982e2caf59SThomas Veerman     if (targets)
31992e2caf59SThomas Veerman 	Lst_Destroy(targets, NULL);
32002e2caf59SThomas Veerman     Lst_Destroy(defIncPath, Dir_Destroy);
32012e2caf59SThomas Veerman     Lst_Destroy(sysIncPath, Dir_Destroy);
32022e2caf59SThomas Veerman     Lst_Destroy(parseIncPath, Dir_Destroy);
32032e2caf59SThomas Veerman     Lst_Destroy(includes, NULL);	/* Should be empty now */
32042e2caf59SThomas Veerman #endif
32052e2caf59SThomas Veerman }
32062e2caf59SThomas Veerman 
32072e2caf59SThomas Veerman 
32082e2caf59SThomas Veerman /*-
32092e2caf59SThomas Veerman  *-----------------------------------------------------------------------
32102e2caf59SThomas Veerman  * Parse_MainName --
32112e2caf59SThomas Veerman  *	Return a Lst of the main target to create for main()'s sake. If
32122e2caf59SThomas Veerman  *	no such target exists, we Punt with an obnoxious error message.
32132e2caf59SThomas Veerman  *
32142e2caf59SThomas Veerman  * Results:
32152e2caf59SThomas Veerman  *	A Lst of the single node to create.
32162e2caf59SThomas Veerman  *
32172e2caf59SThomas Veerman  * Side Effects:
32182e2caf59SThomas Veerman  *	None.
32192e2caf59SThomas Veerman  *
32202e2caf59SThomas Veerman  *-----------------------------------------------------------------------
32212e2caf59SThomas Veerman  */
32222e2caf59SThomas Veerman Lst
Parse_MainName(void)32232e2caf59SThomas Veerman Parse_MainName(void)
32242e2caf59SThomas Veerman {
32252e2caf59SThomas Veerman     Lst           mainList;	/* result list */
32262e2caf59SThomas Veerman 
32272e2caf59SThomas Veerman     mainList = Lst_Init(FALSE);
32282e2caf59SThomas Veerman 
32292e2caf59SThomas Veerman     if (mainNode == NULL) {
32302e2caf59SThomas Veerman 	Punt("no target to make.");
32312e2caf59SThomas Veerman     	/*NOTREACHED*/
32322e2caf59SThomas Veerman     } else if (mainNode->type & OP_DOUBLEDEP) {
32332e2caf59SThomas Veerman 	(void)Lst_AtEnd(mainList, mainNode);
32342e2caf59SThomas Veerman 	Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW);
32352e2caf59SThomas Veerman     }
32362e2caf59SThomas Veerman     else
32372e2caf59SThomas Veerman 	(void)Lst_AtEnd(mainList, mainNode);
32382e2caf59SThomas Veerman     Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL);
32392e2caf59SThomas Veerman     return (mainList);
32402e2caf59SThomas Veerman }
32412e2caf59SThomas Veerman 
32422e2caf59SThomas Veerman /*-
32432e2caf59SThomas Veerman  *-----------------------------------------------------------------------
32442e2caf59SThomas Veerman  * ParseMark --
32452e2caf59SThomas Veerman  *	Add the filename and lineno to the GNode so that we remember
32462e2caf59SThomas Veerman  *	where it was first defined.
32472e2caf59SThomas Veerman  *
32482e2caf59SThomas Veerman  * Side Effects:
32492e2caf59SThomas Veerman  *	None.
32502e2caf59SThomas Veerman  *
32512e2caf59SThomas Veerman  *-----------------------------------------------------------------------
32522e2caf59SThomas Veerman  */
32532e2caf59SThomas Veerman static void
ParseMark(GNode * gn)32542e2caf59SThomas Veerman ParseMark(GNode *gn)
32552e2caf59SThomas Veerman {
32562e2caf59SThomas Veerman     gn->fname = curFile->fname;
32572e2caf59SThomas Veerman     gn->lineno = curFile->lineno;
32582e2caf59SThomas Veerman }
3259