1331246Ssjg/* $NetBSD: parse.c,v 1.227 2018/02/22 01:59:28 sjg Exp $ */ 2236769Sobrien 3236769Sobrien/* 4236769Sobrien * Copyright (c) 1988, 1989, 1990, 1993 5236769Sobrien * The Regents of the University of California. All rights reserved. 6236769Sobrien * 7236769Sobrien * This code is derived from software contributed to Berkeley by 8236769Sobrien * Adam de Boor. 9236769Sobrien * 10236769Sobrien * Redistribution and use in source and binary forms, with or without 11236769Sobrien * modification, are permitted provided that the following conditions 12236769Sobrien * are met: 13236769Sobrien * 1. Redistributions of source code must retain the above copyright 14236769Sobrien * notice, this list of conditions and the following disclaimer. 15236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 16236769Sobrien * notice, this list of conditions and the following disclaimer in the 17236769Sobrien * documentation and/or other materials provided with the distribution. 18236769Sobrien * 3. Neither the name of the University nor the names of its contributors 19236769Sobrien * may be used to endorse or promote products derived from this software 20236769Sobrien * without specific prior written permission. 21236769Sobrien * 22236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32236769Sobrien * SUCH DAMAGE. 33236769Sobrien */ 34236769Sobrien 35236769Sobrien/* 36236769Sobrien * Copyright (c) 1989 by Berkeley Softworks 37236769Sobrien * All rights reserved. 38236769Sobrien * 39236769Sobrien * This code is derived from software contributed to Berkeley by 40236769Sobrien * Adam de Boor. 41236769Sobrien * 42236769Sobrien * Redistribution and use in source and binary forms, with or without 43236769Sobrien * modification, are permitted provided that the following conditions 44236769Sobrien * are met: 45236769Sobrien * 1. Redistributions of source code must retain the above copyright 46236769Sobrien * notice, this list of conditions and the following disclaimer. 47236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 48236769Sobrien * notice, this list of conditions and the following disclaimer in the 49236769Sobrien * documentation and/or other materials provided with the distribution. 50236769Sobrien * 3. All advertising materials mentioning features or use of this software 51236769Sobrien * must display the following acknowledgement: 52236769Sobrien * This product includes software developed by the University of 53236769Sobrien * California, Berkeley and its contributors. 54236769Sobrien * 4. Neither the name of the University nor the names of its contributors 55236769Sobrien * may be used to endorse or promote products derived from this software 56236769Sobrien * without specific prior written permission. 57236769Sobrien * 58236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68236769Sobrien * SUCH DAMAGE. 69236769Sobrien */ 70236769Sobrien 71236769Sobrien#ifndef MAKE_NATIVE 72331246Ssjgstatic char rcsid[] = "$NetBSD: parse.c,v 1.227 2018/02/22 01:59:28 sjg Exp $"; 73236769Sobrien#else 74236769Sobrien#include <sys/cdefs.h> 75236769Sobrien#ifndef lint 76236769Sobrien#if 0 77236769Sobrienstatic char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; 78236769Sobrien#else 79331246Ssjg__RCSID("$NetBSD: parse.c,v 1.227 2018/02/22 01:59:28 sjg Exp $"); 80236769Sobrien#endif 81236769Sobrien#endif /* not lint */ 82236769Sobrien#endif 83236769Sobrien 84236769Sobrien/*- 85236769Sobrien * parse.c -- 86236769Sobrien * Functions to parse a makefile. 87236769Sobrien * 88236769Sobrien * One function, Parse_Init, must be called before any functions 89236769Sobrien * in this module are used. After that, the function Parse_File is the 90236769Sobrien * main entry point and controls most of the other functions in this 91236769Sobrien * module. 92236769Sobrien * 93236769Sobrien * Most important structures are kept in Lsts. Directories for 94236769Sobrien * the .include "..." function are kept in the 'parseIncPath' Lst, while 95236769Sobrien * those for the .include <...> are kept in the 'sysIncPath' Lst. The 96236769Sobrien * targets currently being defined are kept in the 'targets' Lst. 97236769Sobrien * 98236769Sobrien * The variables 'fname' and 'lineno' are used to track the name 99236769Sobrien * of the current file and the line number in that file so that error 100236769Sobrien * messages can be more meaningful. 101236769Sobrien * 102236769Sobrien * Interface: 103236769Sobrien * Parse_Init Initialization function which must be 104236769Sobrien * called before anything else in this module 105236769Sobrien * is used. 106236769Sobrien * 107236769Sobrien * Parse_End Cleanup the module 108236769Sobrien * 109236769Sobrien * Parse_File Function used to parse a makefile. It must 110236769Sobrien * be given the name of the file, which should 111236769Sobrien * already have been opened, and a function 112236769Sobrien * to call to read a character from the file. 113236769Sobrien * 114236769Sobrien * Parse_IsVar Returns TRUE if the given line is a 115236769Sobrien * variable assignment. Used by MainParseArgs 116236769Sobrien * to determine if an argument is a target 117236769Sobrien * or a variable assignment. Used internally 118236769Sobrien * for pretty much the same thing... 119236769Sobrien * 120236769Sobrien * Parse_Error Function called when an error occurs in 121236769Sobrien * parsing. Used by the variable and 122236769Sobrien * conditional modules. 123236769Sobrien * Parse_MainName Returns a Lst of the main target to create. 124236769Sobrien */ 125236769Sobrien 126236769Sobrien#include <sys/types.h> 127236769Sobrien#include <sys/stat.h> 128236769Sobrien#include <assert.h> 129236769Sobrien#include <ctype.h> 130236769Sobrien#include <errno.h> 131236769Sobrien#include <stdarg.h> 132236769Sobrien#include <stdio.h> 133236769Sobrien 134236769Sobrien#include "make.h" 135236769Sobrien#include "hash.h" 136236769Sobrien#include "dir.h" 137236769Sobrien#include "job.h" 138236769Sobrien#include "buf.h" 139236769Sobrien#include "pathnames.h" 140236769Sobrien 141319884Ssjg#ifdef HAVE_STDINT_H 142319884Ssjg#include <stdint.h> 143319884Ssjg#endif 144319884Ssjg 145236769Sobrien#ifdef HAVE_MMAP 146236769Sobrien#include <sys/mman.h> 147236769Sobrien 148236769Sobrien#ifndef MAP_COPY 149236769Sobrien#define MAP_COPY MAP_PRIVATE 150236769Sobrien#endif 151236769Sobrien#ifndef MAP_FILE 152236769Sobrien#define MAP_FILE 0 153236769Sobrien#endif 154236769Sobrien#endif 155236769Sobrien 156236769Sobrien//////////////////////////////////////////////////////////// 157236769Sobrien// types and constants 158236769Sobrien 159236769Sobrien/* 160236769Sobrien * Structure for a file being read ("included file") 161236769Sobrien */ 162236769Sobrientypedef struct IFile { 163251958Ssjg char *fname; /* name of file */ 164236769Sobrien int lineno; /* current line number in file */ 165236769Sobrien int first_lineno; /* line number of start of text */ 166236769Sobrien int cond_depth; /* 'if' nesting when file opened */ 167296637Ssjg Boolean depending; /* state of doing_depend on EOF */ 168236769Sobrien char *P_str; /* point to base of string buffer */ 169236769Sobrien char *P_ptr; /* point to next char of string buffer */ 170236769Sobrien char *P_end; /* point to the end of string buffer */ 171236769Sobrien char *(*nextbuf)(void *, size_t *); /* Function to get more data */ 172236769Sobrien void *nextbuf_arg; /* Opaque arg for nextbuf() */ 173236769Sobrien struct loadedfile *lf; /* loadedfile object, if any */ 174236769Sobrien} IFile; 175236769Sobrien 176236769Sobrien 177236769Sobrien/* 178236769Sobrien * These values are returned by ParseEOF to tell Parse_File whether to 179236769Sobrien * CONTINUE parsing, i.e. it had only reached the end of an include file, 180236769Sobrien * or if it's DONE. 181236769Sobrien */ 182236769Sobrien#define CONTINUE 1 183236769Sobrien#define DONE 0 184236769Sobrien 185236769Sobrien/* 186236769Sobrien * Tokens for target attributes 187236769Sobrien */ 188236769Sobrientypedef enum { 189236769Sobrien Begin, /* .BEGIN */ 190236769Sobrien Default, /* .DEFAULT */ 191319884Ssjg DeleteOnError, /* .DELETE_ON_ERROR */ 192236769Sobrien End, /* .END */ 193236769Sobrien dotError, /* .ERROR */ 194236769Sobrien Ignore, /* .IGNORE */ 195236769Sobrien Includes, /* .INCLUDES */ 196236769Sobrien Interrupt, /* .INTERRUPT */ 197236769Sobrien Libs, /* .LIBS */ 198236769Sobrien Meta, /* .META */ 199236769Sobrien MFlags, /* .MFLAGS or .MAKEFLAGS */ 200236769Sobrien Main, /* .MAIN and we don't have anything user-specified to 201236769Sobrien * make */ 202236769Sobrien NoExport, /* .NOEXPORT */ 203236769Sobrien NoMeta, /* .NOMETA */ 204236769Sobrien NoMetaCmp, /* .NOMETA_CMP */ 205236769Sobrien NoPath, /* .NOPATH */ 206236769Sobrien Not, /* Not special */ 207236769Sobrien NotParallel, /* .NOTPARALLEL */ 208236769Sobrien Null, /* .NULL */ 209236769Sobrien ExObjdir, /* .OBJDIR */ 210236769Sobrien Order, /* .ORDER */ 211236769Sobrien Parallel, /* .PARALLEL */ 212236769Sobrien ExPath, /* .PATH */ 213236769Sobrien Phony, /* .PHONY */ 214236769Sobrien#ifdef POSIX 215236769Sobrien Posix, /* .POSIX */ 216236769Sobrien#endif 217236769Sobrien Precious, /* .PRECIOUS */ 218236769Sobrien ExShell, /* .SHELL */ 219236769Sobrien Silent, /* .SILENT */ 220236769Sobrien SingleShell, /* .SINGLESHELL */ 221249033Ssjg Stale, /* .STALE */ 222236769Sobrien Suffixes, /* .SUFFIXES */ 223236769Sobrien Wait, /* .WAIT */ 224236769Sobrien Attribute /* Generic attribute */ 225236769Sobrien} ParseSpecial; 226236769Sobrien 227236769Sobrien/* 228236769Sobrien * Other tokens 229236769Sobrien */ 230236769Sobrien#define LPAREN '(' 231236769Sobrien#define RPAREN ')' 232236769Sobrien 233236769Sobrien 234236769Sobrien//////////////////////////////////////////////////////////// 235236769Sobrien// result data 236236769Sobrien 237236769Sobrien/* 238236769Sobrien * The main target to create. This is the first target on the first 239236769Sobrien * dependency line in the first makefile. 240236769Sobrien */ 241236769Sobrienstatic GNode *mainNode; 242236769Sobrien 243236769Sobrien//////////////////////////////////////////////////////////// 244236769Sobrien// eval state 245236769Sobrien 246236769Sobrien/* targets we're working on */ 247236769Sobrienstatic Lst targets; 248236769Sobrien 249236769Sobrien#ifdef CLEANUP 250236769Sobrien/* command lines for targets */ 251236769Sobrienstatic Lst targCmds; 252236769Sobrien#endif 253236769Sobrien 254236769Sobrien/* 255236769Sobrien * specType contains the SPECial TYPE of the current target. It is 256236769Sobrien * Not if the target is unspecial. If it *is* special, however, the children 257236769Sobrien * are linked as children of the parent but not vice versa. This variable is 258236769Sobrien * set in ParseDoDependency 259236769Sobrien */ 260236769Sobrienstatic ParseSpecial specType; 261236769Sobrien 262236769Sobrien/* 263236769Sobrien * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER 264236769Sobrien * seen, then set to each successive source on the line. 265236769Sobrien */ 266236769Sobrienstatic GNode *predecessor; 267236769Sobrien 268236769Sobrien//////////////////////////////////////////////////////////// 269236769Sobrien// parser state 270236769Sobrien 271236769Sobrien/* true if currently in a dependency line or its commands */ 272236769Sobrienstatic Boolean inLine; 273236769Sobrien 274236769Sobrien/* number of fatal errors */ 275236769Sobrienstatic int fatals = 0; 276236769Sobrien 277236769Sobrien/* 278236769Sobrien * Variables for doing includes 279236769Sobrien */ 280236769Sobrien 281236769Sobrien/* current file being read */ 282236769Sobrienstatic IFile *curFile; 283236769Sobrien 284236769Sobrien/* stack of IFiles generated by .includes */ 285236769Sobrienstatic Lst includes; 286236769Sobrien 287236769Sobrien/* include paths (lists of directories) */ 288236769SobrienLst parseIncPath; /* dirs for "..." includes */ 289236769SobrienLst sysIncPath; /* dirs for <...> includes */ 290236769SobrienLst defIncPath; /* default for sysIncPath */ 291236769Sobrien 292236769Sobrien//////////////////////////////////////////////////////////// 293236769Sobrien// parser tables 294236769Sobrien 295236769Sobrien/* 296236769Sobrien * The parseKeywords table is searched using binary search when deciding 297236769Sobrien * if a target or source is special. The 'spec' field is the ParseSpecial 298236769Sobrien * type of the keyword ("Not" if the keyword isn't special as a target) while 299236769Sobrien * the 'op' field is the operator to apply to the list of targets if the 300236769Sobrien * keyword is used as a source ("0" if the keyword isn't special as a source) 301236769Sobrien */ 302236769Sobrienstatic const struct { 303236769Sobrien const char *name; /* Name of keyword */ 304236769Sobrien ParseSpecial spec; /* Type when used as a target */ 305236769Sobrien int op; /* Operator when used as a source */ 306236769Sobrien} parseKeywords[] = { 307236769Sobrien{ ".BEGIN", Begin, 0 }, 308236769Sobrien{ ".DEFAULT", Default, 0 }, 309319884Ssjg{ ".DELETE_ON_ERROR", DeleteOnError, 0 }, 310236769Sobrien{ ".END", End, 0 }, 311236769Sobrien{ ".ERROR", dotError, 0 }, 312236769Sobrien{ ".EXEC", Attribute, OP_EXEC }, 313236769Sobrien{ ".IGNORE", Ignore, OP_IGNORE }, 314236769Sobrien{ ".INCLUDES", Includes, 0 }, 315236769Sobrien{ ".INTERRUPT", Interrupt, 0 }, 316236769Sobrien{ ".INVISIBLE", Attribute, OP_INVISIBLE }, 317236769Sobrien{ ".JOIN", Attribute, OP_JOIN }, 318236769Sobrien{ ".LIBS", Libs, 0 }, 319236769Sobrien{ ".MADE", Attribute, OP_MADE }, 320236769Sobrien{ ".MAIN", Main, 0 }, 321236769Sobrien{ ".MAKE", Attribute, OP_MAKE }, 322236769Sobrien{ ".MAKEFLAGS", MFlags, 0 }, 323236769Sobrien{ ".META", Meta, OP_META }, 324236769Sobrien{ ".MFLAGS", MFlags, 0 }, 325236769Sobrien{ ".NOMETA", NoMeta, OP_NOMETA }, 326236769Sobrien{ ".NOMETA_CMP", NoMetaCmp, OP_NOMETA_CMP }, 327236769Sobrien{ ".NOPATH", NoPath, OP_NOPATH }, 328236769Sobrien{ ".NOTMAIN", Attribute, OP_NOTMAIN }, 329236769Sobrien{ ".NOTPARALLEL", NotParallel, 0 }, 330236769Sobrien{ ".NO_PARALLEL", NotParallel, 0 }, 331236769Sobrien{ ".NULL", Null, 0 }, 332236769Sobrien{ ".OBJDIR", ExObjdir, 0 }, 333236769Sobrien{ ".OPTIONAL", Attribute, OP_OPTIONAL }, 334236769Sobrien{ ".ORDER", Order, 0 }, 335236769Sobrien{ ".PARALLEL", Parallel, 0 }, 336236769Sobrien{ ".PATH", ExPath, 0 }, 337236769Sobrien{ ".PHONY", Phony, OP_PHONY }, 338236769Sobrien#ifdef POSIX 339236769Sobrien{ ".POSIX", Posix, 0 }, 340236769Sobrien#endif 341236769Sobrien{ ".PRECIOUS", Precious, OP_PRECIOUS }, 342236769Sobrien{ ".RECURSIVE", Attribute, OP_MAKE }, 343236769Sobrien{ ".SHELL", ExShell, 0 }, 344236769Sobrien{ ".SILENT", Silent, OP_SILENT }, 345236769Sobrien{ ".SINGLESHELL", SingleShell, 0 }, 346249033Ssjg{ ".STALE", Stale, 0 }, 347236769Sobrien{ ".SUFFIXES", Suffixes, 0 }, 348236769Sobrien{ ".USE", Attribute, OP_USE }, 349236769Sobrien{ ".USEBEFORE", Attribute, OP_USEBEFORE }, 350236769Sobrien{ ".WAIT", Wait, 0 }, 351236769Sobrien}; 352236769Sobrien 353236769Sobrien//////////////////////////////////////////////////////////// 354236769Sobrien// local functions 355236769Sobrien 356236769Sobrienstatic int ParseIsEscaped(const char *, const char *); 357236769Sobrienstatic void ParseErrorInternal(const char *, size_t, int, const char *, ...) 358237578Sobrien MAKE_ATTR_PRINTFLIKE(4,5); 359236769Sobrienstatic void ParseVErrorInternal(FILE *, const char *, size_t, int, const char *, va_list) 360237578Sobrien MAKE_ATTR_PRINTFLIKE(5, 0); 361236769Sobrienstatic int ParseFindKeyword(const char *); 362236769Sobrienstatic int ParseLinkSrc(void *, void *); 363236769Sobrienstatic int ParseDoOp(void *, void *); 364236769Sobrienstatic void ParseDoSrc(int, const char *); 365236769Sobrienstatic int ParseFindMain(void *, void *); 366236769Sobrienstatic int ParseAddDir(void *, void *); 367236769Sobrienstatic int ParseClearPath(void *, void *); 368236769Sobrienstatic void ParseDoDependency(char *); 369236769Sobrienstatic int ParseAddCmd(void *, void *); 370236769Sobrienstatic void ParseHasCommands(void *); 371236769Sobrienstatic void ParseDoInclude(char *); 372236769Sobrienstatic void ParseSetParseFile(const char *); 373268437Ssjgstatic void ParseSetIncludedFile(void); 374236769Sobrien#ifdef SYSVINCLUDE 375236769Sobrienstatic void ParseTraditionalInclude(char *); 376236769Sobrien#endif 377236769Sobrien#ifdef GMAKEEXPORT 378236769Sobrienstatic void ParseGmakeExport(char *); 379236769Sobrien#endif 380236769Sobrienstatic int ParseEOF(void); 381236769Sobrienstatic char *ParseReadLine(void); 382236769Sobrienstatic void ParseFinishLine(void); 383236769Sobrienstatic void ParseMark(GNode *); 384236769Sobrien 385236769Sobrien//////////////////////////////////////////////////////////// 386236769Sobrien// file loader 387236769Sobrien 388236769Sobrienstruct loadedfile { 389236769Sobrien const char *path; /* name, for error reports */ 390236769Sobrien char *buf; /* contents buffer */ 391236769Sobrien size_t len; /* length of contents */ 392236769Sobrien size_t maplen; /* length of mmap area, or 0 */ 393236769Sobrien Boolean used; /* XXX: have we used the data yet */ 394236769Sobrien}; 395236769Sobrien 396236769Sobrien/* 397236769Sobrien * Constructor/destructor for loadedfile 398236769Sobrien */ 399236769Sobrienstatic struct loadedfile * 400236769Sobrienloadedfile_create(const char *path) 401236769Sobrien{ 402236769Sobrien struct loadedfile *lf; 403236769Sobrien 404236769Sobrien lf = bmake_malloc(sizeof(*lf)); 405236769Sobrien lf->path = (path == NULL ? "(stdin)" : path); 406236769Sobrien lf->buf = NULL; 407236769Sobrien lf->len = 0; 408236769Sobrien lf->maplen = 0; 409236769Sobrien lf->used = FALSE; 410236769Sobrien return lf; 411236769Sobrien} 412236769Sobrien 413236769Sobrienstatic void 414236769Sobrienloadedfile_destroy(struct loadedfile *lf) 415236769Sobrien{ 416236769Sobrien if (lf->buf != NULL) { 417236769Sobrien if (lf->maplen > 0) { 418236769Sobrien#ifdef HAVE_MMAP 419236769Sobrien munmap(lf->buf, lf->maplen); 420236769Sobrien#endif 421236769Sobrien } else { 422236769Sobrien free(lf->buf); 423236769Sobrien } 424236769Sobrien } 425236769Sobrien free(lf); 426236769Sobrien} 427236769Sobrien 428236769Sobrien/* 429236769Sobrien * nextbuf() operation for loadedfile, as needed by the weird and twisted 430236769Sobrien * logic below. Once that's cleaned up, we can get rid of lf->used... 431236769Sobrien */ 432236769Sobrienstatic char * 433236769Sobrienloadedfile_nextbuf(void *x, size_t *len) 434236769Sobrien{ 435236769Sobrien struct loadedfile *lf = x; 436236769Sobrien 437236769Sobrien if (lf->used) { 438236769Sobrien return NULL; 439236769Sobrien } 440236769Sobrien lf->used = TRUE; 441236769Sobrien *len = lf->len; 442236769Sobrien return lf->buf; 443236769Sobrien} 444236769Sobrien 445236769Sobrien/* 446236769Sobrien * Try to get the size of a file. 447236769Sobrien */ 448236769Sobrienstatic ReturnStatus 449236769Sobrienload_getsize(int fd, size_t *ret) 450236769Sobrien{ 451236769Sobrien struct stat st; 452236769Sobrien 453236769Sobrien if (fstat(fd, &st) < 0) { 454236769Sobrien return FAILURE; 455236769Sobrien } 456236769Sobrien 457236769Sobrien if (!S_ISREG(st.st_mode)) { 458236769Sobrien return FAILURE; 459236769Sobrien } 460236769Sobrien 461236769Sobrien /* 462236769Sobrien * st_size is an off_t, which is 64 bits signed; *ret is 463236769Sobrien * size_t, which might be 32 bits unsigned or 64 bits 464236769Sobrien * unsigned. Rather than being elaborate, just punt on 465236769Sobrien * files that are more than 2^31 bytes. We should never 466236769Sobrien * see a makefile that size in practice... 467236769Sobrien * 468236769Sobrien * While we're at it reject negative sizes too, just in case. 469236769Sobrien */ 470236769Sobrien if (st.st_size < 0 || st.st_size > 0x7fffffff) { 471236769Sobrien return FAILURE; 472236769Sobrien } 473236769Sobrien 474236769Sobrien *ret = (size_t) st.st_size; 475236769Sobrien return SUCCESS; 476236769Sobrien} 477236769Sobrien 478236769Sobrien/* 479236769Sobrien * Read in a file. 480236769Sobrien * 481236769Sobrien * Until the path search logic can be moved under here instead of 482236769Sobrien * being in the caller in another source file, we need to have the fd 483236769Sobrien * passed in already open. Bleh. 484236769Sobrien * 485236769Sobrien * If the path is NULL use stdin and (to insure against fd leaks) 486236769Sobrien * assert that the caller passed in -1. 487236769Sobrien */ 488236769Sobrienstatic struct loadedfile * 489236769Sobrienloadfile(const char *path, int fd) 490236769Sobrien{ 491236769Sobrien struct loadedfile *lf; 492236769Sobrien#ifdef HAVE_MMAP 493331246Ssjg static long pagesize = 0; 494236769Sobrien#endif 495236769Sobrien ssize_t result; 496236769Sobrien size_t bufpos; 497236769Sobrien 498236769Sobrien lf = loadedfile_create(path); 499236769Sobrien 500236769Sobrien if (path == NULL) { 501236769Sobrien assert(fd == -1); 502236769Sobrien fd = STDIN_FILENO; 503236769Sobrien } else { 504236769Sobrien#if 0 /* notyet */ 505236769Sobrien fd = open(path, O_RDONLY); 506236769Sobrien if (fd < 0) { 507236769Sobrien ... 508236769Sobrien Error("%s: %s", path, strerror(errno)); 509236769Sobrien exit(1); 510236769Sobrien } 511236769Sobrien#endif 512236769Sobrien } 513236769Sobrien 514236769Sobrien#ifdef HAVE_MMAP 515236769Sobrien if (load_getsize(fd, &lf->len) == SUCCESS) { 516236769Sobrien /* found a size, try mmap */ 517291978Ssjg#ifdef _SC_PAGESIZE 518331246Ssjg if (pagesize == 0) 519331246Ssjg pagesize = sysconf(_SC_PAGESIZE); 520291978Ssjg#endif 521236769Sobrien if (pagesize <= 0) { 522236769Sobrien pagesize = 0x1000; 523236769Sobrien } 524236769Sobrien /* round size up to a page */ 525236769Sobrien lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize); 526236769Sobrien 527236769Sobrien /* 528236769Sobrien * XXX hack for dealing with empty files; remove when 529236769Sobrien * we're no longer limited by interfacing to the old 530236769Sobrien * logic elsewhere in this file. 531236769Sobrien */ 532236769Sobrien if (lf->maplen == 0) { 533236769Sobrien lf->maplen = pagesize; 534236769Sobrien } 535236769Sobrien 536236769Sobrien /* 537236769Sobrien * FUTURE: remove PROT_WRITE when the parser no longer 538236769Sobrien * needs to scribble on the input. 539236769Sobrien */ 540236769Sobrien lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE, 541236769Sobrien MAP_FILE|MAP_COPY, fd, 0); 542236769Sobrien if (lf->buf != MAP_FAILED) { 543236769Sobrien /* succeeded */ 544236769Sobrien if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') { 545319884Ssjg char *b = bmake_malloc(lf->len + 1); 546236769Sobrien b[lf->len] = '\n'; 547236769Sobrien memcpy(b, lf->buf, lf->len++); 548236769Sobrien munmap(lf->buf, lf->maplen); 549236769Sobrien lf->maplen = 0; 550236769Sobrien lf->buf = b; 551236769Sobrien } 552236769Sobrien goto done; 553236769Sobrien } 554236769Sobrien } 555236769Sobrien#endif 556236769Sobrien /* cannot mmap; load the traditional way */ 557236769Sobrien 558236769Sobrien lf->maplen = 0; 559236769Sobrien lf->len = 1024; 560236769Sobrien lf->buf = bmake_malloc(lf->len); 561236769Sobrien 562236769Sobrien bufpos = 0; 563236769Sobrien while (1) { 564236769Sobrien assert(bufpos <= lf->len); 565236769Sobrien if (bufpos == lf->len) { 566319884Ssjg if (lf->len > SIZE_MAX/2) { 567319884Ssjg errno = EFBIG; 568319884Ssjg Error("%s: file too large", path); 569319884Ssjg exit(1); 570319884Ssjg } 571236769Sobrien lf->len *= 2; 572236769Sobrien lf->buf = bmake_realloc(lf->buf, lf->len); 573236769Sobrien } 574319884Ssjg assert(bufpos < lf->len); 575236769Sobrien result = read(fd, lf->buf + bufpos, lf->len - bufpos); 576236769Sobrien if (result < 0) { 577236769Sobrien Error("%s: read error: %s", path, strerror(errno)); 578236769Sobrien exit(1); 579236769Sobrien } 580236769Sobrien if (result == 0) { 581236769Sobrien break; 582236769Sobrien } 583236769Sobrien bufpos += result; 584236769Sobrien } 585236769Sobrien assert(bufpos <= lf->len); 586236769Sobrien lf->len = bufpos; 587236769Sobrien 588236769Sobrien /* truncate malloc region to actual length (maybe not useful) */ 589236769Sobrien if (lf->len > 0) { 590319884Ssjg /* as for mmap case, ensure trailing \n */ 591319884Ssjg if (lf->buf[lf->len - 1] != '\n') 592319884Ssjg lf->len++; 593236769Sobrien lf->buf = bmake_realloc(lf->buf, lf->len); 594319884Ssjg lf->buf[lf->len - 1] = '\n'; 595236769Sobrien } 596236769Sobrien 597236769Sobrien#ifdef HAVE_MMAP 598236769Sobriendone: 599236769Sobrien#endif 600236769Sobrien if (path != NULL) { 601236769Sobrien close(fd); 602236769Sobrien } 603236769Sobrien return lf; 604236769Sobrien} 605236769Sobrien 606236769Sobrien//////////////////////////////////////////////////////////// 607236769Sobrien// old code 608236769Sobrien 609236769Sobrien/*- 610236769Sobrien *---------------------------------------------------------------------- 611236769Sobrien * ParseIsEscaped -- 612236769Sobrien * Check if the current character is escaped on the current line 613236769Sobrien * 614236769Sobrien * Results: 615236769Sobrien * 0 if the character is not backslash escaped, 1 otherwise 616236769Sobrien * 617236769Sobrien * Side Effects: 618236769Sobrien * None 619236769Sobrien *---------------------------------------------------------------------- 620236769Sobrien */ 621236769Sobrienstatic int 622236769SobrienParseIsEscaped(const char *line, const char *c) 623236769Sobrien{ 624236769Sobrien int active = 0; 625236769Sobrien for (;;) { 626236769Sobrien if (line == c) 627236769Sobrien return active; 628236769Sobrien if (*--c != '\\') 629236769Sobrien return active; 630236769Sobrien active = !active; 631236769Sobrien } 632236769Sobrien} 633236769Sobrien 634236769Sobrien/*- 635236769Sobrien *---------------------------------------------------------------------- 636236769Sobrien * ParseFindKeyword -- 637236769Sobrien * Look in the table of keywords for one matching the given string. 638236769Sobrien * 639236769Sobrien * Input: 640236769Sobrien * str String to find 641236769Sobrien * 642236769Sobrien * Results: 643236769Sobrien * The index of the keyword, or -1 if it isn't there. 644236769Sobrien * 645236769Sobrien * Side Effects: 646236769Sobrien * None 647236769Sobrien *---------------------------------------------------------------------- 648236769Sobrien */ 649236769Sobrienstatic int 650236769SobrienParseFindKeyword(const char *str) 651236769Sobrien{ 652236769Sobrien int start, end, cur; 653236769Sobrien int diff; 654236769Sobrien 655236769Sobrien start = 0; 656236769Sobrien end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; 657236769Sobrien 658236769Sobrien do { 659236769Sobrien cur = start + ((end - start) / 2); 660236769Sobrien diff = strcmp(str, parseKeywords[cur].name); 661236769Sobrien 662236769Sobrien if (diff == 0) { 663236769Sobrien return (cur); 664236769Sobrien } else if (diff < 0) { 665236769Sobrien end = cur - 1; 666236769Sobrien } else { 667236769Sobrien start = cur + 1; 668236769Sobrien } 669236769Sobrien } while (start <= end); 670236769Sobrien return (-1); 671236769Sobrien} 672236769Sobrien 673236769Sobrien/*- 674236769Sobrien * ParseVErrorInternal -- 675236769Sobrien * Error message abort function for parsing. Prints out the context 676236769Sobrien * of the error (line number and file) as well as the message with 677236769Sobrien * two optional arguments. 678236769Sobrien * 679236769Sobrien * Results: 680236769Sobrien * None 681236769Sobrien * 682236769Sobrien * Side Effects: 683236769Sobrien * "fatals" is incremented if the level is PARSE_FATAL. 684236769Sobrien */ 685236769Sobrien/* VARARGS */ 686236769Sobrienstatic void 687236769SobrienParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type, 688236769Sobrien const char *fmt, va_list ap) 689236769Sobrien{ 690236769Sobrien static Boolean fatal_warning_error_printed = FALSE; 691236769Sobrien 692236769Sobrien (void)fprintf(f, "%s: ", progname); 693236769Sobrien 694236769Sobrien if (cfname != NULL) { 695236769Sobrien (void)fprintf(f, "\""); 696236769Sobrien if (*cfname != '/' && strcmp(cfname, "(stdin)") != 0) { 697236769Sobrien char *cp; 698236769Sobrien const char *dir; 699236769Sobrien 700236769Sobrien /* 701236769Sobrien * Nothing is more annoying than not knowing 702236769Sobrien * which Makefile is the culprit. 703236769Sobrien */ 704236769Sobrien dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp); 705236769Sobrien if (dir == NULL || *dir == '\0' || 706236769Sobrien (*dir == '.' && dir[1] == '\0')) 707236769Sobrien dir = Var_Value(".CURDIR", VAR_GLOBAL, &cp); 708236769Sobrien if (dir == NULL) 709236769Sobrien dir = "."; 710236769Sobrien 711236769Sobrien (void)fprintf(f, "%s/%s", dir, cfname); 712236769Sobrien } else 713236769Sobrien (void)fprintf(f, "%s", cfname); 714236769Sobrien 715236769Sobrien (void)fprintf(f, "\" line %d: ", (int)clineno); 716236769Sobrien } 717236769Sobrien if (type == PARSE_WARNING) 718236769Sobrien (void)fprintf(f, "warning: "); 719236769Sobrien (void)vfprintf(f, fmt, ap); 720236769Sobrien (void)fprintf(f, "\n"); 721236769Sobrien (void)fflush(f); 722331246Ssjg if (type == PARSE_INFO) 723331246Ssjg return; 724236769Sobrien if (type == PARSE_FATAL || parseWarnFatal) 725236769Sobrien fatals += 1; 726236769Sobrien if (parseWarnFatal && !fatal_warning_error_printed) { 727236769Sobrien Error("parsing warnings being treated as errors"); 728236769Sobrien fatal_warning_error_printed = TRUE; 729236769Sobrien } 730236769Sobrien} 731236769Sobrien 732236769Sobrien/*- 733236769Sobrien * ParseErrorInternal -- 734236769Sobrien * Error function 735236769Sobrien * 736236769Sobrien * Results: 737236769Sobrien * None 738236769Sobrien * 739236769Sobrien * Side Effects: 740236769Sobrien * None 741236769Sobrien */ 742236769Sobrien/* VARARGS */ 743236769Sobrienstatic void 744236769SobrienParseErrorInternal(const char *cfname, size_t clineno, int type, 745236769Sobrien const char *fmt, ...) 746236769Sobrien{ 747236769Sobrien va_list ap; 748236769Sobrien 749236769Sobrien va_start(ap, fmt); 750236769Sobrien (void)fflush(stdout); 751236769Sobrien ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap); 752236769Sobrien va_end(ap); 753236769Sobrien 754236769Sobrien if (debug_file != stderr && debug_file != stdout) { 755236769Sobrien va_start(ap, fmt); 756236769Sobrien ParseVErrorInternal(debug_file, cfname, clineno, type, fmt, ap); 757236769Sobrien va_end(ap); 758236769Sobrien } 759236769Sobrien} 760236769Sobrien 761236769Sobrien/*- 762236769Sobrien * Parse_Error -- 763236769Sobrien * External interface to ParseErrorInternal; uses the default filename 764236769Sobrien * Line number. 765236769Sobrien * 766236769Sobrien * Results: 767236769Sobrien * None 768236769Sobrien * 769236769Sobrien * Side Effects: 770236769Sobrien * None 771236769Sobrien */ 772236769Sobrien/* VARARGS */ 773236769Sobrienvoid 774236769SobrienParse_Error(int type, const char *fmt, ...) 775236769Sobrien{ 776236769Sobrien va_list ap; 777236769Sobrien const char *fname; 778236769Sobrien size_t lineno; 779236769Sobrien 780236769Sobrien if (curFile == NULL) { 781236769Sobrien fname = NULL; 782236769Sobrien lineno = 0; 783236769Sobrien } else { 784236769Sobrien fname = curFile->fname; 785236769Sobrien lineno = curFile->lineno; 786236769Sobrien } 787236769Sobrien 788236769Sobrien va_start(ap, fmt); 789236769Sobrien (void)fflush(stdout); 790236769Sobrien ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap); 791236769Sobrien va_end(ap); 792236769Sobrien 793236769Sobrien if (debug_file != stderr && debug_file != stdout) { 794236769Sobrien va_start(ap, fmt); 795236769Sobrien ParseVErrorInternal(debug_file, fname, lineno, type, fmt, ap); 796236769Sobrien va_end(ap); 797236769Sobrien } 798236769Sobrien} 799236769Sobrien 800236769Sobrien 801236769Sobrien/* 802236769Sobrien * ParseMessage 803236769Sobrien * Parse a .info .warning or .error directive 804236769Sobrien * 805236769Sobrien * The input is the line minus the ".". We substitute 806236769Sobrien * variables, print the message and exit(1) (for .error) or just print 807236769Sobrien * a warning if the directive is malformed. 808236769Sobrien */ 809236769Sobrienstatic Boolean 810236769SobrienParseMessage(char *line) 811236769Sobrien{ 812236769Sobrien int mtype; 813236769Sobrien 814236769Sobrien switch(*line) { 815236769Sobrien case 'i': 816331246Ssjg mtype = PARSE_INFO; 817236769Sobrien break; 818236769Sobrien case 'w': 819236769Sobrien mtype = PARSE_WARNING; 820236769Sobrien break; 821236769Sobrien case 'e': 822236769Sobrien mtype = PARSE_FATAL; 823236769Sobrien break; 824236769Sobrien default: 825236769Sobrien Parse_Error(PARSE_WARNING, "invalid syntax: \".%s\"", line); 826236769Sobrien return FALSE; 827236769Sobrien } 828236769Sobrien 829300313Ssjg while (isalpha((unsigned char)*line)) 830236769Sobrien line++; 831300313Ssjg if (!isspace((unsigned char)*line)) 832236769Sobrien return FALSE; /* not for us */ 833300313Ssjg while (isspace((unsigned char)*line)) 834236769Sobrien line++; 835236769Sobrien 836296637Ssjg line = Var_Subst(NULL, line, VAR_CMD, VARF_WANTRES); 837236769Sobrien Parse_Error(mtype, "%s", line); 838236769Sobrien free(line); 839236769Sobrien 840236769Sobrien if (mtype == PARSE_FATAL) { 841236769Sobrien /* Terminate immediately. */ 842236769Sobrien exit(1); 843236769Sobrien } 844236769Sobrien return TRUE; 845236769Sobrien} 846236769Sobrien 847236769Sobrien/*- 848236769Sobrien *--------------------------------------------------------------------- 849236769Sobrien * ParseLinkSrc -- 850236769Sobrien * Link the parent node to its new child. Used in a Lst_ForEach by 851236769Sobrien * ParseDoDependency. If the specType isn't 'Not', the parent 852236769Sobrien * isn't linked as a parent of the child. 853236769Sobrien * 854236769Sobrien * Input: 855236769Sobrien * pgnp The parent node 856236769Sobrien * cgpn The child node 857236769Sobrien * 858236769Sobrien * Results: 859236769Sobrien * Always = 0 860236769Sobrien * 861236769Sobrien * Side Effects: 862236769Sobrien * New elements are added to the parents list of cgn and the 863236769Sobrien * children list of cgn. the unmade field of pgn is updated 864236769Sobrien * to reflect the additional child. 865236769Sobrien *--------------------------------------------------------------------- 866236769Sobrien */ 867236769Sobrienstatic int 868236769SobrienParseLinkSrc(void *pgnp, void *cgnp) 869236769Sobrien{ 870236769Sobrien GNode *pgn = (GNode *)pgnp; 871236769Sobrien GNode *cgn = (GNode *)cgnp; 872236769Sobrien 873236769Sobrien if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) 874236769Sobrien pgn = (GNode *)Lst_Datum(Lst_Last(pgn->cohorts)); 875236769Sobrien (void)Lst_AtEnd(pgn->children, cgn); 876236769Sobrien if (specType == Not) 877236769Sobrien (void)Lst_AtEnd(cgn->parents, pgn); 878236769Sobrien pgn->unmade += 1; 879236769Sobrien if (DEBUG(PARSE)) { 880268437Ssjg fprintf(debug_file, "# %s: added child %s - %s\n", __func__, 881268437Ssjg pgn->name, cgn->name); 882236769Sobrien Targ_PrintNode(pgn, 0); 883236769Sobrien Targ_PrintNode(cgn, 0); 884236769Sobrien } 885236769Sobrien return (0); 886236769Sobrien} 887236769Sobrien 888236769Sobrien/*- 889236769Sobrien *--------------------------------------------------------------------- 890236769Sobrien * ParseDoOp -- 891236769Sobrien * Apply the parsed operator to the given target node. Used in a 892236769Sobrien * Lst_ForEach call by ParseDoDependency once all targets have 893236769Sobrien * been found and their operator parsed. If the previous and new 894236769Sobrien * operators are incompatible, a major error is taken. 895236769Sobrien * 896236769Sobrien * Input: 897236769Sobrien * gnp The node to which the operator is to be applied 898236769Sobrien * opp The operator to apply 899236769Sobrien * 900236769Sobrien * Results: 901236769Sobrien * Always 0 902236769Sobrien * 903236769Sobrien * Side Effects: 904236769Sobrien * The type field of the node is altered to reflect any new bits in 905236769Sobrien * the op. 906236769Sobrien *--------------------------------------------------------------------- 907236769Sobrien */ 908236769Sobrienstatic int 909236769SobrienParseDoOp(void *gnp, void *opp) 910236769Sobrien{ 911236769Sobrien GNode *gn = (GNode *)gnp; 912236769Sobrien int op = *(int *)opp; 913236769Sobrien /* 914236769Sobrien * If the dependency mask of the operator and the node don't match and 915236769Sobrien * the node has actually had an operator applied to it before, and 916236769Sobrien * the operator actually has some dependency information in it, complain. 917236769Sobrien */ 918236769Sobrien if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && 919236769Sobrien !OP_NOP(gn->type) && !OP_NOP(op)) 920236769Sobrien { 921236769Sobrien Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); 922236769Sobrien return (1); 923236769Sobrien } 924236769Sobrien 925236769Sobrien if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { 926236769Sobrien /* 927236769Sobrien * If the node was the object of a :: operator, we need to create a 928236769Sobrien * new instance of it for the children and commands on this dependency 929236769Sobrien * line. The new instance is placed on the 'cohorts' list of the 930236769Sobrien * initial one (note the initial one is not on its own cohorts list) 931236769Sobrien * and the new instance is linked to all parents of the initial 932236769Sobrien * instance. 933236769Sobrien */ 934236769Sobrien GNode *cohort; 935236769Sobrien 936236769Sobrien /* 937236769Sobrien * Propagate copied bits to the initial node. They'll be propagated 938236769Sobrien * back to the rest of the cohorts later. 939236769Sobrien */ 940236769Sobrien gn->type |= op & ~OP_OPMASK; 941236769Sobrien 942236769Sobrien cohort = Targ_FindNode(gn->name, TARG_NOHASH); 943249033Ssjg if (doing_depend) 944249033Ssjg ParseMark(cohort); 945236769Sobrien /* 946236769Sobrien * Make the cohort invisible as well to avoid duplicating it into 947236769Sobrien * other variables. True, parents of this target won't tend to do 948236769Sobrien * anything with their local variables, but better safe than 949236769Sobrien * sorry. (I think this is pointless now, since the relevant list 950236769Sobrien * traversals will no longer see this node anyway. -mycroft) 951236769Sobrien */ 952236769Sobrien cohort->type = op | OP_INVISIBLE; 953236769Sobrien (void)Lst_AtEnd(gn->cohorts, cohort); 954236769Sobrien cohort->centurion = gn; 955236769Sobrien gn->unmade_cohorts += 1; 956236769Sobrien snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", 957236769Sobrien gn->unmade_cohorts); 958236769Sobrien } else { 959236769Sobrien /* 960236769Sobrien * We don't want to nuke any previous flags (whatever they were) so we 961236769Sobrien * just OR the new operator into the old 962236769Sobrien */ 963236769Sobrien gn->type |= op; 964236769Sobrien } 965236769Sobrien 966236769Sobrien return (0); 967236769Sobrien} 968236769Sobrien 969236769Sobrien/*- 970236769Sobrien *--------------------------------------------------------------------- 971236769Sobrien * ParseDoSrc -- 972236769Sobrien * Given the name of a source, figure out if it is an attribute 973236769Sobrien * and apply it to the targets if it is. Else decide if there is 974236769Sobrien * some attribute which should be applied *to* the source because 975236769Sobrien * of some special target and apply it if so. Otherwise, make the 976236769Sobrien * source be a child of the targets in the list 'targets' 977236769Sobrien * 978236769Sobrien * Input: 979236769Sobrien * tOp operator (if any) from special targets 980236769Sobrien * src name of the source to handle 981236769Sobrien * 982236769Sobrien * Results: 983236769Sobrien * None 984236769Sobrien * 985236769Sobrien * Side Effects: 986236769Sobrien * Operator bits may be added to the list of targets or to the source. 987236769Sobrien * The targets may have a new source added to their lists of children. 988236769Sobrien *--------------------------------------------------------------------- 989236769Sobrien */ 990236769Sobrienstatic void 991236769SobrienParseDoSrc(int tOp, const char *src) 992236769Sobrien{ 993236769Sobrien GNode *gn = NULL; 994236769Sobrien static int wait_number = 0; 995236769Sobrien char wait_src[16]; 996236769Sobrien 997236769Sobrien if (*src == '.' && isupper ((unsigned char)src[1])) { 998236769Sobrien int keywd = ParseFindKeyword(src); 999236769Sobrien if (keywd != -1) { 1000236769Sobrien int op = parseKeywords[keywd].op; 1001236769Sobrien if (op != 0) { 1002236769Sobrien Lst_ForEach(targets, ParseDoOp, &op); 1003236769Sobrien return; 1004236769Sobrien } 1005236769Sobrien if (parseKeywords[keywd].spec == Wait) { 1006236769Sobrien /* 1007236769Sobrien * We add a .WAIT node in the dependency list. 1008236769Sobrien * After any dynamic dependencies (and filename globbing) 1009236769Sobrien * have happened, it is given a dependency on the each 1010236769Sobrien * previous child back to and previous .WAIT node. 1011236769Sobrien * The next child won't be scheduled until the .WAIT node 1012236769Sobrien * is built. 1013236769Sobrien * We give each .WAIT node a unique name (mainly for diag). 1014236769Sobrien */ 1015236769Sobrien snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); 1016236769Sobrien gn = Targ_FindNode(wait_src, TARG_NOHASH); 1017249033Ssjg if (doing_depend) 1018249033Ssjg ParseMark(gn); 1019236769Sobrien gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; 1020236769Sobrien Lst_ForEach(targets, ParseLinkSrc, gn); 1021236769Sobrien return; 1022236769Sobrien } 1023236769Sobrien } 1024236769Sobrien } 1025236769Sobrien 1026236769Sobrien switch (specType) { 1027236769Sobrien case Main: 1028236769Sobrien /* 1029236769Sobrien * If we have noted the existence of a .MAIN, it means we need 1030236769Sobrien * to add the sources of said target to the list of things 1031236769Sobrien * to create. The string 'src' is likely to be free, so we 1032236769Sobrien * must make a new copy of it. Note that this will only be 1033236769Sobrien * invoked if the user didn't specify a target on the command 1034236769Sobrien * line. This is to allow #ifmake's to succeed, or something... 1035236769Sobrien */ 1036236769Sobrien (void)Lst_AtEnd(create, bmake_strdup(src)); 1037236769Sobrien /* 1038236769Sobrien * Add the name to the .TARGETS variable as well, so the user can 1039236769Sobrien * employ that, if desired. 1040236769Sobrien */ 1041236769Sobrien Var_Append(".TARGETS", src, VAR_GLOBAL); 1042236769Sobrien return; 1043236769Sobrien 1044236769Sobrien case Order: 1045236769Sobrien /* 1046236769Sobrien * Create proper predecessor/successor links between the previous 1047236769Sobrien * source and the current one. 1048236769Sobrien */ 1049236769Sobrien gn = Targ_FindNode(src, TARG_CREATE); 1050249033Ssjg if (doing_depend) 1051249033Ssjg ParseMark(gn); 1052236769Sobrien if (predecessor != NULL) { 1053236769Sobrien (void)Lst_AtEnd(predecessor->order_succ, gn); 1054236769Sobrien (void)Lst_AtEnd(gn->order_pred, predecessor); 1055236769Sobrien if (DEBUG(PARSE)) { 1056268437Ssjg fprintf(debug_file, "# %s: added Order dependency %s - %s\n", 1057268437Ssjg __func__, predecessor->name, gn->name); 1058236769Sobrien Targ_PrintNode(predecessor, 0); 1059236769Sobrien Targ_PrintNode(gn, 0); 1060236769Sobrien } 1061236769Sobrien } 1062236769Sobrien /* 1063236769Sobrien * The current source now becomes the predecessor for the next one. 1064236769Sobrien */ 1065236769Sobrien predecessor = gn; 1066236769Sobrien break; 1067236769Sobrien 1068236769Sobrien default: 1069236769Sobrien /* 1070236769Sobrien * If the source is not an attribute, we need to find/create 1071236769Sobrien * a node for it. After that we can apply any operator to it 1072236769Sobrien * from a special target or link it to its parents, as 1073236769Sobrien * appropriate. 1074236769Sobrien * 1075236769Sobrien * In the case of a source that was the object of a :: operator, 1076236769Sobrien * the attribute is applied to all of its instances (as kept in 1077236769Sobrien * the 'cohorts' list of the node) or all the cohorts are linked 1078236769Sobrien * to all the targets. 1079236769Sobrien */ 1080236769Sobrien 1081236769Sobrien /* Find/create the 'src' node and attach to all targets */ 1082236769Sobrien gn = Targ_FindNode(src, TARG_CREATE); 1083249033Ssjg if (doing_depend) 1084249033Ssjg ParseMark(gn); 1085236769Sobrien if (tOp) { 1086236769Sobrien gn->type |= tOp; 1087236769Sobrien } else { 1088236769Sobrien Lst_ForEach(targets, ParseLinkSrc, gn); 1089236769Sobrien } 1090236769Sobrien break; 1091236769Sobrien } 1092236769Sobrien} 1093236769Sobrien 1094236769Sobrien/*- 1095236769Sobrien *----------------------------------------------------------------------- 1096236769Sobrien * ParseFindMain -- 1097236769Sobrien * Find a real target in the list and set it to be the main one. 1098236769Sobrien * Called by ParseDoDependency when a main target hasn't been found 1099236769Sobrien * yet. 1100236769Sobrien * 1101236769Sobrien * Input: 1102236769Sobrien * gnp Node to examine 1103236769Sobrien * 1104236769Sobrien * Results: 1105236769Sobrien * 0 if main not found yet, 1 if it is. 1106236769Sobrien * 1107236769Sobrien * Side Effects: 1108236769Sobrien * mainNode is changed and Targ_SetMain is called. 1109236769Sobrien * 1110236769Sobrien *----------------------------------------------------------------------- 1111236769Sobrien */ 1112236769Sobrienstatic int 1113319884SsjgParseFindMain(void *gnp, void *dummy MAKE_ATTR_UNUSED) 1114236769Sobrien{ 1115236769Sobrien GNode *gn = (GNode *)gnp; 1116236769Sobrien if ((gn->type & OP_NOTARGET) == 0) { 1117236769Sobrien mainNode = gn; 1118236769Sobrien Targ_SetMain(gn); 1119319884Ssjg return 1; 1120236769Sobrien } else { 1121319884Ssjg return 0; 1122236769Sobrien } 1123236769Sobrien} 1124236769Sobrien 1125236769Sobrien/*- 1126236769Sobrien *----------------------------------------------------------------------- 1127236769Sobrien * ParseAddDir -- 1128236769Sobrien * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going 1129236769Sobrien * 1130236769Sobrien * Results: 1131236769Sobrien * === 0 1132236769Sobrien * 1133236769Sobrien * Side Effects: 1134236769Sobrien * See Dir_AddDir. 1135236769Sobrien * 1136236769Sobrien *----------------------------------------------------------------------- 1137236769Sobrien */ 1138236769Sobrienstatic int 1139236769SobrienParseAddDir(void *path, void *name) 1140236769Sobrien{ 1141236769Sobrien (void)Dir_AddDir((Lst) path, (char *)name); 1142236769Sobrien return(0); 1143236769Sobrien} 1144236769Sobrien 1145236769Sobrien/*- 1146236769Sobrien *----------------------------------------------------------------------- 1147236769Sobrien * ParseClearPath -- 1148236769Sobrien * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going 1149236769Sobrien * 1150236769Sobrien * Results: 1151236769Sobrien * === 0 1152236769Sobrien * 1153236769Sobrien * Side Effects: 1154236769Sobrien * See Dir_ClearPath 1155236769Sobrien * 1156236769Sobrien *----------------------------------------------------------------------- 1157236769Sobrien */ 1158236769Sobrienstatic int 1159319884SsjgParseClearPath(void *path, void *dummy MAKE_ATTR_UNUSED) 1160236769Sobrien{ 1161236769Sobrien Dir_ClearPath((Lst) path); 1162319884Ssjg return 0; 1163236769Sobrien} 1164236769Sobrien 1165236769Sobrien/*- 1166236769Sobrien *--------------------------------------------------------------------- 1167236769Sobrien * ParseDoDependency -- 1168236769Sobrien * Parse the dependency line in line. 1169236769Sobrien * 1170236769Sobrien * Input: 1171236769Sobrien * line the line to parse 1172236769Sobrien * 1173236769Sobrien * Results: 1174236769Sobrien * None 1175236769Sobrien * 1176236769Sobrien * Side Effects: 1177236769Sobrien * The nodes of the sources are linked as children to the nodes of the 1178236769Sobrien * targets. Some nodes may be created. 1179236769Sobrien * 1180236769Sobrien * We parse a dependency line by first extracting words from the line and 1181236769Sobrien * finding nodes in the list of all targets with that name. This is done 1182236769Sobrien * until a character is encountered which is an operator character. Currently 1183236769Sobrien * these are only ! and :. At this point the operator is parsed and the 1184236769Sobrien * pointer into the line advanced until the first source is encountered. 1185236769Sobrien * The parsed operator is applied to each node in the 'targets' list, 1186236769Sobrien * which is where the nodes found for the targets are kept, by means of 1187236769Sobrien * the ParseDoOp function. 1188236769Sobrien * The sources are read in much the same way as the targets were except 1189236769Sobrien * that now they are expanded using the wildcarding scheme of the C-Shell 1190236769Sobrien * and all instances of the resulting words in the list of all targets 1191236769Sobrien * are found. Each of the resulting nodes is then linked to each of the 1192236769Sobrien * targets as one of its children. 1193236769Sobrien * Certain targets are handled specially. These are the ones detailed 1194236769Sobrien * by the specType variable. 1195236769Sobrien * The storing of transformation rules is also taken care of here. 1196236769Sobrien * A target is recognized as a transformation rule by calling 1197236769Sobrien * Suff_IsTransform. If it is a transformation rule, its node is gotten 1198236769Sobrien * from the suffix module via Suff_AddTransform rather than the standard 1199236769Sobrien * Targ_FindNode in the target module. 1200236769Sobrien *--------------------------------------------------------------------- 1201236769Sobrien */ 1202236769Sobrienstatic void 1203236769SobrienParseDoDependency(char *line) 1204236769Sobrien{ 1205236769Sobrien char *cp; /* our current position */ 1206236769Sobrien GNode *gn = NULL; /* a general purpose temporary node */ 1207236769Sobrien int op; /* the operator on the line */ 1208236769Sobrien char savec; /* a place to save a character */ 1209236769Sobrien Lst paths; /* List of search paths to alter when parsing 1210236769Sobrien * a list of .PATH targets */ 1211236769Sobrien int tOp; /* operator from special target */ 1212236769Sobrien Lst sources; /* list of archive source names after 1213236769Sobrien * expansion */ 1214236769Sobrien Lst curTargs; /* list of target names to be found and added 1215236769Sobrien * to the targets list */ 1216236769Sobrien char *lstart = line; 1217236769Sobrien 1218236769Sobrien if (DEBUG(PARSE)) 1219236769Sobrien fprintf(debug_file, "ParseDoDependency(%s)\n", line); 1220236769Sobrien tOp = 0; 1221236769Sobrien 1222236769Sobrien specType = Not; 1223236769Sobrien paths = NULL; 1224236769Sobrien 1225236769Sobrien curTargs = Lst_Init(FALSE); 1226236769Sobrien 1227281812Ssjg /* 1228281812Ssjg * First, grind through the targets. 1229281812Ssjg */ 1230281812Ssjg 1231236769Sobrien do { 1232281812Ssjg /* 1233281812Ssjg * Here LINE points to the beginning of the next word, and 1234281812Ssjg * LSTART points to the actual beginning of the line. 1235281812Ssjg */ 1236281812Ssjg 1237281812Ssjg /* Find the end of the next word. */ 1238236769Sobrien for (cp = line; *cp && (ParseIsEscaped(lstart, cp) || 1239236769Sobrien !(isspace((unsigned char)*cp) || 1240236769Sobrien *cp == '!' || *cp == ':' || *cp == LPAREN)); 1241236769Sobrien cp++) { 1242236769Sobrien if (*cp == '$') { 1243236769Sobrien /* 1244236769Sobrien * Must be a dynamic source (would have been expanded 1245236769Sobrien * otherwise), so call the Var module to parse the puppy 1246236769Sobrien * so we can safely advance beyond it...There should be 1247236769Sobrien * no errors in this, as they would have been discovered 1248236769Sobrien * in the initial Var_Subst and we wouldn't be here. 1249236769Sobrien */ 1250236769Sobrien int length; 1251236769Sobrien void *freeIt; 1252236769Sobrien 1253296637Ssjg (void)Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES, 1254296637Ssjg &length, &freeIt); 1255296637Ssjg free(freeIt); 1256236769Sobrien cp += length-1; 1257236769Sobrien } 1258236769Sobrien } 1259236769Sobrien 1260281812Ssjg /* 1261281812Ssjg * If the word is followed by a left parenthesis, it's the 1262281812Ssjg * name of an object file inside an archive (ar file). 1263281812Ssjg */ 1264236769Sobrien if (!ParseIsEscaped(lstart, cp) && *cp == LPAREN) { 1265236769Sobrien /* 1266236769Sobrien * Archives must be handled specially to make sure the OP_ARCHV 1267236769Sobrien * flag is set in their 'type' field, for one thing, and because 1268236769Sobrien * things like "archive(file1.o file2.o file3.o)" are permissible. 1269236769Sobrien * Arch_ParseArchive will set 'line' to be the first non-blank 1270236769Sobrien * after the archive-spec. It creates/finds nodes for the members 1271236769Sobrien * and places them on the given list, returning SUCCESS if all 1272236769Sobrien * went well and FAILURE if there was an error in the 1273236769Sobrien * specification. On error, line should remain untouched. 1274236769Sobrien */ 1275236769Sobrien if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) { 1276236769Sobrien Parse_Error(PARSE_FATAL, 1277236769Sobrien "Error in archive specification: \"%s\"", line); 1278236769Sobrien goto out; 1279236769Sobrien } else { 1280281812Ssjg /* Done with this word; on to the next. */ 1281297040Ssjg cp = line; 1282236769Sobrien continue; 1283236769Sobrien } 1284236769Sobrien } 1285236769Sobrien 1286236769Sobrien if (!*cp) { 1287236769Sobrien /* 1288281812Ssjg * We got to the end of the line while we were still 1289281812Ssjg * looking at targets. 1290281812Ssjg * 1291236769Sobrien * Ending a dependency line without an operator is a Bozo 1292236769Sobrien * no-no. As a heuristic, this is also often triggered by 1293236769Sobrien * undetected conflicts from cvs/rcs merges. 1294236769Sobrien */ 1295236769Sobrien if ((strncmp(line, "<<<<<<", 6) == 0) || 1296236769Sobrien (strncmp(line, "======", 6) == 0) || 1297236769Sobrien (strncmp(line, ">>>>>>", 6) == 0)) 1298236769Sobrien Parse_Error(PARSE_FATAL, 1299236769Sobrien "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); 1300236769Sobrien else 1301236769Sobrien Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive" 1302236769Sobrien : "Need an operator"); 1303236769Sobrien goto out; 1304236769Sobrien } 1305281812Ssjg 1306281812Ssjg /* Insert a null terminator. */ 1307281812Ssjg savec = *cp; 1308236769Sobrien *cp = '\0'; 1309236769Sobrien 1310236769Sobrien /* 1311281812Ssjg * Got the word. See if it's a special target and if so set 1312236769Sobrien * specType to match it. 1313236769Sobrien */ 1314236769Sobrien if (*line == '.' && isupper ((unsigned char)line[1])) { 1315236769Sobrien /* 1316236769Sobrien * See if the target is a special target that must have it 1317236769Sobrien * or its sources handled specially. 1318236769Sobrien */ 1319236769Sobrien int keywd = ParseFindKeyword(line); 1320236769Sobrien if (keywd != -1) { 1321236769Sobrien if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { 1322236769Sobrien Parse_Error(PARSE_FATAL, "Mismatched special targets"); 1323236769Sobrien goto out; 1324236769Sobrien } 1325236769Sobrien 1326236769Sobrien specType = parseKeywords[keywd].spec; 1327236769Sobrien tOp = parseKeywords[keywd].op; 1328236769Sobrien 1329236769Sobrien /* 1330236769Sobrien * Certain special targets have special semantics: 1331236769Sobrien * .PATH Have to set the dirSearchPath 1332236769Sobrien * variable too 1333236769Sobrien * .MAIN Its sources are only used if 1334236769Sobrien * nothing has been specified to 1335236769Sobrien * create. 1336236769Sobrien * .DEFAULT Need to create a node to hang 1337236769Sobrien * commands on, but we don't want 1338236769Sobrien * it in the graph, nor do we want 1339236769Sobrien * it to be the Main Target, so we 1340236769Sobrien * create it, set OP_NOTMAIN and 1341236769Sobrien * add it to the list, setting 1342236769Sobrien * DEFAULT to the new node for 1343236769Sobrien * later use. We claim the node is 1344236769Sobrien * A transformation rule to make 1345236769Sobrien * life easier later, when we'll 1346236769Sobrien * use Make_HandleUse to actually 1347236769Sobrien * apply the .DEFAULT commands. 1348236769Sobrien * .PHONY The list of targets 1349236769Sobrien * .NOPATH Don't search for file in the path 1350249033Ssjg * .STALE 1351236769Sobrien * .BEGIN 1352236769Sobrien * .END 1353236769Sobrien * .ERROR 1354319884Ssjg * .DELETE_ON_ERROR 1355236769Sobrien * .INTERRUPT Are not to be considered the 1356236769Sobrien * main target. 1357236769Sobrien * .NOTPARALLEL Make only one target at a time. 1358236769Sobrien * .SINGLESHELL Create a shell for each command. 1359236769Sobrien * .ORDER Must set initial predecessor to NULL 1360236769Sobrien */ 1361236769Sobrien switch (specType) { 1362249033Ssjg case ExPath: 1363249033Ssjg if (paths == NULL) { 1364249033Ssjg paths = Lst_Init(FALSE); 1365249033Ssjg } 1366249033Ssjg (void)Lst_AtEnd(paths, dirSearchPath); 1367249033Ssjg break; 1368249033Ssjg case Main: 1369249033Ssjg if (!Lst_IsEmpty(create)) { 1370249033Ssjg specType = Not; 1371249033Ssjg } 1372249033Ssjg break; 1373249033Ssjg case Begin: 1374249033Ssjg case End: 1375249033Ssjg case Stale: 1376249033Ssjg case dotError: 1377249033Ssjg case Interrupt: 1378249033Ssjg gn = Targ_FindNode(line, TARG_CREATE); 1379249033Ssjg if (doing_depend) 1380249033Ssjg ParseMark(gn); 1381249033Ssjg gn->type |= OP_NOTMAIN|OP_SPECIAL; 1382249033Ssjg (void)Lst_AtEnd(targets, gn); 1383249033Ssjg break; 1384249033Ssjg case Default: 1385249033Ssjg gn = Targ_NewGN(".DEFAULT"); 1386249033Ssjg gn->type |= (OP_NOTMAIN|OP_TRANSFORM); 1387249033Ssjg (void)Lst_AtEnd(targets, gn); 1388249033Ssjg DEFAULT = gn; 1389249033Ssjg break; 1390319884Ssjg case DeleteOnError: 1391319884Ssjg deleteOnError = TRUE; 1392319884Ssjg break; 1393249033Ssjg case NotParallel: 1394249033Ssjg maxJobs = 1; 1395249033Ssjg break; 1396249033Ssjg case SingleShell: 1397249033Ssjg compatMake = TRUE; 1398249033Ssjg break; 1399249033Ssjg case Order: 1400249033Ssjg predecessor = NULL; 1401249033Ssjg break; 1402249033Ssjg default: 1403249033Ssjg break; 1404236769Sobrien } 1405236769Sobrien } else if (strncmp(line, ".PATH", 5) == 0) { 1406236769Sobrien /* 1407236769Sobrien * .PATH<suffix> has to be handled specially. 1408236769Sobrien * Call on the suffix module to give us a path to 1409236769Sobrien * modify. 1410236769Sobrien */ 1411236769Sobrien Lst path; 1412236769Sobrien 1413236769Sobrien specType = ExPath; 1414236769Sobrien path = Suff_GetPath(&line[5]); 1415236769Sobrien if (path == NULL) { 1416236769Sobrien Parse_Error(PARSE_FATAL, 1417236769Sobrien "Suffix '%s' not defined (yet)", 1418236769Sobrien &line[5]); 1419236769Sobrien goto out; 1420236769Sobrien } else { 1421236769Sobrien if (paths == NULL) { 1422236769Sobrien paths = Lst_Init(FALSE); 1423236769Sobrien } 1424236769Sobrien (void)Lst_AtEnd(paths, path); 1425236769Sobrien } 1426236769Sobrien } 1427236769Sobrien } 1428236769Sobrien 1429236769Sobrien /* 1430236769Sobrien * Have word in line. Get or create its node and stick it at 1431236769Sobrien * the end of the targets list 1432236769Sobrien */ 1433236769Sobrien if ((specType == Not) && (*line != '\0')) { 1434236769Sobrien if (Dir_HasWildcards(line)) { 1435236769Sobrien /* 1436236769Sobrien * Targets are to be sought only in the current directory, 1437236769Sobrien * so create an empty path for the thing. Note we need to 1438236769Sobrien * use Dir_Destroy in the destruction of the path as the 1439236769Sobrien * Dir module could have added a directory to the path... 1440236769Sobrien */ 1441236769Sobrien Lst emptyPath = Lst_Init(FALSE); 1442236769Sobrien 1443236769Sobrien Dir_Expand(line, emptyPath, curTargs); 1444236769Sobrien 1445236769Sobrien Lst_Destroy(emptyPath, Dir_Destroy); 1446236769Sobrien } else { 1447236769Sobrien /* 1448236769Sobrien * No wildcards, but we want to avoid code duplication, 1449236769Sobrien * so create a list with the word on it. 1450236769Sobrien */ 1451236769Sobrien (void)Lst_AtEnd(curTargs, line); 1452236769Sobrien } 1453236769Sobrien 1454281812Ssjg /* Apply the targets. */ 1455281812Ssjg 1456236769Sobrien while(!Lst_IsEmpty(curTargs)) { 1457236769Sobrien char *targName = (char *)Lst_DeQueue(curTargs); 1458236769Sobrien 1459236769Sobrien if (!Suff_IsTransform (targName)) { 1460236769Sobrien gn = Targ_FindNode(targName, TARG_CREATE); 1461236769Sobrien } else { 1462236769Sobrien gn = Suff_AddTransform(targName); 1463236769Sobrien } 1464249033Ssjg if (doing_depend) 1465249033Ssjg ParseMark(gn); 1466236769Sobrien 1467236769Sobrien (void)Lst_AtEnd(targets, gn); 1468236769Sobrien } 1469236769Sobrien } else if (specType == ExPath && *line != '.' && *line != '\0') { 1470236769Sobrien Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); 1471236769Sobrien } 1472236769Sobrien 1473281812Ssjg /* Don't need the inserted null terminator any more. */ 1474236769Sobrien *cp = savec; 1475281812Ssjg 1476236769Sobrien /* 1477236769Sobrien * If it is a special type and not .PATH, it's the only target we 1478236769Sobrien * allow on this line... 1479236769Sobrien */ 1480236769Sobrien if (specType != Not && specType != ExPath) { 1481236769Sobrien Boolean warning = FALSE; 1482236769Sobrien 1483236769Sobrien while (*cp && (ParseIsEscaped(lstart, cp) || 1484236769Sobrien ((*cp != '!') && (*cp != ':')))) { 1485236769Sobrien if (ParseIsEscaped(lstart, cp) || 1486236769Sobrien (*cp != ' ' && *cp != '\t')) { 1487236769Sobrien warning = TRUE; 1488236769Sobrien } 1489236769Sobrien cp++; 1490236769Sobrien } 1491236769Sobrien if (warning) { 1492236769Sobrien Parse_Error(PARSE_WARNING, "Extra target ignored"); 1493236769Sobrien } 1494236769Sobrien } else { 1495236769Sobrien while (*cp && isspace ((unsigned char)*cp)) { 1496236769Sobrien cp++; 1497236769Sobrien } 1498236769Sobrien } 1499236769Sobrien line = cp; 1500236769Sobrien } while (*line && (ParseIsEscaped(lstart, line) || 1501236769Sobrien ((*line != '!') && (*line != ':')))); 1502236769Sobrien 1503236769Sobrien /* 1504236769Sobrien * Don't need the list of target names anymore... 1505236769Sobrien */ 1506236769Sobrien Lst_Destroy(curTargs, NULL); 1507236769Sobrien curTargs = NULL; 1508236769Sobrien 1509236769Sobrien if (!Lst_IsEmpty(targets)) { 1510236769Sobrien switch(specType) { 1511236769Sobrien default: 1512236769Sobrien Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); 1513236769Sobrien break; 1514236769Sobrien case Default: 1515249033Ssjg case Stale: 1516236769Sobrien case Begin: 1517236769Sobrien case End: 1518236769Sobrien case dotError: 1519236769Sobrien case Interrupt: 1520236769Sobrien /* 1521236769Sobrien * These four create nodes on which to hang commands, so 1522236769Sobrien * targets shouldn't be empty... 1523236769Sobrien */ 1524236769Sobrien case Not: 1525236769Sobrien /* 1526236769Sobrien * Nothing special here -- targets can be empty if it wants. 1527236769Sobrien */ 1528236769Sobrien break; 1529236769Sobrien } 1530236769Sobrien } 1531236769Sobrien 1532236769Sobrien /* 1533236769Sobrien * Have now parsed all the target names. Must parse the operator next. The 1534236769Sobrien * result is left in op . 1535236769Sobrien */ 1536236769Sobrien if (*cp == '!') { 1537236769Sobrien op = OP_FORCE; 1538236769Sobrien } else if (*cp == ':') { 1539236769Sobrien if (cp[1] == ':') { 1540236769Sobrien op = OP_DOUBLEDEP; 1541236769Sobrien cp++; 1542236769Sobrien } else { 1543236769Sobrien op = OP_DEPENDS; 1544236769Sobrien } 1545236769Sobrien } else { 1546236769Sobrien Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive" 1547236769Sobrien : "Missing dependency operator"); 1548236769Sobrien goto out; 1549236769Sobrien } 1550236769Sobrien 1551281812Ssjg /* Advance beyond the operator */ 1552281812Ssjg cp++; 1553236769Sobrien 1554281812Ssjg /* 1555281812Ssjg * Apply the operator to the target. This is how we remember which 1556281812Ssjg * operator a target was defined with. It fails if the operator 1557281812Ssjg * used isn't consistent across all references. 1558281812Ssjg */ 1559236769Sobrien Lst_ForEach(targets, ParseDoOp, &op); 1560236769Sobrien 1561236769Sobrien /* 1562281812Ssjg * Onward to the sources. 1563281812Ssjg * 1564281812Ssjg * LINE will now point to the first source word, if any, or the 1565281812Ssjg * end of the string if not. 1566236769Sobrien */ 1567236769Sobrien while (*cp && isspace ((unsigned char)*cp)) { 1568236769Sobrien cp++; 1569236769Sobrien } 1570236769Sobrien line = cp; 1571236769Sobrien 1572236769Sobrien /* 1573236769Sobrien * Several special targets take different actions if present with no 1574236769Sobrien * sources: 1575236769Sobrien * a .SUFFIXES line with no sources clears out all old suffixes 1576236769Sobrien * a .PRECIOUS line makes all targets precious 1577236769Sobrien * a .IGNORE line ignores errors for all targets 1578236769Sobrien * a .SILENT line creates silence when making all targets 1579236769Sobrien * a .PATH removes all directories from the search path(s). 1580236769Sobrien */ 1581236769Sobrien if (!*line) { 1582236769Sobrien switch (specType) { 1583236769Sobrien case Suffixes: 1584236769Sobrien Suff_ClearSuffixes(); 1585236769Sobrien break; 1586236769Sobrien case Precious: 1587236769Sobrien allPrecious = TRUE; 1588236769Sobrien break; 1589236769Sobrien case Ignore: 1590236769Sobrien ignoreErrors = TRUE; 1591236769Sobrien break; 1592236769Sobrien case Silent: 1593236769Sobrien beSilent = TRUE; 1594236769Sobrien break; 1595236769Sobrien case ExPath: 1596236769Sobrien Lst_ForEach(paths, ParseClearPath, NULL); 1597236769Sobrien Dir_SetPATH(); 1598236769Sobrien break; 1599236769Sobrien#ifdef POSIX 1600236769Sobrien case Posix: 1601236769Sobrien Var_Set("%POSIX", "1003.2", VAR_GLOBAL, 0); 1602236769Sobrien break; 1603236769Sobrien#endif 1604236769Sobrien default: 1605236769Sobrien break; 1606236769Sobrien } 1607236769Sobrien } else if (specType == MFlags) { 1608236769Sobrien /* 1609236769Sobrien * Call on functions in main.c to deal with these arguments and 1610236769Sobrien * set the initial character to a null-character so the loop to 1611236769Sobrien * get sources won't get anything 1612236769Sobrien */ 1613236769Sobrien Main_ParseArgLine(line); 1614236769Sobrien *line = '\0'; 1615236769Sobrien } else if (specType == ExShell) { 1616236769Sobrien if (Job_ParseShell(line) != SUCCESS) { 1617236769Sobrien Parse_Error(PARSE_FATAL, "improper shell specification"); 1618236769Sobrien goto out; 1619236769Sobrien } 1620236769Sobrien *line = '\0'; 1621319884Ssjg } else if ((specType == NotParallel) || (specType == SingleShell) || 1622319884Ssjg (specType == DeleteOnError)) { 1623236769Sobrien *line = '\0'; 1624236769Sobrien } 1625236769Sobrien 1626236769Sobrien /* 1627236769Sobrien * NOW GO FOR THE SOURCES 1628236769Sobrien */ 1629236769Sobrien if ((specType == Suffixes) || (specType == ExPath) || 1630236769Sobrien (specType == Includes) || (specType == Libs) || 1631236769Sobrien (specType == Null) || (specType == ExObjdir)) 1632236769Sobrien { 1633236769Sobrien while (*line) { 1634236769Sobrien /* 1635236769Sobrien * If the target was one that doesn't take files as its sources 1636236769Sobrien * but takes something like suffixes, we take each 1637236769Sobrien * space-separated word on the line as a something and deal 1638236769Sobrien * with it accordingly. 1639236769Sobrien * 1640236769Sobrien * If the target was .SUFFIXES, we take each source as a 1641236769Sobrien * suffix and add it to the list of suffixes maintained by the 1642236769Sobrien * Suff module. 1643236769Sobrien * 1644236769Sobrien * If the target was a .PATH, we add the source as a directory 1645236769Sobrien * to search on the search path. 1646236769Sobrien * 1647236769Sobrien * If it was .INCLUDES, the source is taken to be the suffix of 1648236769Sobrien * files which will be #included and whose search path should 1649236769Sobrien * be present in the .INCLUDES variable. 1650236769Sobrien * 1651236769Sobrien * If it was .LIBS, the source is taken to be the suffix of 1652236769Sobrien * files which are considered libraries and whose search path 1653236769Sobrien * should be present in the .LIBS variable. 1654236769Sobrien * 1655236769Sobrien * If it was .NULL, the source is the suffix to use when a file 1656236769Sobrien * has no valid suffix. 1657236769Sobrien * 1658236769Sobrien * If it was .OBJDIR, the source is a new definition for .OBJDIR, 1659236769Sobrien * and will cause make to do a new chdir to that path. 1660236769Sobrien */ 1661236769Sobrien while (*cp && !isspace ((unsigned char)*cp)) { 1662236769Sobrien cp++; 1663236769Sobrien } 1664236769Sobrien savec = *cp; 1665236769Sobrien *cp = '\0'; 1666236769Sobrien switch (specType) { 1667236769Sobrien case Suffixes: 1668236769Sobrien Suff_AddSuffix(line, &mainNode); 1669236769Sobrien break; 1670236769Sobrien case ExPath: 1671236769Sobrien Lst_ForEach(paths, ParseAddDir, line); 1672236769Sobrien break; 1673236769Sobrien case Includes: 1674236769Sobrien Suff_AddInclude(line); 1675236769Sobrien break; 1676236769Sobrien case Libs: 1677236769Sobrien Suff_AddLib(line); 1678236769Sobrien break; 1679236769Sobrien case Null: 1680236769Sobrien Suff_SetNull(line); 1681236769Sobrien break; 1682236769Sobrien case ExObjdir: 1683319884Ssjg Main_SetObjdir("%s", line); 1684236769Sobrien break; 1685236769Sobrien default: 1686236769Sobrien break; 1687236769Sobrien } 1688236769Sobrien *cp = savec; 1689236769Sobrien if (savec != '\0') { 1690236769Sobrien cp++; 1691236769Sobrien } 1692236769Sobrien while (*cp && isspace ((unsigned char)*cp)) { 1693236769Sobrien cp++; 1694236769Sobrien } 1695236769Sobrien line = cp; 1696236769Sobrien } 1697236769Sobrien if (paths) { 1698236769Sobrien Lst_Destroy(paths, NULL); 1699319884Ssjg paths = NULL; 1700236769Sobrien } 1701236769Sobrien if (specType == ExPath) 1702236769Sobrien Dir_SetPATH(); 1703236769Sobrien } else { 1704319884Ssjg assert(paths == NULL); 1705236769Sobrien while (*line) { 1706236769Sobrien /* 1707236769Sobrien * The targets take real sources, so we must beware of archive 1708236769Sobrien * specifications (i.e. things with left parentheses in them) 1709236769Sobrien * and handle them accordingly. 1710236769Sobrien */ 1711236769Sobrien for (; *cp && !isspace ((unsigned char)*cp); cp++) { 1712236769Sobrien if ((*cp == LPAREN) && (cp > line) && (cp[-1] != '$')) { 1713236769Sobrien /* 1714236769Sobrien * Only stop for a left parenthesis if it isn't at the 1715236769Sobrien * start of a word (that'll be for variable changes 1716236769Sobrien * later) and isn't preceded by a dollar sign (a dynamic 1717236769Sobrien * source). 1718236769Sobrien */ 1719236769Sobrien break; 1720236769Sobrien } 1721236769Sobrien } 1722236769Sobrien 1723236769Sobrien if (*cp == LPAREN) { 1724236769Sobrien sources = Lst_Init(FALSE); 1725236769Sobrien if (Arch_ParseArchive(&line, sources, VAR_CMD) != SUCCESS) { 1726236769Sobrien Parse_Error(PARSE_FATAL, 1727236769Sobrien "Error in source archive spec \"%s\"", line); 1728236769Sobrien goto out; 1729236769Sobrien } 1730236769Sobrien 1731236769Sobrien while (!Lst_IsEmpty (sources)) { 1732236769Sobrien gn = (GNode *)Lst_DeQueue(sources); 1733236769Sobrien ParseDoSrc(tOp, gn->name); 1734236769Sobrien } 1735236769Sobrien Lst_Destroy(sources, NULL); 1736236769Sobrien cp = line; 1737236769Sobrien } else { 1738236769Sobrien if (*cp) { 1739236769Sobrien *cp = '\0'; 1740236769Sobrien cp += 1; 1741236769Sobrien } 1742236769Sobrien 1743236769Sobrien ParseDoSrc(tOp, line); 1744236769Sobrien } 1745236769Sobrien while (*cp && isspace ((unsigned char)*cp)) { 1746236769Sobrien cp++; 1747236769Sobrien } 1748236769Sobrien line = cp; 1749236769Sobrien } 1750236769Sobrien } 1751236769Sobrien 1752236769Sobrien if (mainNode == NULL) { 1753236769Sobrien /* 1754236769Sobrien * If we have yet to decide on a main target to make, in the 1755236769Sobrien * absence of any user input, we want the first target on 1756236769Sobrien * the first dependency line that is actually a real target 1757236769Sobrien * (i.e. isn't a .USE or .EXEC rule) to be made. 1758236769Sobrien */ 1759236769Sobrien Lst_ForEach(targets, ParseFindMain, NULL); 1760236769Sobrien } 1761236769Sobrien 1762236769Sobrienout: 1763319884Ssjg assert(paths == NULL); 1764236769Sobrien if (curTargs) 1765236769Sobrien Lst_Destroy(curTargs, NULL); 1766236769Sobrien} 1767236769Sobrien 1768236769Sobrien/*- 1769236769Sobrien *--------------------------------------------------------------------- 1770236769Sobrien * Parse_IsVar -- 1771236769Sobrien * Return TRUE if the passed line is a variable assignment. A variable 1772236769Sobrien * assignment consists of a single word followed by optional whitespace 1773236769Sobrien * followed by either a += or an = operator. 1774236769Sobrien * This function is used both by the Parse_File function and main when 1775236769Sobrien * parsing the command-line arguments. 1776236769Sobrien * 1777236769Sobrien * Input: 1778236769Sobrien * line the line to check 1779236769Sobrien * 1780236769Sobrien * Results: 1781236769Sobrien * TRUE if it is. FALSE if it ain't 1782236769Sobrien * 1783236769Sobrien * Side Effects: 1784236769Sobrien * none 1785236769Sobrien *--------------------------------------------------------------------- 1786236769Sobrien */ 1787236769SobrienBoolean 1788236769SobrienParse_IsVar(char *line) 1789236769Sobrien{ 1790236769Sobrien Boolean wasSpace = FALSE; /* set TRUE if found a space */ 1791236769Sobrien char ch; 1792236769Sobrien int level = 0; 1793236769Sobrien#define ISEQOPERATOR(c) \ 1794236769Sobrien (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!')) 1795236769Sobrien 1796236769Sobrien /* 1797236769Sobrien * Skip to variable name 1798236769Sobrien */ 1799236769Sobrien for (;(*line == ' ') || (*line == '\t'); line++) 1800236769Sobrien continue; 1801236769Sobrien 1802236769Sobrien /* Scan for one of the assignment operators outside a variable expansion */ 1803236769Sobrien while ((ch = *line++) != 0) { 1804236769Sobrien if (ch == '(' || ch == '{') { 1805236769Sobrien level++; 1806236769Sobrien continue; 1807236769Sobrien } 1808236769Sobrien if (ch == ')' || ch == '}') { 1809236769Sobrien level--; 1810236769Sobrien continue; 1811236769Sobrien } 1812236769Sobrien if (level != 0) 1813236769Sobrien continue; 1814236769Sobrien while (ch == ' ' || ch == '\t') { 1815236769Sobrien ch = *line++; 1816236769Sobrien wasSpace = TRUE; 1817236769Sobrien } 1818255253Ssjg#ifdef SUNSHCMD 1819255253Ssjg if (ch == ':' && strncmp(line, "sh", 2) == 0) { 1820255253Ssjg line += 2; 1821255253Ssjg continue; 1822255253Ssjg } 1823255253Ssjg#endif 1824236769Sobrien if (ch == '=') 1825236769Sobrien return TRUE; 1826236769Sobrien if (*line == '=' && ISEQOPERATOR(ch)) 1827236769Sobrien return TRUE; 1828236769Sobrien if (wasSpace) 1829236769Sobrien return FALSE; 1830236769Sobrien } 1831236769Sobrien 1832236769Sobrien return FALSE; 1833236769Sobrien} 1834236769Sobrien 1835236769Sobrien/*- 1836236769Sobrien *--------------------------------------------------------------------- 1837236769Sobrien * Parse_DoVar -- 1838236769Sobrien * Take the variable assignment in the passed line and do it in the 1839236769Sobrien * global context. 1840236769Sobrien * 1841236769Sobrien * Note: There is a lexical ambiguity with assignment modifier characters 1842236769Sobrien * in variable names. This routine interprets the character before the = 1843236769Sobrien * as a modifier. Therefore, an assignment like 1844236769Sobrien * C++=/usr/bin/CC 1845236769Sobrien * is interpreted as "C+ +=" instead of "C++ =". 1846236769Sobrien * 1847236769Sobrien * Input: 1848236769Sobrien * line a line guaranteed to be a variable assignment. 1849236769Sobrien * This reduces error checks 1850236769Sobrien * ctxt Context in which to do the assignment 1851236769Sobrien * 1852236769Sobrien * Results: 1853236769Sobrien * none 1854236769Sobrien * 1855236769Sobrien * Side Effects: 1856236769Sobrien * the variable structure of the given variable name is altered in the 1857236769Sobrien * global context. 1858236769Sobrien *--------------------------------------------------------------------- 1859236769Sobrien */ 1860236769Sobrienvoid 1861236769SobrienParse_DoVar(char *line, GNode *ctxt) 1862236769Sobrien{ 1863236769Sobrien char *cp; /* pointer into line */ 1864236769Sobrien enum { 1865236769Sobrien VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL 1866236769Sobrien } type; /* Type of assignment */ 1867236769Sobrien char *opc; /* ptr to operator character to 1868236769Sobrien * null-terminate the variable name */ 1869236769Sobrien Boolean freeCp = FALSE; /* TRUE if cp needs to be freed, 1870236769Sobrien * i.e. if any variable expansion was 1871236769Sobrien * performed */ 1872236769Sobrien int depth; 1873236769Sobrien 1874236769Sobrien /* 1875236769Sobrien * Skip to variable name 1876236769Sobrien */ 1877236769Sobrien while ((*line == ' ') || (*line == '\t')) { 1878236769Sobrien line++; 1879236769Sobrien } 1880236769Sobrien 1881236769Sobrien /* 1882236769Sobrien * Skip to operator character, nulling out whitespace as we go 1883236769Sobrien * XXX Rather than counting () and {} we should look for $ and 1884236769Sobrien * then expand the variable. 1885236769Sobrien */ 1886319884Ssjg for (depth = 0, cp = line + 1; depth > 0 || *cp != '='; cp++) { 1887236769Sobrien if (*cp == '(' || *cp == '{') { 1888236769Sobrien depth++; 1889236769Sobrien continue; 1890236769Sobrien } 1891236769Sobrien if (*cp == ')' || *cp == '}') { 1892236769Sobrien depth--; 1893236769Sobrien continue; 1894236769Sobrien } 1895236769Sobrien if (depth == 0 && isspace ((unsigned char)*cp)) { 1896236769Sobrien *cp = '\0'; 1897236769Sobrien } 1898236769Sobrien } 1899236769Sobrien opc = cp-1; /* operator is the previous character */ 1900236769Sobrien *cp++ = '\0'; /* nuke the = */ 1901236769Sobrien 1902236769Sobrien /* 1903236769Sobrien * Check operator type 1904236769Sobrien */ 1905236769Sobrien switch (*opc) { 1906236769Sobrien case '+': 1907236769Sobrien type = VAR_APPEND; 1908236769Sobrien *opc = '\0'; 1909236769Sobrien break; 1910236769Sobrien 1911236769Sobrien case '?': 1912236769Sobrien /* 1913236769Sobrien * If the variable already has a value, we don't do anything. 1914236769Sobrien */ 1915236769Sobrien *opc = '\0'; 1916236769Sobrien if (Var_Exists(line, ctxt)) { 1917236769Sobrien return; 1918236769Sobrien } else { 1919236769Sobrien type = VAR_NORMAL; 1920236769Sobrien } 1921236769Sobrien break; 1922236769Sobrien 1923236769Sobrien case ':': 1924236769Sobrien type = VAR_SUBST; 1925236769Sobrien *opc = '\0'; 1926236769Sobrien break; 1927236769Sobrien 1928236769Sobrien case '!': 1929236769Sobrien type = VAR_SHELL; 1930236769Sobrien *opc = '\0'; 1931236769Sobrien break; 1932236769Sobrien 1933236769Sobrien default: 1934236769Sobrien#ifdef SUNSHCMD 1935236769Sobrien while (opc > line && *opc != ':') 1936236769Sobrien opc--; 1937236769Sobrien 1938236769Sobrien if (strncmp(opc, ":sh", 3) == 0) { 1939236769Sobrien type = VAR_SHELL; 1940236769Sobrien *opc = '\0'; 1941236769Sobrien break; 1942236769Sobrien } 1943236769Sobrien#endif 1944236769Sobrien type = VAR_NORMAL; 1945236769Sobrien break; 1946236769Sobrien } 1947236769Sobrien 1948236769Sobrien while (isspace ((unsigned char)*cp)) { 1949236769Sobrien cp++; 1950236769Sobrien } 1951236769Sobrien 1952236769Sobrien if (type == VAR_APPEND) { 1953236769Sobrien Var_Append(line, cp, ctxt); 1954236769Sobrien } else if (type == VAR_SUBST) { 1955236769Sobrien /* 1956236769Sobrien * Allow variables in the old value to be undefined, but leave their 1957236769Sobrien * invocation alone -- this is done by forcing oldVars to be false. 1958236769Sobrien * XXX: This can cause recursive variables, but that's not hard to do, 1959236769Sobrien * and this allows someone to do something like 1960236769Sobrien * 1961236769Sobrien * CFLAGS = $(.INCLUDES) 1962236769Sobrien * CFLAGS := -I.. $(CFLAGS) 1963236769Sobrien * 1964236769Sobrien * And not get an error. 1965236769Sobrien */ 1966236769Sobrien Boolean oldOldVars = oldVars; 1967236769Sobrien 1968236769Sobrien oldVars = FALSE; 1969236769Sobrien 1970236769Sobrien /* 1971236769Sobrien * make sure that we set the variable the first time to nothing 1972236769Sobrien * so that it gets substituted! 1973236769Sobrien */ 1974236769Sobrien if (!Var_Exists(line, ctxt)) 1975236769Sobrien Var_Set(line, "", ctxt, 0); 1976236769Sobrien 1977296637Ssjg cp = Var_Subst(NULL, cp, ctxt, VARF_WANTRES|VARF_ASSIGN); 1978236769Sobrien oldVars = oldOldVars; 1979236769Sobrien freeCp = TRUE; 1980236769Sobrien 1981236769Sobrien Var_Set(line, cp, ctxt, 0); 1982236769Sobrien } else if (type == VAR_SHELL) { 1983236769Sobrien char *res; 1984236769Sobrien const char *error; 1985236769Sobrien 1986236769Sobrien if (strchr(cp, '$') != NULL) { 1987236769Sobrien /* 1988236769Sobrien * There's a dollar sign in the command, so perform variable 1989236769Sobrien * expansion on the whole thing. The resulting string will need 1990236769Sobrien * freeing when we're done, so set freeCmd to TRUE. 1991236769Sobrien */ 1992296637Ssjg cp = Var_Subst(NULL, cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES); 1993236769Sobrien freeCp = TRUE; 1994236769Sobrien } 1995236769Sobrien 1996236769Sobrien res = Cmd_Exec(cp, &error); 1997236769Sobrien Var_Set(line, res, ctxt, 0); 1998236769Sobrien free(res); 1999236769Sobrien 2000236769Sobrien if (error) 2001236769Sobrien Parse_Error(PARSE_WARNING, error, cp); 2002236769Sobrien } else { 2003236769Sobrien /* 2004236769Sobrien * Normal assignment -- just do it. 2005236769Sobrien */ 2006236769Sobrien Var_Set(line, cp, ctxt, 0); 2007236769Sobrien } 2008236769Sobrien if (strcmp(line, MAKEOVERRIDES) == 0) 2009236769Sobrien Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */ 2010236769Sobrien else if (strcmp(line, ".CURDIR") == 0) { 2011236769Sobrien /* 2012236769Sobrien * Somone is being (too?) clever... 2013236769Sobrien * Let's pretend they know what they are doing and 2014236769Sobrien * re-initialize the 'cur' Path. 2015236769Sobrien */ 2016236769Sobrien Dir_InitCur(cp); 2017236769Sobrien Dir_SetPATH(); 2018236769Sobrien } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) { 2019236769Sobrien Job_SetPrefix(); 2020236769Sobrien } else if (strcmp(line, MAKE_EXPORTED) == 0) { 2021236769Sobrien Var_Export(cp, 0); 2022236769Sobrien } 2023236769Sobrien if (freeCp) 2024236769Sobrien free(cp); 2025236769Sobrien} 2026236769Sobrien 2027236769Sobrien 2028281812Ssjg/* 2029281812Ssjg * ParseMaybeSubMake -- 2030281812Ssjg * Scan the command string to see if it a possible submake node 2031281812Ssjg * Input: 2032281812Ssjg * cmd the command to scan 2033281812Ssjg * Results: 2034281812Ssjg * TRUE if the command is possibly a submake, FALSE if not. 2035281812Ssjg */ 2036281812Ssjgstatic Boolean 2037281812SsjgParseMaybeSubMake(const char *cmd) 2038281812Ssjg{ 2039281812Ssjg size_t i; 2040281812Ssjg static struct { 2041281812Ssjg const char *name; 2042281812Ssjg size_t len; 2043281812Ssjg } vals[] = { 2044281812Ssjg#define MKV(A) { A, sizeof(A) - 1 } 2045281812Ssjg MKV("${MAKE}"), 2046281812Ssjg MKV("${.MAKE}"), 2047281812Ssjg MKV("$(MAKE)"), 2048281812Ssjg MKV("$(.MAKE)"), 2049281812Ssjg MKV("make"), 2050281812Ssjg }; 2051281812Ssjg for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { 2052281812Ssjg char *ptr; 2053281812Ssjg if ((ptr = strstr(cmd, vals[i].name)) == NULL) 2054281812Ssjg continue; 2055281812Ssjg if ((ptr == cmd || !isalnum((unsigned char)ptr[-1])) 2056281812Ssjg && !isalnum((unsigned char)ptr[vals[i].len])) 2057281812Ssjg return TRUE; 2058281812Ssjg } 2059281812Ssjg return FALSE; 2060281812Ssjg} 2061281812Ssjg 2062236769Sobrien/*- 2063236769Sobrien * ParseAddCmd -- 2064236769Sobrien * Lst_ForEach function to add a command line to all targets 2065236769Sobrien * 2066236769Sobrien * Input: 2067236769Sobrien * gnp the node to which the command is to be added 2068236769Sobrien * cmd the command to add 2069236769Sobrien * 2070236769Sobrien * Results: 2071236769Sobrien * Always 0 2072236769Sobrien * 2073236769Sobrien * Side Effects: 2074281812Ssjg * A new element is added to the commands list of the node, 2075281812Ssjg * and the node can be marked as a submake node if the command is 2076281812Ssjg * determined to be that. 2077236769Sobrien */ 2078236769Sobrienstatic int 2079236769SobrienParseAddCmd(void *gnp, void *cmd) 2080236769Sobrien{ 2081236769Sobrien GNode *gn = (GNode *)gnp; 2082236769Sobrien 2083236769Sobrien /* Add to last (ie current) cohort for :: targets */ 2084236769Sobrien if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) 2085236769Sobrien gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); 2086236769Sobrien 2087236769Sobrien /* if target already supplied, ignore commands */ 2088236769Sobrien if (!(gn->type & OP_HAS_COMMANDS)) { 2089236769Sobrien (void)Lst_AtEnd(gn->commands, cmd); 2090281812Ssjg if (ParseMaybeSubMake(cmd)) 2091281812Ssjg gn->type |= OP_SUBMAKE; 2092236769Sobrien ParseMark(gn); 2093236769Sobrien } else { 2094236769Sobrien#ifdef notyet 2095236769Sobrien /* XXX: We cannot do this until we fix the tree */ 2096236769Sobrien (void)Lst_AtEnd(gn->commands, cmd); 2097236769Sobrien Parse_Error(PARSE_WARNING, 2098236769Sobrien "overriding commands for target \"%s\"; " 2099236769Sobrien "previous commands defined at %s: %d ignored", 2100236769Sobrien gn->name, gn->fname, gn->lineno); 2101236769Sobrien#else 2102236769Sobrien Parse_Error(PARSE_WARNING, 2103236769Sobrien "duplicate script for target \"%s\" ignored", 2104236769Sobrien gn->name); 2105236769Sobrien ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING, 2106236769Sobrien "using previous script for \"%s\" defined here", 2107236769Sobrien gn->name); 2108236769Sobrien#endif 2109236769Sobrien } 2110236769Sobrien return(0); 2111236769Sobrien} 2112236769Sobrien 2113236769Sobrien/*- 2114236769Sobrien *----------------------------------------------------------------------- 2115236769Sobrien * ParseHasCommands -- 2116236769Sobrien * Callback procedure for Parse_File when destroying the list of 2117236769Sobrien * targets on the last dependency line. Marks a target as already 2118236769Sobrien * having commands if it does, to keep from having shell commands 2119236769Sobrien * on multiple dependency lines. 2120236769Sobrien * 2121236769Sobrien * Input: 2122236769Sobrien * gnp Node to examine 2123236769Sobrien * 2124236769Sobrien * Results: 2125236769Sobrien * None 2126236769Sobrien * 2127236769Sobrien * Side Effects: 2128236769Sobrien * OP_HAS_COMMANDS may be set for the target. 2129236769Sobrien * 2130236769Sobrien *----------------------------------------------------------------------- 2131236769Sobrien */ 2132236769Sobrienstatic void 2133236769SobrienParseHasCommands(void *gnp) 2134236769Sobrien{ 2135236769Sobrien GNode *gn = (GNode *)gnp; 2136236769Sobrien if (!Lst_IsEmpty(gn->commands)) { 2137236769Sobrien gn->type |= OP_HAS_COMMANDS; 2138236769Sobrien } 2139236769Sobrien} 2140236769Sobrien 2141236769Sobrien/*- 2142236769Sobrien *----------------------------------------------------------------------- 2143236769Sobrien * Parse_AddIncludeDir -- 2144236769Sobrien * Add a directory to the path searched for included makefiles 2145236769Sobrien * bracketed by double-quotes. Used by functions in main.c 2146236769Sobrien * 2147236769Sobrien * Input: 2148236769Sobrien * dir The name of the directory to add 2149236769Sobrien * 2150236769Sobrien * Results: 2151236769Sobrien * None. 2152236769Sobrien * 2153236769Sobrien * Side Effects: 2154236769Sobrien * The directory is appended to the list. 2155236769Sobrien * 2156236769Sobrien *----------------------------------------------------------------------- 2157236769Sobrien */ 2158236769Sobrienvoid 2159236769SobrienParse_AddIncludeDir(char *dir) 2160236769Sobrien{ 2161236769Sobrien (void)Dir_AddDir(parseIncPath, dir); 2162236769Sobrien} 2163236769Sobrien 2164236769Sobrien/*- 2165236769Sobrien *--------------------------------------------------------------------- 2166236769Sobrien * ParseDoInclude -- 2167236769Sobrien * Push to another file. 2168236769Sobrien * 2169236769Sobrien * The input is the line minus the `.'. A file spec is a string 2170236769Sobrien * enclosed in <> or "". The former is looked for only in sysIncPath. 2171236769Sobrien * The latter in . and the directories specified by -I command line 2172236769Sobrien * options 2173236769Sobrien * 2174236769Sobrien * Results: 2175236769Sobrien * None 2176236769Sobrien * 2177236769Sobrien * Side Effects: 2178236769Sobrien * A structure is added to the includes Lst and readProc, lineno, 2179236769Sobrien * fname and curFILE are altered for the new file 2180236769Sobrien *--------------------------------------------------------------------- 2181236769Sobrien */ 2182236769Sobrien 2183236769Sobrienstatic void 2184296637SsjgParse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent) 2185236769Sobrien{ 2186236769Sobrien struct loadedfile *lf; 2187236769Sobrien char *fullname; /* full pathname of file */ 2188236769Sobrien char *newName; 2189236769Sobrien char *prefEnd, *incdir; 2190236769Sobrien int fd; 2191236769Sobrien int i; 2192236769Sobrien 2193236769Sobrien /* 2194236769Sobrien * Now we know the file's name and its search path, we attempt to 2195236769Sobrien * find the durn thing. A return of NULL indicates the file don't 2196236769Sobrien * exist. 2197236769Sobrien */ 2198236769Sobrien fullname = file[0] == '/' ? bmake_strdup(file) : NULL; 2199236769Sobrien 2200236769Sobrien if (fullname == NULL && !isSystem) { 2201236769Sobrien /* 2202236769Sobrien * Include files contained in double-quotes are first searched for 2203236769Sobrien * relative to the including file's location. We don't want to 2204236769Sobrien * cd there, of course, so we just tack on the old file's 2205236769Sobrien * leading path components and call Dir_FindFile to see if 2206236769Sobrien * we can locate the beast. 2207236769Sobrien */ 2208236769Sobrien 2209236769Sobrien incdir = bmake_strdup(curFile->fname); 2210236769Sobrien prefEnd = strrchr(incdir, '/'); 2211236769Sobrien if (prefEnd != NULL) { 2212236769Sobrien *prefEnd = '\0'; 2213236769Sobrien /* Now do lexical processing of leading "../" on the filename */ 2214236769Sobrien for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { 2215236769Sobrien prefEnd = strrchr(incdir + 1, '/'); 2216236769Sobrien if (prefEnd == NULL || strcmp(prefEnd, "/..") == 0) 2217236769Sobrien break; 2218236769Sobrien *prefEnd = '\0'; 2219236769Sobrien } 2220236769Sobrien newName = str_concat(incdir, file + i, STR_ADDSLASH); 2221236769Sobrien fullname = Dir_FindFile(newName, parseIncPath); 2222236769Sobrien if (fullname == NULL) 2223236769Sobrien fullname = Dir_FindFile(newName, dirSearchPath); 2224236769Sobrien free(newName); 2225236769Sobrien } 2226236769Sobrien free(incdir); 2227236769Sobrien 2228236769Sobrien if (fullname == NULL) { 2229236769Sobrien /* 2230236769Sobrien * Makefile wasn't found in same directory as included makefile. 2231236769Sobrien * Search for it first on the -I search path, 2232236769Sobrien * then on the .PATH search path, if not found in a -I directory. 2233236769Sobrien * If we have a suffix specific path we should use that. 2234236769Sobrien */ 2235236769Sobrien char *suff; 2236236769Sobrien Lst suffPath = NULL; 2237236769Sobrien 2238236769Sobrien if ((suff = strrchr(file, '.'))) { 2239236769Sobrien suffPath = Suff_GetPath(suff); 2240236769Sobrien if (suffPath != NULL) { 2241236769Sobrien fullname = Dir_FindFile(file, suffPath); 2242236769Sobrien } 2243236769Sobrien } 2244236769Sobrien if (fullname == NULL) { 2245236769Sobrien fullname = Dir_FindFile(file, parseIncPath); 2246236769Sobrien if (fullname == NULL) { 2247236769Sobrien fullname = Dir_FindFile(file, dirSearchPath); 2248236769Sobrien } 2249236769Sobrien } 2250236769Sobrien } 2251236769Sobrien } 2252236769Sobrien 2253236769Sobrien /* Looking for a system file or file still not found */ 2254236769Sobrien if (fullname == NULL) { 2255236769Sobrien /* 2256236769Sobrien * Look for it on the system path 2257236769Sobrien */ 2258236769Sobrien fullname = Dir_FindFile(file, 2259236769Sobrien Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); 2260236769Sobrien } 2261236769Sobrien 2262236769Sobrien if (fullname == NULL) { 2263236769Sobrien if (!silent) 2264236769Sobrien Parse_Error(PARSE_FATAL, "Could not find %s", file); 2265236769Sobrien return; 2266236769Sobrien } 2267236769Sobrien 2268236769Sobrien /* Actually open the file... */ 2269236769Sobrien fd = open(fullname, O_RDONLY); 2270236769Sobrien if (fd == -1) { 2271236769Sobrien if (!silent) 2272236769Sobrien Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); 2273236769Sobrien free(fullname); 2274236769Sobrien return; 2275236769Sobrien } 2276236769Sobrien 2277236769Sobrien /* load it */ 2278236769Sobrien lf = loadfile(fullname, fd); 2279236769Sobrien 2280268437Ssjg ParseSetIncludedFile(); 2281236769Sobrien /* Start reading from this file next */ 2282236769Sobrien Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf); 2283236769Sobrien curFile->lf = lf; 2284296637Ssjg if (depinc) 2285296637Ssjg doing_depend = depinc; /* only turn it on */ 2286236769Sobrien} 2287236769Sobrien 2288236769Sobrienstatic void 2289236769SobrienParseDoInclude(char *line) 2290236769Sobrien{ 2291236769Sobrien char endc; /* the character which ends the file spec */ 2292236769Sobrien char *cp; /* current position in file spec */ 2293236769Sobrien int silent = (*line != 'i') ? 1 : 0; 2294236769Sobrien char *file = &line[7 + silent]; 2295236769Sobrien 2296236769Sobrien /* Skip to delimiter character so we know where to look */ 2297236769Sobrien while (*file == ' ' || *file == '\t') 2298236769Sobrien file++; 2299236769Sobrien 2300236769Sobrien if (*file != '"' && *file != '<') { 2301236769Sobrien Parse_Error(PARSE_FATAL, 2302236769Sobrien ".include filename must be delimited by '\"' or '<'"); 2303236769Sobrien return; 2304236769Sobrien } 2305236769Sobrien 2306236769Sobrien /* 2307236769Sobrien * Set the search path on which to find the include file based on the 2308236769Sobrien * characters which bracket its name. Angle-brackets imply it's 2309236769Sobrien * a system Makefile while double-quotes imply it's a user makefile 2310236769Sobrien */ 2311236769Sobrien if (*file == '<') { 2312236769Sobrien endc = '>'; 2313236769Sobrien } else { 2314236769Sobrien endc = '"'; 2315236769Sobrien } 2316236769Sobrien 2317236769Sobrien /* Skip to matching delimiter */ 2318236769Sobrien for (cp = ++file; *cp && *cp != endc; cp++) 2319236769Sobrien continue; 2320236769Sobrien 2321236769Sobrien if (*cp != endc) { 2322236769Sobrien Parse_Error(PARSE_FATAL, 2323236769Sobrien "Unclosed %cinclude filename. '%c' expected", 2324236769Sobrien '.', endc); 2325236769Sobrien return; 2326236769Sobrien } 2327236769Sobrien *cp = '\0'; 2328236769Sobrien 2329236769Sobrien /* 2330236769Sobrien * Substitute for any variables in the file name before trying to 2331236769Sobrien * find the thing. 2332236769Sobrien */ 2333296637Ssjg file = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES); 2334236769Sobrien 2335296637Ssjg Parse_include_file(file, endc == '>', (*line == 'd'), silent); 2336236769Sobrien free(file); 2337236769Sobrien} 2338236769Sobrien 2339236769Sobrien 2340236769Sobrien/*- 2341236769Sobrien *--------------------------------------------------------------------- 2342268437Ssjg * ParseSetIncludedFile -- 2343268437Ssjg * Set the .INCLUDEDFROMFILE variable to the contents of .PARSEFILE 2344268437Ssjg * and the .INCLUDEDFROMDIR variable to the contents of .PARSEDIR 2345268437Ssjg * 2346268437Ssjg * Results: 2347268437Ssjg * None 2348268437Ssjg * 2349268437Ssjg * Side Effects: 2350268437Ssjg * The .INCLUDEDFROMFILE variable is overwritten by the contents 2351268437Ssjg * of .PARSEFILE and the .INCLUDEDFROMDIR variable is overwriten 2352268437Ssjg * by the contents of .PARSEDIR 2353268437Ssjg *--------------------------------------------------------------------- 2354268437Ssjg */ 2355268437Ssjgstatic void 2356268437SsjgParseSetIncludedFile(void) 2357268437Ssjg{ 2358268437Ssjg char *pf, *fp = NULL; 2359268437Ssjg char *pd, *dp = NULL; 2360268437Ssjg 2361268437Ssjg pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &fp); 2362268437Ssjg Var_Set(".INCLUDEDFROMFILE", pf, VAR_GLOBAL, 0); 2363268437Ssjg pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &dp); 2364268437Ssjg Var_Set(".INCLUDEDFROMDIR", pd, VAR_GLOBAL, 0); 2365268437Ssjg 2366268437Ssjg if (DEBUG(PARSE)) 2367268437Ssjg fprintf(debug_file, "%s: ${.INCLUDEDFROMDIR} = `%s' " 2368268437Ssjg "${.INCLUDEDFROMFILE} = `%s'\n", __func__, pd, pf); 2369268437Ssjg 2370296637Ssjg free(fp); 2371296637Ssjg free(dp); 2372268437Ssjg} 2373268437Ssjg/*- 2374268437Ssjg *--------------------------------------------------------------------- 2375236769Sobrien * ParseSetParseFile -- 2376236769Sobrien * Set the .PARSEDIR and .PARSEFILE variables to the dirname and 2377236769Sobrien * basename of the given filename 2378236769Sobrien * 2379236769Sobrien * Results: 2380236769Sobrien * None 2381236769Sobrien * 2382236769Sobrien * Side Effects: 2383236769Sobrien * The .PARSEDIR and .PARSEFILE variables are overwritten by the 2384236769Sobrien * dirname and basename of the given filename. 2385236769Sobrien *--------------------------------------------------------------------- 2386236769Sobrien */ 2387236769Sobrienstatic void 2388236769SobrienParseSetParseFile(const char *filename) 2389236769Sobrien{ 2390236769Sobrien char *slash, *dirname; 2391236769Sobrien const char *pd, *pf; 2392236769Sobrien int len; 2393236769Sobrien 2394236769Sobrien slash = strrchr(filename, '/'); 2395236769Sobrien if (slash == NULL) { 2396236769Sobrien Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL, 0); 2397236769Sobrien Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL, 0); 2398236769Sobrien dirname= NULL; 2399236769Sobrien } else { 2400236769Sobrien len = slash - filename; 2401236769Sobrien dirname = bmake_malloc(len + 1); 2402236769Sobrien memcpy(dirname, filename, len); 2403236769Sobrien dirname[len] = '\0'; 2404236769Sobrien Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL, 0); 2405236769Sobrien Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL, 0); 2406236769Sobrien } 2407236769Sobrien if (DEBUG(PARSE)) 2408268437Ssjg fprintf(debug_file, "%s: ${.PARSEDIR} = `%s' ${.PARSEFILE} = `%s'\n", 2409268437Ssjg __func__, pd, pf); 2410236769Sobrien free(dirname); 2411236769Sobrien} 2412236769Sobrien 2413236769Sobrien/* 2414236769Sobrien * Track the makefiles we read - so makefiles can 2415236769Sobrien * set dependencies on them. 2416236769Sobrien * Avoid adding anything more than once. 2417236769Sobrien */ 2418236769Sobrien 2419236769Sobrienstatic void 2420236769SobrienParseTrackInput(const char *name) 2421236769Sobrien{ 2422236769Sobrien char *old; 2423291342Ssjg char *ep; 2424236769Sobrien char *fp = NULL; 2425236769Sobrien size_t name_len = strlen(name); 2426236769Sobrien 2427236769Sobrien old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); 2428236769Sobrien if (old) { 2429291342Ssjg ep = old + strlen(old) - name_len; 2430236769Sobrien /* does it contain name? */ 2431236769Sobrien for (; old != NULL; old = strchr(old, ' ')) { 2432236769Sobrien if (*old == ' ') 2433236769Sobrien old++; 2434291342Ssjg if (old >= ep) 2435291342Ssjg break; /* cannot contain name */ 2436236769Sobrien if (memcmp(old, name, name_len) == 0 2437236769Sobrien && (old[name_len] == 0 || old[name_len] == ' ')) 2438236769Sobrien goto cleanup; 2439236769Sobrien } 2440236769Sobrien } 2441236769Sobrien Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL); 2442236769Sobrien cleanup: 2443236769Sobrien if (fp) { 2444236769Sobrien free(fp); 2445236769Sobrien } 2446236769Sobrien} 2447236769Sobrien 2448236769Sobrien 2449236769Sobrien/*- 2450236769Sobrien *--------------------------------------------------------------------- 2451236769Sobrien * Parse_setInput -- 2452236769Sobrien * Start Parsing from the given source 2453236769Sobrien * 2454236769Sobrien * Results: 2455236769Sobrien * None 2456236769Sobrien * 2457236769Sobrien * Side Effects: 2458236769Sobrien * A structure is added to the includes Lst and readProc, lineno, 2459236769Sobrien * fname and curFile are altered for the new file 2460236769Sobrien *--------------------------------------------------------------------- 2461236769Sobrien */ 2462236769Sobrienvoid 2463236769SobrienParse_SetInput(const char *name, int line, int fd, 2464236769Sobrien char *(*nextbuf)(void *, size_t *), void *arg) 2465236769Sobrien{ 2466236769Sobrien char *buf; 2467236769Sobrien size_t len; 2468236769Sobrien 2469236769Sobrien if (name == NULL) 2470236769Sobrien name = curFile->fname; 2471236769Sobrien else 2472236769Sobrien ParseTrackInput(name); 2473236769Sobrien 2474236769Sobrien if (DEBUG(PARSE)) 2475268437Ssjg fprintf(debug_file, "%s: file %s, line %d, fd %d, nextbuf %p, arg %p\n", 2476268437Ssjg __func__, name, line, fd, nextbuf, arg); 2477236769Sobrien 2478236769Sobrien if (fd == -1 && nextbuf == NULL) 2479236769Sobrien /* sanity */ 2480236769Sobrien return; 2481236769Sobrien 2482236769Sobrien if (curFile != NULL) 2483236769Sobrien /* Save exiting file info */ 2484236769Sobrien Lst_AtFront(includes, curFile); 2485236769Sobrien 2486236769Sobrien /* Allocate and fill in new structure */ 2487236769Sobrien curFile = bmake_malloc(sizeof *curFile); 2488236769Sobrien 2489236769Sobrien /* 2490236769Sobrien * Once the previous state has been saved, we can get down to reading 2491236769Sobrien * the new file. We set up the name of the file to be the absolute 2492236769Sobrien * name of the include file so error messages refer to the right 2493236769Sobrien * place. 2494236769Sobrien */ 2495251958Ssjg curFile->fname = bmake_strdup(name); 2496236769Sobrien curFile->lineno = line; 2497236769Sobrien curFile->first_lineno = line; 2498236769Sobrien curFile->nextbuf = nextbuf; 2499236769Sobrien curFile->nextbuf_arg = arg; 2500236769Sobrien curFile->lf = NULL; 2501296637Ssjg curFile->depending = doing_depend; /* restore this on EOF */ 2502236769Sobrien 2503236769Sobrien assert(nextbuf != NULL); 2504236769Sobrien 2505236769Sobrien /* Get first block of input data */ 2506236769Sobrien buf = curFile->nextbuf(curFile->nextbuf_arg, &len); 2507236769Sobrien if (buf == NULL) { 2508236769Sobrien /* Was all a waste of time ... */ 2509251958Ssjg if (curFile->fname) 2510251958Ssjg free(curFile->fname); 2511236769Sobrien free(curFile); 2512236769Sobrien return; 2513236769Sobrien } 2514236769Sobrien curFile->P_str = buf; 2515236769Sobrien curFile->P_ptr = buf; 2516236769Sobrien curFile->P_end = buf+len; 2517236769Sobrien 2518236769Sobrien curFile->cond_depth = Cond_save_depth(); 2519236769Sobrien ParseSetParseFile(name); 2520236769Sobrien} 2521236769Sobrien 2522236769Sobrien#ifdef SYSVINCLUDE 2523236769Sobrien/*- 2524236769Sobrien *--------------------------------------------------------------------- 2525236769Sobrien * ParseTraditionalInclude -- 2526236769Sobrien * Push to another file. 2527236769Sobrien * 2528236769Sobrien * The input is the current line. The file name(s) are 2529236769Sobrien * following the "include". 2530236769Sobrien * 2531236769Sobrien * Results: 2532236769Sobrien * None 2533236769Sobrien * 2534236769Sobrien * Side Effects: 2535236769Sobrien * A structure is added to the includes Lst and readProc, lineno, 2536236769Sobrien * fname and curFILE are altered for the new file 2537236769Sobrien *--------------------------------------------------------------------- 2538236769Sobrien */ 2539236769Sobrienstatic void 2540236769SobrienParseTraditionalInclude(char *line) 2541236769Sobrien{ 2542236769Sobrien char *cp; /* current position in file spec */ 2543236769Sobrien int done = 0; 2544236769Sobrien int silent = (line[0] != 'i') ? 1 : 0; 2545236769Sobrien char *file = &line[silent + 7]; 2546236769Sobrien char *all_files; 2547236769Sobrien 2548236769Sobrien if (DEBUG(PARSE)) { 2549268437Ssjg fprintf(debug_file, "%s: %s\n", __func__, file); 2550236769Sobrien } 2551236769Sobrien 2552236769Sobrien /* 2553236769Sobrien * Skip over whitespace 2554236769Sobrien */ 2555236769Sobrien while (isspace((unsigned char)*file)) 2556236769Sobrien file++; 2557236769Sobrien 2558236769Sobrien /* 2559236769Sobrien * Substitute for any variables in the file name before trying to 2560236769Sobrien * find the thing. 2561236769Sobrien */ 2562296637Ssjg all_files = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES); 2563236769Sobrien 2564236769Sobrien if (*file == '\0') { 2565236769Sobrien Parse_Error(PARSE_FATAL, 2566236769Sobrien "Filename missing from \"include\""); 2567319884Ssjg goto out; 2568236769Sobrien } 2569236769Sobrien 2570236769Sobrien for (file = all_files; !done; file = cp + 1) { 2571236769Sobrien /* Skip to end of line or next whitespace */ 2572236769Sobrien for (cp = file; *cp && !isspace((unsigned char) *cp); cp++) 2573236769Sobrien continue; 2574236769Sobrien 2575236769Sobrien if (*cp) 2576236769Sobrien *cp = '\0'; 2577236769Sobrien else 2578236769Sobrien done = 1; 2579236769Sobrien 2580296637Ssjg Parse_include_file(file, FALSE, FALSE, silent); 2581236769Sobrien } 2582319884Ssjgout: 2583236769Sobrien free(all_files); 2584236769Sobrien} 2585236769Sobrien#endif 2586236769Sobrien 2587236769Sobrien#ifdef GMAKEEXPORT 2588236769Sobrien/*- 2589236769Sobrien *--------------------------------------------------------------------- 2590236769Sobrien * ParseGmakeExport -- 2591236769Sobrien * Parse export <variable>=<value> 2592236769Sobrien * 2593236769Sobrien * And set the environment with it. 2594236769Sobrien * 2595236769Sobrien * Results: 2596236769Sobrien * None 2597236769Sobrien * 2598236769Sobrien * Side Effects: 2599236769Sobrien * None 2600236769Sobrien *--------------------------------------------------------------------- 2601236769Sobrien */ 2602236769Sobrienstatic void 2603236769SobrienParseGmakeExport(char *line) 2604236769Sobrien{ 2605236769Sobrien char *variable = &line[6]; 2606236769Sobrien char *value; 2607236769Sobrien 2608236769Sobrien if (DEBUG(PARSE)) { 2609268437Ssjg fprintf(debug_file, "%s: %s\n", __func__, variable); 2610236769Sobrien } 2611236769Sobrien 2612236769Sobrien /* 2613236769Sobrien * Skip over whitespace 2614236769Sobrien */ 2615236769Sobrien while (isspace((unsigned char)*variable)) 2616236769Sobrien variable++; 2617236769Sobrien 2618236769Sobrien for (value = variable; *value && *value != '='; value++) 2619236769Sobrien continue; 2620236769Sobrien 2621236769Sobrien if (*value != '=') { 2622236769Sobrien Parse_Error(PARSE_FATAL, 2623236769Sobrien "Variable/Value missing from \"export\""); 2624236769Sobrien return; 2625236769Sobrien } 2626249033Ssjg *value++ = '\0'; /* terminate variable */ 2627236769Sobrien 2628236769Sobrien /* 2629236769Sobrien * Expand the value before putting it in the environment. 2630236769Sobrien */ 2631296637Ssjg value = Var_Subst(NULL, value, VAR_CMD, VARF_WANTRES); 2632236769Sobrien setenv(variable, value, 1); 2633319884Ssjg free(value); 2634236769Sobrien} 2635236769Sobrien#endif 2636236769Sobrien 2637236769Sobrien/*- 2638236769Sobrien *--------------------------------------------------------------------- 2639236769Sobrien * ParseEOF -- 2640236769Sobrien * Called when EOF is reached in the current file. If we were reading 2641236769Sobrien * an include file, the includes stack is popped and things set up 2642236769Sobrien * to go back to reading the previous file at the previous location. 2643236769Sobrien * 2644236769Sobrien * Results: 2645236769Sobrien * CONTINUE if there's more to do. DONE if not. 2646236769Sobrien * 2647236769Sobrien * Side Effects: 2648236769Sobrien * The old curFILE, is closed. The includes list is shortened. 2649236769Sobrien * lineno, curFILE, and fname are changed if CONTINUE is returned. 2650236769Sobrien *--------------------------------------------------------------------- 2651236769Sobrien */ 2652236769Sobrienstatic int 2653236769SobrienParseEOF(void) 2654236769Sobrien{ 2655236769Sobrien char *ptr; 2656236769Sobrien size_t len; 2657236769Sobrien 2658236769Sobrien assert(curFile->nextbuf != NULL); 2659236769Sobrien 2660296637Ssjg doing_depend = curFile->depending; /* restore this */ 2661236769Sobrien /* get next input buffer, if any */ 2662236769Sobrien ptr = curFile->nextbuf(curFile->nextbuf_arg, &len); 2663236769Sobrien curFile->P_ptr = ptr; 2664236769Sobrien curFile->P_str = ptr; 2665236769Sobrien curFile->P_end = ptr + len; 2666236769Sobrien curFile->lineno = curFile->first_lineno; 2667236769Sobrien if (ptr != NULL) { 2668236769Sobrien /* Iterate again */ 2669236769Sobrien return CONTINUE; 2670236769Sobrien } 2671236769Sobrien 2672236769Sobrien /* Ensure the makefile (or loop) didn't have mismatched conditionals */ 2673236769Sobrien Cond_restore_depth(curFile->cond_depth); 2674236769Sobrien 2675236769Sobrien if (curFile->lf != NULL) { 2676236769Sobrien loadedfile_destroy(curFile->lf); 2677236769Sobrien curFile->lf = NULL; 2678236769Sobrien } 2679236769Sobrien 2680236769Sobrien /* Dispose of curFile info */ 2681236769Sobrien /* Leak curFile->fname because all the gnodes have pointers to it */ 2682236769Sobrien free(curFile->P_str); 2683236769Sobrien free(curFile); 2684236769Sobrien 2685236769Sobrien curFile = Lst_DeQueue(includes); 2686236769Sobrien 2687236769Sobrien if (curFile == NULL) { 2688236769Sobrien /* We've run out of input */ 2689236769Sobrien Var_Delete(".PARSEDIR", VAR_GLOBAL); 2690236769Sobrien Var_Delete(".PARSEFILE", VAR_GLOBAL); 2691268437Ssjg Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); 2692268437Ssjg Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); 2693236769Sobrien return DONE; 2694236769Sobrien } 2695236769Sobrien 2696236769Sobrien if (DEBUG(PARSE)) 2697236769Sobrien fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n", 2698236769Sobrien curFile->fname, curFile->lineno); 2699236769Sobrien 2700236769Sobrien /* Restore the PARSEDIR/PARSEFILE variables */ 2701236769Sobrien ParseSetParseFile(curFile->fname); 2702236769Sobrien return (CONTINUE); 2703236769Sobrien} 2704236769Sobrien 2705236769Sobrien#define PARSE_RAW 1 2706236769Sobrien#define PARSE_SKIP 2 2707236769Sobrien 2708236769Sobrienstatic char * 2709236769SobrienParseGetLine(int flags, int *length) 2710236769Sobrien{ 2711236769Sobrien IFile *cf = curFile; 2712236769Sobrien char *ptr; 2713236769Sobrien char ch; 2714236769Sobrien char *line; 2715236769Sobrien char *line_end; 2716236769Sobrien char *escaped; 2717236769Sobrien char *comment; 2718236769Sobrien char *tp; 2719236769Sobrien 2720236769Sobrien /* Loop through blank lines and comment lines */ 2721236769Sobrien for (;;) { 2722236769Sobrien cf->lineno++; 2723236769Sobrien line = cf->P_ptr; 2724236769Sobrien ptr = line; 2725236769Sobrien line_end = line; 2726236769Sobrien escaped = NULL; 2727236769Sobrien comment = NULL; 2728236769Sobrien for (;;) { 2729236769Sobrien if (cf->P_end != NULL && ptr == cf->P_end) { 2730236769Sobrien /* end of buffer */ 2731236769Sobrien ch = 0; 2732236769Sobrien break; 2733236769Sobrien } 2734236769Sobrien ch = *ptr; 2735236769Sobrien if (ch == 0 || (ch == '\\' && ptr[1] == 0)) { 2736236769Sobrien if (cf->P_end == NULL) 2737236769Sobrien /* End of string (aka for loop) data */ 2738236769Sobrien break; 2739254194Ssjg /* see if there is more we can parse */ 2740254194Ssjg while (ptr++ < cf->P_end) { 2741254194Ssjg if ((ch = *ptr) == '\n') { 2742254194Ssjg if (ptr > line && ptr[-1] == '\\') 2743254194Ssjg continue; 2744254194Ssjg Parse_Error(PARSE_WARNING, 2745254194Ssjg "Zero byte read from file, skipping rest of line."); 2746254194Ssjg break; 2747254194Ssjg } 2748254194Ssjg } 2749236769Sobrien if (cf->nextbuf != NULL) { 2750236769Sobrien /* 2751236769Sobrien * End of this buffer; return EOF and outer logic 2752236769Sobrien * will get the next one. (eww) 2753236769Sobrien */ 2754236769Sobrien break; 2755236769Sobrien } 2756236769Sobrien Parse_Error(PARSE_FATAL, "Zero byte read from file"); 2757236769Sobrien return NULL; 2758236769Sobrien } 2759236769Sobrien 2760236769Sobrien if (ch == '\\') { 2761236769Sobrien /* Don't treat next character as special, remember first one */ 2762236769Sobrien if (escaped == NULL) 2763236769Sobrien escaped = ptr; 2764236769Sobrien if (ptr[1] == '\n') 2765236769Sobrien cf->lineno++; 2766236769Sobrien ptr += 2; 2767236769Sobrien line_end = ptr; 2768236769Sobrien continue; 2769236769Sobrien } 2770236769Sobrien if (ch == '#' && comment == NULL) { 2771236769Sobrien /* Remember first '#' for comment stripping */ 2772236769Sobrien /* Unless previous char was '[', as in modifier :[#] */ 2773236769Sobrien if (!(ptr > line && ptr[-1] == '[')) 2774236769Sobrien comment = line_end; 2775236769Sobrien } 2776236769Sobrien ptr++; 2777236769Sobrien if (ch == '\n') 2778236769Sobrien break; 2779236769Sobrien if (!isspace((unsigned char)ch)) 2780236769Sobrien /* We are not interested in trailing whitespace */ 2781236769Sobrien line_end = ptr; 2782236769Sobrien } 2783236769Sobrien 2784236769Sobrien /* Save next 'to be processed' location */ 2785236769Sobrien cf->P_ptr = ptr; 2786236769Sobrien 2787236769Sobrien /* Check we have a non-comment, non-blank line */ 2788236769Sobrien if (line_end == line || comment == line) { 2789236769Sobrien if (ch == 0) 2790236769Sobrien /* At end of file */ 2791236769Sobrien return NULL; 2792236769Sobrien /* Parse another line */ 2793236769Sobrien continue; 2794236769Sobrien } 2795236769Sobrien 2796236769Sobrien /* We now have a line of data */ 2797236769Sobrien *line_end = 0; 2798236769Sobrien 2799236769Sobrien if (flags & PARSE_RAW) { 2800236769Sobrien /* Leave '\' (etc) in line buffer (eg 'for' lines) */ 2801236769Sobrien *length = line_end - line; 2802236769Sobrien return line; 2803236769Sobrien } 2804236769Sobrien 2805236769Sobrien if (flags & PARSE_SKIP) { 2806236769Sobrien /* Completely ignore non-directives */ 2807236769Sobrien if (line[0] != '.') 2808236769Sobrien continue; 2809236769Sobrien /* We could do more of the .else/.elif/.endif checks here */ 2810236769Sobrien } 2811236769Sobrien break; 2812236769Sobrien } 2813236769Sobrien 2814236769Sobrien /* Brutally ignore anything after a non-escaped '#' in non-commands */ 2815236769Sobrien if (comment != NULL && line[0] != '\t') { 2816236769Sobrien line_end = comment; 2817236769Sobrien *line_end = 0; 2818236769Sobrien } 2819236769Sobrien 2820236769Sobrien /* If we didn't see a '\\' then the in-situ data is fine */ 2821236769Sobrien if (escaped == NULL) { 2822236769Sobrien *length = line_end - line; 2823236769Sobrien return line; 2824236769Sobrien } 2825236769Sobrien 2826236769Sobrien /* Remove escapes from '\n' and '#' */ 2827236769Sobrien tp = ptr = escaped; 2828236769Sobrien escaped = line; 2829236769Sobrien for (; ; *tp++ = ch) { 2830236769Sobrien ch = *ptr++; 2831236769Sobrien if (ch != '\\') { 2832236769Sobrien if (ch == 0) 2833236769Sobrien break; 2834236769Sobrien continue; 2835236769Sobrien } 2836236769Sobrien 2837236769Sobrien ch = *ptr++; 2838236769Sobrien if (ch == 0) { 2839236769Sobrien /* Delete '\\' at end of buffer */ 2840236769Sobrien tp--; 2841236769Sobrien break; 2842236769Sobrien } 2843236769Sobrien 2844236769Sobrien if (ch == '#' && line[0] != '\t') 2845236769Sobrien /* Delete '\\' from before '#' on non-command lines */ 2846236769Sobrien continue; 2847236769Sobrien 2848236769Sobrien if (ch != '\n') { 2849236769Sobrien /* Leave '\\' in buffer for later */ 2850236769Sobrien *tp++ = '\\'; 2851236769Sobrien /* Make sure we don't delete an escaped ' ' from the line end */ 2852236769Sobrien escaped = tp + 1; 2853236769Sobrien continue; 2854236769Sobrien } 2855236769Sobrien 2856236769Sobrien /* Escaped '\n' replace following whitespace with a single ' ' */ 2857236769Sobrien while (ptr[0] == ' ' || ptr[0] == '\t') 2858236769Sobrien ptr++; 2859236769Sobrien ch = ' '; 2860236769Sobrien } 2861236769Sobrien 2862236769Sobrien /* Delete any trailing spaces - eg from empty continuations */ 2863236769Sobrien while (tp > escaped && isspace((unsigned char)tp[-1])) 2864236769Sobrien tp--; 2865236769Sobrien 2866236769Sobrien *tp = 0; 2867236769Sobrien *length = tp - line; 2868236769Sobrien return line; 2869236769Sobrien} 2870236769Sobrien 2871236769Sobrien/*- 2872236769Sobrien *--------------------------------------------------------------------- 2873236769Sobrien * ParseReadLine -- 2874236769Sobrien * Read an entire line from the input file. Called only by Parse_File. 2875236769Sobrien * 2876236769Sobrien * Results: 2877236769Sobrien * A line w/o its newline 2878236769Sobrien * 2879236769Sobrien * Side Effects: 2880236769Sobrien * Only those associated with reading a character 2881236769Sobrien *--------------------------------------------------------------------- 2882236769Sobrien */ 2883236769Sobrienstatic char * 2884236769SobrienParseReadLine(void) 2885236769Sobrien{ 2886236769Sobrien char *line; /* Result */ 2887236769Sobrien int lineLength; /* Length of result */ 2888236769Sobrien int lineno; /* Saved line # */ 2889236769Sobrien int rval; 2890236769Sobrien 2891236769Sobrien for (;;) { 2892236769Sobrien line = ParseGetLine(0, &lineLength); 2893236769Sobrien if (line == NULL) 2894236769Sobrien return NULL; 2895236769Sobrien 2896236769Sobrien if (line[0] != '.') 2897236769Sobrien return line; 2898236769Sobrien 2899236769Sobrien /* 2900236769Sobrien * The line might be a conditional. Ask the conditional module 2901236769Sobrien * about it and act accordingly 2902236769Sobrien */ 2903236769Sobrien switch (Cond_Eval(line)) { 2904236769Sobrien case COND_SKIP: 2905236769Sobrien /* Skip to next conditional that evaluates to COND_PARSE. */ 2906236769Sobrien do { 2907236769Sobrien line = ParseGetLine(PARSE_SKIP, &lineLength); 2908236769Sobrien } while (line && Cond_Eval(line) != COND_PARSE); 2909236769Sobrien if (line == NULL) 2910236769Sobrien break; 2911236769Sobrien continue; 2912236769Sobrien case COND_PARSE: 2913236769Sobrien continue; 2914236769Sobrien case COND_INVALID: /* Not a conditional line */ 2915236769Sobrien /* Check for .for loops */ 2916236769Sobrien rval = For_Eval(line); 2917236769Sobrien if (rval == 0) 2918236769Sobrien /* Not a .for line */ 2919236769Sobrien break; 2920236769Sobrien if (rval < 0) 2921236769Sobrien /* Syntax error - error printed, ignore line */ 2922236769Sobrien continue; 2923236769Sobrien /* Start of a .for loop */ 2924236769Sobrien lineno = curFile->lineno; 2925236769Sobrien /* Accumulate loop lines until matching .endfor */ 2926236769Sobrien do { 2927236769Sobrien line = ParseGetLine(PARSE_RAW, &lineLength); 2928236769Sobrien if (line == NULL) { 2929236769Sobrien Parse_Error(PARSE_FATAL, 2930236769Sobrien "Unexpected end of file in for loop."); 2931236769Sobrien break; 2932236769Sobrien } 2933236769Sobrien } while (For_Accum(line)); 2934236769Sobrien /* Stash each iteration as a new 'input file' */ 2935236769Sobrien For_Run(lineno); 2936236769Sobrien /* Read next line from for-loop buffer */ 2937236769Sobrien continue; 2938236769Sobrien } 2939236769Sobrien return (line); 2940236769Sobrien } 2941236769Sobrien} 2942236769Sobrien 2943236769Sobrien/*- 2944236769Sobrien *----------------------------------------------------------------------- 2945236769Sobrien * ParseFinishLine -- 2946236769Sobrien * Handle the end of a dependency group. 2947236769Sobrien * 2948236769Sobrien * Results: 2949236769Sobrien * Nothing. 2950236769Sobrien * 2951236769Sobrien * Side Effects: 2952236769Sobrien * inLine set FALSE. 'targets' list destroyed. 2953236769Sobrien * 2954236769Sobrien *----------------------------------------------------------------------- 2955236769Sobrien */ 2956236769Sobrienstatic void 2957236769SobrienParseFinishLine(void) 2958236769Sobrien{ 2959236769Sobrien if (inLine) { 2960236769Sobrien Lst_ForEach(targets, Suff_EndTransform, NULL); 2961236769Sobrien Lst_Destroy(targets, ParseHasCommands); 2962236769Sobrien targets = NULL; 2963236769Sobrien inLine = FALSE; 2964236769Sobrien } 2965236769Sobrien} 2966236769Sobrien 2967236769Sobrien 2968236769Sobrien/*- 2969236769Sobrien *--------------------------------------------------------------------- 2970236769Sobrien * Parse_File -- 2971236769Sobrien * Parse a file into its component parts, incorporating it into the 2972236769Sobrien * current dependency graph. This is the main function and controls 2973236769Sobrien * almost every other function in this module 2974236769Sobrien * 2975236769Sobrien * Input: 2976236769Sobrien * name the name of the file being read 2977236769Sobrien * fd Open file to makefile to parse 2978236769Sobrien * 2979236769Sobrien * Results: 2980236769Sobrien * None 2981236769Sobrien * 2982236769Sobrien * Side Effects: 2983236769Sobrien * closes fd. 2984236769Sobrien * Loads. Nodes are added to the list of all targets, nodes and links 2985236769Sobrien * are added to the dependency graph. etc. etc. etc. 2986236769Sobrien *--------------------------------------------------------------------- 2987236769Sobrien */ 2988236769Sobrienvoid 2989236769SobrienParse_File(const char *name, int fd) 2990236769Sobrien{ 2991236769Sobrien char *cp; /* pointer into the line */ 2992236769Sobrien char *line; /* the line we're working on */ 2993236769Sobrien struct loadedfile *lf; 2994236769Sobrien 2995236769Sobrien lf = loadfile(name, fd); 2996236769Sobrien 2997236769Sobrien inLine = FALSE; 2998236769Sobrien fatals = 0; 2999236769Sobrien 3000236769Sobrien if (name == NULL) { 3001236769Sobrien name = "(stdin)"; 3002236769Sobrien } 3003236769Sobrien 3004236769Sobrien Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); 3005236769Sobrien curFile->lf = lf; 3006236769Sobrien 3007236769Sobrien do { 3008236769Sobrien for (; (line = ParseReadLine()) != NULL; ) { 3009236769Sobrien if (DEBUG(PARSE)) 3010236769Sobrien fprintf(debug_file, "ParseReadLine (%d): '%s'\n", 3011236769Sobrien curFile->lineno, line); 3012236769Sobrien if (*line == '.') { 3013236769Sobrien /* 3014236769Sobrien * Lines that begin with the special character may be 3015236769Sobrien * include or undef directives. 3016236769Sobrien * On the other hand they can be suffix rules (.c.o: ...) 3017236769Sobrien * or just dependencies for filenames that start '.'. 3018236769Sobrien */ 3019236769Sobrien for (cp = line + 1; isspace((unsigned char)*cp); cp++) { 3020236769Sobrien continue; 3021236769Sobrien } 3022236769Sobrien if (strncmp(cp, "include", 7) == 0 || 3023296637Ssjg ((cp[0] == 'd' || cp[0] == 's' || cp[0] == '-') && 3024236769Sobrien strncmp(&cp[1], "include", 7) == 0)) { 3025236769Sobrien ParseDoInclude(cp); 3026236769Sobrien continue; 3027236769Sobrien } 3028236769Sobrien if (strncmp(cp, "undef", 5) == 0) { 3029236769Sobrien char *cp2; 3030236769Sobrien for (cp += 5; isspace((unsigned char) *cp); cp++) 3031236769Sobrien continue; 3032236769Sobrien for (cp2 = cp; !isspace((unsigned char) *cp2) && 3033236769Sobrien (*cp2 != '\0'); cp2++) 3034236769Sobrien continue; 3035236769Sobrien *cp2 = '\0'; 3036236769Sobrien Var_Delete(cp, VAR_GLOBAL); 3037236769Sobrien continue; 3038236769Sobrien } else if (strncmp(cp, "export", 6) == 0) { 3039236769Sobrien for (cp += 6; isspace((unsigned char) *cp); cp++) 3040236769Sobrien continue; 3041236769Sobrien Var_Export(cp, 1); 3042236769Sobrien continue; 3043236769Sobrien } else if (strncmp(cp, "unexport", 8) == 0) { 3044236769Sobrien Var_UnExport(cp); 3045236769Sobrien continue; 3046236769Sobrien } else if (strncmp(cp, "info", 4) == 0 || 3047236769Sobrien strncmp(cp, "error", 5) == 0 || 3048236769Sobrien strncmp(cp, "warning", 7) == 0) { 3049236769Sobrien if (ParseMessage(cp)) 3050236769Sobrien continue; 3051236769Sobrien } 3052236769Sobrien } 3053236769Sobrien 3054236769Sobrien if (*line == '\t') { 3055236769Sobrien /* 3056236769Sobrien * If a line starts with a tab, it can only hope to be 3057236769Sobrien * a creation command. 3058236769Sobrien */ 3059236769Sobrien cp = line + 1; 3060236769Sobrien shellCommand: 3061236769Sobrien for (; isspace ((unsigned char)*cp); cp++) { 3062236769Sobrien continue; 3063236769Sobrien } 3064236769Sobrien if (*cp) { 3065236769Sobrien if (!inLine) 3066236769Sobrien Parse_Error(PARSE_FATAL, 3067236769Sobrien "Unassociated shell command \"%s\"", 3068236769Sobrien cp); 3069236769Sobrien /* 3070236769Sobrien * So long as it's not a blank line and we're actually 3071236769Sobrien * in a dependency spec, add the command to the list of 3072236769Sobrien * commands of all targets in the dependency spec 3073236769Sobrien */ 3074236769Sobrien if (targets) { 3075236769Sobrien cp = bmake_strdup(cp); 3076236769Sobrien Lst_ForEach(targets, ParseAddCmd, cp); 3077236769Sobrien#ifdef CLEANUP 3078236769Sobrien Lst_AtEnd(targCmds, cp); 3079236769Sobrien#endif 3080236769Sobrien } 3081236769Sobrien } 3082236769Sobrien continue; 3083236769Sobrien } 3084236769Sobrien 3085236769Sobrien#ifdef SYSVINCLUDE 3086236769Sobrien if (((strncmp(line, "include", 7) == 0 && 3087236769Sobrien isspace((unsigned char) line[7])) || 3088236769Sobrien ((line[0] == 's' || line[0] == '-') && 3089236769Sobrien strncmp(&line[1], "include", 7) == 0 && 3090236769Sobrien isspace((unsigned char) line[8]))) && 3091236769Sobrien strchr(line, ':') == NULL) { 3092236769Sobrien /* 3093236769Sobrien * It's an S3/S5-style "include". 3094236769Sobrien */ 3095236769Sobrien ParseTraditionalInclude(line); 3096236769Sobrien continue; 3097236769Sobrien } 3098236769Sobrien#endif 3099236769Sobrien#ifdef GMAKEEXPORT 3100236769Sobrien if (strncmp(line, "export", 6) == 0 && 3101236769Sobrien isspace((unsigned char) line[6]) && 3102236769Sobrien strchr(line, ':') == NULL) { 3103236769Sobrien /* 3104236769Sobrien * It's a Gmake "export". 3105236769Sobrien */ 3106236769Sobrien ParseGmakeExport(line); 3107236769Sobrien continue; 3108236769Sobrien } 3109236769Sobrien#endif 3110236769Sobrien if (Parse_IsVar(line)) { 3111236769Sobrien ParseFinishLine(); 3112236769Sobrien Parse_DoVar(line, VAR_GLOBAL); 3113236769Sobrien continue; 3114236769Sobrien } 3115236769Sobrien 3116236769Sobrien#ifndef POSIX 3117236769Sobrien /* 3118236769Sobrien * To make life easier on novices, if the line is indented we 3119236769Sobrien * first make sure the line has a dependency operator in it. 3120236769Sobrien * If it doesn't have an operator and we're in a dependency 3121236769Sobrien * line's script, we assume it's actually a shell command 3122236769Sobrien * and add it to the current list of targets. 3123236769Sobrien */ 3124236769Sobrien cp = line; 3125236769Sobrien if (isspace((unsigned char) line[0])) { 3126236769Sobrien while ((*cp != '\0') && isspace((unsigned char) *cp)) 3127236769Sobrien cp++; 3128236769Sobrien while (*cp && (ParseIsEscaped(line, cp) || 3129236769Sobrien (*cp != ':') && (*cp != '!'))) { 3130236769Sobrien cp++; 3131236769Sobrien } 3132236769Sobrien if (*cp == '\0') { 3133236769Sobrien if (inLine) { 3134236769Sobrien Parse_Error(PARSE_WARNING, 3135236769Sobrien "Shell command needs a leading tab"); 3136236769Sobrien goto shellCommand; 3137236769Sobrien } 3138236769Sobrien } 3139236769Sobrien } 3140236769Sobrien#endif 3141236769Sobrien ParseFinishLine(); 3142236769Sobrien 3143236769Sobrien /* 3144236769Sobrien * For some reason - probably to make the parser impossible - 3145236769Sobrien * a ';' can be used to separate commands from dependencies. 3146236769Sobrien * Attempt to avoid ';' inside substitution patterns. 3147236769Sobrien */ 3148236769Sobrien { 3149236769Sobrien int level = 0; 3150236769Sobrien 3151236769Sobrien for (cp = line; *cp != 0; cp++) { 3152236769Sobrien if (*cp == '\\' && cp[1] != 0) { 3153236769Sobrien cp++; 3154236769Sobrien continue; 3155236769Sobrien } 3156236769Sobrien if (*cp == '$' && 3157236769Sobrien (cp[1] == '(' || cp[1] == '{')) { 3158236769Sobrien level++; 3159236769Sobrien continue; 3160236769Sobrien } 3161236769Sobrien if (level > 0) { 3162236769Sobrien if (*cp == ')' || *cp == '}') { 3163236769Sobrien level--; 3164236769Sobrien continue; 3165236769Sobrien } 3166236769Sobrien } else if (*cp == ';') { 3167236769Sobrien break; 3168236769Sobrien } 3169236769Sobrien } 3170236769Sobrien } 3171236769Sobrien if (*cp != 0) 3172236769Sobrien /* Terminate the dependency list at the ';' */ 3173236769Sobrien *cp++ = 0; 3174236769Sobrien else 3175236769Sobrien cp = NULL; 3176236769Sobrien 3177236769Sobrien /* 3178236769Sobrien * We now know it's a dependency line so it needs to have all 3179236769Sobrien * variables expanded before being parsed. Tell the variable 3180236769Sobrien * module to complain if some variable is undefined... 3181236769Sobrien */ 3182296637Ssjg line = Var_Subst(NULL, line, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES); 3183236769Sobrien 3184236769Sobrien /* 3185236769Sobrien * Need a non-circular list for the target nodes 3186236769Sobrien */ 3187236769Sobrien if (targets) 3188236769Sobrien Lst_Destroy(targets, NULL); 3189236769Sobrien 3190236769Sobrien targets = Lst_Init(FALSE); 3191236769Sobrien inLine = TRUE; 3192236769Sobrien 3193236769Sobrien ParseDoDependency(line); 3194236769Sobrien free(line); 3195236769Sobrien 3196236769Sobrien /* If there were commands after a ';', add them now */ 3197236769Sobrien if (cp != NULL) { 3198236769Sobrien goto shellCommand; 3199236769Sobrien } 3200236769Sobrien } 3201236769Sobrien /* 3202236769Sobrien * Reached EOF, but it may be just EOF of an include file... 3203236769Sobrien */ 3204236769Sobrien } while (ParseEOF() == CONTINUE); 3205236769Sobrien 3206236769Sobrien if (fatals) { 3207236769Sobrien (void)fflush(stdout); 3208236769Sobrien (void)fprintf(stderr, 3209236769Sobrien "%s: Fatal errors encountered -- cannot continue", 3210236769Sobrien progname); 3211236769Sobrien PrintOnError(NULL, NULL); 3212236769Sobrien exit(1); 3213236769Sobrien } 3214236769Sobrien} 3215236769Sobrien 3216236769Sobrien/*- 3217236769Sobrien *--------------------------------------------------------------------- 3218236769Sobrien * Parse_Init -- 3219236769Sobrien * initialize the parsing module 3220236769Sobrien * 3221236769Sobrien * Results: 3222236769Sobrien * none 3223236769Sobrien * 3224236769Sobrien * Side Effects: 3225236769Sobrien * the parseIncPath list is initialized... 3226236769Sobrien *--------------------------------------------------------------------- 3227236769Sobrien */ 3228236769Sobrienvoid 3229236769SobrienParse_Init(void) 3230236769Sobrien{ 3231236769Sobrien mainNode = NULL; 3232236769Sobrien parseIncPath = Lst_Init(FALSE); 3233236769Sobrien sysIncPath = Lst_Init(FALSE); 3234236769Sobrien defIncPath = Lst_Init(FALSE); 3235236769Sobrien includes = Lst_Init(FALSE); 3236236769Sobrien#ifdef CLEANUP 3237236769Sobrien targCmds = Lst_Init(FALSE); 3238236769Sobrien#endif 3239236769Sobrien} 3240236769Sobrien 3241236769Sobrienvoid 3242236769SobrienParse_End(void) 3243236769Sobrien{ 3244236769Sobrien#ifdef CLEANUP 3245236769Sobrien Lst_Destroy(targCmds, (FreeProc *)free); 3246236769Sobrien if (targets) 3247236769Sobrien Lst_Destroy(targets, NULL); 3248236769Sobrien Lst_Destroy(defIncPath, Dir_Destroy); 3249236769Sobrien Lst_Destroy(sysIncPath, Dir_Destroy); 3250236769Sobrien Lst_Destroy(parseIncPath, Dir_Destroy); 3251236769Sobrien Lst_Destroy(includes, NULL); /* Should be empty now */ 3252236769Sobrien#endif 3253236769Sobrien} 3254236769Sobrien 3255236769Sobrien 3256236769Sobrien/*- 3257236769Sobrien *----------------------------------------------------------------------- 3258236769Sobrien * Parse_MainName -- 3259236769Sobrien * Return a Lst of the main target to create for main()'s sake. If 3260236769Sobrien * no such target exists, we Punt with an obnoxious error message. 3261236769Sobrien * 3262236769Sobrien * Results: 3263236769Sobrien * A Lst of the single node to create. 3264236769Sobrien * 3265236769Sobrien * Side Effects: 3266236769Sobrien * None. 3267236769Sobrien * 3268236769Sobrien *----------------------------------------------------------------------- 3269236769Sobrien */ 3270236769SobrienLst 3271236769SobrienParse_MainName(void) 3272236769Sobrien{ 3273236769Sobrien Lst mainList; /* result list */ 3274236769Sobrien 3275236769Sobrien mainList = Lst_Init(FALSE); 3276236769Sobrien 3277236769Sobrien if (mainNode == NULL) { 3278236769Sobrien Punt("no target to make."); 3279236769Sobrien /*NOTREACHED*/ 3280236769Sobrien } else if (mainNode->type & OP_DOUBLEDEP) { 3281236769Sobrien (void)Lst_AtEnd(mainList, mainNode); 3282236769Sobrien Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW); 3283236769Sobrien } 3284236769Sobrien else 3285236769Sobrien (void)Lst_AtEnd(mainList, mainNode); 3286236769Sobrien Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); 3287236769Sobrien return (mainList); 3288236769Sobrien} 3289236769Sobrien 3290236769Sobrien/*- 3291236769Sobrien *----------------------------------------------------------------------- 3292236769Sobrien * ParseMark -- 3293236769Sobrien * Add the filename and lineno to the GNode so that we remember 3294236769Sobrien * where it was first defined. 3295236769Sobrien * 3296236769Sobrien * Side Effects: 3297236769Sobrien * None. 3298236769Sobrien * 3299236769Sobrien *----------------------------------------------------------------------- 3300236769Sobrien */ 3301236769Sobrienstatic void 3302236769SobrienParseMark(GNode *gn) 3303236769Sobrien{ 3304236769Sobrien gn->fname = curFile->fname; 3305236769Sobrien gn->lineno = curFile->lineno; 3306236769Sobrien} 3307