1*22619282SSimon J. Gerraty /* $NetBSD: parse.c,v 1.734 2024/07/09 19:43:01 rillig Exp $ */ 23955d011SMarcel Moolenaar 33955d011SMarcel Moolenaar /* 43955d011SMarcel Moolenaar * Copyright (c) 1988, 1989, 1990, 1993 53955d011SMarcel Moolenaar * The Regents of the University of California. All rights reserved. 63955d011SMarcel Moolenaar * 73955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 83955d011SMarcel Moolenaar * Adam de Boor. 93955d011SMarcel Moolenaar * 103955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 113955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 123955d011SMarcel Moolenaar * are met: 133955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 143955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 153955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 163955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 173955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 183955d011SMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors 193955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 203955d011SMarcel Moolenaar * without specific prior written permission. 213955d011SMarcel Moolenaar * 223955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 233955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 243955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 253955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 263955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 273955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 283955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 293955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 303955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 313955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 323955d011SMarcel Moolenaar * SUCH DAMAGE. 333955d011SMarcel Moolenaar */ 343955d011SMarcel Moolenaar 353955d011SMarcel Moolenaar /* 363955d011SMarcel Moolenaar * Copyright (c) 1989 by Berkeley Softworks 373955d011SMarcel Moolenaar * All rights reserved. 383955d011SMarcel Moolenaar * 393955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 403955d011SMarcel Moolenaar * Adam de Boor. 413955d011SMarcel Moolenaar * 423955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 433955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 443955d011SMarcel Moolenaar * are met: 453955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 463955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 473955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 483955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 493955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 503955d011SMarcel Moolenaar * 3. All advertising materials mentioning features or use of this software 513955d011SMarcel Moolenaar * must display the following acknowledgement: 523955d011SMarcel Moolenaar * This product includes software developed by the University of 533955d011SMarcel Moolenaar * California, Berkeley and its contributors. 543955d011SMarcel Moolenaar * 4. Neither the name of the University nor the names of its contributors 553955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 563955d011SMarcel Moolenaar * without specific prior written permission. 573955d011SMarcel Moolenaar * 583955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 593955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 603955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 613955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 623955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 633955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 643955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 653955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 663955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 673955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 683955d011SMarcel Moolenaar * SUCH DAMAGE. 693955d011SMarcel Moolenaar */ 703955d011SMarcel Moolenaar 71956e45f6SSimon J. Gerraty /* 72956e45f6SSimon J. Gerraty * Parsing of makefiles. 733955d011SMarcel Moolenaar * 74956e45f6SSimon J. Gerraty * Parse_File is the main entry point and controls most of the other 75956e45f6SSimon J. Gerraty * functions in this module. 763955d011SMarcel Moolenaar * 773955d011SMarcel Moolenaar * Interface: 78956e45f6SSimon J. Gerraty * Parse_Init Initialize the module 793955d011SMarcel Moolenaar * 803955d011SMarcel Moolenaar * Parse_End Clean up the module 813955d011SMarcel Moolenaar * 82956e45f6SSimon J. Gerraty * Parse_File Parse a top-level makefile. Included files are 83dba7b0efSSimon J. Gerraty * handled by IncludeFile instead. 843955d011SMarcel Moolenaar * 859f45a3c8SSimon J. Gerraty * Parse_VarAssign 869f45a3c8SSimon J. Gerraty * Try to parse the given line as a variable assignment. 879f45a3c8SSimon J. Gerraty * Used by MainParseArgs to determine if an argument is 889f45a3c8SSimon J. Gerraty * a target or a variable assignment. Used internally 899f45a3c8SSimon J. Gerraty * for pretty much the same thing. 903955d011SMarcel Moolenaar * 91956e45f6SSimon J. Gerraty * Parse_Error Report a parse error, a warning or an informational 92956e45f6SSimon J. Gerraty * message. 93956e45f6SSimon J. Gerraty * 94c1d01b5fSSimon J. Gerraty * Parse_MainName Populate the list of targets to create. 953955d011SMarcel Moolenaar */ 963955d011SMarcel Moolenaar 973955d011SMarcel Moolenaar #include <sys/types.h> 983955d011SMarcel Moolenaar #include <sys/stat.h> 993955d011SMarcel Moolenaar #include <errno.h> 1003955d011SMarcel Moolenaar #include <stdarg.h> 1013955d011SMarcel Moolenaar 1023955d011SMarcel Moolenaar #include "make.h" 1033955d011SMarcel Moolenaar 104cac6fd11SSimon J. Gerraty #ifdef HAVE_STDINT_H 105cac6fd11SSimon J. Gerraty #include <stdint.h> 106cac6fd11SSimon J. Gerraty #endif 107cac6fd11SSimon J. Gerraty 108956e45f6SSimon J. Gerraty #include "dir.h" 109956e45f6SSimon J. Gerraty #include "job.h" 110956e45f6SSimon J. Gerraty #include "pathnames.h" 111956e45f6SSimon J. Gerraty 112956e45f6SSimon J. Gerraty /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ 113*22619282SSimon J. Gerraty MAKE_RCSID("$NetBSD: parse.c,v 1.734 2024/07/09 19:43:01 rillig Exp $"); 114148ee845SSimon J. Gerraty 115148ee845SSimon J. Gerraty /* Detects a multiple-inclusion guard in a makefile. */ 116148ee845SSimon J. Gerraty typedef enum { 117148ee845SSimon J. Gerraty GS_START, /* at the beginning of the file */ 118148ee845SSimon J. Gerraty GS_COND, /* after the guard condition */ 119148ee845SSimon J. Gerraty GS_DONE, /* after the closing .endif */ 120148ee845SSimon J. Gerraty GS_NO /* the file is not guarded */ 121148ee845SSimon J. Gerraty } GuardState; 1223955d011SMarcel Moolenaar 123d5e0a182SSimon J. Gerraty /* A file being parsed. */ 1249f45a3c8SSimon J. Gerraty typedef struct IncludedFile { 1259f45a3c8SSimon J. Gerraty FStr name; /* absolute or relative to the cwd */ 1269f45a3c8SSimon J. Gerraty unsigned lineno; /* 1-based */ 1279f45a3c8SSimon J. Gerraty unsigned readLines; /* the number of physical lines that have 1289f45a3c8SSimon J. Gerraty * been read from the file */ 1299f45a3c8SSimon J. Gerraty unsigned forHeadLineno; /* 1-based */ 1309f45a3c8SSimon J. Gerraty unsigned forBodyReadLines; /* the number of physical lines that have 1319f45a3c8SSimon J. Gerraty * been read from the file above the body of 1329f45a3c8SSimon J. Gerraty * the .for loop */ 1334fde40d9SSimon J. Gerraty unsigned int condMinDepth; /* depth of nested 'if' directives, at the 1344fde40d9SSimon J. Gerraty * beginning of the file */ 135b0c40a00SSimon J. Gerraty bool depending; /* state of doing_depend on EOF */ 136956e45f6SSimon J. Gerraty 1379f45a3c8SSimon J. Gerraty Buffer buf; /* the file's content or the body of the .for 1381d3f2ddcSSimon J. Gerraty * loop; either empty or ends with '\n' */ 139148ee845SSimon J. Gerraty char *buf_ptr; /* next char to be read from buf */ 14012904384SSimon J. Gerraty char *buf_end; /* buf_end[-1] == '\n' */ 141956e45f6SSimon J. Gerraty 142148ee845SSimon J. Gerraty GuardState guardState; 143148ee845SSimon J. Gerraty Guard *guard; 144148ee845SSimon J. Gerraty 1459f45a3c8SSimon J. Gerraty struct ForLoop *forLoop; 1469f45a3c8SSimon J. Gerraty } IncludedFile; 14706b9b3e0SSimon J. Gerraty 1489f45a3c8SSimon J. Gerraty /* Special attributes for target nodes. */ 149956e45f6SSimon J. Gerraty typedef enum ParseSpecial { 150956e45f6SSimon J. Gerraty SP_ATTRIBUTE, /* Generic attribute */ 151956e45f6SSimon J. Gerraty SP_BEGIN, /* .BEGIN */ 152956e45f6SSimon J. Gerraty SP_DEFAULT, /* .DEFAULT */ 153956e45f6SSimon J. Gerraty SP_DELETE_ON_ERROR, /* .DELETE_ON_ERROR */ 154956e45f6SSimon J. Gerraty SP_END, /* .END */ 155956e45f6SSimon J. Gerraty SP_ERROR, /* .ERROR */ 156956e45f6SSimon J. Gerraty SP_IGNORE, /* .IGNORE */ 157956e45f6SSimon J. Gerraty SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */ 158956e45f6SSimon J. Gerraty SP_INTERRUPT, /* .INTERRUPT */ 159956e45f6SSimon J. Gerraty SP_LIBS, /* .LIBS; not mentioned in the manual page */ 1609f45a3c8SSimon J. Gerraty SP_MAIN, /* .MAIN and no user-specified targets to make */ 161956e45f6SSimon J. Gerraty SP_META, /* .META */ 162956e45f6SSimon J. Gerraty SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */ 163956e45f6SSimon J. Gerraty SP_NOMETA, /* .NOMETA */ 164956e45f6SSimon J. Gerraty SP_NOMETA_CMP, /* .NOMETA_CMP */ 165956e45f6SSimon J. Gerraty SP_NOPATH, /* .NOPATH */ 1664fde40d9SSimon J. Gerraty SP_NOREADONLY, /* .NOREADONLY */ 167956e45f6SSimon J. Gerraty SP_NOT, /* Not special */ 168956e45f6SSimon J. Gerraty SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */ 169956e45f6SSimon J. Gerraty SP_NULL, /* .NULL; not mentioned in the manual page */ 170956e45f6SSimon J. Gerraty SP_OBJDIR, /* .OBJDIR */ 171956e45f6SSimon J. Gerraty SP_ORDER, /* .ORDER */ 172956e45f6SSimon J. Gerraty SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */ 173956e45f6SSimon J. Gerraty SP_PATH, /* .PATH or .PATH.suffix */ 174956e45f6SSimon J. Gerraty SP_PHONY, /* .PHONY */ 175956e45f6SSimon J. Gerraty SP_POSIX, /* .POSIX; not mentioned in the manual page */ 176956e45f6SSimon J. Gerraty SP_PRECIOUS, /* .PRECIOUS */ 1774fde40d9SSimon J. Gerraty SP_READONLY, /* .READONLY */ 178956e45f6SSimon J. Gerraty SP_SHELL, /* .SHELL */ 179956e45f6SSimon J. Gerraty SP_SILENT, /* .SILENT */ 180956e45f6SSimon J. Gerraty SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */ 181956e45f6SSimon J. Gerraty SP_STALE, /* .STALE */ 182956e45f6SSimon J. Gerraty SP_SUFFIXES, /* .SUFFIXES */ 1834fde40d9SSimon J. Gerraty SP_SYSPATH, /* .SYSPATH */ 184956e45f6SSimon J. Gerraty SP_WAIT /* .WAIT */ 1853955d011SMarcel Moolenaar } ParseSpecial; 1863955d011SMarcel Moolenaar 187956e45f6SSimon J. Gerraty typedef List SearchPathList; 188956e45f6SSimon J. Gerraty typedef ListNode SearchPathListNode; 1893955d011SMarcel Moolenaar 1909f45a3c8SSimon J. Gerraty 1919f45a3c8SSimon J. Gerraty typedef enum VarAssignOp { 1929f45a3c8SSimon J. Gerraty VAR_NORMAL, /* = */ 1939f45a3c8SSimon J. Gerraty VAR_APPEND, /* += */ 1949f45a3c8SSimon J. Gerraty VAR_DEFAULT, /* ?= */ 1959f45a3c8SSimon J. Gerraty VAR_SUBST, /* := */ 1969f45a3c8SSimon J. Gerraty VAR_SHELL /* != or :sh= */ 1979f45a3c8SSimon J. Gerraty } VarAssignOp; 1989f45a3c8SSimon J. Gerraty 1999f45a3c8SSimon J. Gerraty typedef struct VarAssign { 2009f45a3c8SSimon J. Gerraty char *varname; /* unexpanded */ 2019f45a3c8SSimon J. Gerraty VarAssignOp op; 2029f45a3c8SSimon J. Gerraty const char *value; /* unexpanded */ 2039f45a3c8SSimon J. Gerraty } VarAssign; 2049f45a3c8SSimon J. Gerraty 2059f45a3c8SSimon J. Gerraty static bool Parse_IsVar(const char *, VarAssign *); 2069f45a3c8SSimon J. Gerraty static void Parse_Var(VarAssign *, GNode *); 2073955d011SMarcel Moolenaar 2083955d011SMarcel Moolenaar /* 2099f45a3c8SSimon J. Gerraty * The target to be made if no targets are specified in the command line. 2109f45a3c8SSimon J. Gerraty * This is the first target defined in any of the makefiles. 2113955d011SMarcel Moolenaar */ 2129f45a3c8SSimon J. Gerraty GNode *mainNode; 2133955d011SMarcel Moolenaar 21406b9b3e0SSimon J. Gerraty /* 21506b9b3e0SSimon J. Gerraty * During parsing, the targets from the left-hand side of the currently 216956e45f6SSimon J. Gerraty * active dependency line, or NULL if the current line does not belong to a 217956e45f6SSimon J. Gerraty * dependency line, for example because it is a variable assignment. 218956e45f6SSimon J. Gerraty * 21906b9b3e0SSimon J. Gerraty * See unit-tests/deptgt.mk, keyword "parse.c:targets". 22006b9b3e0SSimon J. Gerraty */ 221956e45f6SSimon J. Gerraty static GNodeList *targets; 2223955d011SMarcel Moolenaar 2233955d011SMarcel Moolenaar #ifdef CLEANUP 22406b9b3e0SSimon J. Gerraty /* 22506b9b3e0SSimon J. Gerraty * All shell commands for all targets, in no particular order and possibly 2268d5c8e21SSimon J. Gerraty * with duplicate values. Kept in a separate list since the commands from 2278d5c8e21SSimon J. Gerraty * .USE or .USEBEFORE nodes are shared with other GNodes, thereby giving up 2288d5c8e21SSimon J. Gerraty * the easily understandable ownership over the allocated strings. 22906b9b3e0SSimon J. Gerraty */ 23006b9b3e0SSimon J. Gerraty static StringList targCmds = LST_INIT; 2313955d011SMarcel Moolenaar #endif 2323955d011SMarcel Moolenaar 2333955d011SMarcel Moolenaar /* 2343955d011SMarcel Moolenaar * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER 2359f45a3c8SSimon J. Gerraty * is seen, then set to each successive source on the line. 2363955d011SMarcel Moolenaar */ 237956e45f6SSimon J. Gerraty static GNode *order_pred; 2383955d011SMarcel Moolenaar 239*22619282SSimon J. Gerraty int parseErrors; 2403955d011SMarcel Moolenaar 2413955d011SMarcel Moolenaar /* 242dba7b0efSSimon J. Gerraty * The include chain of makefiles. At index 0 is the top-level makefile from 243dba7b0efSSimon J. Gerraty * the command line, followed by the included files or .for loops, up to and 244dba7b0efSSimon J. Gerraty * including the current file. 245956e45f6SSimon J. Gerraty * 246dba7b0efSSimon J. Gerraty * See PrintStackTrace for how to interpret the data. 247956e45f6SSimon J. Gerraty */ 2489f45a3c8SSimon J. Gerraty static Vector /* of IncludedFile */ includes; 2493955d011SMarcel Moolenaar 250dba7b0efSSimon J. Gerraty SearchPath *parseIncPath; /* directories for "..." includes */ 251dba7b0efSSimon J. Gerraty SearchPath *sysIncPath; /* directories for <...> includes */ 252956e45f6SSimon J. Gerraty SearchPath *defSysIncPath; /* default for sysIncPath */ 2533955d011SMarcel Moolenaar 2543955d011SMarcel Moolenaar /* 2553955d011SMarcel Moolenaar * The parseKeywords table is searched using binary search when deciding 256c1d01b5fSSimon J. Gerraty * if a target or source is special. 2573955d011SMarcel Moolenaar */ 2583955d011SMarcel Moolenaar static const struct { 2599f45a3c8SSimon J. Gerraty const char name[17]; 2609f45a3c8SSimon J. Gerraty ParseSpecial special; /* when used as a target */ 2619f45a3c8SSimon J. Gerraty GNodeType targetAttr; /* when used as a source */ 2623955d011SMarcel Moolenaar } parseKeywords[] = { 26306b9b3e0SSimon J. Gerraty { ".BEGIN", SP_BEGIN, OP_NONE }, 26406b9b3e0SSimon J. Gerraty { ".DEFAULT", SP_DEFAULT, OP_NONE }, 26506b9b3e0SSimon J. Gerraty { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, OP_NONE }, 26606b9b3e0SSimon J. Gerraty { ".END", SP_END, OP_NONE }, 26706b9b3e0SSimon J. Gerraty { ".ERROR", SP_ERROR, OP_NONE }, 268956e45f6SSimon J. Gerraty { ".EXEC", SP_ATTRIBUTE, OP_EXEC }, 269956e45f6SSimon J. Gerraty { ".IGNORE", SP_IGNORE, OP_IGNORE }, 27006b9b3e0SSimon J. Gerraty { ".INCLUDES", SP_INCLUDES, OP_NONE }, 27106b9b3e0SSimon J. Gerraty { ".INTERRUPT", SP_INTERRUPT, OP_NONE }, 272956e45f6SSimon J. Gerraty { ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE }, 273956e45f6SSimon J. Gerraty { ".JOIN", SP_ATTRIBUTE, OP_JOIN }, 27406b9b3e0SSimon J. Gerraty { ".LIBS", SP_LIBS, OP_NONE }, 275956e45f6SSimon J. Gerraty { ".MADE", SP_ATTRIBUTE, OP_MADE }, 27606b9b3e0SSimon J. Gerraty { ".MAIN", SP_MAIN, OP_NONE }, 277956e45f6SSimon J. Gerraty { ".MAKE", SP_ATTRIBUTE, OP_MAKE }, 27806b9b3e0SSimon J. Gerraty { ".MAKEFLAGS", SP_MFLAGS, OP_NONE }, 279956e45f6SSimon J. Gerraty { ".META", SP_META, OP_META }, 28006b9b3e0SSimon J. Gerraty { ".MFLAGS", SP_MFLAGS, OP_NONE }, 281956e45f6SSimon J. Gerraty { ".NOMETA", SP_NOMETA, OP_NOMETA }, 282956e45f6SSimon J. Gerraty { ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP }, 283956e45f6SSimon J. Gerraty { ".NOPATH", SP_NOPATH, OP_NOPATH }, 2844fde40d9SSimon J. Gerraty { ".NOREADONLY", SP_NOREADONLY, OP_NONE }, 285956e45f6SSimon J. Gerraty { ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN }, 28606b9b3e0SSimon J. Gerraty { ".NOTPARALLEL", SP_NOTPARALLEL, OP_NONE }, 28706b9b3e0SSimon J. Gerraty { ".NO_PARALLEL", SP_NOTPARALLEL, OP_NONE }, 28806b9b3e0SSimon J. Gerraty { ".NULL", SP_NULL, OP_NONE }, 28906b9b3e0SSimon J. Gerraty { ".OBJDIR", SP_OBJDIR, OP_NONE }, 290956e45f6SSimon J. Gerraty { ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL }, 29106b9b3e0SSimon J. Gerraty { ".ORDER", SP_ORDER, OP_NONE }, 29206b9b3e0SSimon J. Gerraty { ".PARALLEL", SP_PARALLEL, OP_NONE }, 29306b9b3e0SSimon J. Gerraty { ".PATH", SP_PATH, OP_NONE }, 294956e45f6SSimon J. Gerraty { ".PHONY", SP_PHONY, OP_PHONY }, 29506b9b3e0SSimon J. Gerraty { ".POSIX", SP_POSIX, OP_NONE }, 296956e45f6SSimon J. Gerraty { ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS }, 2974fde40d9SSimon J. Gerraty { ".READONLY", SP_READONLY, OP_NONE }, 298956e45f6SSimon J. Gerraty { ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE }, 29906b9b3e0SSimon J. Gerraty { ".SHELL", SP_SHELL, OP_NONE }, 300956e45f6SSimon J. Gerraty { ".SILENT", SP_SILENT, OP_SILENT }, 30106b9b3e0SSimon J. Gerraty { ".SINGLESHELL", SP_SINGLESHELL, OP_NONE }, 30206b9b3e0SSimon J. Gerraty { ".STALE", SP_STALE, OP_NONE }, 30306b9b3e0SSimon J. Gerraty { ".SUFFIXES", SP_SUFFIXES, OP_NONE }, 3044fde40d9SSimon J. Gerraty { ".SYSPATH", SP_SYSPATH, OP_NONE }, 305956e45f6SSimon J. Gerraty { ".USE", SP_ATTRIBUTE, OP_USE }, 306956e45f6SSimon J. Gerraty { ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE }, 30706b9b3e0SSimon J. Gerraty { ".WAIT", SP_WAIT, OP_NONE }, 3083955d011SMarcel Moolenaar }; 3093955d011SMarcel Moolenaar 3102f2a5ecdSSimon J. Gerraty enum PosixState posix_state = PS_NOT_YET; 3113955d011SMarcel Moolenaar 312148ee845SSimon J. Gerraty static HashTable /* full file name -> Guard */ guards; 313148ee845SSimon J. Gerraty 314d5e0a182SSimon J. Gerraty 315d5e0a182SSimon J. Gerraty static List * 316d5e0a182SSimon J. Gerraty Lst_New(void) 317d5e0a182SSimon J. Gerraty { 318d5e0a182SSimon J. Gerraty List *list = bmake_malloc(sizeof *list); 319d5e0a182SSimon J. Gerraty Lst_Init(list); 320d5e0a182SSimon J. Gerraty return list; 321d5e0a182SSimon J. Gerraty } 322d5e0a182SSimon J. Gerraty 323d5e0a182SSimon J. Gerraty static void 324d5e0a182SSimon J. Gerraty Lst_Free(List *list) 325d5e0a182SSimon J. Gerraty { 326d5e0a182SSimon J. Gerraty 327d5e0a182SSimon J. Gerraty Lst_Done(list); 328d5e0a182SSimon J. Gerraty free(list); 329d5e0a182SSimon J. Gerraty } 330d5e0a182SSimon J. Gerraty 3319f45a3c8SSimon J. Gerraty static IncludedFile * 3329f45a3c8SSimon J. Gerraty GetInclude(size_t i) 3333955d011SMarcel Moolenaar { 3344fde40d9SSimon J. Gerraty assert(i < includes.len); 3359f45a3c8SSimon J. Gerraty return Vector_Get(&includes, i); 3363955d011SMarcel Moolenaar } 3373955d011SMarcel Moolenaar 338d5e0a182SSimon J. Gerraty /* The makefile or the body of a .for loop that is currently being read. */ 3399f45a3c8SSimon J. Gerraty static IncludedFile * 3409f45a3c8SSimon J. Gerraty CurFile(void) 3413955d011SMarcel Moolenaar { 3429f45a3c8SSimon J. Gerraty return GetInclude(includes.len - 1); 3433955d011SMarcel Moolenaar } 3443955d011SMarcel Moolenaar 3454fde40d9SSimon J. Gerraty unsigned int 3464fde40d9SSimon J. Gerraty CurFile_CondMinDepth(void) 3474fde40d9SSimon J. Gerraty { 3484fde40d9SSimon J. Gerraty return CurFile()->condMinDepth; 3494fde40d9SSimon J. Gerraty } 3504fde40d9SSimon J. Gerraty 3519f45a3c8SSimon J. Gerraty static Buffer 352954401e6SSimon J. Gerraty LoadFile(const char *path, int fd) 353956e45f6SSimon J. Gerraty { 35406b9b3e0SSimon J. Gerraty ssize_t n; 35506b9b3e0SSimon J. Gerraty Buffer buf; 3569f45a3c8SSimon J. Gerraty size_t bufSize; 3579f45a3c8SSimon J. Gerraty struct stat st; 358956e45f6SSimon J. Gerraty 3599f45a3c8SSimon J. Gerraty bufSize = fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && 3609f45a3c8SSimon J. Gerraty st.st_size > 0 && st.st_size < 1024 * 1024 * 1024 3619f45a3c8SSimon J. Gerraty ? (size_t)st.st_size : 1024; 3629f45a3c8SSimon J. Gerraty Buf_InitSize(&buf, bufSize); 363956e45f6SSimon J. Gerraty 364e2eeea75SSimon J. Gerraty for (;;) { 36506b9b3e0SSimon J. Gerraty if (buf.len == buf.cap) { 3669f45a3c8SSimon J. Gerraty if (buf.cap >= 512 * 1024 * 1024) { 367e1cee40dSSimon J. Gerraty Error("%s: file too large", path); 36806b9b3e0SSimon J. Gerraty exit(2); /* Not 1 so -q can distinguish error */ 369e1cee40dSSimon J. Gerraty } 37006b9b3e0SSimon J. Gerraty Buf_Expand(&buf); 3713955d011SMarcel Moolenaar } 37206b9b3e0SSimon J. Gerraty assert(buf.len < buf.cap); 37306b9b3e0SSimon J. Gerraty n = read(fd, buf.data + buf.len, buf.cap - buf.len); 37406b9b3e0SSimon J. Gerraty if (n < 0) { 3753955d011SMarcel Moolenaar Error("%s: read error: %s", path, strerror(errno)); 37606b9b3e0SSimon J. Gerraty exit(2); /* Not 1 so -q can distinguish error */ 3773955d011SMarcel Moolenaar } 37806b9b3e0SSimon J. Gerraty if (n == 0) 3793955d011SMarcel Moolenaar break; 380e2eeea75SSimon J. Gerraty 38106b9b3e0SSimon J. Gerraty buf.len += (size_t)n; 3823955d011SMarcel Moolenaar } 38306b9b3e0SSimon J. Gerraty assert(buf.len <= buf.cap); 3843955d011SMarcel Moolenaar 385d5e0a182SSimon J. Gerraty if (buf.len > 0 && !Buf_EndsWith(&buf, '\n')) 38606b9b3e0SSimon J. Gerraty Buf_AddByte(&buf, '\n'); 3873955d011SMarcel Moolenaar 3889f45a3c8SSimon J. Gerraty return buf; /* may not be null-terminated */ 38906b9b3e0SSimon J. Gerraty } 3903955d011SMarcel Moolenaar 3919f45a3c8SSimon J. Gerraty /* 3929f45a3c8SSimon J. Gerraty * Print the current chain of .include and .for directives. In Parse_Fatal 3939f45a3c8SSimon J. Gerraty * or other functions that already print the location, includingInnermost 3949f45a3c8SSimon J. Gerraty * would be redundant, but in other cases like Error or Fatal it needs to be 3959f45a3c8SSimon J. Gerraty * included. 3969f45a3c8SSimon J. Gerraty */ 3979f45a3c8SSimon J. Gerraty void 3989f45a3c8SSimon J. Gerraty PrintStackTrace(bool includingInnermost) 399dba7b0efSSimon J. Gerraty { 4009f45a3c8SSimon J. Gerraty const IncludedFile *entries; 401dba7b0efSSimon J. Gerraty size_t i, n; 402dba7b0efSSimon J. Gerraty 403dba7b0efSSimon J. Gerraty n = includes.len; 404dba7b0efSSimon J. Gerraty if (n == 0) 405dba7b0efSSimon J. Gerraty return; 406dba7b0efSSimon J. Gerraty 4074fde40d9SSimon J. Gerraty entries = GetInclude(0); 4089f45a3c8SSimon J. Gerraty if (!includingInnermost && entries[n - 1].forLoop == NULL) 4099f45a3c8SSimon J. Gerraty n--; /* already in the diagnostic */ 410dba7b0efSSimon J. Gerraty 411dba7b0efSSimon J. Gerraty for (i = n; i-- > 0;) { 4129f45a3c8SSimon J. Gerraty const IncludedFile *entry = entries + i; 4139f45a3c8SSimon J. Gerraty const char *fname = entry->name.str; 414dba7b0efSSimon J. Gerraty char dirbuf[MAXPATHLEN + 1]; 415dba7b0efSSimon J. Gerraty 416c1d01b5fSSimon J. Gerraty if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) { 417c1d01b5fSSimon J. Gerraty const char *realPath = realpath(fname, dirbuf); 418c1d01b5fSSimon J. Gerraty if (realPath != NULL) 419c1d01b5fSSimon J. Gerraty fname = realPath; 420c1d01b5fSSimon J. Gerraty } 421dba7b0efSSimon J. Gerraty 4229f45a3c8SSimon J. Gerraty if (entry->forLoop != NULL) { 4239f45a3c8SSimon J. Gerraty char *details = ForLoop_Details(entry->forLoop); 4249f45a3c8SSimon J. Gerraty debug_printf("\tin .for loop from %s:%u with %s\n", 4259f45a3c8SSimon J. Gerraty fname, entry->forHeadLineno, details); 4269f45a3c8SSimon J. Gerraty free(details); 4279f45a3c8SSimon J. Gerraty } else if (i + 1 < n && entries[i + 1].forLoop != NULL) { 4289f45a3c8SSimon J. Gerraty /* entry->lineno is not a useful line number */ 4299f45a3c8SSimon J. Gerraty } else 4309f45a3c8SSimon J. Gerraty debug_printf("\tin %s:%u\n", fname, entry->lineno); 431dba7b0efSSimon J. Gerraty } 432*22619282SSimon J. Gerraty if (makelevel > 0) 433*22619282SSimon J. Gerraty debug_printf("\tin directory %s\n", curdir); 434dba7b0efSSimon J. Gerraty } 4353955d011SMarcel Moolenaar 436956e45f6SSimon J. Gerraty /* Check if the current character is escaped on the current line. */ 437b0c40a00SSimon J. Gerraty static bool 4389f45a3c8SSimon J. Gerraty IsEscaped(const char *line, const char *p) 4393955d011SMarcel Moolenaar { 4409f45a3c8SSimon J. Gerraty bool escaped = false; 4419f45a3c8SSimon J. Gerraty while (p > line && *--p == '\\') 4429f45a3c8SSimon J. Gerraty escaped = !escaped; 4439f45a3c8SSimon J. Gerraty return escaped; 4443955d011SMarcel Moolenaar } 4453955d011SMarcel Moolenaar 44606b9b3e0SSimon J. Gerraty /* 447d5e0a182SSimon J. Gerraty * Remember the location (filename and lineno) where the last command was 448d5e0a182SSimon J. Gerraty * added or where the node was mentioned in a .depend file. 44906b9b3e0SSimon J. Gerraty */ 450956e45f6SSimon J. Gerraty static void 45112904384SSimon J. Gerraty RememberLocation(GNode *gn) 452956e45f6SSimon J. Gerraty { 4539f45a3c8SSimon J. Gerraty IncludedFile *curFile = CurFile(); 4549f45a3c8SSimon J. Gerraty gn->fname = Str_Intern(curFile->name.str); 455956e45f6SSimon J. Gerraty gn->lineno = curFile->lineno; 456956e45f6SSimon J. Gerraty } 457956e45f6SSimon J. Gerraty 45806b9b3e0SSimon J. Gerraty /* 45906b9b3e0SSimon J. Gerraty * Look in the table of keywords for one matching the given string. 46006b9b3e0SSimon J. Gerraty * Return the index of the keyword, or -1 if it isn't there. 46106b9b3e0SSimon J. Gerraty */ 4623955d011SMarcel Moolenaar static int 4639f45a3c8SSimon J. Gerraty FindKeyword(const char *str) 4643955d011SMarcel Moolenaar { 465e2eeea75SSimon J. Gerraty int start = 0; 466e2eeea75SSimon J. Gerraty int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1; 4673955d011SMarcel Moolenaar 4689f45a3c8SSimon J. Gerraty while (start <= end) { 46906b9b3e0SSimon J. Gerraty int curr = start + (end - start) / 2; 47006b9b3e0SSimon J. Gerraty int diff = strcmp(str, parseKeywords[curr].name); 4713955d011SMarcel Moolenaar 472e2eeea75SSimon J. Gerraty if (diff == 0) 47306b9b3e0SSimon J. Gerraty return curr; 474e2eeea75SSimon J. Gerraty if (diff < 0) 47506b9b3e0SSimon J. Gerraty end = curr - 1; 476e2eeea75SSimon J. Gerraty else 47706b9b3e0SSimon J. Gerraty start = curr + 1; 4789f45a3c8SSimon J. Gerraty } 479e2eeea75SSimon J. Gerraty 4803841c287SSimon J. Gerraty return -1; 4813955d011SMarcel Moolenaar } 4823955d011SMarcel Moolenaar 4839f45a3c8SSimon J. Gerraty void 484954401e6SSimon J. Gerraty PrintLocation(FILE *f, bool useVars, const GNode *gn) 485956e45f6SSimon J. Gerraty { 486956e45f6SSimon J. Gerraty char dirbuf[MAXPATHLEN + 1]; 48706b9b3e0SSimon J. Gerraty FStr dir, base; 488954401e6SSimon J. Gerraty const char *fname; 489954401e6SSimon J. Gerraty unsigned lineno; 490954401e6SSimon J. Gerraty 491954401e6SSimon J. Gerraty if (gn != NULL) { 492954401e6SSimon J. Gerraty fname = gn->fname; 493954401e6SSimon J. Gerraty lineno = gn->lineno; 494954401e6SSimon J. Gerraty } else if (includes.len > 0) { 495954401e6SSimon J. Gerraty IncludedFile *curFile = CurFile(); 496954401e6SSimon J. Gerraty fname = curFile->name.str; 497954401e6SSimon J. Gerraty lineno = curFile->lineno; 498954401e6SSimon J. Gerraty } else 499954401e6SSimon J. Gerraty return; 500956e45f6SSimon J. Gerraty 5019f45a3c8SSimon J. Gerraty if (!useVars || fname[0] == '/' || strcmp(fname, "(stdin)") == 0) { 5029f45a3c8SSimon J. Gerraty (void)fprintf(f, "\"%s\" line %u: ", fname, lineno); 503956e45f6SSimon J. Gerraty return; 504956e45f6SSimon J. Gerraty } 505956e45f6SSimon J. Gerraty 506dba7b0efSSimon J. Gerraty dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR"); 50706b9b3e0SSimon J. Gerraty if (dir.str == NULL) 50806b9b3e0SSimon J. Gerraty dir.str = "."; 50906b9b3e0SSimon J. Gerraty if (dir.str[0] != '/') 51006b9b3e0SSimon J. Gerraty dir.str = realpath(dir.str, dirbuf); 511956e45f6SSimon J. Gerraty 512dba7b0efSSimon J. Gerraty base = Var_Value(SCOPE_GLOBAL, ".PARSEFILE"); 51306b9b3e0SSimon J. Gerraty if (base.str == NULL) 51406b9b3e0SSimon J. Gerraty base.str = str_basename(fname); 515956e45f6SSimon J. Gerraty 5169f45a3c8SSimon J. Gerraty (void)fprintf(f, "\"%s/%s\" line %u: ", dir.str, base.str, lineno); 51706b9b3e0SSimon J. Gerraty 51806b9b3e0SSimon J. Gerraty FStr_Done(&base); 51906b9b3e0SSimon J. Gerraty FStr_Done(&dir); 520956e45f6SSimon J. Gerraty } 521956e45f6SSimon J. Gerraty 522954401e6SSimon J. Gerraty static void MAKE_ATTR_PRINTFLIKE(5, 0) 523954401e6SSimon J. Gerraty ParseVErrorInternal(FILE *f, bool useVars, const GNode *gn, 5241d3f2ddcSSimon J. Gerraty ParseErrorLevel level, const char *fmt, va_list ap) 5253955d011SMarcel Moolenaar { 526b0c40a00SSimon J. Gerraty static bool fatal_warning_error_printed = false; 5273955d011SMarcel Moolenaar 5283955d011SMarcel Moolenaar (void)fprintf(f, "%s: ", progname); 5293955d011SMarcel Moolenaar 530954401e6SSimon J. Gerraty PrintLocation(f, useVars, gn); 5311d3f2ddcSSimon J. Gerraty if (level == PARSE_WARNING) 5323955d011SMarcel Moolenaar (void)fprintf(f, "warning: "); 5338d5c8e21SSimon J. Gerraty fprintf(f, "%s", EvalStack_Details()); 5343955d011SMarcel Moolenaar (void)vfprintf(f, fmt, ap); 5353955d011SMarcel Moolenaar (void)fprintf(f, "\n"); 5363955d011SMarcel Moolenaar (void)fflush(f); 537956e45f6SSimon J. Gerraty 5381d3f2ddcSSimon J. Gerraty if (level == PARSE_FATAL) 53912904384SSimon J. Gerraty parseErrors++; 5401d3f2ddcSSimon J. Gerraty if (level == PARSE_WARNING && opts.parseWarnFatal) { 5419f45a3c8SSimon J. Gerraty if (!fatal_warning_error_printed) { 5423955d011SMarcel Moolenaar Error("parsing warnings being treated as errors"); 543b0c40a00SSimon J. Gerraty fatal_warning_error_printed = true; 5443955d011SMarcel Moolenaar } 5459f45a3c8SSimon J. Gerraty parseErrors++; 5463955d011SMarcel Moolenaar } 5473955d011SMarcel Moolenaar 548*22619282SSimon J. Gerraty if (level == PARSE_FATAL || DEBUG(PARSE)) 5499f45a3c8SSimon J. Gerraty PrintStackTrace(false); 5509f45a3c8SSimon J. Gerraty } 5519f45a3c8SSimon J. Gerraty 552954401e6SSimon J. Gerraty static void MAKE_ATTR_PRINTFLIKE(3, 4) 553954401e6SSimon J. Gerraty ParseErrorInternal(const GNode *gn, 5541d3f2ddcSSimon J. Gerraty ParseErrorLevel level, const char *fmt, ...) 5553955d011SMarcel Moolenaar { 5563955d011SMarcel Moolenaar va_list ap; 5573955d011SMarcel Moolenaar 5583955d011SMarcel Moolenaar (void)fflush(stdout); 559e2eeea75SSimon J. Gerraty va_start(ap, fmt); 560954401e6SSimon J. Gerraty ParseVErrorInternal(stderr, false, gn, level, fmt, ap); 5613955d011SMarcel Moolenaar va_end(ap); 5623955d011SMarcel Moolenaar 5639f45a3c8SSimon J. Gerraty if (opts.debug_file != stdout && opts.debug_file != stderr) { 5643955d011SMarcel Moolenaar va_start(ap, fmt); 565954401e6SSimon J. Gerraty ParseVErrorInternal(opts.debug_file, false, gn, 5661d3f2ddcSSimon J. Gerraty level, fmt, ap); 5673955d011SMarcel Moolenaar va_end(ap); 5683955d011SMarcel Moolenaar } 5693955d011SMarcel Moolenaar } 5703955d011SMarcel Moolenaar 57106b9b3e0SSimon J. Gerraty /* 572d5e0a182SSimon J. Gerraty * Print a message, including location information. 573e2eeea75SSimon J. Gerraty * 574e2eeea75SSimon J. Gerraty * If the level is PARSE_FATAL, continue parsing until the end of the 575e2eeea75SSimon J. Gerraty * current top-level makefile, then exit (see Parse_File). 5763955d011SMarcel Moolenaar * 57706b9b3e0SSimon J. Gerraty * Fmt is given without a trailing newline. 57806b9b3e0SSimon J. Gerraty */ 5793955d011SMarcel Moolenaar void 5801d3f2ddcSSimon J. Gerraty Parse_Error(ParseErrorLevel level, const char *fmt, ...) 5813955d011SMarcel Moolenaar { 5823955d011SMarcel Moolenaar va_list ap; 5833955d011SMarcel Moolenaar 5843955d011SMarcel Moolenaar (void)fflush(stdout); 5859f45a3c8SSimon J. Gerraty va_start(ap, fmt); 586954401e6SSimon J. Gerraty ParseVErrorInternal(stderr, true, NULL, level, fmt, ap); 5873955d011SMarcel Moolenaar va_end(ap); 5883955d011SMarcel Moolenaar 5899f45a3c8SSimon J. Gerraty if (opts.debug_file != stdout && opts.debug_file != stderr) { 5903955d011SMarcel Moolenaar va_start(ap, fmt); 591954401e6SSimon J. Gerraty ParseVErrorInternal(opts.debug_file, true, NULL, 5921d3f2ddcSSimon J. Gerraty level, fmt, ap); 5933955d011SMarcel Moolenaar va_end(ap); 5943955d011SMarcel Moolenaar } 5953955d011SMarcel Moolenaar } 5963955d011SMarcel Moolenaar 5973955d011SMarcel Moolenaar 59806b9b3e0SSimon J. Gerraty /* 5999f45a3c8SSimon J. Gerraty * Handle an .info, .warning or .error directive. For an .error directive, 6009f45a3c8SSimon J. Gerraty * exit immediately. 60106b9b3e0SSimon J. Gerraty */ 60206b9b3e0SSimon J. Gerraty static void 6039f45a3c8SSimon J. Gerraty HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg) 6043955d011SMarcel Moolenaar { 60506b9b3e0SSimon J. Gerraty char *xmsg; 6063955d011SMarcel Moolenaar 60706b9b3e0SSimon J. Gerraty if (umsg[0] == '\0') { 60806b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Missing argument for \".%s\"", 60906b9b3e0SSimon J. Gerraty levelName); 61006b9b3e0SSimon J. Gerraty return; 61106b9b3e0SSimon J. Gerraty } 6123955d011SMarcel Moolenaar 6138d5c8e21SSimon J. Gerraty xmsg = Var_Subst(umsg, SCOPE_CMDLINE, VARE_EVAL); 614956e45f6SSimon J. Gerraty /* TODO: handle errors */ 6153955d011SMarcel Moolenaar 61606b9b3e0SSimon J. Gerraty Parse_Error(level, "%s", xmsg); 61706b9b3e0SSimon J. Gerraty free(xmsg); 6183955d011SMarcel Moolenaar 61906b9b3e0SSimon J. Gerraty if (level == PARSE_FATAL) { 6209f45a3c8SSimon J. Gerraty PrintOnError(NULL, "\n"); 6213955d011SMarcel Moolenaar exit(1); 6223955d011SMarcel Moolenaar } 6233955d011SMarcel Moolenaar } 6243955d011SMarcel Moolenaar 62506b9b3e0SSimon J. Gerraty /* 6269f45a3c8SSimon J. Gerraty * Add the child to the parent's children, and for non-special targets, vice 627d5e0a182SSimon J. Gerraty * versa. 62806b9b3e0SSimon J. Gerraty */ 629956e45f6SSimon J. Gerraty static void 630b0c40a00SSimon J. Gerraty LinkSource(GNode *pgn, GNode *cgn, bool isSpecial) 6313955d011SMarcel Moolenaar { 63206b9b3e0SSimon J. Gerraty if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(&pgn->cohorts)) 63306b9b3e0SSimon J. Gerraty pgn = pgn->cohorts.last->datum; 634956e45f6SSimon J. Gerraty 63506b9b3e0SSimon J. Gerraty Lst_Append(&pgn->children, cgn); 636956e45f6SSimon J. Gerraty pgn->unmade++; 637956e45f6SSimon J. Gerraty 638d5e0a182SSimon J. Gerraty /* 639d5e0a182SSimon J. Gerraty * Special targets like .END do not need to be informed once the child 640d5e0a182SSimon J. Gerraty * target has been made. 641d5e0a182SSimon J. Gerraty */ 642956e45f6SSimon J. Gerraty if (!isSpecial) 64306b9b3e0SSimon J. Gerraty Lst_Append(&cgn->parents, pgn); 644956e45f6SSimon J. Gerraty 6453955d011SMarcel Moolenaar if (DEBUG(PARSE)) { 6468d5c8e21SSimon J. Gerraty debug_printf("Target \"%s\" depends on \"%s\"\n", 6479f45a3c8SSimon J. Gerraty pgn->name, cgn->name); 6483955d011SMarcel Moolenaar Targ_PrintNode(pgn, 0); 6493955d011SMarcel Moolenaar Targ_PrintNode(cgn, 0); 6503955d011SMarcel Moolenaar } 6513955d011SMarcel Moolenaar } 6523955d011SMarcel Moolenaar 653956e45f6SSimon J. Gerraty /* Add the node to each target from the current dependency group. */ 654956e45f6SSimon J. Gerraty static void 655b0c40a00SSimon J. Gerraty LinkToTargets(GNode *gn, bool isSpecial) 6563955d011SMarcel Moolenaar { 657956e45f6SSimon J. Gerraty GNodeListNode *ln; 65806b9b3e0SSimon J. Gerraty 659956e45f6SSimon J. Gerraty for (ln = targets->first; ln != NULL; ln = ln->next) 660956e45f6SSimon J. Gerraty LinkSource(ln->datum, gn, isSpecial); 661956e45f6SSimon J. Gerraty } 662956e45f6SSimon J. Gerraty 663b0c40a00SSimon J. Gerraty static bool 664956e45f6SSimon J. Gerraty TryApplyDependencyOperator(GNode *gn, GNodeType op) 665956e45f6SSimon J. Gerraty { 6663955d011SMarcel Moolenaar /* 667956e45f6SSimon J. Gerraty * If the node occurred on the left-hand side of a dependency and the 668956e45f6SSimon J. Gerraty * operator also defines a dependency, they must match. 6693955d011SMarcel Moolenaar */ 670956e45f6SSimon J. Gerraty if ((op & OP_OPMASK) && (gn->type & OP_OPMASK) && 67106b9b3e0SSimon J. Gerraty ((op & OP_OPMASK) != (gn->type & OP_OPMASK))) { 67206b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", 67306b9b3e0SSimon J. Gerraty gn->name); 674b0c40a00SSimon J. Gerraty return false; 6753955d011SMarcel Moolenaar } 6763955d011SMarcel Moolenaar 6772c3632d1SSimon J. Gerraty if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { 6783955d011SMarcel Moolenaar /* 679c1d01b5fSSimon J. Gerraty * If the node was on the left-hand side of a '::' operator, 680d5e0a182SSimon J. Gerraty * create a new node for the children and commands on this 681d5e0a182SSimon J. Gerraty * dependency line, since each of these dependency groups has 682d5e0a182SSimon J. Gerraty * its own attributes and commands, separate from the others. 683e2eeea75SSimon J. Gerraty * 684e2eeea75SSimon J. Gerraty * The new instance is placed on the 'cohorts' list of the 68506b9b3e0SSimon J. Gerraty * initial one (note the initial one is not on its own 68606b9b3e0SSimon J. Gerraty * cohorts list) and the new instance is linked to all 68706b9b3e0SSimon J. Gerraty * parents of the initial instance. 6883955d011SMarcel Moolenaar */ 6893955d011SMarcel Moolenaar GNode *cohort; 6903955d011SMarcel Moolenaar 6913955d011SMarcel Moolenaar /* 69206b9b3e0SSimon J. Gerraty * Propagate copied bits to the initial node. They'll be 69306b9b3e0SSimon J. Gerraty * propagated back to the rest of the cohorts later. 6943955d011SMarcel Moolenaar */ 6954fde40d9SSimon J. Gerraty gn->type |= op & (unsigned)~OP_OPMASK; 6963955d011SMarcel Moolenaar 697956e45f6SSimon J. Gerraty cohort = Targ_NewInternalNode(gn->name); 6981748de26SSimon J. Gerraty if (doing_depend) 69912904384SSimon J. Gerraty RememberLocation(cohort); 7003955d011SMarcel Moolenaar /* 701d5e0a182SSimon J. Gerraty * Make the cohort invisible to avoid duplicating it 70206b9b3e0SSimon J. Gerraty * into other variables. True, parents of this target won't 70306b9b3e0SSimon J. Gerraty * tend to do anything with their local variables, but better 70406b9b3e0SSimon J. Gerraty * safe than sorry. 70506b9b3e0SSimon J. Gerraty * 70606b9b3e0SSimon J. Gerraty * (I think this is pointless now, since the relevant list 7073955d011SMarcel Moolenaar * traversals will no longer see this node anyway. -mycroft) 7083955d011SMarcel Moolenaar */ 7093955d011SMarcel Moolenaar cohort->type = op | OP_INVISIBLE; 71006b9b3e0SSimon J. Gerraty Lst_Append(&gn->cohorts, cohort); 7113955d011SMarcel Moolenaar cohort->centurion = gn; 712956e45f6SSimon J. Gerraty gn->unmade_cohorts++; 7133955d011SMarcel Moolenaar snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", 714956e45f6SSimon J. Gerraty (unsigned int)gn->unmade_cohorts % 1000000); 7153955d011SMarcel Moolenaar } else { 716d5e0a182SSimon J. Gerraty gn->type |= op; /* preserve any previous flags */ 7173955d011SMarcel Moolenaar } 7183955d011SMarcel Moolenaar 719b0c40a00SSimon J. Gerraty return true; 7203955d011SMarcel Moolenaar } 7213955d011SMarcel Moolenaar 7223955d011SMarcel Moolenaar static void 723956e45f6SSimon J. Gerraty ApplyDependencyOperator(GNodeType op) 7243955d011SMarcel Moolenaar { 725956e45f6SSimon J. Gerraty GNodeListNode *ln; 72606b9b3e0SSimon J. Gerraty 727956e45f6SSimon J. Gerraty for (ln = targets->first; ln != NULL; ln = ln->next) 728956e45f6SSimon J. Gerraty if (!TryApplyDependencyOperator(ln->datum, op)) 729956e45f6SSimon J. Gerraty break; 730956e45f6SSimon J. Gerraty } 731956e45f6SSimon J. Gerraty 73206b9b3e0SSimon J. Gerraty /* 733d5e0a182SSimon J. Gerraty * Add a .WAIT node in the dependency list. After any dynamic dependencies 73406b9b3e0SSimon J. Gerraty * (and filename globbing) have happened, it is given a dependency on each 73506b9b3e0SSimon J. Gerraty * previous child, back until the previous .WAIT node. The next child won't 73606b9b3e0SSimon J. Gerraty * be scheduled until the .WAIT node is built. 73706b9b3e0SSimon J. Gerraty * 738d5e0a182SSimon J. Gerraty * Give each .WAIT node a unique name (mainly for diagnostics). 73906b9b3e0SSimon J. Gerraty */ 74006b9b3e0SSimon J. Gerraty static void 7419f45a3c8SSimon J. Gerraty ApplyDependencySourceWait(bool isSpecial) 742956e45f6SSimon J. Gerraty { 7439f45a3c8SSimon J. Gerraty static unsigned wait_number = 0; 744954401e6SSimon J. Gerraty char name[6 + 10 + 1]; 745956e45f6SSimon J. Gerraty GNode *gn; 7463955d011SMarcel Moolenaar 747954401e6SSimon J. Gerraty snprintf(name, sizeof name, ".WAIT_%u", ++wait_number); 748954401e6SSimon J. Gerraty gn = Targ_NewInternalNode(name); 7491748de26SSimon J. Gerraty if (doing_depend) 75012904384SSimon J. Gerraty RememberLocation(gn); 7513955d011SMarcel Moolenaar gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; 75206b9b3e0SSimon J. Gerraty LinkToTargets(gn, isSpecial); 75306b9b3e0SSimon J. Gerraty } 75406b9b3e0SSimon J. Gerraty 755b0c40a00SSimon J. Gerraty static bool 7569f45a3c8SSimon J. Gerraty ApplyDependencySourceKeyword(const char *src, ParseSpecial special) 75706b9b3e0SSimon J. Gerraty { 75806b9b3e0SSimon J. Gerraty int keywd; 7599f45a3c8SSimon J. Gerraty GNodeType targetAttr; 76006b9b3e0SSimon J. Gerraty 76106b9b3e0SSimon J. Gerraty if (*src != '.' || !ch_isupper(src[1])) 762b0c40a00SSimon J. Gerraty return false; 76306b9b3e0SSimon J. Gerraty 7649f45a3c8SSimon J. Gerraty keywd = FindKeyword(src); 76506b9b3e0SSimon J. Gerraty if (keywd == -1) 766b0c40a00SSimon J. Gerraty return false; 76706b9b3e0SSimon J. Gerraty 7689f45a3c8SSimon J. Gerraty targetAttr = parseKeywords[keywd].targetAttr; 7699f45a3c8SSimon J. Gerraty if (targetAttr != OP_NONE) { 7709f45a3c8SSimon J. Gerraty ApplyDependencyOperator(targetAttr); 771b0c40a00SSimon J. Gerraty return true; 7723955d011SMarcel Moolenaar } 7739f45a3c8SSimon J. Gerraty if (parseKeywords[keywd].special == SP_WAIT) { 7749f45a3c8SSimon J. Gerraty ApplyDependencySourceWait(special != SP_NOT); 775b0c40a00SSimon J. Gerraty return true; 7763955d011SMarcel Moolenaar } 777b0c40a00SSimon J. Gerraty return false; 778956e45f6SSimon J. Gerraty } 7793955d011SMarcel Moolenaar 7803955d011SMarcel Moolenaar /* 7819f45a3c8SSimon J. Gerraty * In a line like ".MAIN: source1 source2", add all sources to the list of 7829f45a3c8SSimon J. Gerraty * things to create, but only if the user didn't specify a target on the 7839f45a3c8SSimon J. Gerraty * command line and .MAIN occurs for the first time. 784e2eeea75SSimon J. Gerraty * 7859f45a3c8SSimon J. Gerraty * See HandleDependencyTargetSpecial, branch SP_MAIN. 78606b9b3e0SSimon J. Gerraty * See unit-tests/cond-func-make-main.mk. 7873955d011SMarcel Moolenaar */ 7889f45a3c8SSimon J. Gerraty static void 7899f45a3c8SSimon J. Gerraty ApplyDependencySourceMain(const char *src) 7909f45a3c8SSimon J. Gerraty { 79106b9b3e0SSimon J. Gerraty Lst_Append(&opts.create, bmake_strdup(src)); 7923955d011SMarcel Moolenaar /* 7933955d011SMarcel Moolenaar * Add the name to the .TARGETS variable as well, so the user can 7943955d011SMarcel Moolenaar * employ that, if desired. 7953955d011SMarcel Moolenaar */ 796dba7b0efSSimon J. Gerraty Global_Append(".TARGETS", src); 797956e45f6SSimon J. Gerraty } 7983955d011SMarcel Moolenaar 7991d3f2ddcSSimon J. Gerraty /* 8001d3f2ddcSSimon J. Gerraty * For the sources of a .ORDER target, create predecessor/successor links 8011d3f2ddcSSimon J. Gerraty * between the previous source and the current one. 8021d3f2ddcSSimon J. Gerraty */ 803956e45f6SSimon J. Gerraty static void 8049f45a3c8SSimon J. Gerraty ApplyDependencySourceOrder(const char *src) 805956e45f6SSimon J. Gerraty { 806956e45f6SSimon J. Gerraty GNode *gn; 8071d3f2ddcSSimon J. Gerraty 808956e45f6SSimon J. Gerraty gn = Targ_GetNode(src); 8091748de26SSimon J. Gerraty if (doing_depend) 81012904384SSimon J. Gerraty RememberLocation(gn); 811956e45f6SSimon J. Gerraty if (order_pred != NULL) { 81206b9b3e0SSimon J. Gerraty Lst_Append(&order_pred->order_succ, gn); 81306b9b3e0SSimon J. Gerraty Lst_Append(&gn->order_pred, order_pred); 8143955d011SMarcel Moolenaar if (DEBUG(PARSE)) { 8159f45a3c8SSimon J. Gerraty debug_printf( 8169f45a3c8SSimon J. Gerraty "# .ORDER forces '%s' to be made before '%s'\n", 8179f45a3c8SSimon J. Gerraty order_pred->name, gn->name); 818956e45f6SSimon J. Gerraty Targ_PrintNode(order_pred, 0); 8193955d011SMarcel Moolenaar Targ_PrintNode(gn, 0); 8203955d011SMarcel Moolenaar } 8213955d011SMarcel Moolenaar } 822d5e0a182SSimon J. Gerraty /* The current source now becomes the predecessor for the next one. */ 823956e45f6SSimon J. Gerraty order_pred = gn; 824956e45f6SSimon J. Gerraty } 8253955d011SMarcel Moolenaar 8269f45a3c8SSimon J. Gerraty /* The source is not an attribute, so find/create a node for it. */ 827956e45f6SSimon J. Gerraty static void 8289f45a3c8SSimon J. Gerraty ApplyDependencySourceOther(const char *src, GNodeType targetAttr, 8299f45a3c8SSimon J. Gerraty ParseSpecial special) 830956e45f6SSimon J. Gerraty { 831956e45f6SSimon J. Gerraty GNode *gn; 832956e45f6SSimon J. Gerraty 833956e45f6SSimon J. Gerraty gn = Targ_GetNode(src); 8341748de26SSimon J. Gerraty if (doing_depend) 83512904384SSimon J. Gerraty RememberLocation(gn); 8369f45a3c8SSimon J. Gerraty if (targetAttr != OP_NONE) 8379f45a3c8SSimon J. Gerraty gn->type |= targetAttr; 838e2eeea75SSimon J. Gerraty else 8399f45a3c8SSimon J. Gerraty LinkToTargets(gn, special != SP_NOT); 8403955d011SMarcel Moolenaar } 8413955d011SMarcel Moolenaar 84206b9b3e0SSimon J. Gerraty /* 84306b9b3e0SSimon J. Gerraty * Given the name of a source in a dependency line, figure out if it is an 8449f45a3c8SSimon J. Gerraty * attribute (such as .SILENT) and if so, apply it to all targets. Otherwise 845956e45f6SSimon J. Gerraty * decide if there is some attribute which should be applied *to* the source 846956e45f6SSimon J. Gerraty * because of some special target (such as .PHONY) and apply it if so. 8479f45a3c8SSimon J. Gerraty * Otherwise, make the source a child of the targets. 8483955d011SMarcel Moolenaar */ 849956e45f6SSimon J. Gerraty static void 8509f45a3c8SSimon J. Gerraty ApplyDependencySource(GNodeType targetAttr, const char *src, 8519f45a3c8SSimon J. Gerraty ParseSpecial special) 8523955d011SMarcel Moolenaar { 8539f45a3c8SSimon J. Gerraty if (ApplyDependencySourceKeyword(src, special)) 854956e45f6SSimon J. Gerraty return; 855956e45f6SSimon J. Gerraty 8569f45a3c8SSimon J. Gerraty if (special == SP_MAIN) 8579f45a3c8SSimon J. Gerraty ApplyDependencySourceMain(src); 8589f45a3c8SSimon J. Gerraty else if (special == SP_ORDER) 8599f45a3c8SSimon J. Gerraty ApplyDependencySourceOrder(src); 860956e45f6SSimon J. Gerraty else 8619f45a3c8SSimon J. Gerraty ApplyDependencySourceOther(src, targetAttr, special); 862956e45f6SSimon J. Gerraty } 863956e45f6SSimon J. Gerraty 86406b9b3e0SSimon J. Gerraty /* 86506b9b3e0SSimon J. Gerraty * If we have yet to decide on a main target to make, in the absence of any 866956e45f6SSimon J. Gerraty * user input, we want the first target on the first dependency line that is 86706b9b3e0SSimon J. Gerraty * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. 86806b9b3e0SSimon J. Gerraty */ 869956e45f6SSimon J. Gerraty static void 8709f45a3c8SSimon J. Gerraty MaybeUpdateMainTarget(void) 871956e45f6SSimon J. Gerraty { 872956e45f6SSimon J. Gerraty GNodeListNode *ln; 873956e45f6SSimon J. Gerraty 874956e45f6SSimon J. Gerraty if (mainNode != NULL) 875956e45f6SSimon J. Gerraty return; 876956e45f6SSimon J. Gerraty 877956e45f6SSimon J. Gerraty for (ln = targets->first; ln != NULL; ln = ln->next) { 878956e45f6SSimon J. Gerraty GNode *gn = ln->datum; 8799f45a3c8SSimon J. Gerraty if (GNode_IsMainCandidate(gn)) { 880d5e0a182SSimon J. Gerraty DEBUG1(MAKE, "Setting main node to \"%s\"\n", 881d5e0a182SSimon J. Gerraty gn->name); 8823955d011SMarcel Moolenaar mainNode = gn; 883956e45f6SSimon J. Gerraty return; 884956e45f6SSimon J. Gerraty } 8853955d011SMarcel Moolenaar } 8863955d011SMarcel Moolenaar } 8873955d011SMarcel Moolenaar 8883955d011SMarcel Moolenaar static void 88998875883SSimon J. Gerraty InvalidLineType(const char *line, const char *unexpanded_line) 8903955d011SMarcel Moolenaar { 89198875883SSimon J. Gerraty if (unexpanded_line[0] == '.') { 89298875883SSimon J. Gerraty const char *dirstart = unexpanded_line + 1; 893956e45f6SSimon J. Gerraty const char *dirend; 894956e45f6SSimon J. Gerraty cpp_skip_whitespace(&dirstart); 895956e45f6SSimon J. Gerraty dirend = dirstart; 896956e45f6SSimon J. Gerraty while (ch_isalnum(*dirend) || *dirend == '-') 897956e45f6SSimon J. Gerraty dirend++; 898956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", 899956e45f6SSimon J. Gerraty (int)(dirend - dirstart), dirstart); 90098875883SSimon J. Gerraty } else if (strcmp(line, unexpanded_line) == 0) 90198875883SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Invalid line '%s'", line); 90298875883SSimon J. Gerraty else 903d5e0a182SSimon J. Gerraty Parse_Error(PARSE_FATAL, 904d5e0a182SSimon J. Gerraty "Invalid line '%s', expanded to '%s'", 90598875883SSimon J. Gerraty unexpanded_line, line); 906956e45f6SSimon J. Gerraty } 9073955d011SMarcel Moolenaar 908956e45f6SSimon J. Gerraty static void 9099f45a3c8SSimon J. Gerraty ParseDependencyTargetWord(char **pp, const char *lstart) 910956e45f6SSimon J. Gerraty { 911d5e0a182SSimon J. Gerraty const char *p = *pp; 9123955d011SMarcel Moolenaar 913d5e0a182SSimon J. Gerraty while (*p != '\0') { 914d5e0a182SSimon J. Gerraty if ((ch_isspace(*p) || *p == '!' || *p == ':' || *p == '(') 915d5e0a182SSimon J. Gerraty && !IsEscaped(lstart, p)) 916956e45f6SSimon J. Gerraty break; 9173955d011SMarcel Moolenaar 918d5e0a182SSimon J. Gerraty if (*p == '$') { 9198d5c8e21SSimon J. Gerraty FStr val = Var_Parse(&p, SCOPE_CMDLINE, VARE_PARSE); 920d5e0a182SSimon J. Gerraty /* TODO: handle errors */ 9219f45a3c8SSimon J. Gerraty FStr_Done(&val); 9222c3632d1SSimon J. Gerraty } else 923d5e0a182SSimon J. Gerraty p++; 9243955d011SMarcel Moolenaar } 925db29cad8SSimon J. Gerraty 926d5e0a182SSimon J. Gerraty *pp += p - *pp; 9273955d011SMarcel Moolenaar } 9283955d011SMarcel Moolenaar 929b0c40a00SSimon J. Gerraty /* 930b0c40a00SSimon J. Gerraty * Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. 931b0c40a00SSimon J. Gerraty * 932b0c40a00SSimon J. Gerraty * See the tests deptgt-*.mk. 933b0c40a00SSimon J. Gerraty */ 934956e45f6SSimon J. Gerraty static void 9359f45a3c8SSimon J. Gerraty HandleDependencyTargetSpecial(const char *targetName, 9369f45a3c8SSimon J. Gerraty ParseSpecial *inout_special, 937956e45f6SSimon J. Gerraty SearchPathList **inout_paths) 938956e45f6SSimon J. Gerraty { 9399f45a3c8SSimon J. Gerraty switch (*inout_special) { 940956e45f6SSimon J. Gerraty case SP_PATH: 941e2eeea75SSimon J. Gerraty if (*inout_paths == NULL) 942956e45f6SSimon J. Gerraty *inout_paths = Lst_New(); 94306b9b3e0SSimon J. Gerraty Lst_Append(*inout_paths, &dirSearchPath); 9443955d011SMarcel Moolenaar break; 9454fde40d9SSimon J. Gerraty case SP_SYSPATH: 9464fde40d9SSimon J. Gerraty if (*inout_paths == NULL) 9474fde40d9SSimon J. Gerraty *inout_paths = Lst_New(); 9484fde40d9SSimon J. Gerraty Lst_Append(*inout_paths, sysIncPath); 9494fde40d9SSimon J. Gerraty break; 950956e45f6SSimon J. Gerraty case SP_MAIN: 95106b9b3e0SSimon J. Gerraty /* 95206b9b3e0SSimon J. Gerraty * Allow targets from the command line to override the 95306b9b3e0SSimon J. Gerraty * .MAIN node. 95406b9b3e0SSimon J. Gerraty */ 95506b9b3e0SSimon J. Gerraty if (!Lst_IsEmpty(&opts.create)) 9569f45a3c8SSimon J. Gerraty *inout_special = SP_NOT; 9573955d011SMarcel Moolenaar break; 958956e45f6SSimon J. Gerraty case SP_BEGIN: 959956e45f6SSimon J. Gerraty case SP_END: 960956e45f6SSimon J. Gerraty case SP_STALE: 961956e45f6SSimon J. Gerraty case SP_ERROR: 962956e45f6SSimon J. Gerraty case SP_INTERRUPT: { 963dba7b0efSSimon J. Gerraty GNode *gn = Targ_GetNode(targetName); 9641748de26SSimon J. Gerraty if (doing_depend) 96512904384SSimon J. Gerraty RememberLocation(gn); 9663955d011SMarcel Moolenaar gn->type |= OP_NOTMAIN | OP_SPECIAL; 9672c3632d1SSimon J. Gerraty Lst_Append(targets, gn); 9683955d011SMarcel Moolenaar break; 969956e45f6SSimon J. Gerraty } 970956e45f6SSimon J. Gerraty case SP_DEFAULT: { 97106b9b3e0SSimon J. Gerraty /* 97206b9b3e0SSimon J. Gerraty * Need to create a node to hang commands on, but we don't 97306b9b3e0SSimon J. Gerraty * want it in the graph, nor do we want it to be the Main 97406b9b3e0SSimon J. Gerraty * Target. We claim the node is a transformation rule to make 97506b9b3e0SSimon J. Gerraty * life easier later, when we'll use Make_HandleUse to 97606b9b3e0SSimon J. Gerraty * actually apply the .DEFAULT commands. 97706b9b3e0SSimon J. Gerraty */ 978e2eeea75SSimon J. Gerraty GNode *gn = GNode_New(".DEFAULT"); 979956e45f6SSimon J. Gerraty gn->type |= OP_NOTMAIN | OP_TRANSFORM; 9802c3632d1SSimon J. Gerraty Lst_Append(targets, gn); 981e2eeea75SSimon J. Gerraty defaultNode = gn; 9823955d011SMarcel Moolenaar break; 983956e45f6SSimon J. Gerraty } 984956e45f6SSimon J. Gerraty case SP_DELETE_ON_ERROR: 985b0c40a00SSimon J. Gerraty deleteOnError = true; 98645447996SSimon J. Gerraty break; 987956e45f6SSimon J. Gerraty case SP_NOTPARALLEL: 988956e45f6SSimon J. Gerraty opts.maxJobs = 1; 9893955d011SMarcel Moolenaar break; 990956e45f6SSimon J. Gerraty case SP_SINGLESHELL: 991b0c40a00SSimon J. Gerraty opts.compatMake = true; 9923955d011SMarcel Moolenaar break; 993956e45f6SSimon J. Gerraty case SP_ORDER: 994956e45f6SSimon J. Gerraty order_pred = NULL; 9953955d011SMarcel Moolenaar break; 9963955d011SMarcel Moolenaar default: 9973955d011SMarcel Moolenaar break; 9983955d011SMarcel Moolenaar } 999956e45f6SSimon J. Gerraty } 1000956e45f6SSimon J. Gerraty 1001b0c40a00SSimon J. Gerraty static bool 10029f45a3c8SSimon J. Gerraty HandleDependencyTargetPath(const char *suffixName, 100306b9b3e0SSimon J. Gerraty SearchPathList **inout_paths) 1004956e45f6SSimon J. Gerraty { 1005956e45f6SSimon J. Gerraty SearchPath *path; 10063955d011SMarcel Moolenaar 1007dba7b0efSSimon J. Gerraty path = Suff_GetPath(suffixName); 10083955d011SMarcel Moolenaar if (path == NULL) { 10093955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, 1010dba7b0efSSimon J. Gerraty "Suffix '%s' not defined (yet)", suffixName); 1011b0c40a00SSimon J. Gerraty return false; 1012e2eeea75SSimon J. Gerraty } 1013e2eeea75SSimon J. Gerraty 1014e2eeea75SSimon J. Gerraty if (*inout_paths == NULL) 1015956e45f6SSimon J. Gerraty *inout_paths = Lst_New(); 1016956e45f6SSimon J. Gerraty Lst_Append(*inout_paths, path); 1017e2eeea75SSimon J. Gerraty 1018b0c40a00SSimon J. Gerraty return true; 10193955d011SMarcel Moolenaar } 10203955d011SMarcel Moolenaar 10219f45a3c8SSimon J. Gerraty /* See if it's a special target and if so set inout_special to match it. */ 1022b0c40a00SSimon J. Gerraty static bool 10239f45a3c8SSimon J. Gerraty HandleDependencyTarget(const char *targetName, 10249f45a3c8SSimon J. Gerraty ParseSpecial *inout_special, 10259f45a3c8SSimon J. Gerraty GNodeType *inout_targetAttr, 10269f45a3c8SSimon J. Gerraty SearchPathList **inout_paths) 1027956e45f6SSimon J. Gerraty { 1028956e45f6SSimon J. Gerraty int keywd; 1029956e45f6SSimon J. Gerraty 1030dba7b0efSSimon J. Gerraty if (!(targetName[0] == '.' && ch_isupper(targetName[1]))) 1031b0c40a00SSimon J. Gerraty return true; 1032956e45f6SSimon J. Gerraty 1033956e45f6SSimon J. Gerraty /* 1034956e45f6SSimon J. Gerraty * See if the target is a special target that must have it 1035956e45f6SSimon J. Gerraty * or its sources handled specially. 1036956e45f6SSimon J. Gerraty */ 10379f45a3c8SSimon J. Gerraty keywd = FindKeyword(targetName); 1038956e45f6SSimon J. Gerraty if (keywd != -1) { 10399f45a3c8SSimon J. Gerraty if (*inout_special == SP_PATH && 10409f45a3c8SSimon J. Gerraty parseKeywords[keywd].special != SP_PATH) { 1041956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Mismatched special targets"); 1042b0c40a00SSimon J. Gerraty return false; 1043956e45f6SSimon J. Gerraty } 1044956e45f6SSimon J. Gerraty 10459f45a3c8SSimon J. Gerraty *inout_special = parseKeywords[keywd].special; 10469f45a3c8SSimon J. Gerraty *inout_targetAttr = parseKeywords[keywd].targetAttr; 1047956e45f6SSimon J. Gerraty 10489f45a3c8SSimon J. Gerraty HandleDependencyTargetSpecial(targetName, inout_special, 104906b9b3e0SSimon J. Gerraty inout_paths); 1050956e45f6SSimon J. Gerraty 1051dba7b0efSSimon J. Gerraty } else if (strncmp(targetName, ".PATH", 5) == 0) { 10529f45a3c8SSimon J. Gerraty *inout_special = SP_PATH; 10539f45a3c8SSimon J. Gerraty if (!HandleDependencyTargetPath(targetName + 5, inout_paths)) 1054b0c40a00SSimon J. Gerraty return false; 1055956e45f6SSimon J. Gerraty } 1056b0c40a00SSimon J. Gerraty return true; 1057956e45f6SSimon J. Gerraty } 1058956e45f6SSimon J. Gerraty 1059956e45f6SSimon J. Gerraty static void 1060954401e6SSimon J. Gerraty HandleSingleDependencyTargetMundane(const char *name) 1061956e45f6SSimon J. Gerraty { 1062954401e6SSimon J. Gerraty GNode *gn = Suff_IsTransform(name) 1063954401e6SSimon J. Gerraty ? Suff_AddTransform(name) 1064954401e6SSimon J. Gerraty : Targ_GetNode(name); 10651748de26SSimon J. Gerraty if (doing_depend) 106612904384SSimon J. Gerraty RememberLocation(gn); 10673955d011SMarcel Moolenaar 10682c3632d1SSimon J. Gerraty Lst_Append(targets, gn); 10693955d011SMarcel Moolenaar } 1070954401e6SSimon J. Gerraty 1071954401e6SSimon J. Gerraty static void 1072954401e6SSimon J. Gerraty HandleDependencyTargetMundane(const char *targetName) 1073954401e6SSimon J. Gerraty { 1074954401e6SSimon J. Gerraty if (Dir_HasWildcards(targetName)) { 1075954401e6SSimon J. Gerraty StringList targetNames = LST_INIT; 1076954401e6SSimon J. Gerraty 1077954401e6SSimon J. Gerraty SearchPath *emptyPath = SearchPath_New(); 1078954401e6SSimon J. Gerraty SearchPath_Expand(emptyPath, targetName, &targetNames); 1079954401e6SSimon J. Gerraty SearchPath_Free(emptyPath); 1080954401e6SSimon J. Gerraty 1081954401e6SSimon J. Gerraty while (!Lst_IsEmpty(&targetNames)) { 1082954401e6SSimon J. Gerraty char *targName = Lst_Dequeue(&targetNames); 1083954401e6SSimon J. Gerraty HandleSingleDependencyTargetMundane(targName); 1084954401e6SSimon J. Gerraty free(targName); 1085954401e6SSimon J. Gerraty } 1086954401e6SSimon J. Gerraty } else 1087954401e6SSimon J. Gerraty HandleSingleDependencyTargetMundane(targetName); 10883955d011SMarcel Moolenaar } 10893955d011SMarcel Moolenaar 1090956e45f6SSimon J. Gerraty static void 10919f45a3c8SSimon J. Gerraty SkipExtraTargets(char **pp, const char *lstart) 1092956e45f6SSimon J. Gerraty { 1093b0c40a00SSimon J. Gerraty bool warning = false; 10949f45a3c8SSimon J. Gerraty const char *p = *pp; 10953955d011SMarcel Moolenaar 10969f45a3c8SSimon J. Gerraty while (*p != '\0') { 10979f45a3c8SSimon J. Gerraty if (!IsEscaped(lstart, p) && (*p == '!' || *p == ':')) 1098e2eeea75SSimon J. Gerraty break; 10999f45a3c8SSimon J. Gerraty if (IsEscaped(lstart, p) || (*p != ' ' && *p != '\t')) 1100b0c40a00SSimon J. Gerraty warning = true; 11019f45a3c8SSimon J. Gerraty p++; 11023955d011SMarcel Moolenaar } 11034fde40d9SSimon J. Gerraty if (warning) { 11044fde40d9SSimon J. Gerraty const char *start = *pp; 11054fde40d9SSimon J. Gerraty cpp_skip_whitespace(&start); 11064fde40d9SSimon J. Gerraty Parse_Error(PARSE_WARNING, "Extra target '%.*s' ignored", 11074fde40d9SSimon J. Gerraty (int)(p - start), start); 11084fde40d9SSimon J. Gerraty } 1109e2eeea75SSimon J. Gerraty 11109f45a3c8SSimon J. Gerraty *pp += p - *pp; 11113955d011SMarcel Moolenaar } 11123955d011SMarcel Moolenaar 1113956e45f6SSimon J. Gerraty static void 11149f45a3c8SSimon J. Gerraty CheckSpecialMundaneMixture(ParseSpecial special) 1115956e45f6SSimon J. Gerraty { 11169f45a3c8SSimon J. Gerraty switch (special) { 1117956e45f6SSimon J. Gerraty case SP_DEFAULT: 1118956e45f6SSimon J. Gerraty case SP_STALE: 1119956e45f6SSimon J. Gerraty case SP_BEGIN: 1120956e45f6SSimon J. Gerraty case SP_END: 1121956e45f6SSimon J. Gerraty case SP_ERROR: 1122956e45f6SSimon J. Gerraty case SP_INTERRUPT: 11233955d011SMarcel Moolenaar /* 1124e2eeea75SSimon J. Gerraty * These create nodes on which to hang commands, so targets 112506b9b3e0SSimon J. Gerraty * shouldn't be empty. 11263955d011SMarcel Moolenaar */ 1127956e45f6SSimon J. Gerraty case SP_NOT: 11281d3f2ddcSSimon J. Gerraty /* Nothing special here -- targets may be empty. */ 11299f45a3c8SSimon J. Gerraty break; 11309f45a3c8SSimon J. Gerraty default: 11319f45a3c8SSimon J. Gerraty Parse_Error(PARSE_WARNING, 11329f45a3c8SSimon J. Gerraty "Special and mundane targets don't mix. " 11339f45a3c8SSimon J. Gerraty "Mundane ones ignored"); 11343955d011SMarcel Moolenaar break; 11353955d011SMarcel Moolenaar } 11363955d011SMarcel Moolenaar } 11373955d011SMarcel Moolenaar 1138b0c40a00SSimon J. Gerraty /* 1139b0c40a00SSimon J. Gerraty * In a dependency line like 'targets: sources' or 'targets! sources', parse 1140b0c40a00SSimon J. Gerraty * the operator ':', '::' or '!' from between the targets and the sources. 1141b0c40a00SSimon J. Gerraty */ 11429f45a3c8SSimon J. Gerraty static GNodeType 11439f45a3c8SSimon J. Gerraty ParseDependencyOp(char **pp) 1144956e45f6SSimon J. Gerraty { 11459f45a3c8SSimon J. Gerraty if (**pp == '!') 11469f45a3c8SSimon J. Gerraty return (*pp)++, OP_FORCE; 1147954401e6SSimon J. Gerraty if (**pp == ':' && (*pp)[1] == ':') 11489f45a3c8SSimon J. Gerraty return *pp += 2, OP_DOUBLEDEP; 1149954401e6SSimon J. Gerraty else if (**pp == ':') 11509f45a3c8SSimon J. Gerraty return (*pp)++, OP_DEPENDS; 1151954401e6SSimon J. Gerraty else 1152954401e6SSimon J. Gerraty return OP_NONE; 1153956e45f6SSimon J. Gerraty } 11543955d011SMarcel Moolenaar 1155956e45f6SSimon J. Gerraty static void 11564fde40d9SSimon J. Gerraty ClearPaths(ParseSpecial special, SearchPathList *paths) 1157956e45f6SSimon J. Gerraty { 1158956e45f6SSimon J. Gerraty if (paths != NULL) { 1159956e45f6SSimon J. Gerraty SearchPathListNode *ln; 1160956e45f6SSimon J. Gerraty for (ln = paths->first; ln != NULL; ln = ln->next) 116106b9b3e0SSimon J. Gerraty SearchPath_Clear(ln->datum); 1162956e45f6SSimon J. Gerraty } 11634fde40d9SSimon J. Gerraty if (special == SP_SYSPATH) 11644fde40d9SSimon J. Gerraty Dir_SetSYSPATH(); 11654fde40d9SSimon J. Gerraty else 1166956e45f6SSimon J. Gerraty Dir_SetPATH(); 1167956e45f6SSimon J. Gerraty } 1168956e45f6SSimon J. Gerraty 1169954401e6SSimon J. Gerraty static char * 1170954401e6SSimon J. Gerraty FindInDirOfIncludingFile(const char *file) 11711d3f2ddcSSimon J. Gerraty { 1172954401e6SSimon J. Gerraty char *fullname, *incdir, *slash, *newName; 11731d3f2ddcSSimon J. Gerraty int i; 11741d3f2ddcSSimon J. Gerraty 1175954401e6SSimon J. Gerraty fullname = NULL; 11761d3f2ddcSSimon J. Gerraty incdir = bmake_strdup(CurFile()->name.str); 11771d3f2ddcSSimon J. Gerraty slash = strrchr(incdir, '/'); 11781d3f2ddcSSimon J. Gerraty if (slash != NULL) { 11791d3f2ddcSSimon J. Gerraty *slash = '\0'; 11801d3f2ddcSSimon J. Gerraty /* 11811d3f2ddcSSimon J. Gerraty * Now do lexical processing of leading "../" on the 11821d3f2ddcSSimon J. Gerraty * filename. 11831d3f2ddcSSimon J. Gerraty */ 11841d3f2ddcSSimon J. Gerraty for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { 11851d3f2ddcSSimon J. Gerraty slash = strrchr(incdir + 1, '/'); 11861d3f2ddcSSimon J. Gerraty if (slash == NULL || strcmp(slash, "/..") == 0) 11871d3f2ddcSSimon J. Gerraty break; 11881d3f2ddcSSimon J. Gerraty *slash = '\0'; 11891d3f2ddcSSimon J. Gerraty } 11901d3f2ddcSSimon J. Gerraty newName = str_concat3(incdir, "/", file + i); 11911d3f2ddcSSimon J. Gerraty fullname = Dir_FindFile(newName, parseIncPath); 11921d3f2ddcSSimon J. Gerraty if (fullname == NULL) 1193954401e6SSimon J. Gerraty fullname = Dir_FindFile(newName, &dirSearchPath); 11941d3f2ddcSSimon J. Gerraty free(newName); 11951d3f2ddcSSimon J. Gerraty } 11961d3f2ddcSSimon J. Gerraty free(incdir); 1197954401e6SSimon J. Gerraty return fullname; 11981d3f2ddcSSimon J. Gerraty } 1199954401e6SSimon J. Gerraty 1200954401e6SSimon J. Gerraty static char * 1201954401e6SSimon J. Gerraty FindInQuotPath(const char *file) 1202954401e6SSimon J. Gerraty { 1203954401e6SSimon J. Gerraty const char *suff; 1204954401e6SSimon J. Gerraty SearchPath *suffPath; 1205954401e6SSimon J. Gerraty char *fullname; 1206954401e6SSimon J. Gerraty 1207954401e6SSimon J. Gerraty fullname = FindInDirOfIncludingFile(file); 1208954401e6SSimon J. Gerraty if (fullname == NULL && 1209954401e6SSimon J. Gerraty (suff = strrchr(file, '.')) != NULL && 1210954401e6SSimon J. Gerraty (suffPath = Suff_GetPath(suff)) != NULL) 1211954401e6SSimon J. Gerraty fullname = Dir_FindFile(file, suffPath); 1212954401e6SSimon J. Gerraty if (fullname == NULL) 12131d3f2ddcSSimon J. Gerraty fullname = Dir_FindFile(file, parseIncPath); 12141d3f2ddcSSimon J. Gerraty if (fullname == NULL) 1215954401e6SSimon J. Gerraty fullname = Dir_FindFile(file, &dirSearchPath); 1216954401e6SSimon J. Gerraty return fullname; 12171d3f2ddcSSimon J. Gerraty } 12181d3f2ddcSSimon J. Gerraty 1219148ee845SSimon J. Gerraty static bool 1220148ee845SSimon J. Gerraty SkipGuarded(const char *fullname) 1221148ee845SSimon J. Gerraty { 1222148ee845SSimon J. Gerraty Guard *guard = HashTable_FindValue(&guards, fullname); 1223148ee845SSimon J. Gerraty if (guard != NULL && guard->kind == GK_VARIABLE 1224148ee845SSimon J. Gerraty && GNode_ValueDirect(SCOPE_GLOBAL, guard->name) != NULL) 1225148ee845SSimon J. Gerraty goto skip; 1226148ee845SSimon J. Gerraty if (guard != NULL && guard->kind == GK_TARGET 1227148ee845SSimon J. Gerraty && Targ_FindNode(guard->name) != NULL) 1228148ee845SSimon J. Gerraty goto skip; 1229148ee845SSimon J. Gerraty return false; 1230148ee845SSimon J. Gerraty 1231148ee845SSimon J. Gerraty skip: 1232148ee845SSimon J. Gerraty DEBUG2(PARSE, "Skipping '%s' because '%s' is defined\n", 1233148ee845SSimon J. Gerraty fullname, guard->name); 1234148ee845SSimon J. Gerraty return true; 1235148ee845SSimon J. Gerraty } 1236148ee845SSimon J. Gerraty 12371d3f2ddcSSimon J. Gerraty /* 1238954401e6SSimon J. Gerraty * Handle one of the .[-ds]include directives by remembering the current file 1239954401e6SSimon J. Gerraty * and pushing the included file on the stack. After the included file has 1240954401e6SSimon J. Gerraty * finished, parsing continues with the including file; see Parse_PushInput 1241954401e6SSimon J. Gerraty * and ParseEOF. 1242954401e6SSimon J. Gerraty * 1243954401e6SSimon J. Gerraty * System includes are looked up in sysIncPath, any other includes are looked 1244954401e6SSimon J. Gerraty * up in the parsedir and then in the directories specified by the -I command 1245954401e6SSimon J. Gerraty * line options. 12461d3f2ddcSSimon J. Gerraty */ 1247954401e6SSimon J. Gerraty static void 1248954401e6SSimon J. Gerraty IncludeFile(const char *file, bool isSystem, bool depinc, bool silent) 1249954401e6SSimon J. Gerraty { 1250954401e6SSimon J. Gerraty Buffer buf; 1251954401e6SSimon J. Gerraty char *fullname; /* full pathname of file */ 1252954401e6SSimon J. Gerraty int fd; 1253954401e6SSimon J. Gerraty 1254954401e6SSimon J. Gerraty fullname = file[0] == '/' ? bmake_strdup(file) : NULL; 1255954401e6SSimon J. Gerraty 1256954401e6SSimon J. Gerraty if (fullname == NULL && !isSystem) 1257954401e6SSimon J. Gerraty fullname = FindInQuotPath(file); 1258954401e6SSimon J. Gerraty 1259954401e6SSimon J. Gerraty if (fullname == NULL) { 12601d3f2ddcSSimon J. Gerraty SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs) 12611d3f2ddcSSimon J. Gerraty ? defSysIncPath : sysIncPath; 12629d3df31eSSimon J. Gerraty fullname = Dir_FindInclude(file, path); 12631d3f2ddcSSimon J. Gerraty } 12641d3f2ddcSSimon J. Gerraty 12651d3f2ddcSSimon J. Gerraty if (fullname == NULL) { 12661d3f2ddcSSimon J. Gerraty if (!silent) 12671d3f2ddcSSimon J. Gerraty Parse_Error(PARSE_FATAL, "Could not find %s", file); 12681d3f2ddcSSimon J. Gerraty return; 12691d3f2ddcSSimon J. Gerraty } 12701d3f2ddcSSimon J. Gerraty 1271148ee845SSimon J. Gerraty if (SkipGuarded(fullname)) 12728d5c8e21SSimon J. Gerraty goto done; 1273148ee845SSimon J. Gerraty 1274954401e6SSimon J. Gerraty if ((fd = open(fullname, O_RDONLY)) == -1) { 12751d3f2ddcSSimon J. Gerraty if (!silent) 12761d3f2ddcSSimon J. Gerraty Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); 12778d5c8e21SSimon J. Gerraty goto done; 12781d3f2ddcSSimon J. Gerraty } 12791d3f2ddcSSimon J. Gerraty 1280954401e6SSimon J. Gerraty buf = LoadFile(fullname, fd); 12811d3f2ddcSSimon J. Gerraty (void)close(fd); 12821d3f2ddcSSimon J. Gerraty 12831d3f2ddcSSimon J. Gerraty Parse_PushInput(fullname, 1, 0, buf, NULL); 12841d3f2ddcSSimon J. Gerraty if (depinc) 12851d3f2ddcSSimon J. Gerraty doing_depend = depinc; /* only turn it on */ 12868d5c8e21SSimon J. Gerraty done: 12871d3f2ddcSSimon J. Gerraty free(fullname); 12881d3f2ddcSSimon J. Gerraty } 12891d3f2ddcSSimon J. Gerraty 12909f45a3c8SSimon J. Gerraty /* Handle a "dependency" line like '.SPECIAL:' without any sources. */ 1291956e45f6SSimon J. Gerraty static void 12929f45a3c8SSimon J. Gerraty HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths) 1293956e45f6SSimon J. Gerraty { 12949f45a3c8SSimon J. Gerraty switch (special) { 1295956e45f6SSimon J. Gerraty case SP_SUFFIXES: 12963955d011SMarcel Moolenaar Suff_ClearSuffixes(); 12973955d011SMarcel Moolenaar break; 1298956e45f6SSimon J. Gerraty case SP_PRECIOUS: 1299b0c40a00SSimon J. Gerraty allPrecious = true; 13003955d011SMarcel Moolenaar break; 1301956e45f6SSimon J. Gerraty case SP_IGNORE: 1302b0c40a00SSimon J. Gerraty opts.ignoreErrors = true; 13033955d011SMarcel Moolenaar break; 1304956e45f6SSimon J. Gerraty case SP_SILENT: 13059f45a3c8SSimon J. Gerraty opts.silent = true; 13063955d011SMarcel Moolenaar break; 1307956e45f6SSimon J. Gerraty case SP_PATH: 13084fde40d9SSimon J. Gerraty case SP_SYSPATH: 13094fde40d9SSimon J. Gerraty ClearPaths(special, paths); 13103955d011SMarcel Moolenaar break; 1311956e45f6SSimon J. Gerraty case SP_POSIX: 13122f2a5ecdSSimon J. Gerraty if (posix_state == PS_NOW_OR_NEVER) { 13131d3f2ddcSSimon J. Gerraty /* 13142f2a5ecdSSimon J. Gerraty * With '-r', 'posix.mk' (if it exists) 13152f2a5ecdSSimon J. Gerraty * can effectively substitute for 'sys.mk', 13162f2a5ecdSSimon J. Gerraty * otherwise it is an extension. 13171d3f2ddcSSimon J. Gerraty */ 13182f2a5ecdSSimon J. Gerraty Global_Set("%POSIX", "1003.2"); 13191d3f2ddcSSimon J. Gerraty IncludeFile("posix.mk", true, false, true); 13201d3f2ddcSSimon J. Gerraty } 13213955d011SMarcel Moolenaar break; 13223955d011SMarcel Moolenaar default: 13233955d011SMarcel Moolenaar break; 13243955d011SMarcel Moolenaar } 13253955d011SMarcel Moolenaar } 13263955d011SMarcel Moolenaar 1327956e45f6SSimon J. Gerraty static void 1328956e45f6SSimon J. Gerraty AddToPaths(const char *dir, SearchPathList *paths) 13293955d011SMarcel Moolenaar { 1330956e45f6SSimon J. Gerraty if (paths != NULL) { 1331956e45f6SSimon J. Gerraty SearchPathListNode *ln; 1332956e45f6SSimon J. Gerraty for (ln = paths->first; ln != NULL; ln = ln->next) 1333dba7b0efSSimon J. Gerraty (void)SearchPath_Add(ln->datum, dir); 1334956e45f6SSimon J. Gerraty } 1335956e45f6SSimon J. Gerraty } 1336956e45f6SSimon J. Gerraty 13373955d011SMarcel Moolenaar /* 13389f45a3c8SSimon J. Gerraty * If the target was one that doesn't take files as its sources but takes 13399f45a3c8SSimon J. Gerraty * something like suffixes, we take each space-separated word on the line as 13409f45a3c8SSimon J. Gerraty * a something and deal with it accordingly. 13413955d011SMarcel Moolenaar */ 1342956e45f6SSimon J. Gerraty static void 13439f45a3c8SSimon J. Gerraty ParseDependencySourceSpecial(ParseSpecial special, const char *word, 1344956e45f6SSimon J. Gerraty SearchPathList *paths) 1345956e45f6SSimon J. Gerraty { 13469f45a3c8SSimon J. Gerraty switch (special) { 1347956e45f6SSimon J. Gerraty case SP_SUFFIXES: 13489f45a3c8SSimon J. Gerraty Suff_AddSuffix(word); 13493955d011SMarcel Moolenaar break; 1350956e45f6SSimon J. Gerraty case SP_PATH: 1351d5e0a182SSimon J. Gerraty case SP_SYSPATH: 1352956e45f6SSimon J. Gerraty AddToPaths(word, paths); 13533955d011SMarcel Moolenaar break; 1354956e45f6SSimon J. Gerraty case SP_INCLUDES: 1355956e45f6SSimon J. Gerraty Suff_AddInclude(word); 13563955d011SMarcel Moolenaar break; 1357956e45f6SSimon J. Gerraty case SP_LIBS: 1358956e45f6SSimon J. Gerraty Suff_AddLib(word); 13593955d011SMarcel Moolenaar break; 13604fde40d9SSimon J. Gerraty case SP_NOREADONLY: 13614fde40d9SSimon J. Gerraty Var_ReadOnly(word, false); 13624fde40d9SSimon J. Gerraty break; 1363956e45f6SSimon J. Gerraty case SP_NULL: 1364956e45f6SSimon J. Gerraty Suff_SetNull(word); 13653955d011SMarcel Moolenaar break; 1366956e45f6SSimon J. Gerraty case SP_OBJDIR: 1367b0c40a00SSimon J. Gerraty Main_SetObjdir(false, "%s", word); 13683955d011SMarcel Moolenaar break; 13694fde40d9SSimon J. Gerraty case SP_READONLY: 13704fde40d9SSimon J. Gerraty Var_ReadOnly(word, true); 13714fde40d9SSimon J. Gerraty break; 13723955d011SMarcel Moolenaar default: 13733955d011SMarcel Moolenaar break; 13743955d011SMarcel Moolenaar } 13753955d011SMarcel Moolenaar } 1376956e45f6SSimon J. Gerraty 1377b0c40a00SSimon J. Gerraty static bool 13789f45a3c8SSimon J. Gerraty ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special, 13799f45a3c8SSimon J. Gerraty GNodeType *inout_targetAttr, 13809f45a3c8SSimon J. Gerraty SearchPathList **inout_paths) 1381956e45f6SSimon J. Gerraty { 1382d5e0a182SSimon J. Gerraty char savedNameEnd = *nameEnd; 13839f45a3c8SSimon J. Gerraty *nameEnd = '\0'; 13849f45a3c8SSimon J. Gerraty 13859f45a3c8SSimon J. Gerraty if (!HandleDependencyTarget(name, inout_special, 13869f45a3c8SSimon J. Gerraty inout_targetAttr, inout_paths)) 13879f45a3c8SSimon J. Gerraty return false; 13889f45a3c8SSimon J. Gerraty 13899f45a3c8SSimon J. Gerraty if (*inout_special == SP_NOT && *name != '\0') 13909f45a3c8SSimon J. Gerraty HandleDependencyTargetMundane(name); 13919f45a3c8SSimon J. Gerraty else if (*inout_special == SP_PATH && *name != '.' && *name != '\0') 13929f45a3c8SSimon J. Gerraty Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name); 13939f45a3c8SSimon J. Gerraty 1394d5e0a182SSimon J. Gerraty *nameEnd = savedNameEnd; 13959f45a3c8SSimon J. Gerraty return true; 13969f45a3c8SSimon J. Gerraty } 13979f45a3c8SSimon J. Gerraty 13989f45a3c8SSimon J. Gerraty static bool 13994fde40d9SSimon J. Gerraty ParseDependencyTargets(char **pp, 14009f45a3c8SSimon J. Gerraty const char *lstart, 14019f45a3c8SSimon J. Gerraty ParseSpecial *inout_special, 14029f45a3c8SSimon J. Gerraty GNodeType *inout_targetAttr, 140398875883SSimon J. Gerraty SearchPathList **inout_paths, 140498875883SSimon J. Gerraty const char *unexpanded_line) 14059f45a3c8SSimon J. Gerraty { 14064fde40d9SSimon J. Gerraty char *p = *pp; 1407956e45f6SSimon J. Gerraty 1408956e45f6SSimon J. Gerraty for (;;) { 14094fde40d9SSimon J. Gerraty char *tgt = p; 1410956e45f6SSimon J. Gerraty 14114fde40d9SSimon J. Gerraty ParseDependencyTargetWord(&p, lstart); 1412956e45f6SSimon J. Gerraty 1413956e45f6SSimon J. Gerraty /* 1414956e45f6SSimon J. Gerraty * If the word is followed by a left parenthesis, it's the 14159f45a3c8SSimon J. Gerraty * name of one or more files inside an archive. 1416956e45f6SSimon J. Gerraty */ 14174fde40d9SSimon J. Gerraty if (!IsEscaped(lstart, p) && *p == '(') { 14184fde40d9SSimon J. Gerraty p = tgt; 14194fde40d9SSimon J. Gerraty if (!Arch_ParseArchive(&p, targets, SCOPE_CMDLINE)) { 1420956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, 142106b9b3e0SSimon J. Gerraty "Error in archive specification: \"%s\"", 142206b9b3e0SSimon J. Gerraty tgt); 1423b0c40a00SSimon J. Gerraty return false; 1424956e45f6SSimon J. Gerraty } 142506b9b3e0SSimon J. Gerraty continue; 142606b9b3e0SSimon J. Gerraty } 142706b9b3e0SSimon J. Gerraty 14284fde40d9SSimon J. Gerraty if (*p == '\0') { 142998875883SSimon J. Gerraty InvalidLineType(lstart, unexpanded_line); 1430b0c40a00SSimon J. Gerraty return false; 1431956e45f6SSimon J. Gerraty } 1432956e45f6SSimon J. Gerraty 14334fde40d9SSimon J. Gerraty if (!ApplyDependencyTarget(tgt, p, inout_special, 14349f45a3c8SSimon J. Gerraty inout_targetAttr, inout_paths)) 1435b0c40a00SSimon J. Gerraty return false; 1436956e45f6SSimon J. Gerraty 14379f45a3c8SSimon J. Gerraty if (*inout_special != SP_NOT && *inout_special != SP_PATH) 14384fde40d9SSimon J. Gerraty SkipExtraTargets(&p, lstart); 1439e2eeea75SSimon J. Gerraty else 14404fde40d9SSimon J. Gerraty pp_skip_whitespace(&p); 1441e2eeea75SSimon J. Gerraty 14424fde40d9SSimon J. Gerraty if (*p == '\0') 1443956e45f6SSimon J. Gerraty break; 14444fde40d9SSimon J. Gerraty if ((*p == '!' || *p == ':') && !IsEscaped(lstart, p)) 1445956e45f6SSimon J. Gerraty break; 14463955d011SMarcel Moolenaar } 1447956e45f6SSimon J. Gerraty 14484fde40d9SSimon J. Gerraty *pp = p; 1449b0c40a00SSimon J. Gerraty return true; 14503955d011SMarcel Moolenaar } 1451956e45f6SSimon J. Gerraty 1452956e45f6SSimon J. Gerraty static void 14539f45a3c8SSimon J. Gerraty ParseDependencySourcesSpecial(char *start, 14549f45a3c8SSimon J. Gerraty ParseSpecial special, SearchPathList *paths) 1455956e45f6SSimon J. Gerraty { 1456956e45f6SSimon J. Gerraty 145706b9b3e0SSimon J. Gerraty while (*start != '\0') { 1458d5e0a182SSimon J. Gerraty char savedEnd; 14599f45a3c8SSimon J. Gerraty char *end = start; 146006b9b3e0SSimon J. Gerraty while (*end != '\0' && !ch_isspace(*end)) 1461956e45f6SSimon J. Gerraty end++; 1462d5e0a182SSimon J. Gerraty savedEnd = *end; 1463956e45f6SSimon J. Gerraty *end = '\0'; 14649f45a3c8SSimon J. Gerraty ParseDependencySourceSpecial(special, start, paths); 1465d5e0a182SSimon J. Gerraty *end = savedEnd; 1466d5e0a182SSimon J. Gerraty if (savedEnd != '\0') 1467956e45f6SSimon J. Gerraty end++; 1468956e45f6SSimon J. Gerraty pp_skip_whitespace(&end); 1469956e45f6SSimon J. Gerraty start = end; 1470956e45f6SSimon J. Gerraty } 1471956e45f6SSimon J. Gerraty } 1472956e45f6SSimon J. Gerraty 14739f45a3c8SSimon J. Gerraty static void 14749f45a3c8SSimon J. Gerraty LinkVarToTargets(VarAssign *var) 14759f45a3c8SSimon J. Gerraty { 14769f45a3c8SSimon J. Gerraty GNodeListNode *ln; 14779f45a3c8SSimon J. Gerraty 14789f45a3c8SSimon J. Gerraty for (ln = targets->first; ln != NULL; ln = ln->next) 14799f45a3c8SSimon J. Gerraty Parse_Var(var, ln->datum); 14809f45a3c8SSimon J. Gerraty } 14819f45a3c8SSimon J. Gerraty 1482b0c40a00SSimon J. Gerraty static bool 14839f45a3c8SSimon J. Gerraty ParseDependencySourcesMundane(char *start, 14849f45a3c8SSimon J. Gerraty ParseSpecial special, GNodeType targetAttr) 1485956e45f6SSimon J. Gerraty { 1486e2eeea75SSimon J. Gerraty while (*start != '\0') { 14879f45a3c8SSimon J. Gerraty char *end = start; 14889f45a3c8SSimon J. Gerraty VarAssign var; 14899f45a3c8SSimon J. Gerraty 14909f45a3c8SSimon J. Gerraty /* 14919f45a3c8SSimon J. Gerraty * Check for local variable assignment, 14929f45a3c8SSimon J. Gerraty * rest of the line is the value. 14939f45a3c8SSimon J. Gerraty */ 14949f45a3c8SSimon J. Gerraty if (Parse_IsVar(start, &var)) { 1495d5e0a182SSimon J. Gerraty bool targetVarsEnabled = GetBooleanExpr( 14969f45a3c8SSimon J. Gerraty "${.MAKE.TARGET_LOCAL_VARIABLES}", true); 14979f45a3c8SSimon J. Gerraty 1498d5e0a182SSimon J. Gerraty if (targetVarsEnabled) 14999f45a3c8SSimon J. Gerraty LinkVarToTargets(&var); 15009f45a3c8SSimon J. Gerraty free(var.varname); 1501d5e0a182SSimon J. Gerraty if (targetVarsEnabled) 15029f45a3c8SSimon J. Gerraty return true; 15039f45a3c8SSimon J. Gerraty } 15049f45a3c8SSimon J. Gerraty 15053955d011SMarcel Moolenaar /* 15063955d011SMarcel Moolenaar * The targets take real sources, so we must beware of archive 15073955d011SMarcel Moolenaar * specifications (i.e. things with left parentheses in them) 15083955d011SMarcel Moolenaar * and handle them accordingly. 15093955d011SMarcel Moolenaar */ 151006b9b3e0SSimon J. Gerraty for (; *end != '\0' && !ch_isspace(*end); end++) { 1511956e45f6SSimon J. Gerraty if (*end == '(' && end > start && end[-1] != '$') { 15123955d011SMarcel Moolenaar /* 151306b9b3e0SSimon J. Gerraty * Only stop for a left parenthesis if it 151406b9b3e0SSimon J. Gerraty * isn't at the start of a word (that'll be 151506b9b3e0SSimon J. Gerraty * for variable changes later) and isn't 151606b9b3e0SSimon J. Gerraty * preceded by a dollar sign (a dynamic 15173955d011SMarcel Moolenaar * source). 15183955d011SMarcel Moolenaar */ 15193955d011SMarcel Moolenaar break; 15203955d011SMarcel Moolenaar } 15213955d011SMarcel Moolenaar } 15223955d011SMarcel Moolenaar 1523956e45f6SSimon J. Gerraty if (*end == '(') { 152406b9b3e0SSimon J. Gerraty GNodeList sources = LST_INIT; 1525dba7b0efSSimon J. Gerraty if (!Arch_ParseArchive(&start, &sources, 1526dba7b0efSSimon J. Gerraty SCOPE_CMDLINE)) { 15273955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, 152806b9b3e0SSimon J. Gerraty "Error in source archive spec \"%s\"", 152906b9b3e0SSimon J. Gerraty start); 1530b0c40a00SSimon J. Gerraty return false; 15313955d011SMarcel Moolenaar } 15323955d011SMarcel Moolenaar 153306b9b3e0SSimon J. Gerraty while (!Lst_IsEmpty(&sources)) { 153406b9b3e0SSimon J. Gerraty GNode *gn = Lst_Dequeue(&sources); 15359f45a3c8SSimon J. Gerraty ApplyDependencySource(targetAttr, gn->name, 15369f45a3c8SSimon J. Gerraty special); 15373955d011SMarcel Moolenaar } 153806b9b3e0SSimon J. Gerraty Lst_Done(&sources); 1539956e45f6SSimon J. Gerraty end = start; 15403955d011SMarcel Moolenaar } else { 154106b9b3e0SSimon J. Gerraty if (*end != '\0') { 1542956e45f6SSimon J. Gerraty *end = '\0'; 1543956e45f6SSimon J. Gerraty end++; 15443955d011SMarcel Moolenaar } 15453955d011SMarcel Moolenaar 15469f45a3c8SSimon J. Gerraty ApplyDependencySource(targetAttr, start, special); 15473955d011SMarcel Moolenaar } 1548956e45f6SSimon J. Gerraty pp_skip_whitespace(&end); 1549956e45f6SSimon J. Gerraty start = end; 15503955d011SMarcel Moolenaar } 1551b0c40a00SSimon J. Gerraty return true; 1552b0c40a00SSimon J. Gerraty } 1553b0c40a00SSimon J. Gerraty 1554b0c40a00SSimon J. Gerraty /* 15559f45a3c8SSimon J. Gerraty * From a dependency line like 'targets: sources', parse the sources. 1556b0c40a00SSimon J. Gerraty * 1557b0c40a00SSimon J. Gerraty * See the tests depsrc-*.mk. 1558b0c40a00SSimon J. Gerraty */ 1559b0c40a00SSimon J. Gerraty static void 15609f45a3c8SSimon J. Gerraty ParseDependencySources(char *p, GNodeType targetAttr, 15619f45a3c8SSimon J. Gerraty ParseSpecial special, SearchPathList **inout_paths) 1562b0c40a00SSimon J. Gerraty { 15639f45a3c8SSimon J. Gerraty if (*p == '\0') { 15649f45a3c8SSimon J. Gerraty HandleDependencySourcesEmpty(special, *inout_paths); 15659f45a3c8SSimon J. Gerraty } else if (special == SP_MFLAGS) { 15669f45a3c8SSimon J. Gerraty Main_ParseArgLine(p); 15679f45a3c8SSimon J. Gerraty return; 15689f45a3c8SSimon J. Gerraty } else if (special == SP_SHELL) { 15699f45a3c8SSimon J. Gerraty if (!Job_ParseShell(p)) { 1570b0c40a00SSimon J. Gerraty Parse_Error(PARSE_FATAL, 1571b0c40a00SSimon J. Gerraty "improper shell specification"); 1572b0c40a00SSimon J. Gerraty return; 1573b0c40a00SSimon J. Gerraty } 15749f45a3c8SSimon J. Gerraty return; 15759f45a3c8SSimon J. Gerraty } else if (special == SP_NOTPARALLEL || special == SP_SINGLESHELL || 15769f45a3c8SSimon J. Gerraty special == SP_DELETE_ON_ERROR) { 15779f45a3c8SSimon J. Gerraty return; 1578b0c40a00SSimon J. Gerraty } 1579b0c40a00SSimon J. Gerraty 15804fde40d9SSimon J. Gerraty switch (special) { 15814fde40d9SSimon J. Gerraty case SP_INCLUDES: 15824fde40d9SSimon J. Gerraty case SP_LIBS: 15834fde40d9SSimon J. Gerraty case SP_NOREADONLY: 15844fde40d9SSimon J. Gerraty case SP_NULL: 15854fde40d9SSimon J. Gerraty case SP_OBJDIR: 15864fde40d9SSimon J. Gerraty case SP_PATH: 15874fde40d9SSimon J. Gerraty case SP_READONLY: 15884fde40d9SSimon J. Gerraty case SP_SUFFIXES: 15894fde40d9SSimon J. Gerraty case SP_SYSPATH: 15909f45a3c8SSimon J. Gerraty ParseDependencySourcesSpecial(p, special, *inout_paths); 1591b0c40a00SSimon J. Gerraty if (*inout_paths != NULL) { 1592b0c40a00SSimon J. Gerraty Lst_Free(*inout_paths); 1593b0c40a00SSimon J. Gerraty *inout_paths = NULL; 1594b0c40a00SSimon J. Gerraty } 15959f45a3c8SSimon J. Gerraty if (special == SP_PATH) 1596b0c40a00SSimon J. Gerraty Dir_SetPATH(); 15974fde40d9SSimon J. Gerraty if (special == SP_SYSPATH) 15984fde40d9SSimon J. Gerraty Dir_SetSYSPATH(); 15994fde40d9SSimon J. Gerraty break; 16004fde40d9SSimon J. Gerraty default: 1601b0c40a00SSimon J. Gerraty assert(*inout_paths == NULL); 16029f45a3c8SSimon J. Gerraty if (!ParseDependencySourcesMundane(p, special, targetAttr)) 1603b0c40a00SSimon J. Gerraty return; 16044fde40d9SSimon J. Gerraty break; 1605b0c40a00SSimon J. Gerraty } 1606b0c40a00SSimon J. Gerraty 16079f45a3c8SSimon J. Gerraty MaybeUpdateMainTarget(); 16083955d011SMarcel Moolenaar } 16093955d011SMarcel Moolenaar 161006b9b3e0SSimon J. Gerraty /* 161106b9b3e0SSimon J. Gerraty * Parse a dependency line consisting of targets, followed by a dependency 1612956e45f6SSimon J. Gerraty * operator, optionally followed by sources. 1613956e45f6SSimon J. Gerraty * 1614956e45f6SSimon J. Gerraty * The nodes of the sources are linked as children to the nodes of the 1615956e45f6SSimon J. Gerraty * targets. Nodes are created as necessary. 1616956e45f6SSimon J. Gerraty * 1617956e45f6SSimon J. Gerraty * The operator is applied to each node in the global 'targets' list, 16189f45a3c8SSimon J. Gerraty * which is where the nodes found for the targets are kept. 1619956e45f6SSimon J. Gerraty * 1620956e45f6SSimon J. Gerraty * The sources are parsed in much the same way as the targets, except 1621956e45f6SSimon J. Gerraty * that they are expanded using the wildcarding scheme of the C-Shell, 1622e2eeea75SSimon J. Gerraty * and a target is created for each expanded word. Each of the resulting 1623e2eeea75SSimon J. Gerraty * nodes is then linked to each of the targets as one of its children. 1624956e45f6SSimon J. Gerraty * 1625956e45f6SSimon J. Gerraty * Certain targets and sources such as .PHONY or .PRECIOUS are handled 16269f45a3c8SSimon J. Gerraty * specially, see ParseSpecial. 1627956e45f6SSimon J. Gerraty * 16289f45a3c8SSimon J. Gerraty * Transformation rules such as '.c.o' are also handled here, see 16299f45a3c8SSimon J. Gerraty * Suff_AddTransform. 1630e2eeea75SSimon J. Gerraty * 1631548bfc56SSimon J. Gerraty * Upon return, the value of expandedLine is unspecified. 16323955d011SMarcel Moolenaar */ 1633956e45f6SSimon J. Gerraty static void 1634548bfc56SSimon J. Gerraty ParseDependency(char *expandedLine, const char *unexpandedLine) 1635956e45f6SSimon J. Gerraty { 16369f45a3c8SSimon J. Gerraty char *p; 16379f45a3c8SSimon J. Gerraty SearchPathList *paths; /* search paths to alter when parsing a list 16389f45a3c8SSimon J. Gerraty * of .PATH targets */ 16399f45a3c8SSimon J. Gerraty GNodeType targetAttr; /* from special sources */ 16409f45a3c8SSimon J. Gerraty ParseSpecial special; /* in special targets, the children are 16419f45a3c8SSimon J. Gerraty * linked as children of the parent but not 16429f45a3c8SSimon J. Gerraty * vice versa */ 1643954401e6SSimon J. Gerraty GNodeType op; 1644956e45f6SSimon J. Gerraty 1645548bfc56SSimon J. Gerraty DEBUG1(PARSE, "ParseDependency(%s)\n", expandedLine); 1646548bfc56SSimon J. Gerraty p = expandedLine; 1647956e45f6SSimon J. Gerraty paths = NULL; 16489f45a3c8SSimon J. Gerraty targetAttr = OP_NONE; 16499f45a3c8SSimon J. Gerraty special = SP_NOT; 1650956e45f6SSimon J. Gerraty 1651548bfc56SSimon J. Gerraty if (!ParseDependencyTargets(&p, expandedLine, &special, &targetAttr, 1652548bfc56SSimon J. Gerraty &paths, unexpandedLine)) 1653956e45f6SSimon J. Gerraty goto out; 1654956e45f6SSimon J. Gerraty 1655956e45f6SSimon J. Gerraty if (!Lst_IsEmpty(targets)) 16569f45a3c8SSimon J. Gerraty CheckSpecialMundaneMixture(special); 1657956e45f6SSimon J. Gerraty 1658954401e6SSimon J. Gerraty op = ParseDependencyOp(&p); 1659954401e6SSimon J. Gerraty if (op == OP_NONE) { 1660548bfc56SSimon J. Gerraty InvalidLineType(expandedLine, unexpandedLine); 1661954401e6SSimon J. Gerraty goto out; 1662954401e6SSimon J. Gerraty } 1663954401e6SSimon J. Gerraty ApplyDependencyOperator(op); 1664956e45f6SSimon J. Gerraty 16659f45a3c8SSimon J. Gerraty pp_skip_whitespace(&p); 1666956e45f6SSimon J. Gerraty 16679f45a3c8SSimon J. Gerraty ParseDependencySources(p, targetAttr, special, &paths); 16683955d011SMarcel Moolenaar 16693955d011SMarcel Moolenaar out: 16702c3632d1SSimon J. Gerraty if (paths != NULL) 16712c3632d1SSimon J. Gerraty Lst_Free(paths); 16723955d011SMarcel Moolenaar } 16733955d011SMarcel Moolenaar 167406b9b3e0SSimon J. Gerraty /* 167506b9b3e0SSimon J. Gerraty * Determine the assignment operator and adjust the end of the variable 167606b9b3e0SSimon J. Gerraty * name accordingly. 167706b9b3e0SSimon J. Gerraty */ 16789f45a3c8SSimon J. Gerraty static VarAssign 16799f45a3c8SSimon J. Gerraty AdjustVarassignOp(const char *name, const char *nameEnd, const char *op, 16809f45a3c8SSimon J. Gerraty const char *value) 16813955d011SMarcel Moolenaar { 1682956e45f6SSimon J. Gerraty VarAssignOp type; 16839f45a3c8SSimon J. Gerraty VarAssign va; 1684956e45f6SSimon J. Gerraty 1685956e45f6SSimon J. Gerraty if (op > name && op[-1] == '+') { 1686956e45f6SSimon J. Gerraty op--; 16879f45a3c8SSimon J. Gerraty type = VAR_APPEND; 1688956e45f6SSimon J. Gerraty 1689956e45f6SSimon J. Gerraty } else if (op > name && op[-1] == '?') { 1690956e45f6SSimon J. Gerraty op--; 1691956e45f6SSimon J. Gerraty type = VAR_DEFAULT; 1692956e45f6SSimon J. Gerraty 1693956e45f6SSimon J. Gerraty } else if (op > name && op[-1] == ':') { 1694956e45f6SSimon J. Gerraty op--; 1695956e45f6SSimon J. Gerraty type = VAR_SUBST; 1696956e45f6SSimon J. Gerraty 1697956e45f6SSimon J. Gerraty } else if (op > name && op[-1] == '!') { 1698956e45f6SSimon J. Gerraty op--; 1699956e45f6SSimon J. Gerraty type = VAR_SHELL; 1700956e45f6SSimon J. Gerraty 1701956e45f6SSimon J. Gerraty } else { 1702956e45f6SSimon J. Gerraty type = VAR_NORMAL; 1703956e45f6SSimon J. Gerraty while (op > name && ch_isspace(op[-1])) 1704956e45f6SSimon J. Gerraty op--; 1705956e45f6SSimon J. Gerraty 17069f45a3c8SSimon J. Gerraty if (op - name >= 3 && memcmp(op - 3, ":sh", 3) == 0) { 1707956e45f6SSimon J. Gerraty op -= 3; 17089f45a3c8SSimon J. Gerraty type = VAR_SHELL; 1709956e45f6SSimon J. Gerraty } 1710956e45f6SSimon J. Gerraty } 1711956e45f6SSimon J. Gerraty 17129f45a3c8SSimon J. Gerraty va.varname = bmake_strsedup(name, nameEnd < op ? nameEnd : op); 17139f45a3c8SSimon J. Gerraty va.op = type; 17149f45a3c8SSimon J. Gerraty va.value = value; 17159f45a3c8SSimon J. Gerraty return va; 1716956e45f6SSimon J. Gerraty } 1717956e45f6SSimon J. Gerraty 171806b9b3e0SSimon J. Gerraty /* 171906b9b3e0SSimon J. Gerraty * Parse a variable assignment, consisting of a single-word variable name, 1720956e45f6SSimon J. Gerraty * optional whitespace, an assignment operator, optional whitespace and the 1721956e45f6SSimon J. Gerraty * variable value. 1722956e45f6SSimon J. Gerraty * 1723956e45f6SSimon J. Gerraty * Note: There is a lexical ambiguity with assignment modifier characters 1724956e45f6SSimon J. Gerraty * in variable names. This routine interprets the character before the = 1725956e45f6SSimon J. Gerraty * as a modifier. Therefore, an assignment like 1726956e45f6SSimon J. Gerraty * C++=/usr/bin/CC 1727956e45f6SSimon J. Gerraty * is interpreted as "C+ +=" instead of "C++ =". 1728956e45f6SSimon J. Gerraty * 172906b9b3e0SSimon J. Gerraty * Used for both lines in a file and command line arguments. 173006b9b3e0SSimon J. Gerraty */ 17319f45a3c8SSimon J. Gerraty static bool 1732956e45f6SSimon J. Gerraty Parse_IsVar(const char *p, VarAssign *out_var) 1733956e45f6SSimon J. Gerraty { 17349f45a3c8SSimon J. Gerraty const char *nameStart, *nameEnd, *firstSpace, *eq; 17353955d011SMarcel Moolenaar int level = 0; 17363955d011SMarcel Moolenaar 1737e2eeea75SSimon J. Gerraty cpp_skip_hspace(&p); /* Skip to variable name */ 1738956e45f6SSimon J. Gerraty 173906b9b3e0SSimon J. Gerraty /* 17401d3f2ddcSSimon J. Gerraty * During parsing, the '+' of the operator '+=' is initially parsed 174106b9b3e0SSimon J. Gerraty * as part of the variable name. It is later corrected, as is the 17429f45a3c8SSimon J. Gerraty * ':sh' modifier. Of these two (nameEnd and eq), the earlier one 174306b9b3e0SSimon J. Gerraty * determines the actual end of the variable name. 174406b9b3e0SSimon J. Gerraty */ 17459f45a3c8SSimon J. Gerraty 17469f45a3c8SSimon J. Gerraty nameStart = p; 17479f45a3c8SSimon J. Gerraty firstSpace = NULL; 17483955d011SMarcel Moolenaar 1749d5e0a182SSimon J. Gerraty /* Scan for one of the assignment operators outside an expression. */ 1750e2eeea75SSimon J. Gerraty while (*p != '\0') { 1751e2eeea75SSimon J. Gerraty char ch = *p++; 17523955d011SMarcel Moolenaar if (ch == '(' || ch == '{') { 17533955d011SMarcel Moolenaar level++; 17543955d011SMarcel Moolenaar continue; 17553955d011SMarcel Moolenaar } 17563955d011SMarcel Moolenaar if (ch == ')' || ch == '}') { 17573955d011SMarcel Moolenaar level--; 17583955d011SMarcel Moolenaar continue; 17593955d011SMarcel Moolenaar } 1760956e45f6SSimon J. Gerraty 17613955d011SMarcel Moolenaar if (level != 0) 17623955d011SMarcel Moolenaar continue; 1763956e45f6SSimon J. Gerraty 17649f45a3c8SSimon J. Gerraty if ((ch == ' ' || ch == '\t') && firstSpace == NULL) 1765956e45f6SSimon J. Gerraty firstSpace = p - 1; 1766956e45f6SSimon J. Gerraty while (ch == ' ' || ch == '\t') 1767956e45f6SSimon J. Gerraty ch = *p++; 1768956e45f6SSimon J. Gerraty 17699f45a3c8SSimon J. Gerraty if (ch == '\0') 17709f45a3c8SSimon J. Gerraty return false; 1771e2eeea75SSimon J. Gerraty if (ch == ':' && p[0] == 's' && p[1] == 'h') { 1772956e45f6SSimon J. Gerraty p += 2; 17731bbe5942SSimon J. Gerraty continue; 17741bbe5942SSimon J. Gerraty } 17759f45a3c8SSimon J. Gerraty if (ch == '=') 17769f45a3c8SSimon J. Gerraty eq = p - 1; 17779f45a3c8SSimon J. Gerraty else if (*p == '=' && 17789f45a3c8SSimon J. Gerraty (ch == '+' || ch == ':' || ch == '?' || ch == '!')) 17799f45a3c8SSimon J. Gerraty eq = p; 17809f45a3c8SSimon J. Gerraty else if (firstSpace != NULL) 1781b0c40a00SSimon J. Gerraty return false; 17829f45a3c8SSimon J. Gerraty else 17839f45a3c8SSimon J. Gerraty continue; 17849f45a3c8SSimon J. Gerraty 17859f45a3c8SSimon J. Gerraty nameEnd = firstSpace != NULL ? firstSpace : eq; 17869f45a3c8SSimon J. Gerraty p = eq + 1; 17879f45a3c8SSimon J. Gerraty cpp_skip_whitespace(&p); 17889f45a3c8SSimon J. Gerraty *out_var = AdjustVarassignOp(nameStart, nameEnd, eq, p); 17899f45a3c8SSimon J. Gerraty return true; 17903955d011SMarcel Moolenaar } 17913955d011SMarcel Moolenaar 1792b0c40a00SSimon J. Gerraty return false; 17933955d011SMarcel Moolenaar } 17943955d011SMarcel Moolenaar 179506b9b3e0SSimon J. Gerraty /* 179606b9b3e0SSimon J. Gerraty * Check for syntax errors such as unclosed expressions or unknown modifiers. 179706b9b3e0SSimon J. Gerraty */ 1798956e45f6SSimon J. Gerraty static void 1799d5e0a182SSimon J. Gerraty VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope) 18003955d011SMarcel Moolenaar { 180106b9b3e0SSimon J. Gerraty if (opts.strict) { 1802d5e0a182SSimon J. Gerraty if (op != VAR_SUBST && strchr(uvalue, '$') != NULL) { 1803d5e0a182SSimon J. Gerraty char *parsedValue = Var_Subst(uvalue, 18048d5c8e21SSimon J. Gerraty scope, VARE_PARSE); 1805956e45f6SSimon J. Gerraty /* TODO: handle errors */ 1806d5e0a182SSimon J. Gerraty free(parsedValue); 1807956e45f6SSimon J. Gerraty } 18082c3632d1SSimon J. Gerraty } 18093955d011SMarcel Moolenaar } 18103955d011SMarcel Moolenaar 18119f45a3c8SSimon J. Gerraty /* Perform a variable assignment that uses the operator ':='. */ 1812956e45f6SSimon J. Gerraty static void 1813dba7b0efSSimon J. Gerraty VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue, 181406b9b3e0SSimon J. Gerraty FStr *out_avalue) 1815956e45f6SSimon J. Gerraty { 1816956e45f6SSimon J. Gerraty char *evalue; 18173955d011SMarcel Moolenaar 18183955d011SMarcel Moolenaar /* 1819d5e0a182SSimon J. Gerraty * Make sure that we set the variable the first time to nothing 1820dba7b0efSSimon J. Gerraty * so that it gets substituted. 1821dba7b0efSSimon J. Gerraty * 1822dba7b0efSSimon J. Gerraty * TODO: Add a test that demonstrates why this code is needed, 1823dba7b0efSSimon J. Gerraty * apart from making the debug log longer. 18249f45a3c8SSimon J. Gerraty * 18259f45a3c8SSimon J. Gerraty * XXX: The variable name is expanded up to 3 times. 18263955d011SMarcel Moolenaar */ 1827dba7b0efSSimon J. Gerraty if (!Var_ExistsExpand(scope, name)) 1828dba7b0efSSimon J. Gerraty Var_SetExpand(scope, name, ""); 18293955d011SMarcel Moolenaar 18308d5c8e21SSimon J. Gerraty evalue = Var_Subst(uvalue, scope, 18318d5c8e21SSimon J. Gerraty VARE_EVAL_KEEP_DOLLAR_AND_UNDEFINED); 1832956e45f6SSimon J. Gerraty /* TODO: handle errors */ 183306b9b3e0SSimon J. Gerraty 1834dba7b0efSSimon J. Gerraty Var_SetExpand(scope, name, evalue); 18353955d011SMarcel Moolenaar 1836dba7b0efSSimon J. Gerraty *out_avalue = FStr_InitOwn(evalue); 18373955d011SMarcel Moolenaar } 18383955d011SMarcel Moolenaar 18399f45a3c8SSimon J. Gerraty /* Perform a variable assignment that uses the operator '!='. */ 1840956e45f6SSimon J. Gerraty static void 1841dba7b0efSSimon J. Gerraty VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope, 184206b9b3e0SSimon J. Gerraty FStr *out_avalue) 1843956e45f6SSimon J. Gerraty { 184406b9b3e0SSimon J. Gerraty FStr cmd; 18459f45a3c8SSimon J. Gerraty char *output, *error; 18463955d011SMarcel Moolenaar 184706b9b3e0SSimon J. Gerraty cmd = FStr_InitRefer(uvalue); 18488d5c8e21SSimon J. Gerraty Var_Expand(&cmd, SCOPE_CMDLINE, VARE_EVAL_DEFINED); 18499f45a3c8SSimon J. Gerraty 18509f45a3c8SSimon J. Gerraty output = Cmd_Exec(cmd.str, &error); 18519f45a3c8SSimon J. Gerraty Var_SetExpand(scope, name, output); 18529f45a3c8SSimon J. Gerraty *out_avalue = FStr_InitOwn(output); 18539f45a3c8SSimon J. Gerraty if (error != NULL) { 18549f45a3c8SSimon J. Gerraty Parse_Error(PARSE_WARNING, "%s", error); 18559f45a3c8SSimon J. Gerraty free(error); 1856956e45f6SSimon J. Gerraty } 1857956e45f6SSimon J. Gerraty 185806b9b3e0SSimon J. Gerraty FStr_Done(&cmd); 1859956e45f6SSimon J. Gerraty } 1860956e45f6SSimon J. Gerraty 186106b9b3e0SSimon J. Gerraty /* 186206b9b3e0SSimon J. Gerraty * Perform a variable assignment. 1863956e45f6SSimon J. Gerraty * 1864b0c40a00SSimon J. Gerraty * The actual value of the variable is returned in *out_true_avalue. 1865dba7b0efSSimon J. Gerraty * Especially for VAR_SUBST and VAR_SHELL this can differ from the literal 1866dba7b0efSSimon J. Gerraty * value. 1867956e45f6SSimon J. Gerraty * 1868dba7b0efSSimon J. Gerraty * Return whether the assignment was actually performed, which is usually 1869dba7b0efSSimon J. Gerraty * the case. It is only skipped if the operator is '?=' and the variable 1870dba7b0efSSimon J. Gerraty * already exists. 187106b9b3e0SSimon J. Gerraty */ 1872b0c40a00SSimon J. Gerraty static bool 1873956e45f6SSimon J. Gerraty VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, 1874b0c40a00SSimon J. Gerraty GNode *scope, FStr *out_true_avalue) 1875956e45f6SSimon J. Gerraty { 187606b9b3e0SSimon J. Gerraty FStr avalue = FStr_InitRefer(uvalue); 1877956e45f6SSimon J. Gerraty 1878e2eeea75SSimon J. Gerraty if (op == VAR_APPEND) 1879dba7b0efSSimon J. Gerraty Var_AppendExpand(scope, name, uvalue); 1880e2eeea75SSimon J. Gerraty else if (op == VAR_SUBST) 1881dba7b0efSSimon J. Gerraty VarAssign_EvalSubst(scope, name, uvalue, &avalue); 1882e2eeea75SSimon J. Gerraty else if (op == VAR_SHELL) 1883dba7b0efSSimon J. Gerraty VarAssign_EvalShell(name, uvalue, scope, &avalue); 1884e2eeea75SSimon J. Gerraty else { 18859f45a3c8SSimon J. Gerraty /* XXX: The variable name is expanded up to 2 times. */ 1886dba7b0efSSimon J. Gerraty if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name)) 1887b0c40a00SSimon J. Gerraty return false; 1888956e45f6SSimon J. Gerraty 1889956e45f6SSimon J. Gerraty /* Normal assignment -- just do it. */ 1890dba7b0efSSimon J. Gerraty Var_SetExpand(scope, name, uvalue); 1891956e45f6SSimon J. Gerraty } 1892956e45f6SSimon J. Gerraty 1893b0c40a00SSimon J. Gerraty *out_true_avalue = avalue; 1894b0c40a00SSimon J. Gerraty return true; 1895956e45f6SSimon J. Gerraty } 1896956e45f6SSimon J. Gerraty 1897956e45f6SSimon J. Gerraty static void 1898956e45f6SSimon J. Gerraty VarAssignSpecial(const char *name, const char *avalue) 1899956e45f6SSimon J. Gerraty { 19008c973ee2SSimon J. Gerraty if (strcmp(name, ".MAKEOVERRIDES") == 0) 1901b0c40a00SSimon J. Gerraty Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */ 1902956e45f6SSimon J. Gerraty else if (strcmp(name, ".CURDIR") == 0) { 19033955d011SMarcel Moolenaar /* 1904956e45f6SSimon J. Gerraty * Someone is being (too?) clever... 19053955d011SMarcel Moolenaar * Let's pretend they know what they are doing and 1906956e45f6SSimon J. Gerraty * re-initialize the 'cur' CachedDir. 19073955d011SMarcel Moolenaar */ 1908956e45f6SSimon J. Gerraty Dir_InitCur(avalue); 19093955d011SMarcel Moolenaar Dir_SetPATH(); 19108c973ee2SSimon J. Gerraty } else if (strcmp(name, ".MAKE.JOB.PREFIX") == 0) 19113955d011SMarcel Moolenaar Job_SetPrefix(); 19128c973ee2SSimon J. Gerraty else if (strcmp(name, ".MAKE.EXPORTED") == 0) 191306b9b3e0SSimon J. Gerraty Var_ExportVars(avalue); 19143955d011SMarcel Moolenaar } 1915956e45f6SSimon J. Gerraty 19169f45a3c8SSimon J. Gerraty /* Perform the variable assignment in the given scope. */ 19179f45a3c8SSimon J. Gerraty static void 1918b0c40a00SSimon J. Gerraty Parse_Var(VarAssign *var, GNode *scope) 1919956e45f6SSimon J. Gerraty { 192006b9b3e0SSimon J. Gerraty FStr avalue; /* actual value (maybe expanded) */ 1921956e45f6SSimon J. Gerraty 1922dba7b0efSSimon J. Gerraty VarCheckSyntax(var->op, var->value, scope); 1923dba7b0efSSimon J. Gerraty if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) { 192406b9b3e0SSimon J. Gerraty VarAssignSpecial(var->varname, avalue.str); 192506b9b3e0SSimon J. Gerraty FStr_Done(&avalue); 192606b9b3e0SSimon J. Gerraty } 19273955d011SMarcel Moolenaar } 19283955d011SMarcel Moolenaar 19293955d011SMarcel Moolenaar 193006b9b3e0SSimon J. Gerraty /* 1931d5e0a182SSimon J. Gerraty * See if the command possibly calls a sub-make by using the 193206b9b3e0SSimon J. Gerraty * expressions ${.MAKE}, ${MAKE} or the plain word "make". 193306b9b3e0SSimon J. Gerraty */ 1934b0c40a00SSimon J. Gerraty static bool 1935e2eeea75SSimon J. Gerraty MaybeSubMake(const char *cmd) 1936db29cad8SSimon J. Gerraty { 1937e2eeea75SSimon J. Gerraty const char *start; 1938e2eeea75SSimon J. Gerraty 1939e2eeea75SSimon J. Gerraty for (start = cmd; *start != '\0'; start++) { 1940e2eeea75SSimon J. Gerraty const char *p = start; 1941e2eeea75SSimon J. Gerraty char endc; 1942e2eeea75SSimon J. Gerraty 1943e2eeea75SSimon J. Gerraty /* XXX: What if progname != "make"? */ 19449f45a3c8SSimon J. Gerraty if (strncmp(p, "make", 4) == 0) 1945e2eeea75SSimon J. Gerraty if (start == cmd || !ch_isalnum(p[-1])) 1946e2eeea75SSimon J. Gerraty if (!ch_isalnum(p[4])) 1947b0c40a00SSimon J. Gerraty return true; 1948e2eeea75SSimon J. Gerraty 1949e2eeea75SSimon J. Gerraty if (*p != '$') 1950db29cad8SSimon J. Gerraty continue; 1951e2eeea75SSimon J. Gerraty p++; 1952e2eeea75SSimon J. Gerraty 1953e2eeea75SSimon J. Gerraty if (*p == '{') 1954e2eeea75SSimon J. Gerraty endc = '}'; 1955e2eeea75SSimon J. Gerraty else if (*p == '(') 1956e2eeea75SSimon J. Gerraty endc = ')'; 1957e2eeea75SSimon J. Gerraty else 1958e2eeea75SSimon J. Gerraty continue; 1959e2eeea75SSimon J. Gerraty p++; 1960e2eeea75SSimon J. Gerraty 1961e2eeea75SSimon J. Gerraty if (*p == '.') /* Accept either ${.MAKE} or ${MAKE}. */ 1962e2eeea75SSimon J. Gerraty p++; 1963e2eeea75SSimon J. Gerraty 19649f45a3c8SSimon J. Gerraty if (strncmp(p, "MAKE", 4) == 0 && p[4] == endc) 1965b0c40a00SSimon J. Gerraty return true; 1966db29cad8SSimon J. Gerraty } 1967b0c40a00SSimon J. Gerraty return false; 1968db29cad8SSimon J. Gerraty } 1969db29cad8SSimon J. Gerraty 1970d5e0a182SSimon J. Gerraty /* Append the command to the target node. */ 1971956e45f6SSimon J. Gerraty static void 19729f45a3c8SSimon J. Gerraty GNode_AddCommand(GNode *gn, char *cmd) 19733955d011SMarcel Moolenaar { 197406b9b3e0SSimon J. Gerraty if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL) 197506b9b3e0SSimon J. Gerraty gn = gn->cohorts.last->datum; 19763955d011SMarcel Moolenaar 19773955d011SMarcel Moolenaar /* if target already supplied, ignore commands */ 19783955d011SMarcel Moolenaar if (!(gn->type & OP_HAS_COMMANDS)) { 197906b9b3e0SSimon J. Gerraty Lst_Append(&gn->commands, cmd); 1980e2eeea75SSimon J. Gerraty if (MaybeSubMake(cmd)) 1981db29cad8SSimon J. Gerraty gn->type |= OP_SUBMAKE; 198212904384SSimon J. Gerraty RememberLocation(gn); 19833955d011SMarcel Moolenaar } else { 19843955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, 19853955d011SMarcel Moolenaar "duplicate script for target \"%s\" ignored", 19863955d011SMarcel Moolenaar gn->name); 1987954401e6SSimon J. Gerraty ParseErrorInternal(gn, PARSE_WARNING, 19883955d011SMarcel Moolenaar "using previous script for \"%s\" defined here", 19893955d011SMarcel Moolenaar gn->name); 19903955d011SMarcel Moolenaar } 19913955d011SMarcel Moolenaar } 19923955d011SMarcel Moolenaar 199306b9b3e0SSimon J. Gerraty /* 199412904384SSimon J. Gerraty * Parse a directive like '.include' or '.-include'. 199512904384SSimon J. Gerraty * 199612904384SSimon J. Gerraty * .include "user-makefile.mk" 199712904384SSimon J. Gerraty * .include <system-makefile.mk> 199812904384SSimon J. Gerraty */ 19993955d011SMarcel Moolenaar static void 2000b0c40a00SSimon J. Gerraty ParseInclude(char *directive) 20013955d011SMarcel Moolenaar { 200212904384SSimon J. Gerraty char endc; /* '>' or '"' */ 200312904384SSimon J. Gerraty char *p; 2004b0c40a00SSimon J. Gerraty bool silent = directive[0] != 'i'; 200512904384SSimon J. Gerraty FStr file; 20063955d011SMarcel Moolenaar 200712904384SSimon J. Gerraty p = directive + (silent ? 8 : 7); 200812904384SSimon J. Gerraty pp_skip_hspace(&p); 20093955d011SMarcel Moolenaar 201012904384SSimon J. Gerraty if (*p != '"' && *p != '<') { 20113955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, 20123955d011SMarcel Moolenaar ".include filename must be delimited by '\"' or '<'"); 20133955d011SMarcel Moolenaar return; 20143955d011SMarcel Moolenaar } 20153955d011SMarcel Moolenaar 2016d5e0a182SSimon J. Gerraty endc = *p++ == '<' ? '>' : '"'; 201712904384SSimon J. Gerraty file = FStr_InitRefer(p); 20183955d011SMarcel Moolenaar 201912904384SSimon J. Gerraty while (*p != '\0' && *p != endc) 202012904384SSimon J. Gerraty p++; 20213955d011SMarcel Moolenaar 202212904384SSimon J. Gerraty if (*p != endc) { 20233955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, 2024e2eeea75SSimon J. Gerraty "Unclosed .include filename. '%c' expected", endc); 20253955d011SMarcel Moolenaar return; 20263955d011SMarcel Moolenaar } 2027e2eeea75SSimon J. Gerraty 202812904384SSimon J. Gerraty *p = '\0'; 20293955d011SMarcel Moolenaar 20308d5c8e21SSimon J. Gerraty Var_Expand(&file, SCOPE_CMDLINE, VARE_EVAL); 203112904384SSimon J. Gerraty IncludeFile(file.str, endc == '>', directive[0] == 'd', silent); 203212904384SSimon J. Gerraty FStr_Done(&file); 20333955d011SMarcel Moolenaar } 20343955d011SMarcel Moolenaar 203506b9b3e0SSimon J. Gerraty /* 203606b9b3e0SSimon J. Gerraty * Split filename into dirname + basename, then assign these to the 203706b9b3e0SSimon J. Gerraty * given variables. 203806b9b3e0SSimon J. Gerraty */ 20395bcb7424SSimon J. Gerraty static void 2040956e45f6SSimon J. Gerraty SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) 20415bcb7424SSimon J. Gerraty { 2042b0c40a00SSimon J. Gerraty const char *slash, *basename; 2043b0c40a00SSimon J. Gerraty FStr dirname; 20443955d011SMarcel Moolenaar 20453955d011SMarcel Moolenaar slash = strrchr(filename, '/'); 20463955d011SMarcel Moolenaar if (slash == NULL) { 2047b0c40a00SSimon J. Gerraty dirname = FStr_InitRefer(curdir); 2048956e45f6SSimon J. Gerraty basename = filename; 20493955d011SMarcel Moolenaar } else { 2050b0c40a00SSimon J. Gerraty dirname = FStr_InitOwn(bmake_strsedup(filename, slash)); 2051956e45f6SSimon J. Gerraty basename = slash + 1; 20523955d011SMarcel Moolenaar } 20533955d011SMarcel Moolenaar 205412904384SSimon J. Gerraty Global_Set(dirvar, dirname.str); 205512904384SSimon J. Gerraty Global_Set(filevar, basename); 20563955d011SMarcel Moolenaar 20579f45a3c8SSimon J. Gerraty DEBUG4(PARSE, "SetFilenameVars: ${%s} = `%s' ${%s} = `%s'\n", 20589f45a3c8SSimon J. Gerraty dirvar, dirname.str, filevar, basename); 2059b0c40a00SSimon J. Gerraty FStr_Done(&dirname); 2060956e45f6SSimon J. Gerraty } 2061956e45f6SSimon J. Gerraty 206206b9b3e0SSimon J. Gerraty /* 206306b9b3e0SSimon J. Gerraty * Return the immediately including file. 2064956e45f6SSimon J. Gerraty * 2065956e45f6SSimon J. Gerraty * This is made complicated since the .for loop is implemented as a special 206606b9b3e0SSimon J. Gerraty * kind of .include; see For_Run. 206706b9b3e0SSimon J. Gerraty */ 2068956e45f6SSimon J. Gerraty static const char * 2069956e45f6SSimon J. Gerraty GetActuallyIncludingFile(void) 2070956e45f6SSimon J. Gerraty { 2071956e45f6SSimon J. Gerraty size_t i; 20729f45a3c8SSimon J. Gerraty const IncludedFile *incs = GetInclude(0); 2073956e45f6SSimon J. Gerraty 2074956e45f6SSimon J. Gerraty for (i = includes.len; i >= 2; i--) 20759f45a3c8SSimon J. Gerraty if (incs[i - 1].forLoop == NULL) 20769f45a3c8SSimon J. Gerraty return incs[i - 2].name.str; 2077956e45f6SSimon J. Gerraty return NULL; 2078956e45f6SSimon J. Gerraty } 2079956e45f6SSimon J. Gerraty 2080956e45f6SSimon J. Gerraty /* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */ 2081956e45f6SSimon J. Gerraty static void 20829f45a3c8SSimon J. Gerraty SetParseFile(const char *filename) 2083956e45f6SSimon J. Gerraty { 2084956e45f6SSimon J. Gerraty const char *including; 2085956e45f6SSimon J. Gerraty 2086956e45f6SSimon J. Gerraty SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE"); 2087956e45f6SSimon J. Gerraty 2088956e45f6SSimon J. Gerraty including = GetActuallyIncludingFile(); 2089956e45f6SSimon J. Gerraty if (including != NULL) { 2090956e45f6SSimon J. Gerraty SetFilenameVars(including, 2091956e45f6SSimon J. Gerraty ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); 2092956e45f6SSimon J. Gerraty } else { 2093dba7b0efSSimon J. Gerraty Global_Delete(".INCLUDEDFROMDIR"); 2094dba7b0efSSimon J. Gerraty Global_Delete(".INCLUDEDFROMFILE"); 2095956e45f6SSimon J. Gerraty } 2096956e45f6SSimon J. Gerraty } 2097956e45f6SSimon J. Gerraty 2098b0c40a00SSimon J. Gerraty static bool 2099956e45f6SSimon J. Gerraty StrContainsWord(const char *str, const char *word) 2100956e45f6SSimon J. Gerraty { 2101956e45f6SSimon J. Gerraty size_t strLen = strlen(str); 2102956e45f6SSimon J. Gerraty size_t wordLen = strlen(word); 21039f45a3c8SSimon J. Gerraty const char *p; 2104956e45f6SSimon J. Gerraty 2105956e45f6SSimon J. Gerraty if (strLen < wordLen) 21069f45a3c8SSimon J. Gerraty return false; 2107956e45f6SSimon J. Gerraty 2108956e45f6SSimon J. Gerraty for (p = str; p != NULL; p = strchr(p, ' ')) { 2109956e45f6SSimon J. Gerraty if (*p == ' ') 2110956e45f6SSimon J. Gerraty p++; 21119f45a3c8SSimon J. Gerraty if (p > str + strLen - wordLen) 21129f45a3c8SSimon J. Gerraty return false; 2113956e45f6SSimon J. Gerraty 2114956e45f6SSimon J. Gerraty if (memcmp(p, word, wordLen) == 0 && 2115956e45f6SSimon J. Gerraty (p[wordLen] == '\0' || p[wordLen] == ' ')) 2116b0c40a00SSimon J. Gerraty return true; 2117956e45f6SSimon J. Gerraty } 2118b0c40a00SSimon J. Gerraty return false; 2119956e45f6SSimon J. Gerraty } 2120956e45f6SSimon J. Gerraty 212106b9b3e0SSimon J. Gerraty /* 212206b9b3e0SSimon J. Gerraty * XXX: Searching through a set of words with this linear search is 212306b9b3e0SSimon J. Gerraty * inefficient for variables that contain thousands of words. 212406b9b3e0SSimon J. Gerraty * 212506b9b3e0SSimon J. Gerraty * XXX: The paths in this list don't seem to be normalized in any way. 212606b9b3e0SSimon J. Gerraty */ 2127b0c40a00SSimon J. Gerraty static bool 2128956e45f6SSimon J. Gerraty VarContainsWord(const char *varname, const char *word) 2129956e45f6SSimon J. Gerraty { 2130dba7b0efSSimon J. Gerraty FStr val = Var_Value(SCOPE_GLOBAL, varname); 2131b0c40a00SSimon J. Gerraty bool found = val.str != NULL && StrContainsWord(val.str, word); 213206b9b3e0SSimon J. Gerraty FStr_Done(&val); 2133956e45f6SSimon J. Gerraty return found; 2134956e45f6SSimon J. Gerraty } 2135956e45f6SSimon J. Gerraty 213606b9b3e0SSimon J. Gerraty /* 213706b9b3e0SSimon J. Gerraty * Track the makefiles we read - so makefiles can set dependencies on them. 2138e2eeea75SSimon J. Gerraty * Avoid adding anything more than once. 2139e2eeea75SSimon J. Gerraty * 2140e2eeea75SSimon J. Gerraty * Time complexity: O(n) per call, in total O(n^2), where n is the number 214106b9b3e0SSimon J. Gerraty * of makefiles that have been loaded. 214206b9b3e0SSimon J. Gerraty */ 21433955d011SMarcel Moolenaar static void 21449f45a3c8SSimon J. Gerraty TrackInput(const char *name) 21453955d011SMarcel Moolenaar { 21468c973ee2SSimon J. Gerraty if (!VarContainsWord(".MAKE.MAKEFILES", name)) 21478c973ee2SSimon J. Gerraty Global_Append(".MAKE.MAKEFILES", name); 21483955d011SMarcel Moolenaar } 21493955d011SMarcel Moolenaar 21503955d011SMarcel Moolenaar 21519f45a3c8SSimon J. Gerraty /* Parse from the given buffer, later return to the current file. */ 21523955d011SMarcel Moolenaar void 21539f45a3c8SSimon J. Gerraty Parse_PushInput(const char *name, unsigned lineno, unsigned readLines, 21549f45a3c8SSimon J. Gerraty Buffer buf, struct ForLoop *forLoop) 21553955d011SMarcel Moolenaar { 21569f45a3c8SSimon J. Gerraty IncludedFile *curFile; 21573955d011SMarcel Moolenaar 21589f45a3c8SSimon J. Gerraty if (forLoop != NULL) 21599f45a3c8SSimon J. Gerraty name = CurFile()->name.str; 21603955d011SMarcel Moolenaar else 21619f45a3c8SSimon J. Gerraty TrackInput(name); 21623955d011SMarcel Moolenaar 21639f45a3c8SSimon J. Gerraty DEBUG3(PARSE, "Parse_PushInput: %s %s, line %u\n", 21649f45a3c8SSimon J. Gerraty forLoop != NULL ? ".for loop in": "file", name, lineno); 21653955d011SMarcel Moolenaar 2166956e45f6SSimon J. Gerraty curFile = Vector_Push(&includes); 21679f45a3c8SSimon J. Gerraty curFile->name = FStr_InitOwn(bmake_strdup(name)); 216806b9b3e0SSimon J. Gerraty curFile->lineno = lineno; 21699f45a3c8SSimon J. Gerraty curFile->readLines = readLines; 21709f45a3c8SSimon J. Gerraty curFile->forHeadLineno = lineno; 21719f45a3c8SSimon J. Gerraty curFile->forBodyReadLines = readLines; 21729f45a3c8SSimon J. Gerraty curFile->buf = buf; 2173be19d90bSSimon J. Gerraty curFile->depending = doing_depend; /* restore this on EOF */ 2174148ee845SSimon J. Gerraty curFile->guardState = forLoop == NULL ? GS_START : GS_NO; 2175148ee845SSimon J. Gerraty curFile->guard = NULL; 21769f45a3c8SSimon J. Gerraty curFile->forLoop = forLoop; 21773955d011SMarcel Moolenaar 21789f45a3c8SSimon J. Gerraty if (forLoop != NULL && !For_NextIteration(forLoop, &curFile->buf)) 21799f45a3c8SSimon J. Gerraty abort(); /* see For_Run */ 21803955d011SMarcel Moolenaar 21819f45a3c8SSimon J. Gerraty curFile->buf_ptr = curFile->buf.data; 21829f45a3c8SSimon J. Gerraty curFile->buf_end = curFile->buf.data + curFile->buf.len; 21834fde40d9SSimon J. Gerraty curFile->condMinDepth = cond_depth; 21849f45a3c8SSimon J. Gerraty SetParseFile(name); 21853955d011SMarcel Moolenaar } 21863955d011SMarcel Moolenaar 2187956e45f6SSimon J. Gerraty /* Check if the directive is an include directive. */ 2188b0c40a00SSimon J. Gerraty static bool 2189b0c40a00SSimon J. Gerraty IsInclude(const char *dir, bool sysv) 2190494f7191SSimon J. Gerraty { 2191956e45f6SSimon J. Gerraty if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv)) 2192956e45f6SSimon J. Gerraty dir++; 2193494f7191SSimon J. Gerraty 2194956e45f6SSimon J. Gerraty if (strncmp(dir, "include", 7) != 0) 2195b0c40a00SSimon J. Gerraty return false; 2196494f7191SSimon J. Gerraty 21972c3632d1SSimon J. Gerraty /* Space is not mandatory for BSD .include */ 2198956e45f6SSimon J. Gerraty return !sysv || ch_isspace(dir[7]); 2199494f7191SSimon J. Gerraty } 2200494f7191SSimon J. Gerraty 2201494f7191SSimon J. Gerraty 2202956e45f6SSimon J. Gerraty /* Check if the line is a SYSV include directive. */ 2203b0c40a00SSimon J. Gerraty static bool 2204494f7191SSimon J. Gerraty IsSysVInclude(const char *line) 2205494f7191SSimon J. Gerraty { 2206494f7191SSimon J. Gerraty const char *p; 2207494f7191SSimon J. Gerraty 2208b0c40a00SSimon J. Gerraty if (!IsInclude(line, true)) 2209b0c40a00SSimon J. Gerraty return false; 2210494f7191SSimon J. Gerraty 2211956e45f6SSimon J. Gerraty /* Avoid interpreting a dependency line as an include */ 2212494f7191SSimon J. Gerraty for (p = line; (p = strchr(p, ':')) != NULL;) { 2213e2eeea75SSimon J. Gerraty 2214e2eeea75SSimon J. Gerraty /* end of line -> it's a dependency */ 2215e2eeea75SSimon J. Gerraty if (*++p == '\0') 2216b0c40a00SSimon J. Gerraty return false; 2217e2eeea75SSimon J. Gerraty 2218e2eeea75SSimon J. Gerraty /* '::' operator or ': ' -> it's a dependency */ 2219e2eeea75SSimon J. Gerraty if (*p == ':' || ch_isspace(*p)) 2220b0c40a00SSimon J. Gerraty return false; 2221494f7191SSimon J. Gerraty } 2222b0c40a00SSimon J. Gerraty return true; 2223494f7191SSimon J. Gerraty } 2224494f7191SSimon J. Gerraty 2225956e45f6SSimon J. Gerraty /* Push to another file. The line points to the word "include". */ 22263955d011SMarcel Moolenaar static void 22273955d011SMarcel Moolenaar ParseTraditionalInclude(char *line) 22283955d011SMarcel Moolenaar { 2229d5e0a182SSimon J. Gerraty char *p; /* current position in file spec */ 2230b0c40a00SSimon J. Gerraty bool done = false; 2231b0c40a00SSimon J. Gerraty bool silent = line[0] != 'i'; 2232956e45f6SSimon J. Gerraty char *file = line + (silent ? 8 : 7); 22333955d011SMarcel Moolenaar char *all_files; 22343955d011SMarcel Moolenaar 22359f45a3c8SSimon J. Gerraty DEBUG1(PARSE, "ParseTraditionalInclude: %s\n", file); 22363955d011SMarcel Moolenaar 2237956e45f6SSimon J. Gerraty pp_skip_whitespace(&file); 22383955d011SMarcel Moolenaar 22398d5c8e21SSimon J. Gerraty all_files = Var_Subst(file, SCOPE_CMDLINE, VARE_EVAL); 2240956e45f6SSimon J. Gerraty /* TODO: handle errors */ 22413955d011SMarcel Moolenaar 2242d5e0a182SSimon J. Gerraty for (file = all_files; !done; file = p + 1) { 22433955d011SMarcel Moolenaar /* Skip to end of line or next whitespace */ 2244d5e0a182SSimon J. Gerraty for (p = file; *p != '\0' && !ch_isspace(*p); p++) 22453955d011SMarcel Moolenaar continue; 22463955d011SMarcel Moolenaar 2247d5e0a182SSimon J. Gerraty if (*p != '\0') 2248d5e0a182SSimon J. Gerraty *p = '\0'; 22493955d011SMarcel Moolenaar else 2250b0c40a00SSimon J. Gerraty done = true; 22513955d011SMarcel Moolenaar 2252b0c40a00SSimon J. Gerraty IncludeFile(file, false, false, silent); 22533955d011SMarcel Moolenaar } 22549f45a3c8SSimon J. Gerraty 22553955d011SMarcel Moolenaar free(all_files); 22563955d011SMarcel Moolenaar } 22573955d011SMarcel Moolenaar 2258956e45f6SSimon J. Gerraty /* Parse "export <variable>=<value>", and actually export it. */ 22593955d011SMarcel Moolenaar static void 22603955d011SMarcel Moolenaar ParseGmakeExport(char *line) 22613955d011SMarcel Moolenaar { 2262956e45f6SSimon J. Gerraty char *variable = line + 6; 22633955d011SMarcel Moolenaar char *value; 22643955d011SMarcel Moolenaar 22659f45a3c8SSimon J. Gerraty DEBUG1(PARSE, "ParseGmakeExport: %s\n", variable); 22663955d011SMarcel Moolenaar 2267956e45f6SSimon J. Gerraty pp_skip_whitespace(&variable); 22683955d011SMarcel Moolenaar 226906b9b3e0SSimon J. Gerraty for (value = variable; *value != '\0' && *value != '='; value++) 22703955d011SMarcel Moolenaar continue; 22713955d011SMarcel Moolenaar 22723955d011SMarcel Moolenaar if (*value != '=') { 22733955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, 22743955d011SMarcel Moolenaar "Variable/Value missing from \"export\""); 22753955d011SMarcel Moolenaar return; 22763955d011SMarcel Moolenaar } 22771748de26SSimon J. Gerraty *value++ = '\0'; /* terminate variable */ 22783955d011SMarcel Moolenaar 22793955d011SMarcel Moolenaar /* 22803955d011SMarcel Moolenaar * Expand the value before putting it in the environment. 22813955d011SMarcel Moolenaar */ 22828d5c8e21SSimon J. Gerraty value = Var_Subst(value, SCOPE_CMDLINE, VARE_EVAL); 2283956e45f6SSimon J. Gerraty /* TODO: handle errors */ 2284956e45f6SSimon J. Gerraty 22853955d011SMarcel Moolenaar setenv(variable, value, 1); 2286e1cee40dSSimon J. Gerraty free(value); 22873955d011SMarcel Moolenaar } 22883955d011SMarcel Moolenaar 228906b9b3e0SSimon J. Gerraty /* 2290d5e0a182SSimon J. Gerraty * When the end of the current file or .for loop is reached, continue reading 2291d5e0a182SSimon J. Gerraty * the previous file at the previous location. 22923955d011SMarcel Moolenaar * 22933955d011SMarcel Moolenaar * Results: 2294b0c40a00SSimon J. Gerraty * true to continue parsing, i.e. it had only reached the end of an 2295b0c40a00SSimon J. Gerraty * included file, false if the main file has been parsed completely. 22963955d011SMarcel Moolenaar */ 2297b0c40a00SSimon J. Gerraty static bool 22983955d011SMarcel Moolenaar ParseEOF(void) 22993955d011SMarcel Moolenaar { 23009f45a3c8SSimon J. Gerraty IncludedFile *curFile = CurFile(); 23013955d011SMarcel Moolenaar 23029f45a3c8SSimon J. Gerraty doing_depend = curFile->depending; 23039f45a3c8SSimon J. Gerraty if (curFile->forLoop != NULL && 23049f45a3c8SSimon J. Gerraty For_NextIteration(curFile->forLoop, &curFile->buf)) { 23059f45a3c8SSimon J. Gerraty curFile->buf_ptr = curFile->buf.data; 23069f45a3c8SSimon J. Gerraty curFile->buf_end = curFile->buf.data + curFile->buf.len; 23079f45a3c8SSimon J. Gerraty curFile->readLines = curFile->forBodyReadLines; 23089f45a3c8SSimon J. Gerraty return true; 23093955d011SMarcel Moolenaar } 23103955d011SMarcel Moolenaar 23114fde40d9SSimon J. Gerraty Cond_EndFile(); 23129f45a3c8SSimon J. Gerraty 23138d5c8e21SSimon J. Gerraty if (curFile->guardState == GS_DONE) { 23148d5c8e21SSimon J. Gerraty HashEntry *he = HashTable_CreateEntry(&guards, 23158d5c8e21SSimon J. Gerraty curFile->name.str, NULL); 23168d5c8e21SSimon J. Gerraty if (he->value != NULL) { 23178d5c8e21SSimon J. Gerraty free(((Guard *)he->value)->name); 23188d5c8e21SSimon J. Gerraty free(he->value); 23198d5c8e21SSimon J. Gerraty } 23208d5c8e21SSimon J. Gerraty HashEntry_Set(he, curFile->guard); 23218d5c8e21SSimon J. Gerraty } else if (curFile->guard != NULL) { 2322148ee845SSimon J. Gerraty free(curFile->guard->name); 2323148ee845SSimon J. Gerraty free(curFile->guard); 2324148ee845SSimon J. Gerraty } 2325148ee845SSimon J. Gerraty 23269f45a3c8SSimon J. Gerraty FStr_Done(&curFile->name); 23279f45a3c8SSimon J. Gerraty Buf_Done(&curFile->buf); 23289f45a3c8SSimon J. Gerraty if (curFile->forLoop != NULL) 23299f45a3c8SSimon J. Gerraty ForLoop_Free(curFile->forLoop); 2330956e45f6SSimon J. Gerraty Vector_Pop(&includes); 23313955d011SMarcel Moolenaar 2332956e45f6SSimon J. Gerraty if (includes.len == 0) { 23333955d011SMarcel Moolenaar /* We've run out of input */ 2334dba7b0efSSimon J. Gerraty Global_Delete(".PARSEDIR"); 2335dba7b0efSSimon J. Gerraty Global_Delete(".PARSEFILE"); 2336dba7b0efSSimon J. Gerraty Global_Delete(".INCLUDEDFROMDIR"); 2337dba7b0efSSimon J. Gerraty Global_Delete(".INCLUDEDFROMFILE"); 2338b0c40a00SSimon J. Gerraty return false; 23393955d011SMarcel Moolenaar } 23403955d011SMarcel Moolenaar 2341956e45f6SSimon J. Gerraty curFile = CurFile(); 23429f45a3c8SSimon J. Gerraty DEBUG2(PARSE, "ParseEOF: returning to file %s, line %u\n", 23439f45a3c8SSimon J. Gerraty curFile->name.str, curFile->readLines + 1); 23443955d011SMarcel Moolenaar 23459f45a3c8SSimon J. Gerraty SetParseFile(curFile->name.str); 2346b0c40a00SSimon J. Gerraty return true; 23473955d011SMarcel Moolenaar } 23483955d011SMarcel Moolenaar 234906b9b3e0SSimon J. Gerraty typedef enum ParseRawLineResult { 235006b9b3e0SSimon J. Gerraty PRLR_LINE, 235106b9b3e0SSimon J. Gerraty PRLR_EOF, 235206b9b3e0SSimon J. Gerraty PRLR_ERROR 235306b9b3e0SSimon J. Gerraty } ParseRawLineResult; 235406b9b3e0SSimon J. Gerraty 235506b9b3e0SSimon J. Gerraty /* 235606b9b3e0SSimon J. Gerraty * Parse until the end of a line, taking into account lines that end with 235712904384SSimon J. Gerraty * backslash-newline. The resulting line goes from out_line to out_line_end; 235812904384SSimon J. Gerraty * the line is not null-terminated. 235906b9b3e0SSimon J. Gerraty */ 236006b9b3e0SSimon J. Gerraty static ParseRawLineResult 23619f45a3c8SSimon J. Gerraty ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end, 23629f45a3c8SSimon J. Gerraty char **out_firstBackslash, char **out_commentLineEnd) 236306b9b3e0SSimon J. Gerraty { 236406b9b3e0SSimon J. Gerraty char *line = curFile->buf_ptr; 236512904384SSimon J. Gerraty char *buf_end = curFile->buf_end; 236606b9b3e0SSimon J. Gerraty char *p = line; 236706b9b3e0SSimon J. Gerraty char *line_end = line; 236806b9b3e0SSimon J. Gerraty char *firstBackslash = NULL; 23699f45a3c8SSimon J. Gerraty char *commentLineEnd = NULL; 237006b9b3e0SSimon J. Gerraty ParseRawLineResult res = PRLR_LINE; 237106b9b3e0SSimon J. Gerraty 23729f45a3c8SSimon J. Gerraty curFile->readLines++; 237306b9b3e0SSimon J. Gerraty 237406b9b3e0SSimon J. Gerraty for (;;) { 237506b9b3e0SSimon J. Gerraty char ch; 237606b9b3e0SSimon J. Gerraty 237712904384SSimon J. Gerraty if (p == buf_end) { 237806b9b3e0SSimon J. Gerraty res = PRLR_EOF; 237906b9b3e0SSimon J. Gerraty break; 238006b9b3e0SSimon J. Gerraty } 238106b9b3e0SSimon J. Gerraty 238206b9b3e0SSimon J. Gerraty ch = *p; 23839f45a3c8SSimon J. Gerraty if (ch == '\0' || (ch == '\\' && p[1] == '\0')) { 238406b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Zero byte read from file"); 2385548bfc56SSimon J. Gerraty exit(2); 238606b9b3e0SSimon J. Gerraty } 238706b9b3e0SSimon J. Gerraty 238806b9b3e0SSimon J. Gerraty /* Treat next character after '\' as literal. */ 238906b9b3e0SSimon J. Gerraty if (ch == '\\') { 239006b9b3e0SSimon J. Gerraty if (firstBackslash == NULL) 239106b9b3e0SSimon J. Gerraty firstBackslash = p; 239206b9b3e0SSimon J. Gerraty if (p[1] == '\n') { 23939f45a3c8SSimon J. Gerraty curFile->readLines++; 239412904384SSimon J. Gerraty if (p + 2 == buf_end) { 239506b9b3e0SSimon J. Gerraty line_end = p; 239606b9b3e0SSimon J. Gerraty *line_end = '\n'; 239706b9b3e0SSimon J. Gerraty p += 2; 239806b9b3e0SSimon J. Gerraty continue; 239906b9b3e0SSimon J. Gerraty } 240006b9b3e0SSimon J. Gerraty } 240106b9b3e0SSimon J. Gerraty p += 2; 240206b9b3e0SSimon J. Gerraty line_end = p; 240312904384SSimon J. Gerraty assert(p <= buf_end); 240406b9b3e0SSimon J. Gerraty continue; 240506b9b3e0SSimon J. Gerraty } 240606b9b3e0SSimon J. Gerraty 240706b9b3e0SSimon J. Gerraty /* 240806b9b3e0SSimon J. Gerraty * Remember the first '#' for comment stripping, unless 240906b9b3e0SSimon J. Gerraty * the previous char was '[', as in the modifier ':[#]'. 241006b9b3e0SSimon J. Gerraty */ 24119f45a3c8SSimon J. Gerraty if (ch == '#' && commentLineEnd == NULL && 241206b9b3e0SSimon J. Gerraty !(p > line && p[-1] == '[')) 24139f45a3c8SSimon J. Gerraty commentLineEnd = line_end; 241406b9b3e0SSimon J. Gerraty 241506b9b3e0SSimon J. Gerraty p++; 241606b9b3e0SSimon J. Gerraty if (ch == '\n') 241706b9b3e0SSimon J. Gerraty break; 241806b9b3e0SSimon J. Gerraty 241906b9b3e0SSimon J. Gerraty /* We are not interested in trailing whitespace. */ 242006b9b3e0SSimon J. Gerraty if (!ch_isspace(ch)) 242106b9b3e0SSimon J. Gerraty line_end = p; 242206b9b3e0SSimon J. Gerraty } 242306b9b3e0SSimon J. Gerraty 242406b9b3e0SSimon J. Gerraty curFile->buf_ptr = p; 24259f45a3c8SSimon J. Gerraty *out_line = line; 242606b9b3e0SSimon J. Gerraty *out_line_end = line_end; 242706b9b3e0SSimon J. Gerraty *out_firstBackslash = firstBackslash; 24289f45a3c8SSimon J. Gerraty *out_commentLineEnd = commentLineEnd; 242906b9b3e0SSimon J. Gerraty return res; 243006b9b3e0SSimon J. Gerraty } 243106b9b3e0SSimon J. Gerraty 243206b9b3e0SSimon J. Gerraty /* 243306b9b3e0SSimon J. Gerraty * Beginning at start, unescape '\#' to '#' and replace backslash-newline 243406b9b3e0SSimon J. Gerraty * with a single space. 243506b9b3e0SSimon J. Gerraty */ 243606b9b3e0SSimon J. Gerraty static void 243706b9b3e0SSimon J. Gerraty UnescapeBackslash(char *line, char *start) 243806b9b3e0SSimon J. Gerraty { 24399f45a3c8SSimon J. Gerraty const char *src = start; 244006b9b3e0SSimon J. Gerraty char *dst = start; 244106b9b3e0SSimon J. Gerraty char *spaceStart = line; 244206b9b3e0SSimon J. Gerraty 244306b9b3e0SSimon J. Gerraty for (;;) { 244406b9b3e0SSimon J. Gerraty char ch = *src++; 244506b9b3e0SSimon J. Gerraty if (ch != '\\') { 244606b9b3e0SSimon J. Gerraty if (ch == '\0') 244706b9b3e0SSimon J. Gerraty break; 244806b9b3e0SSimon J. Gerraty *dst++ = ch; 244906b9b3e0SSimon J. Gerraty continue; 245006b9b3e0SSimon J. Gerraty } 245106b9b3e0SSimon J. Gerraty 245206b9b3e0SSimon J. Gerraty ch = *src++; 245306b9b3e0SSimon J. Gerraty if (ch == '\0') { 24549f45a3c8SSimon J. Gerraty /* Delete '\\' at the end of the buffer. */ 245506b9b3e0SSimon J. Gerraty dst--; 245606b9b3e0SSimon J. Gerraty break; 245706b9b3e0SSimon J. Gerraty } 245806b9b3e0SSimon J. Gerraty 24599f45a3c8SSimon J. Gerraty /* Delete '\\' from before '#' on non-command lines. */ 24609f45a3c8SSimon J. Gerraty if (ch == '#' && line[0] != '\t') 246106b9b3e0SSimon J. Gerraty *dst++ = ch; 24629f45a3c8SSimon J. Gerraty else if (ch == '\n') { 24639f45a3c8SSimon J. Gerraty cpp_skip_hspace(&src); 246406b9b3e0SSimon J. Gerraty *dst++ = ' '; 24659f45a3c8SSimon J. Gerraty } else { 24669f45a3c8SSimon J. Gerraty /* Leave '\\' in the buffer for later. */ 24679f45a3c8SSimon J. Gerraty *dst++ = '\\'; 24689f45a3c8SSimon J. Gerraty *dst++ = ch; 24699f45a3c8SSimon J. Gerraty /* Keep an escaped ' ' at the line end. */ 24709f45a3c8SSimon J. Gerraty spaceStart = dst; 24719f45a3c8SSimon J. Gerraty } 247206b9b3e0SSimon J. Gerraty } 247306b9b3e0SSimon J. Gerraty 247406b9b3e0SSimon J. Gerraty /* Delete any trailing spaces - eg from empty continuations */ 247506b9b3e0SSimon J. Gerraty while (dst > spaceStart && ch_isspace(dst[-1])) 247606b9b3e0SSimon J. Gerraty dst--; 247706b9b3e0SSimon J. Gerraty *dst = '\0'; 247806b9b3e0SSimon J. Gerraty } 247906b9b3e0SSimon J. Gerraty 24809f45a3c8SSimon J. Gerraty typedef enum LineKind { 248106b9b3e0SSimon J. Gerraty /* 248206b9b3e0SSimon J. Gerraty * Return the next line that is neither empty nor a comment. 248306b9b3e0SSimon J. Gerraty * Backslash line continuations are folded into a single space. 248406b9b3e0SSimon J. Gerraty * A trailing comment, if any, is discarded. 248506b9b3e0SSimon J. Gerraty */ 24869f45a3c8SSimon J. Gerraty LK_NONEMPTY, 248706b9b3e0SSimon J. Gerraty 248806b9b3e0SSimon J. Gerraty /* 248906b9b3e0SSimon J. Gerraty * Return the next line, even if it is empty or a comment. 249006b9b3e0SSimon J. Gerraty * Preserve backslash-newline to keep the line numbers correct. 249106b9b3e0SSimon J. Gerraty * 249206b9b3e0SSimon J. Gerraty * Used in .for loops to collect the body of the loop while waiting 249306b9b3e0SSimon J. Gerraty * for the corresponding .endfor. 249406b9b3e0SSimon J. Gerraty */ 24959f45a3c8SSimon J. Gerraty LK_FOR_BODY, 249606b9b3e0SSimon J. Gerraty 249706b9b3e0SSimon J. Gerraty /* 249806b9b3e0SSimon J. Gerraty * Return the next line that starts with a dot. 249906b9b3e0SSimon J. Gerraty * Backslash line continuations are folded into a single space. 250006b9b3e0SSimon J. Gerraty * A trailing comment, if any, is discarded. 250106b9b3e0SSimon J. Gerraty * 250206b9b3e0SSimon J. Gerraty * Used in .if directives to skip over irrelevant branches while 250306b9b3e0SSimon J. Gerraty * waiting for the corresponding .endif. 250406b9b3e0SSimon J. Gerraty */ 25059f45a3c8SSimon J. Gerraty LK_DOT 25069f45a3c8SSimon J. Gerraty } LineKind; 25073955d011SMarcel Moolenaar 25089f45a3c8SSimon J. Gerraty /* 25099f45a3c8SSimon J. Gerraty * Return the next "interesting" logical line from the current file. The 25109f45a3c8SSimon J. Gerraty * returned string will be freed at the end of including the file. 25119f45a3c8SSimon J. Gerraty */ 25123955d011SMarcel Moolenaar static char * 25139f45a3c8SSimon J. Gerraty ReadLowLevelLine(LineKind kind) 25143955d011SMarcel Moolenaar { 25159f45a3c8SSimon J. Gerraty IncludedFile *curFile = CurFile(); 25169f45a3c8SSimon J. Gerraty ParseRawLineResult res; 25173955d011SMarcel Moolenaar char *line; 25183955d011SMarcel Moolenaar char *line_end; 251906b9b3e0SSimon J. Gerraty char *firstBackslash; 25209f45a3c8SSimon J. Gerraty char *commentLineEnd; 25213955d011SMarcel Moolenaar 25223955d011SMarcel Moolenaar for (;;) { 25239f45a3c8SSimon J. Gerraty curFile->lineno = curFile->readLines + 1; 25249f45a3c8SSimon J. Gerraty res = ParseRawLine(curFile, 25259f45a3c8SSimon J. Gerraty &line, &line_end, &firstBackslash, &commentLineEnd); 252606b9b3e0SSimon J. Gerraty if (res == PRLR_ERROR) 25273955d011SMarcel Moolenaar return NULL; 25283955d011SMarcel Moolenaar 25299f45a3c8SSimon J. Gerraty if (line == line_end || line == commentLineEnd) { 253006b9b3e0SSimon J. Gerraty if (res == PRLR_EOF) 25313955d011SMarcel Moolenaar return NULL; 25329f45a3c8SSimon J. Gerraty if (kind != LK_FOR_BODY) 25333955d011SMarcel Moolenaar continue; 25343955d011SMarcel Moolenaar } 25353955d011SMarcel Moolenaar 25363955d011SMarcel Moolenaar /* We now have a line of data */ 253706b9b3e0SSimon J. Gerraty assert(ch_isspace(*line_end)); 2538e2eeea75SSimon J. Gerraty *line_end = '\0'; 25393955d011SMarcel Moolenaar 25409f45a3c8SSimon J. Gerraty if (kind == LK_FOR_BODY) 254106b9b3e0SSimon J. Gerraty return line; /* Don't join the physical lines. */ 25423955d011SMarcel Moolenaar 25439f45a3c8SSimon J. Gerraty if (kind == LK_DOT && line[0] != '.') 25443955d011SMarcel Moolenaar continue; 25453955d011SMarcel Moolenaar break; 25463955d011SMarcel Moolenaar } 25473955d011SMarcel Moolenaar 25489f45a3c8SSimon J. Gerraty if (commentLineEnd != NULL && line[0] != '\t') 25499f45a3c8SSimon J. Gerraty *commentLineEnd = '\0'; 25509f45a3c8SSimon J. Gerraty if (firstBackslash != NULL) 255106b9b3e0SSimon J. Gerraty UnescapeBackslash(line, firstBackslash); 25523955d011SMarcel Moolenaar return line; 25533955d011SMarcel Moolenaar } 25543955d011SMarcel Moolenaar 2555b0c40a00SSimon J. Gerraty static bool 25569f45a3c8SSimon J. Gerraty SkipIrrelevantBranches(void) 255706b9b3e0SSimon J. Gerraty { 25589f45a3c8SSimon J. Gerraty const char *line; 255906b9b3e0SSimon J. Gerraty 2560d5e0a182SSimon J. Gerraty while ((line = ReadLowLevelLine(LK_DOT)) != NULL) 25619f45a3c8SSimon J. Gerraty if (Cond_EvalLine(line) == CR_TRUE) 25629f45a3c8SSimon J. Gerraty return true; 25639f45a3c8SSimon J. Gerraty return false; 256406b9b3e0SSimon J. Gerraty } 256506b9b3e0SSimon J. Gerraty 2566b0c40a00SSimon J. Gerraty static bool 256706b9b3e0SSimon J. Gerraty ParseForLoop(const char *line) 256806b9b3e0SSimon J. Gerraty { 256906b9b3e0SSimon J. Gerraty int rval; 25709f45a3c8SSimon J. Gerraty unsigned forHeadLineno; 25719f45a3c8SSimon J. Gerraty unsigned bodyReadLines; 25729f45a3c8SSimon J. Gerraty int forLevel; 257306b9b3e0SSimon J. Gerraty 257406b9b3e0SSimon J. Gerraty rval = For_Eval(line); 257506b9b3e0SSimon J. Gerraty if (rval == 0) 2576b0c40a00SSimon J. Gerraty return false; /* Not a .for line */ 257706b9b3e0SSimon J. Gerraty if (rval < 0) 2578b0c40a00SSimon J. Gerraty return true; /* Syntax error - error printed, ignore line */ 257906b9b3e0SSimon J. Gerraty 25809f45a3c8SSimon J. Gerraty forHeadLineno = CurFile()->lineno; 25819f45a3c8SSimon J. Gerraty bodyReadLines = CurFile()->readLines; 258206b9b3e0SSimon J. Gerraty 25839f45a3c8SSimon J. Gerraty /* Accumulate the loop body until the matching '.endfor'. */ 25849f45a3c8SSimon J. Gerraty forLevel = 1; 258506b9b3e0SSimon J. Gerraty do { 25869f45a3c8SSimon J. Gerraty line = ReadLowLevelLine(LK_FOR_BODY); 258706b9b3e0SSimon J. Gerraty if (line == NULL) { 258806b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, 258912904384SSimon J. Gerraty "Unexpected end of file in .for loop"); 259006b9b3e0SSimon J. Gerraty break; 259106b9b3e0SSimon J. Gerraty } 25929f45a3c8SSimon J. Gerraty } while (For_Accum(line, &forLevel)); 259306b9b3e0SSimon J. Gerraty 25949f45a3c8SSimon J. Gerraty For_Run(forHeadLineno, bodyReadLines); 25959f45a3c8SSimon J. Gerraty return true; 259606b9b3e0SSimon J. Gerraty } 259706b9b3e0SSimon J. Gerraty 259806b9b3e0SSimon J. Gerraty /* 259906b9b3e0SSimon J. Gerraty * Read an entire line from the input file. 260006b9b3e0SSimon J. Gerraty * 2601d5e0a182SSimon J. Gerraty * Empty lines, .if and .for are handled by this function, while variable 2602d5e0a182SSimon J. Gerraty * assignments, other directives, dependency lines and shell commands are 2603d5e0a182SSimon J. Gerraty * handled by the caller. 26043955d011SMarcel Moolenaar * 26059f45a3c8SSimon J. Gerraty * Return a line without trailing whitespace, or NULL for EOF. The returned 26069f45a3c8SSimon J. Gerraty * string will be freed at the end of including the file. 26073955d011SMarcel Moolenaar */ 26083955d011SMarcel Moolenaar static char * 26099f45a3c8SSimon J. Gerraty ReadHighLevelLine(void) 26103955d011SMarcel Moolenaar { 261106b9b3e0SSimon J. Gerraty char *line; 2612148ee845SSimon J. Gerraty CondResult condResult; 26133955d011SMarcel Moolenaar 26143955d011SMarcel Moolenaar for (;;) { 2615148ee845SSimon J. Gerraty IncludedFile *curFile = CurFile(); 26169f45a3c8SSimon J. Gerraty line = ReadLowLevelLine(LK_NONEMPTY); 26172f2a5ecdSSimon J. Gerraty if (posix_state == PS_MAYBE_NEXT_LINE) 26182f2a5ecdSSimon J. Gerraty posix_state = PS_NOW_OR_NEVER; 26192f2a5ecdSSimon J. Gerraty else 26202f2a5ecdSSimon J. Gerraty posix_state = PS_TOO_LATE; 26213955d011SMarcel Moolenaar if (line == NULL) 26223955d011SMarcel Moolenaar return NULL; 26233955d011SMarcel Moolenaar 2624548bfc56SSimon J. Gerraty DEBUG2(PARSE, "Parsing line %u: %s\n", curFile->lineno, line); 2625148ee845SSimon J. Gerraty if (curFile->guardState != GS_NO 2626148ee845SSimon J. Gerraty && ((curFile->guardState == GS_START && line[0] != '.') 2627148ee845SSimon J. Gerraty || curFile->guardState == GS_DONE)) 2628148ee845SSimon J. Gerraty curFile->guardState = GS_NO; 26293955d011SMarcel Moolenaar if (line[0] != '.') 26303955d011SMarcel Moolenaar return line; 26313955d011SMarcel Moolenaar 2632148ee845SSimon J. Gerraty condResult = Cond_EvalLine(line); 2633148ee845SSimon J. Gerraty if (curFile->guardState == GS_START) { 2634148ee845SSimon J. Gerraty Guard *guard; 2635148ee845SSimon J. Gerraty if (condResult != CR_ERROR 2636148ee845SSimon J. Gerraty && (guard = Cond_ExtractGuard(line)) != NULL) { 2637148ee845SSimon J. Gerraty curFile->guardState = GS_COND; 2638148ee845SSimon J. Gerraty curFile->guard = guard; 2639148ee845SSimon J. Gerraty } else 2640148ee845SSimon J. Gerraty curFile->guardState = GS_NO; 2641148ee845SSimon J. Gerraty } 2642148ee845SSimon J. Gerraty switch (condResult) { 26439f45a3c8SSimon J. Gerraty case CR_FALSE: /* May also mean a syntax error. */ 26449f45a3c8SSimon J. Gerraty if (!SkipIrrelevantBranches()) 264506b9b3e0SSimon J. Gerraty return NULL; 26463955d011SMarcel Moolenaar continue; 26479f45a3c8SSimon J. Gerraty case CR_TRUE: 26483955d011SMarcel Moolenaar continue; 26499f45a3c8SSimon J. Gerraty case CR_ERROR: /* Not a conditional line */ 265006b9b3e0SSimon J. Gerraty if (ParseForLoop(line)) 26513955d011SMarcel Moolenaar continue; 26523955d011SMarcel Moolenaar break; 26533955d011SMarcel Moolenaar } 26543841c287SSimon J. Gerraty return line; 26553955d011SMarcel Moolenaar } 26563955d011SMarcel Moolenaar } 26573955d011SMarcel Moolenaar 26583955d011SMarcel Moolenaar static void 2659956e45f6SSimon J. Gerraty FinishDependencyGroup(void) 26603955d011SMarcel Moolenaar { 2661956e45f6SSimon J. Gerraty GNodeListNode *ln; 2662e2eeea75SSimon J. Gerraty 2663e2eeea75SSimon J. Gerraty if (targets == NULL) 2664e2eeea75SSimon J. Gerraty return; 2665e2eeea75SSimon J. Gerraty 2666956e45f6SSimon J. Gerraty for (ln = targets->first; ln != NULL; ln = ln->next) { 2667956e45f6SSimon J. Gerraty GNode *gn = ln->datum; 2668956e45f6SSimon J. Gerraty 2669956e45f6SSimon J. Gerraty Suff_EndTransform(gn); 2670956e45f6SSimon J. Gerraty 267106b9b3e0SSimon J. Gerraty /* 267206b9b3e0SSimon J. Gerraty * Mark the target as already having commands if it does, to 267306b9b3e0SSimon J. Gerraty * keep from having shell commands on multiple dependency 267406b9b3e0SSimon J. Gerraty * lines. 267506b9b3e0SSimon J. Gerraty */ 267606b9b3e0SSimon J. Gerraty if (!Lst_IsEmpty(&gn->commands)) 2677956e45f6SSimon J. Gerraty gn->type |= OP_HAS_COMMANDS; 26782c3632d1SSimon J. Gerraty } 2679956e45f6SSimon J. Gerraty 2680956e45f6SSimon J. Gerraty Lst_Free(targets); 26813955d011SMarcel Moolenaar targets = NULL; 26823955d011SMarcel Moolenaar } 26833955d011SMarcel Moolenaar 26848d5c8e21SSimon J. Gerraty #ifdef CLEANUP 26858d5c8e21SSimon J. Gerraty void Parse_RegisterCommand(char *cmd) 26868d5c8e21SSimon J. Gerraty { 26878d5c8e21SSimon J. Gerraty Lst_Append(&targCmds, cmd); 26888d5c8e21SSimon J. Gerraty } 26898d5c8e21SSimon J. Gerraty #endif 26908d5c8e21SSimon J. Gerraty 2691956e45f6SSimon J. Gerraty /* Add the command to each target from the current dependency spec. */ 2692956e45f6SSimon J. Gerraty static void 2693956e45f6SSimon J. Gerraty ParseLine_ShellCommand(const char *p) 26943955d011SMarcel Moolenaar { 2695956e45f6SSimon J. Gerraty cpp_skip_whitespace(&p); 2696956e45f6SSimon J. Gerraty if (*p == '\0') 2697956e45f6SSimon J. Gerraty return; /* skip empty commands */ 26983955d011SMarcel Moolenaar 2699956e45f6SSimon J. Gerraty if (targets == NULL) { 270006b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, 270106b9b3e0SSimon J. Gerraty "Unassociated shell command \"%s\"", p); 2702956e45f6SSimon J. Gerraty return; 2703956e45f6SSimon J. Gerraty } 27043955d011SMarcel Moolenaar 2705956e45f6SSimon J. Gerraty { 2706956e45f6SSimon J. Gerraty char *cmd = bmake_strdup(p); 2707956e45f6SSimon J. Gerraty GNodeListNode *ln; 27083955d011SMarcel Moolenaar 2709956e45f6SSimon J. Gerraty for (ln = targets->first; ln != NULL; ln = ln->next) { 2710956e45f6SSimon J. Gerraty GNode *gn = ln->datum; 27119f45a3c8SSimon J. Gerraty GNode_AddCommand(gn, cmd); 2712956e45f6SSimon J. Gerraty } 27138d5c8e21SSimon J. Gerraty Parse_RegisterCommand(cmd); 2714956e45f6SSimon J. Gerraty } 2715956e45f6SSimon J. Gerraty } 27163955d011SMarcel Moolenaar 27174fde40d9SSimon J. Gerraty static void 2718148ee845SSimon J. Gerraty HandleBreak(const char *arg) 27194fde40d9SSimon J. Gerraty { 27204fde40d9SSimon J. Gerraty IncludedFile *curFile = CurFile(); 27214fde40d9SSimon J. Gerraty 2722148ee845SSimon J. Gerraty if (arg[0] != '\0') 2723148ee845SSimon J. Gerraty Parse_Error(PARSE_FATAL, 2724148ee845SSimon J. Gerraty "The .break directive does not take arguments"); 2725148ee845SSimon J. Gerraty 27264fde40d9SSimon J. Gerraty if (curFile->forLoop != NULL) { 27274fde40d9SSimon J. Gerraty /* pretend we reached EOF */ 27284fde40d9SSimon J. Gerraty For_Break(curFile->forLoop); 27294fde40d9SSimon J. Gerraty cond_depth = CurFile_CondMinDepth(); 27304fde40d9SSimon J. Gerraty ParseEOF(); 27314fde40d9SSimon J. Gerraty } else 27324fde40d9SSimon J. Gerraty Parse_Error(PARSE_FATAL, "break outside of for loop"); 27334fde40d9SSimon J. Gerraty } 27344fde40d9SSimon J. Gerraty 273506b9b3e0SSimon J. Gerraty /* 273606b9b3e0SSimon J. Gerraty * See if the line starts with one of the known directives, and if so, handle 273706b9b3e0SSimon J. Gerraty * the directive. 273806b9b3e0SSimon J. Gerraty */ 2739b0c40a00SSimon J. Gerraty static bool 2740956e45f6SSimon J. Gerraty ParseDirective(char *line) 2741956e45f6SSimon J. Gerraty { 2742d5e0a182SSimon J. Gerraty char *p = line + 1; 274312904384SSimon J. Gerraty const char *arg; 274412904384SSimon J. Gerraty Substring dir; 27453955d011SMarcel Moolenaar 2746d5e0a182SSimon J. Gerraty pp_skip_whitespace(&p); 2747d5e0a182SSimon J. Gerraty if (IsInclude(p, false)) { 2748d5e0a182SSimon J. Gerraty ParseInclude(p); 2749b0c40a00SSimon J. Gerraty return true; 27503955d011SMarcel Moolenaar } 275106b9b3e0SSimon J. Gerraty 2752d5e0a182SSimon J. Gerraty dir.start = p; 2753d5e0a182SSimon J. Gerraty while (ch_islower(*p) || *p == '-') 2754d5e0a182SSimon J. Gerraty p++; 2755d5e0a182SSimon J. Gerraty dir.end = p; 275606b9b3e0SSimon J. Gerraty 2757d5e0a182SSimon J. Gerraty if (*p != '\0' && !ch_isspace(*p)) 2758b0c40a00SSimon J. Gerraty return false; 275906b9b3e0SSimon J. Gerraty 2760d5e0a182SSimon J. Gerraty pp_skip_whitespace(&p); 2761d5e0a182SSimon J. Gerraty arg = p; 276206b9b3e0SSimon J. Gerraty 27634fde40d9SSimon J. Gerraty if (Substring_Equals(dir, "break")) 2764148ee845SSimon J. Gerraty HandleBreak(arg); 27654fde40d9SSimon J. Gerraty else if (Substring_Equals(dir, "undef")) 276612904384SSimon J. Gerraty Var_Undef(arg); 27679f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "export")) 276806b9b3e0SSimon J. Gerraty Var_Export(VEM_PLAIN, arg); 27698d5c8e21SSimon J. Gerraty else if (Substring_Equals(dir, "export-all")) 27708d5c8e21SSimon J. Gerraty Var_Export(VEM_ALL, arg); 27719f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "export-env")) 277206b9b3e0SSimon J. Gerraty Var_Export(VEM_ENV, arg); 27739f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "export-literal")) 277406b9b3e0SSimon J. Gerraty Var_Export(VEM_LITERAL, arg); 27759f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "unexport")) 2776b0c40a00SSimon J. Gerraty Var_UnExport(false, arg); 27779f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "unexport-env")) 2778b0c40a00SSimon J. Gerraty Var_UnExport(true, arg); 27799f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "info")) 27809f45a3c8SSimon J. Gerraty HandleMessage(PARSE_INFO, "info", arg); 27819f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "warning")) 27829f45a3c8SSimon J. Gerraty HandleMessage(PARSE_WARNING, "warning", arg); 27839f45a3c8SSimon J. Gerraty else if (Substring_Equals(dir, "error")) 27849f45a3c8SSimon J. Gerraty HandleMessage(PARSE_FATAL, "error", arg); 27859f45a3c8SSimon J. Gerraty else 2786b0c40a00SSimon J. Gerraty return false; 27879f45a3c8SSimon J. Gerraty return true; 2788956e45f6SSimon J. Gerraty } 2789956e45f6SSimon J. Gerraty 27909f45a3c8SSimon J. Gerraty bool 27919f45a3c8SSimon J. Gerraty Parse_VarAssign(const char *line, bool finishDependencyGroup, GNode *scope) 2792956e45f6SSimon J. Gerraty { 2793956e45f6SSimon J. Gerraty VarAssign var; 2794e2eeea75SSimon J. Gerraty 2795e2eeea75SSimon J. Gerraty if (!Parse_IsVar(line, &var)) 2796b0c40a00SSimon J. Gerraty return false; 27979f45a3c8SSimon J. Gerraty if (finishDependencyGroup) 2798956e45f6SSimon J. Gerraty FinishDependencyGroup(); 27999f45a3c8SSimon J. Gerraty Parse_Var(&var, scope); 28009f45a3c8SSimon J. Gerraty free(var.varname); 2801b0c40a00SSimon J. Gerraty return true; 2802956e45f6SSimon J. Gerraty } 2803956e45f6SSimon J. Gerraty 2804148ee845SSimon J. Gerraty void 2805148ee845SSimon J. Gerraty Parse_GuardElse(void) 2806148ee845SSimon J. Gerraty { 2807148ee845SSimon J. Gerraty IncludedFile *curFile = CurFile(); 2808148ee845SSimon J. Gerraty if (cond_depth == curFile->condMinDepth + 1) 2809148ee845SSimon J. Gerraty curFile->guardState = GS_NO; 2810148ee845SSimon J. Gerraty } 2811148ee845SSimon J. Gerraty 2812148ee845SSimon J. Gerraty void 2813148ee845SSimon J. Gerraty Parse_GuardEndif(void) 2814148ee845SSimon J. Gerraty { 2815148ee845SSimon J. Gerraty IncludedFile *curFile = CurFile(); 2816148ee845SSimon J. Gerraty if (cond_depth == curFile->condMinDepth 2817148ee845SSimon J. Gerraty && curFile->guardState == GS_COND) 2818148ee845SSimon J. Gerraty curFile->guardState = GS_DONE; 2819148ee845SSimon J. Gerraty } 2820148ee845SSimon J. Gerraty 2821956e45f6SSimon J. Gerraty static char * 2822956e45f6SSimon J. Gerraty FindSemicolon(char *p) 2823956e45f6SSimon J. Gerraty { 2824d5e0a182SSimon J. Gerraty int depth = 0; 2825956e45f6SSimon J. Gerraty 2826956e45f6SSimon J. Gerraty for (; *p != '\0'; p++) { 2827956e45f6SSimon J. Gerraty if (*p == '\\' && p[1] != '\0') { 2828956e45f6SSimon J. Gerraty p++; 28293955d011SMarcel Moolenaar continue; 28303955d011SMarcel Moolenaar } 2831956e45f6SSimon J. Gerraty 2832e2eeea75SSimon J. Gerraty if (*p == '$' && (p[1] == '(' || p[1] == '{')) 2833d5e0a182SSimon J. Gerraty depth++; 2834d5e0a182SSimon J. Gerraty else if (depth > 0 && (*p == ')' || *p == '}')) 2835d5e0a182SSimon J. Gerraty depth--; 2836d5e0a182SSimon J. Gerraty else if (depth == 0 && *p == ';') 2837956e45f6SSimon J. Gerraty break; 2838956e45f6SSimon J. Gerraty } 2839956e45f6SSimon J. Gerraty return p; 2840956e45f6SSimon J. Gerraty } 2841956e45f6SSimon J. Gerraty 2842956e45f6SSimon J. Gerraty static void 2843b0c40a00SSimon J. Gerraty ParseDependencyLine(char *line) 2844956e45f6SSimon J. Gerraty { 2845b0c40a00SSimon J. Gerraty VarEvalMode emode; 2846956e45f6SSimon J. Gerraty char *expanded_line; 2847956e45f6SSimon J. Gerraty const char *shellcmd = NULL; 2848956e45f6SSimon J. Gerraty 2849956e45f6SSimon J. Gerraty { 2850956e45f6SSimon J. Gerraty char *semicolon = FindSemicolon(line); 2851956e45f6SSimon J. Gerraty if (*semicolon != '\0') { 2852956e45f6SSimon J. Gerraty /* Terminate the dependency list at the ';' */ 2853956e45f6SSimon J. Gerraty *semicolon = '\0'; 2854956e45f6SSimon J. Gerraty shellcmd = semicolon + 1; 2855956e45f6SSimon J. Gerraty } 28563955d011SMarcel Moolenaar } 28573955d011SMarcel Moolenaar 2858956e45f6SSimon J. Gerraty /* 2859d5e0a182SSimon J. Gerraty * We now know it's a dependency line, so it needs to have all 2860956e45f6SSimon J. Gerraty * variables expanded before being parsed. 2861956e45f6SSimon J. Gerraty * 2862956e45f6SSimon J. Gerraty * XXX: Ideally the dependency line would first be split into 2863956e45f6SSimon J. Gerraty * its left-hand side, dependency operator and right-hand side, 2864956e45f6SSimon J. Gerraty * and then each side would be expanded on its own. This would 2865956e45f6SSimon J. Gerraty * allow for the left-hand side to allow only defined variables 2866956e45f6SSimon J. Gerraty * and to allow variables on the right-hand side to be undefined 2867956e45f6SSimon J. Gerraty * as well. 2868956e45f6SSimon J. Gerraty * 2869956e45f6SSimon J. Gerraty * Parsing the line first would also prevent that targets 2870d5e0a182SSimon J. Gerraty * generated from expressions are interpreted as the 2871e2eeea75SSimon J. Gerraty * dependency operator, such as in "target${:U\:} middle: source", 2872956e45f6SSimon J. Gerraty * in which the middle is interpreted as a source, not a target. 2873956e45f6SSimon J. Gerraty */ 2874956e45f6SSimon J. Gerraty 28759f45a3c8SSimon J. Gerraty /* 28769f45a3c8SSimon J. Gerraty * In lint mode, allow undefined variables to appear in dependency 28779f45a3c8SSimon J. Gerraty * lines. 2878956e45f6SSimon J. Gerraty * 28799f45a3c8SSimon J. Gerraty * Ideally, only the right-hand side would allow undefined variables 28809f45a3c8SSimon J. Gerraty * since it is common to have optional dependencies. Having undefined 28819f45a3c8SSimon J. Gerraty * variables on the left-hand side is more unusual though. Since 28829f45a3c8SSimon J. Gerraty * both sides are expanded in a single pass, there is not much choice 28839f45a3c8SSimon J. Gerraty * what to do here. 2884956e45f6SSimon J. Gerraty * 28859f45a3c8SSimon J. Gerraty * In normal mode, it does not matter whether undefined variables are 28869f45a3c8SSimon J. Gerraty * allowed or not since as of 2020-09-14, Var_Parse does not print 28879f45a3c8SSimon J. Gerraty * any parse errors in such a case. It simply returns the special 28889f45a3c8SSimon J. Gerraty * empty string var_Error, which cannot be detected in the result of 28899f45a3c8SSimon J. Gerraty * Var_Subst. 28909f45a3c8SSimon J. Gerraty */ 28918d5c8e21SSimon J. Gerraty emode = opts.strict ? VARE_EVAL : VARE_EVAL_DEFINED; 28928c973ee2SSimon J. Gerraty expanded_line = Var_Subst(line, SCOPE_CMDLINE, emode); 2893956e45f6SSimon J. Gerraty /* TODO: handle errors */ 2894956e45f6SSimon J. Gerraty 2895956e45f6SSimon J. Gerraty /* Need a fresh list for the target nodes */ 2896956e45f6SSimon J. Gerraty if (targets != NULL) 2897956e45f6SSimon J. Gerraty Lst_Free(targets); 2898956e45f6SSimon J. Gerraty targets = Lst_New(); 2899956e45f6SSimon J. Gerraty 290098875883SSimon J. Gerraty ParseDependency(expanded_line, line); 2901956e45f6SSimon J. Gerraty free(expanded_line); 2902956e45f6SSimon J. Gerraty 2903956e45f6SSimon J. Gerraty if (shellcmd != NULL) 2904956e45f6SSimon J. Gerraty ParseLine_ShellCommand(shellcmd); 2905956e45f6SSimon J. Gerraty } 2906956e45f6SSimon J. Gerraty 2907956e45f6SSimon J. Gerraty static void 2908956e45f6SSimon J. Gerraty ParseLine(char *line) 2909956e45f6SSimon J. Gerraty { 291006b9b3e0SSimon J. Gerraty if (line[0] == '.' && ParseDirective(line)) 2911956e45f6SSimon J. Gerraty return; 2912956e45f6SSimon J. Gerraty 291306b9b3e0SSimon J. Gerraty if (line[0] == '\t') { 2914956e45f6SSimon J. Gerraty ParseLine_ShellCommand(line + 1); 2915956e45f6SSimon J. Gerraty return; 29163955d011SMarcel Moolenaar } 29173955d011SMarcel Moolenaar 2918494f7191SSimon J. Gerraty if (IsSysVInclude(line)) { 29193955d011SMarcel Moolenaar ParseTraditionalInclude(line); 2920956e45f6SSimon J. Gerraty return; 29213955d011SMarcel Moolenaar } 2922956e45f6SSimon J. Gerraty 2923956e45f6SSimon J. Gerraty if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) && 29243955d011SMarcel Moolenaar strchr(line, ':') == NULL) { 29253955d011SMarcel Moolenaar ParseGmakeExport(line); 2926956e45f6SSimon J. Gerraty return; 29273955d011SMarcel Moolenaar } 2928956e45f6SSimon J. Gerraty 29299f45a3c8SSimon J. Gerraty if (Parse_VarAssign(line, true, SCOPE_GLOBAL)) 2930956e45f6SSimon J. Gerraty return; 2931956e45f6SSimon J. Gerraty 2932956e45f6SSimon J. Gerraty FinishDependencyGroup(); 2933956e45f6SSimon J. Gerraty 2934b0c40a00SSimon J. Gerraty ParseDependencyLine(line); 29353955d011SMarcel Moolenaar } 29363955d011SMarcel Moolenaar 2937d5e0a182SSimon J. Gerraty /* Interpret a top-level makefile. */ 2938956e45f6SSimon J. Gerraty void 2939956e45f6SSimon J. Gerraty Parse_File(const char *name, int fd) 29403955d011SMarcel Moolenaar { 29419f45a3c8SSimon J. Gerraty char *line; 29429f45a3c8SSimon J. Gerraty Buffer buf; 29433955d011SMarcel Moolenaar 2944954401e6SSimon J. Gerraty buf = LoadFile(name, fd != -1 ? fd : STDIN_FILENO); 29459f45a3c8SSimon J. Gerraty if (fd != -1) 29469f45a3c8SSimon J. Gerraty (void)close(fd); 29473955d011SMarcel Moolenaar 2948956e45f6SSimon J. Gerraty assert(targets == NULL); 29493955d011SMarcel Moolenaar 29509f45a3c8SSimon J. Gerraty Parse_PushInput(name, 1, 0, buf, NULL); 29513955d011SMarcel Moolenaar 2952956e45f6SSimon J. Gerraty do { 29539f45a3c8SSimon J. Gerraty while ((line = ReadHighLevelLine()) != NULL) { 2954956e45f6SSimon J. Gerraty ParseLine(line); 29553955d011SMarcel Moolenaar } 2956956e45f6SSimon J. Gerraty } while (ParseEOF()); 2957956e45f6SSimon J. Gerraty 2958956e45f6SSimon J. Gerraty FinishDependencyGroup(); 29593955d011SMarcel Moolenaar 296012904384SSimon J. Gerraty if (parseErrors != 0) { 29613955d011SMarcel Moolenaar (void)fflush(stdout); 29623955d011SMarcel Moolenaar (void)fprintf(stderr, 29639f45a3c8SSimon J. Gerraty "%s: Fatal errors encountered -- cannot continue\n", 29643955d011SMarcel Moolenaar progname); 29659f45a3c8SSimon J. Gerraty PrintOnError(NULL, ""); 29663955d011SMarcel Moolenaar exit(1); 29673955d011SMarcel Moolenaar } 29683955d011SMarcel Moolenaar } 29693955d011SMarcel Moolenaar 2970956e45f6SSimon J. Gerraty /* Initialize the parsing module. */ 29713955d011SMarcel Moolenaar void 29723955d011SMarcel Moolenaar Parse_Init(void) 29733955d011SMarcel Moolenaar { 29743955d011SMarcel Moolenaar mainNode = NULL; 297506b9b3e0SSimon J. Gerraty parseIncPath = SearchPath_New(); 297606b9b3e0SSimon J. Gerraty sysIncPath = SearchPath_New(); 297706b9b3e0SSimon J. Gerraty defSysIncPath = SearchPath_New(); 29789f45a3c8SSimon J. Gerraty Vector_Init(&includes, sizeof(IncludedFile)); 2979148ee845SSimon J. Gerraty HashTable_Init(&guards); 29803955d011SMarcel Moolenaar } 29813955d011SMarcel Moolenaar 2982*22619282SSimon J. Gerraty #ifdef CLEANUP 2983956e45f6SSimon J. Gerraty /* Clean up the parsing module. */ 29843955d011SMarcel Moolenaar void 29853955d011SMarcel Moolenaar Parse_End(void) 29863955d011SMarcel Moolenaar { 2987148ee845SSimon J. Gerraty HashIter hi; 2988148ee845SSimon J. Gerraty 2989548bfc56SSimon J. Gerraty Lst_DoneFree(&targCmds); 2990956e45f6SSimon J. Gerraty assert(targets == NULL); 299106b9b3e0SSimon J. Gerraty SearchPath_Free(defSysIncPath); 299206b9b3e0SSimon J. Gerraty SearchPath_Free(sysIncPath); 299306b9b3e0SSimon J. Gerraty SearchPath_Free(parseIncPath); 2994956e45f6SSimon J. Gerraty assert(includes.len == 0); 2995956e45f6SSimon J. Gerraty Vector_Done(&includes); 2996148ee845SSimon J. Gerraty HashIter_Init(&hi, &guards); 29978d5c8e21SSimon J. Gerraty while (HashIter_Next(&hi)) { 2998148ee845SSimon J. Gerraty Guard *guard = hi.entry->value; 2999148ee845SSimon J. Gerraty free(guard->name); 3000148ee845SSimon J. Gerraty free(guard); 3001148ee845SSimon J. Gerraty } 3002148ee845SSimon J. Gerraty HashTable_Done(&guards); 30033955d011SMarcel Moolenaar } 3004*22619282SSimon J. Gerraty #endif 30053955d011SMarcel Moolenaar 30063955d011SMarcel Moolenaar 3007c1d01b5fSSimon J. Gerraty /* Populate the list with the single main target to create, or error out. */ 300806b9b3e0SSimon J. Gerraty void 300906b9b3e0SSimon J. Gerraty Parse_MainName(GNodeList *mainList) 30103955d011SMarcel Moolenaar { 3011e2eeea75SSimon J. Gerraty if (mainNode == NULL) 30123955d011SMarcel Moolenaar Punt("no target to make."); 3013e2eeea75SSimon J. Gerraty 30142c3632d1SSimon J. Gerraty Lst_Append(mainList, mainNode); 3015dba7b0efSSimon J. Gerraty if (mainNode->type & OP_DOUBLEDEP) 301606b9b3e0SSimon J. Gerraty Lst_AppendAll(mainList, &mainNode->cohorts); 3017e2eeea75SSimon J. Gerraty 3018dba7b0efSSimon J. Gerraty Global_Append(".TARGETS", mainNode->name); 30193955d011SMarcel Moolenaar } 3020