Annotation of src/usr.bin/make/parse.c, Revision 1.1
1.1 ! deraadt 1: /* $NetBSD: parse.c,v 1.16 1995/09/10 03:58:16 christos Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
! 5: * Copyright (c) 1988, 1989 by Adam de Boor
! 6: * Copyright (c) 1989 by Berkeley Softworks
! 7: * All rights reserved.
! 8: *
! 9: * This code is derived from software contributed to Berkeley by
! 10: * Adam de Boor.
! 11: *
! 12: * Redistribution and use in source and binary forms, with or without
! 13: * modification, are permitted provided that the following conditions
! 14: * are met:
! 15: * 1. Redistributions of source code must retain the above copyright
! 16: * notice, this list of conditions and the following disclaimer.
! 17: * 2. Redistributions in binary form must reproduce the above copyright
! 18: * notice, this list of conditions and the following disclaimer in the
! 19: * documentation and/or other materials provided with the distribution.
! 20: * 3. All advertising materials mentioning features or use of this software
! 21: * must display the following acknowledgement:
! 22: * This product includes software developed by the University of
! 23: * California, Berkeley and its contributors.
! 24: * 4. Neither the name of the University nor the names of its contributors
! 25: * may be used to endorse or promote products derived from this software
! 26: * without specific prior written permission.
! 27: *
! 28: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 29: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 30: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 31: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 32: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 33: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 34: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 35: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 36: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 37: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 38: * SUCH DAMAGE.
! 39: */
! 40:
! 41: #ifndef lint
! 42: #if 0
! 43: static char sccsid[] = "@(#)parse.c 5.18 (Berkeley) 2/19/91";
! 44: #else
! 45: static char rcsid[] = "$NetBSD: parse.c,v 1.16 1995/09/10 03:58:16 christos Exp $";
! 46: #endif
! 47: #endif /* not lint */
! 48:
! 49: /*-
! 50: * parse.c --
! 51: * Functions to parse a makefile.
! 52: *
! 53: * One function, Parse_Init, must be called before any functions
! 54: * in this module are used. After that, the function Parse_File is the
! 55: * main entry point and controls most of the other functions in this
! 56: * module.
! 57: *
! 58: * Most important structures are kept in Lsts. Directories for
! 59: * the #include "..." function are kept in the 'parseIncPath' Lst, while
! 60: * those for the #include <...> are kept in the 'sysIncPath' Lst. The
! 61: * targets currently being defined are kept in the 'targets' Lst.
! 62: *
! 63: * The variables 'fname' and 'lineno' are used to track the name
! 64: * of the current file and the line number in that file so that error
! 65: * messages can be more meaningful.
! 66: *
! 67: * Interface:
! 68: * Parse_Init Initialization function which must be
! 69: * called before anything else in this module
! 70: * is used.
! 71: *
! 72: * Parse_End Cleanup the module
! 73: *
! 74: * Parse_File Function used to parse a makefile. It must
! 75: * be given the name of the file, which should
! 76: * already have been opened, and a function
! 77: * to call to read a character from the file.
! 78: *
! 79: * Parse_IsVar Returns TRUE if the given line is a
! 80: * variable assignment. Used by MainParseArgs
! 81: * to determine if an argument is a target
! 82: * or a variable assignment. Used internally
! 83: * for pretty much the same thing...
! 84: *
! 85: * Parse_Error Function called when an error occurs in
! 86: * parsing. Used by the variable and
! 87: * conditional modules.
! 88: * Parse_MainName Returns a Lst of the main target to create.
! 89: */
! 90:
! 91: #if __STDC__
! 92: #include <stdarg.h>
! 93: #else
! 94: #include <varargs.h>
! 95: #endif
! 96: #include <stdio.h>
! 97: #include <ctype.h>
! 98: #include <errno.h>
! 99: #include <sys/wait.h>
! 100: #include "make.h"
! 101: #include "hash.h"
! 102: #include "dir.h"
! 103: #include "job.h"
! 104: #include "buf.h"
! 105: #include "pathnames.h"
! 106:
! 107: /*
! 108: * These values are returned by ParseEOF to tell Parse_File whether to
! 109: * CONTINUE parsing, i.e. it had only reached the end of an include file,
! 110: * or if it's DONE.
! 111: */
! 112: #define CONTINUE 1
! 113: #define DONE 0
! 114: static Lst targets; /* targets we're working on */
! 115: static Lst targCmds; /* command lines for targets */
! 116: static Boolean inLine; /* true if currently in a dependency
! 117: * line or its commands */
! 118: typedef struct {
! 119: char *str;
! 120: char *ptr;
! 121: } PTR;
! 122:
! 123: static char *fname; /* name of current file (for errors) */
! 124: static int lineno; /* line number in current file */
! 125: static FILE *curFILE = NULL; /* current makefile */
! 126:
! 127: static PTR *curPTR = NULL; /* current makefile */
! 128:
! 129: static int fatals = 0;
! 130:
! 131: static GNode *mainNode; /* The main target to create. This is the
! 132: * first target on the first dependency
! 133: * line in the first makefile */
! 134: /*
! 135: * Definitions for handling #include specifications
! 136: */
! 137: typedef struct IFile {
! 138: char *fname; /* name of previous file */
! 139: int lineno; /* saved line number */
! 140: FILE * F; /* the open stream */
! 141: PTR * p; /* the char pointer */
! 142: } IFile;
! 143:
! 144: static Lst includes; /* stack of IFiles generated by
! 145: * #includes */
! 146: Lst parseIncPath; /* list of directories for "..." includes */
! 147: Lst sysIncPath; /* list of directories for <...> includes */
! 148:
! 149: /*-
! 150: * specType contains the SPECial TYPE of the current target. It is
! 151: * Not if the target is unspecial. If it *is* special, however, the children
! 152: * are linked as children of the parent but not vice versa. This variable is
! 153: * set in ParseDoDependency
! 154: */
! 155: typedef enum {
! 156: Begin, /* .BEGIN */
! 157: Default, /* .DEFAULT */
! 158: End, /* .END */
! 159: Ignore, /* .IGNORE */
! 160: Includes, /* .INCLUDES */
! 161: Interrupt, /* .INTERRUPT */
! 162: Libs, /* .LIBS */
! 163: MFlags, /* .MFLAGS or .MAKEFLAGS */
! 164: Main, /* .MAIN and we don't have anything user-specified to
! 165: * make */
! 166: NoExport, /* .NOEXPORT */
! 167: Not, /* Not special */
! 168: NotParallel, /* .NOTPARALELL */
! 169: Null, /* .NULL */
! 170: Order, /* .ORDER */
! 171: ExPath, /* .PATH */
! 172: Precious, /* .PRECIOUS */
! 173: ExShell, /* .SHELL */
! 174: Silent, /* .SILENT */
! 175: SingleShell, /* .SINGLESHELL */
! 176: Suffixes, /* .SUFFIXES */
! 177: Attribute /* Generic attribute */
! 178: } ParseSpecial;
! 179:
! 180: static ParseSpecial specType;
! 181:
! 182: /*
! 183: * Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER
! 184: * seen, then set to each successive source on the line.
! 185: */
! 186: static GNode *predecessor;
! 187:
! 188: /*
! 189: * The parseKeywords table is searched using binary search when deciding
! 190: * if a target or source is special. The 'spec' field is the ParseSpecial
! 191: * type of the keyword ("Not" if the keyword isn't special as a target) while
! 192: * the 'op' field is the operator to apply to the list of targets if the
! 193: * keyword is used as a source ("0" if the keyword isn't special as a source)
! 194: */
! 195: static struct {
! 196: char *name; /* Name of keyword */
! 197: ParseSpecial spec; /* Type when used as a target */
! 198: int op; /* Operator when used as a source */
! 199: } parseKeywords[] = {
! 200: { ".BEGIN", Begin, 0 },
! 201: { ".DEFAULT", Default, 0 },
! 202: { ".END", End, 0 },
! 203: { ".EXEC", Attribute, OP_EXEC },
! 204: { ".IGNORE", Ignore, OP_IGNORE },
! 205: { ".INCLUDES", Includes, 0 },
! 206: { ".INTERRUPT", Interrupt, 0 },
! 207: { ".INVISIBLE", Attribute, OP_INVISIBLE },
! 208: { ".JOIN", Attribute, OP_JOIN },
! 209: { ".LIBS", Libs, 0 },
! 210: { ".MAIN", Main, 0 },
! 211: { ".MAKE", Attribute, OP_MAKE },
! 212: { ".MAKEFLAGS", MFlags, 0 },
! 213: { ".MFLAGS", MFlags, 0 },
! 214: { ".NOTMAIN", Attribute, OP_NOTMAIN },
! 215: { ".NOTPARALLEL", NotParallel, 0 },
! 216: { ".NULL", Null, 0 },
! 217: { ".OPTIONAL", Attribute, OP_OPTIONAL },
! 218: { ".ORDER", Order, 0 },
! 219: { ".PATH", ExPath, 0 },
! 220: { ".PRECIOUS", Precious, OP_PRECIOUS },
! 221: { ".RECURSIVE", Attribute, OP_MAKE },
! 222: { ".SHELL", ExShell, 0 },
! 223: { ".SILENT", Silent, OP_SILENT },
! 224: { ".SINGLESHELL", SingleShell, 0 },
! 225: { ".SUFFIXES", Suffixes, 0 },
! 226: { ".USE", Attribute, OP_USE },
! 227: };
! 228:
! 229: static int ParseFindKeyword __P((char *));
! 230: static int ParseLinkSrc __P((ClientData, ClientData));
! 231: static int ParseDoOp __P((ClientData, ClientData));
! 232: static void ParseDoSrc __P((int, char *));
! 233: static int ParseFindMain __P((ClientData, ClientData));
! 234: static int ParseAddDir __P((ClientData, ClientData));
! 235: static int ParseClearPath __P((ClientData, ClientData));
! 236: static void ParseDoDependency __P((char *));
! 237: static int ParseAddCmd __P((ClientData, ClientData));
! 238: static int ParseReadc __P((void));
! 239: static void ParseUnreadc __P((int));
! 240: static void ParseHasCommands __P((ClientData));
! 241: static void ParseDoInclude __P((char *));
! 242: #ifdef SYSVINCLUDE
! 243: static void ParseTraditionalInclude __P((char *));
! 244: #endif
! 245: static int ParseEOF __P((int));
! 246: static char *ParseReadLine __P((void));
! 247: static char *ParseSkipLine __P((int));
! 248: static void ParseFinishLine __P((void));
! 249:
! 250: /*-
! 251: *----------------------------------------------------------------------
! 252: * ParseFindKeyword --
! 253: * Look in the table of keywords for one matching the given string.
! 254: *
! 255: * Results:
! 256: * The index of the keyword, or -1 if it isn't there.
! 257: *
! 258: * Side Effects:
! 259: * None
! 260: *----------------------------------------------------------------------
! 261: */
! 262: static int
! 263: ParseFindKeyword (str)
! 264: char *str; /* String to find */
! 265: {
! 266: register int start,
! 267: end,
! 268: cur;
! 269: register int diff;
! 270:
! 271: start = 0;
! 272: end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;
! 273:
! 274: do {
! 275: cur = start + ((end - start) / 2);
! 276: diff = strcmp (str, parseKeywords[cur].name);
! 277:
! 278: if (diff == 0) {
! 279: return (cur);
! 280: } else if (diff < 0) {
! 281: end = cur - 1;
! 282: } else {
! 283: start = cur + 1;
! 284: }
! 285: } while (start <= end);
! 286: return (-1);
! 287: }
! 288:
! 289: /*-
! 290: * Parse_Error --
! 291: * Error message abort function for parsing. Prints out the context
! 292: * of the error (line number and file) as well as the message with
! 293: * two optional arguments.
! 294: *
! 295: * Results:
! 296: * None
! 297: *
! 298: * Side Effects:
! 299: * "fatals" is incremented if the level is PARSE_FATAL.
! 300: */
! 301: /* VARARGS */
! 302: void
! 303: #if __STDC__
! 304: Parse_Error(int type, char *fmt, ...)
! 305: #else
! 306: Parse_Error(va_alist)
! 307: va_dcl
! 308: #endif
! 309: {
! 310: va_list ap;
! 311: #if __STDC__
! 312: va_start(ap, fmt);
! 313: #else
! 314: int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */
! 315: char *fmt;
! 316:
! 317: va_start(ap);
! 318: type = va_arg(ap, int);
! 319: fmt = va_arg(ap, char *);
! 320: #endif
! 321:
! 322: (void)fprintf(stderr, "\"%s\", line %d: ", fname, lineno);
! 323: if (type == PARSE_WARNING)
! 324: (void)fprintf(stderr, "warning: ");
! 325: (void)vfprintf(stderr, fmt, ap);
! 326: va_end(ap);
! 327: (void)fprintf(stderr, "\n");
! 328: (void)fflush(stderr);
! 329: if (type == PARSE_FATAL)
! 330: fatals += 1;
! 331: }
! 332:
! 333: /*-
! 334: *---------------------------------------------------------------------
! 335: * ParseLinkSrc --
! 336: * Link the parent node to its new child. Used in a Lst_ForEach by
! 337: * ParseDoDependency. If the specType isn't 'Not', the parent
! 338: * isn't linked as a parent of the child.
! 339: *
! 340: * Results:
! 341: * Always = 0
! 342: *
! 343: * Side Effects:
! 344: * New elements are added to the parents list of cgn and the
! 345: * children list of cgn. the unmade field of pgn is updated
! 346: * to reflect the additional child.
! 347: *---------------------------------------------------------------------
! 348: */
! 349: static int
! 350: ParseLinkSrc (pgnp, cgnp)
! 351: ClientData pgnp; /* The parent node */
! 352: ClientData cgnp; /* The child node */
! 353: {
! 354: GNode *pgn = (GNode *) pgnp;
! 355: GNode *cgn = (GNode *) cgnp;
! 356: if (Lst_Member (pgn->children, (ClientData)cgn) == NILLNODE) {
! 357: (void)Lst_AtEnd (pgn->children, (ClientData)cgn);
! 358: if (specType == Not) {
! 359: (void)Lst_AtEnd (cgn->parents, (ClientData)pgn);
! 360: }
! 361: pgn->unmade += 1;
! 362: }
! 363: return (0);
! 364: }
! 365:
! 366: /*-
! 367: *---------------------------------------------------------------------
! 368: * ParseDoOp --
! 369: * Apply the parsed operator to the given target node. Used in a
! 370: * Lst_ForEach call by ParseDoDependency once all targets have
! 371: * been found and their operator parsed. If the previous and new
! 372: * operators are incompatible, a major error is taken.
! 373: *
! 374: * Results:
! 375: * Always 0
! 376: *
! 377: * Side Effects:
! 378: * The type field of the node is altered to reflect any new bits in
! 379: * the op.
! 380: *---------------------------------------------------------------------
! 381: */
! 382: static int
! 383: ParseDoOp (gnp, opp)
! 384: ClientData gnp; /* The node to which the operator is to be
! 385: * applied */
! 386: ClientData opp; /* The operator to apply */
! 387: {
! 388: GNode *gn = (GNode *) gnp;
! 389: int op = *(int *) opp;
! 390: /*
! 391: * If the dependency mask of the operator and the node don't match and
! 392: * the node has actually had an operator applied to it before, and
! 393: * the operator actually has some dependency information in it, complain.
! 394: */
! 395: if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) &&
! 396: !OP_NOP(gn->type) && !OP_NOP(op))
! 397: {
! 398: Parse_Error (PARSE_FATAL, "Inconsistent operator for %s", gn->name);
! 399: return (1);
! 400: }
! 401:
! 402: if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) {
! 403: /*
! 404: * If the node was the object of a :: operator, we need to create a
! 405: * new instance of it for the children and commands on this dependency
! 406: * line. The new instance is placed on the 'cohorts' list of the
! 407: * initial one (note the initial one is not on its own cohorts list)
! 408: * and the new instance is linked to all parents of the initial
! 409: * instance.
! 410: */
! 411: register GNode *cohort;
! 412: LstNode ln;
! 413:
! 414: cohort = Targ_NewGN(gn->name);
! 415: /*
! 416: * Duplicate links to parents so graph traversal is simple. Perhaps
! 417: * some type bits should be duplicated?
! 418: *
! 419: * Make the cohort invisible as well to avoid duplicating it into
! 420: * other variables. True, parents of this target won't tend to do
! 421: * anything with their local variables, but better safe than
! 422: * sorry.
! 423: */
! 424: Lst_ForEach(gn->parents, ParseLinkSrc, (ClientData)cohort);
! 425: cohort->type = OP_DOUBLEDEP|OP_INVISIBLE;
! 426: (void)Lst_AtEnd(gn->cohorts, (ClientData)cohort);
! 427:
! 428: /*
! 429: * Replace the node in the targets list with the new copy
! 430: */
! 431: ln = Lst_Member(targets, (ClientData)gn);
! 432: Lst_Replace(ln, (ClientData)cohort);
! 433: gn = cohort;
! 434: }
! 435: /*
! 436: * We don't want to nuke any previous flags (whatever they were) so we
! 437: * just OR the new operator into the old
! 438: */
! 439: gn->type |= op;
! 440:
! 441: return (0);
! 442: }
! 443:
! 444: /*-
! 445: *---------------------------------------------------------------------
! 446: * ParseDoSrc --
! 447: * Given the name of a source, figure out if it is an attribute
! 448: * and apply it to the targets if it is. Else decide if there is
! 449: * some attribute which should be applied *to* the source because
! 450: * of some special target and apply it if so. Otherwise, make the
! 451: * source be a child of the targets in the list 'targets'
! 452: *
! 453: * Results:
! 454: * None
! 455: *
! 456: * Side Effects:
! 457: * Operator bits may be added to the list of targets or to the source.
! 458: * The targets may have a new source added to their lists of children.
! 459: *---------------------------------------------------------------------
! 460: */
! 461: static void
! 462: ParseDoSrc (tOp, src)
! 463: int tOp; /* operator (if any) from special targets */
! 464: char *src; /* name of the source to handle */
! 465: {
! 466: int op; /* operator (if any) from special source */
! 467: GNode *gn;
! 468:
! 469: op = 0;
! 470: if (*src == '.' && isupper (src[1])) {
! 471: int keywd = ParseFindKeyword(src);
! 472: if (keywd != -1) {
! 473: op = parseKeywords[keywd].op;
! 474: }
! 475: }
! 476: if (op != 0) {
! 477: Lst_ForEach (targets, ParseDoOp, (ClientData)&op);
! 478: } else if (specType == Main) {
! 479: /*
! 480: * If we have noted the existence of a .MAIN, it means we need
! 481: * to add the sources of said target to the list of things
! 482: * to create. The string 'src' is likely to be free, so we
! 483: * must make a new copy of it. Note that this will only be
! 484: * invoked if the user didn't specify a target on the command
! 485: * line. This is to allow #ifmake's to succeed, or something...
! 486: */
! 487: (void) Lst_AtEnd (create, (ClientData)strdup(src));
! 488: /*
! 489: * Add the name to the .TARGETS variable as well, so the user cna
! 490: * employ that, if desired.
! 491: */
! 492: Var_Append(".TARGETS", src, VAR_GLOBAL);
! 493: } else if (specType == Order) {
! 494: /*
! 495: * Create proper predecessor/successor links between the previous
! 496: * source and the current one.
! 497: */
! 498: gn = Targ_FindNode(src, TARG_CREATE);
! 499: if (predecessor != NILGNODE) {
! 500: (void)Lst_AtEnd(predecessor->successors, (ClientData)gn);
! 501: (void)Lst_AtEnd(gn->preds, (ClientData)predecessor);
! 502: }
! 503: /*
! 504: * The current source now becomes the predecessor for the next one.
! 505: */
! 506: predecessor = gn;
! 507: } else {
! 508: /*
! 509: * If the source is not an attribute, we need to find/create
! 510: * a node for it. After that we can apply any operator to it
! 511: * from a special target or link it to its parents, as
! 512: * appropriate.
! 513: *
! 514: * In the case of a source that was the object of a :: operator,
! 515: * the attribute is applied to all of its instances (as kept in
! 516: * the 'cohorts' list of the node) or all the cohorts are linked
! 517: * to all the targets.
! 518: */
! 519: gn = Targ_FindNode (src, TARG_CREATE);
! 520: if (tOp) {
! 521: gn->type |= tOp;
! 522: } else {
! 523: Lst_ForEach (targets, ParseLinkSrc, (ClientData)gn);
! 524: }
! 525: if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
! 526: register GNode *cohort;
! 527: register LstNode ln;
! 528:
! 529: for (ln=Lst_First(gn->cohorts); ln != NILLNODE; ln = Lst_Succ(ln)){
! 530: cohort = (GNode *)Lst_Datum(ln);
! 531: if (tOp) {
! 532: cohort->type |= tOp;
! 533: } else {
! 534: Lst_ForEach(targets, ParseLinkSrc, (ClientData)cohort);
! 535: }
! 536: }
! 537: }
! 538: }
! 539: }
! 540:
! 541: /*-
! 542: *-----------------------------------------------------------------------
! 543: * ParseFindMain --
! 544: * Find a real target in the list and set it to be the main one.
! 545: * Called by ParseDoDependency when a main target hasn't been found
! 546: * yet.
! 547: *
! 548: * Results:
! 549: * 0 if main not found yet, 1 if it is.
! 550: *
! 551: * Side Effects:
! 552: * mainNode is changed and Targ_SetMain is called.
! 553: *
! 554: *-----------------------------------------------------------------------
! 555: */
! 556: static int
! 557: ParseFindMain(gnp, dummy)
! 558: ClientData gnp; /* Node to examine */
! 559: ClientData dummy;
! 560: {
! 561: GNode *gn = (GNode *) gnp;
! 562: if ((gn->type & (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)) == 0) {
! 563: mainNode = gn;
! 564: Targ_SetMain(gn);
! 565: return (dummy ? 1 : 1);
! 566: } else {
! 567: return (dummy ? 0 : 0);
! 568: }
! 569: }
! 570:
! 571: /*-
! 572: *-----------------------------------------------------------------------
! 573: * ParseAddDir --
! 574: * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going
! 575: *
! 576: * Results:
! 577: * === 0
! 578: *
! 579: * Side Effects:
! 580: * See Dir_AddDir.
! 581: *
! 582: *-----------------------------------------------------------------------
! 583: */
! 584: static int
! 585: ParseAddDir(path, name)
! 586: ClientData path;
! 587: ClientData name;
! 588: {
! 589: Dir_AddDir((Lst) path, (char *) name);
! 590: return(0);
! 591: }
! 592:
! 593: /*-
! 594: *-----------------------------------------------------------------------
! 595: * ParseClearPath --
! 596: * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going
! 597: *
! 598: * Results:
! 599: * === 0
! 600: *
! 601: * Side Effects:
! 602: * See Dir_ClearPath
! 603: *
! 604: *-----------------------------------------------------------------------
! 605: */
! 606: static int
! 607: ParseClearPath(path, dummy)
! 608: ClientData path;
! 609: ClientData dummy;
! 610: {
! 611: Dir_ClearPath((Lst) path);
! 612: return(dummy ? 0 : 0);
! 613: }
! 614:
! 615: /*-
! 616: *---------------------------------------------------------------------
! 617: * ParseDoDependency --
! 618: * Parse the dependency line in line.
! 619: *
! 620: * Results:
! 621: * None
! 622: *
! 623: * Side Effects:
! 624: * The nodes of the sources are linked as children to the nodes of the
! 625: * targets. Some nodes may be created.
! 626: *
! 627: * We parse a dependency line by first extracting words from the line and
! 628: * finding nodes in the list of all targets with that name. This is done
! 629: * until a character is encountered which is an operator character. Currently
! 630: * these are only ! and :. At this point the operator is parsed and the
! 631: * pointer into the line advanced until the first source is encountered.
! 632: * The parsed operator is applied to each node in the 'targets' list,
! 633: * which is where the nodes found for the targets are kept, by means of
! 634: * the ParseDoOp function.
! 635: * The sources are read in much the same way as the targets were except
! 636: * that now they are expanded using the wildcarding scheme of the C-Shell
! 637: * and all instances of the resulting words in the list of all targets
! 638: * are found. Each of the resulting nodes is then linked to each of the
! 639: * targets as one of its children.
! 640: * Certain targets are handled specially. These are the ones detailed
! 641: * by the specType variable.
! 642: * The storing of transformation rules is also taken care of here.
! 643: * A target is recognized as a transformation rule by calling
! 644: * Suff_IsTransform. If it is a transformation rule, its node is gotten
! 645: * from the suffix module via Suff_AddTransform rather than the standard
! 646: * Targ_FindNode in the target module.
! 647: *---------------------------------------------------------------------
! 648: */
! 649: static void
! 650: ParseDoDependency (line)
! 651: char *line; /* the line to parse */
! 652: {
! 653: char *cp; /* our current position */
! 654: GNode *gn; /* a general purpose temporary node */
! 655: int op; /* the operator on the line */
! 656: char savec; /* a place to save a character */
! 657: Lst paths; /* List of search paths to alter when parsing
! 658: * a list of .PATH targets */
! 659: int tOp; /* operator from special target */
! 660: Lst sources; /* list of source names after expansion */
! 661: Lst curTargs; /* list of target names to be found and added
! 662: * to the targets list */
! 663:
! 664: tOp = 0;
! 665:
! 666: specType = Not;
! 667: paths = (Lst)NULL;
! 668:
! 669: curTargs = Lst_Init(FALSE);
! 670:
! 671: do {
! 672: for (cp = line;
! 673: *cp && !isspace (*cp) &&
! 674: (*cp != '!') && (*cp != ':') && (*cp != '(');
! 675: cp++)
! 676: {
! 677: if (*cp == '$') {
! 678: /*
! 679: * Must be a dynamic source (would have been expanded
! 680: * otherwise), so call the Var module to parse the puppy
! 681: * so we can safely advance beyond it...There should be
! 682: * no errors in this, as they would have been discovered
! 683: * in the initial Var_Subst and we wouldn't be here.
! 684: */
! 685: int length;
! 686: Boolean freeIt;
! 687: char *result;
! 688:
! 689: result=Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt);
! 690:
! 691: if (freeIt) {
! 692: free(result);
! 693: }
! 694: cp += length-1;
! 695: }
! 696: continue;
! 697: }
! 698: if (*cp == '(') {
! 699: /*
! 700: * Archives must be handled specially to make sure the OP_ARCHV
! 701: * flag is set in their 'type' field, for one thing, and because
! 702: * things like "archive(file1.o file2.o file3.o)" are permissible.
! 703: * Arch_ParseArchive will set 'line' to be the first non-blank
! 704: * after the archive-spec. It creates/finds nodes for the members
! 705: * and places them on the given list, returning SUCCESS if all
! 706: * went well and FAILURE if there was an error in the
! 707: * specification. On error, line should remain untouched.
! 708: */
! 709: if (Arch_ParseArchive (&line, targets, VAR_CMD) != SUCCESS) {
! 710: Parse_Error (PARSE_FATAL,
! 711: "Error in archive specification: \"%s\"", line);
! 712: return;
! 713: } else {
! 714: continue;
! 715: }
! 716: }
! 717: savec = *cp;
! 718:
! 719: if (!*cp) {
! 720: /*
! 721: * Ending a dependency line without an operator is a Bozo
! 722: * no-no
! 723: */
! 724: Parse_Error (PARSE_FATAL, "Need an operator");
! 725: return;
! 726: }
! 727: *cp = '\0';
! 728: /*
! 729: * Have a word in line. See if it's a special target and set
! 730: * specType to match it.
! 731: */
! 732: if (*line == '.' && isupper (line[1])) {
! 733: /*
! 734: * See if the target is a special target that must have it
! 735: * or its sources handled specially.
! 736: */
! 737: int keywd = ParseFindKeyword(line);
! 738: if (keywd != -1) {
! 739: if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {
! 740: Parse_Error(PARSE_FATAL, "Mismatched special targets");
! 741: return;
! 742: }
! 743:
! 744: specType = parseKeywords[keywd].spec;
! 745: tOp = parseKeywords[keywd].op;
! 746:
! 747: /*
! 748: * Certain special targets have special semantics:
! 749: * .PATH Have to set the dirSearchPath
! 750: * variable too
! 751: * .MAIN Its sources are only used if
! 752: * nothing has been specified to
! 753: * create.
! 754: * .DEFAULT Need to create a node to hang
! 755: * commands on, but we don't want
! 756: * it in the graph, nor do we want
! 757: * it to be the Main Target, so we
! 758: * create it, set OP_NOTMAIN and
! 759: * add it to the list, setting
! 760: * DEFAULT to the new node for
! 761: * later use. We claim the node is
! 762: * A transformation rule to make
! 763: * life easier later, when we'll
! 764: * use Make_HandleUse to actually
! 765: * apply the .DEFAULT commands.
! 766: * .BEGIN
! 767: * .END
! 768: * .INTERRUPT Are not to be considered the
! 769: * main target.
! 770: * .NOTPARALLEL Make only one target at a time.
! 771: * .SINGLESHELL Create a shell for each command.
! 772: * .ORDER Must set initial predecessor to NIL
! 773: */
! 774: switch (specType) {
! 775: case ExPath:
! 776: if (paths == NULL) {
! 777: paths = Lst_Init(FALSE);
! 778: }
! 779: (void)Lst_AtEnd(paths, (ClientData)dirSearchPath);
! 780: break;
! 781: case Main:
! 782: if (!Lst_IsEmpty(create)) {
! 783: specType = Not;
! 784: }
! 785: break;
! 786: case Begin:
! 787: case End:
! 788: case Interrupt:
! 789: gn = Targ_FindNode(line, TARG_CREATE);
! 790: gn->type |= OP_NOTMAIN;
! 791: (void)Lst_AtEnd(targets, (ClientData)gn);
! 792: break;
! 793: case Default:
! 794: gn = Targ_NewGN(".DEFAULT");
! 795: gn->type |= (OP_NOTMAIN|OP_TRANSFORM);
! 796: (void)Lst_AtEnd(targets, (ClientData)gn);
! 797: DEFAULT = gn;
! 798: break;
! 799: case NotParallel:
! 800: {
! 801: extern int maxJobs;
! 802:
! 803: maxJobs = 1;
! 804: break;
! 805: }
! 806: case SingleShell:
! 807: compatMake = 1;
! 808: break;
! 809: case Order:
! 810: predecessor = NILGNODE;
! 811: break;
! 812: default:
! 813: break;
! 814: }
! 815: } else if (strncmp (line, ".PATH", 5) == 0) {
! 816: /*
! 817: * .PATH<suffix> has to be handled specially.
! 818: * Call on the suffix module to give us a path to
! 819: * modify.
! 820: */
! 821: Lst path;
! 822:
! 823: specType = ExPath;
! 824: path = Suff_GetPath (&line[5]);
! 825: if (path == NILLST) {
! 826: Parse_Error (PARSE_FATAL,
! 827: "Suffix '%s' not defined (yet)",
! 828: &line[5]);
! 829: return;
! 830: } else {
! 831: if (paths == (Lst)NULL) {
! 832: paths = Lst_Init(FALSE);
! 833: }
! 834: (void)Lst_AtEnd(paths, (ClientData)path);
! 835: }
! 836: }
! 837: }
! 838:
! 839: /*
! 840: * Have word in line. Get or create its node and stick it at
! 841: * the end of the targets list
! 842: */
! 843: if ((specType == Not) && (*line != '\0')) {
! 844: if (Dir_HasWildcards(line)) {
! 845: /*
! 846: * Targets are to be sought only in the current directory,
! 847: * so create an empty path for the thing. Note we need to
! 848: * use Dir_Destroy in the destruction of the path as the
! 849: * Dir module could have added a directory to the path...
! 850: */
! 851: Lst emptyPath = Lst_Init(FALSE);
! 852:
! 853: Dir_Expand(line, emptyPath, curTargs);
! 854:
! 855: Lst_Destroy(emptyPath, Dir_Destroy);
! 856: } else {
! 857: /*
! 858: * No wildcards, but we want to avoid code duplication,
! 859: * so create a list with the word on it.
! 860: */
! 861: (void)Lst_AtEnd(curTargs, (ClientData)line);
! 862: }
! 863:
! 864: while(!Lst_IsEmpty(curTargs)) {
! 865: char *targName = (char *)Lst_DeQueue(curTargs);
! 866:
! 867: if (!Suff_IsTransform (targName)) {
! 868: gn = Targ_FindNode (targName, TARG_CREATE);
! 869: } else {
! 870: gn = Suff_AddTransform (targName);
! 871: }
! 872:
! 873: (void)Lst_AtEnd (targets, (ClientData)gn);
! 874: }
! 875: } else if (specType == ExPath && *line != '.' && *line != '\0') {
! 876: Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
! 877: }
! 878:
! 879: *cp = savec;
! 880: /*
! 881: * If it is a special type and not .PATH, it's the only target we
! 882: * allow on this line...
! 883: */
! 884: if (specType != Not && specType != ExPath) {
! 885: Boolean warn = FALSE;
! 886:
! 887: while ((*cp != '!') && (*cp != ':') && *cp) {
! 888: if (*cp != ' ' && *cp != '\t') {
! 889: warn = TRUE;
! 890: }
! 891: cp++;
! 892: }
! 893: if (warn) {
! 894: Parse_Error(PARSE_WARNING, "Extra target ignored");
! 895: }
! 896: } else {
! 897: while (*cp && isspace (*cp)) {
! 898: cp++;
! 899: }
! 900: }
! 901: line = cp;
! 902: } while ((*line != '!') && (*line != ':') && *line);
! 903:
! 904: /*
! 905: * Don't need the list of target names anymore...
! 906: */
! 907: Lst_Destroy(curTargs, NOFREE);
! 908:
! 909: if (!Lst_IsEmpty(targets)) {
! 910: switch(specType) {
! 911: default:
! 912: Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");
! 913: break;
! 914: case Default:
! 915: case Begin:
! 916: case End:
! 917: case Interrupt:
! 918: /*
! 919: * These four create nodes on which to hang commands, so
! 920: * targets shouldn't be empty...
! 921: */
! 922: case Not:
! 923: /*
! 924: * Nothing special here -- targets can be empty if it wants.
! 925: */
! 926: break;
! 927: }
! 928: }
! 929:
! 930: /*
! 931: * Have now parsed all the target names. Must parse the operator next. The
! 932: * result is left in op .
! 933: */
! 934: if (*cp == '!') {
! 935: op = OP_FORCE;
! 936: } else if (*cp == ':') {
! 937: if (cp[1] == ':') {
! 938: op = OP_DOUBLEDEP;
! 939: cp++;
! 940: } else {
! 941: op = OP_DEPENDS;
! 942: }
! 943: } else {
! 944: Parse_Error (PARSE_FATAL, "Missing dependency operator");
! 945: return;
! 946: }
! 947:
! 948: cp++; /* Advance beyond operator */
! 949:
! 950: Lst_ForEach (targets, ParseDoOp, (ClientData)&op);
! 951:
! 952: /*
! 953: * Get to the first source
! 954: */
! 955: while (*cp && isspace (*cp)) {
! 956: cp++;
! 957: }
! 958: line = cp;
! 959:
! 960: /*
! 961: * Several special targets take different actions if present with no
! 962: * sources:
! 963: * a .SUFFIXES line with no sources clears out all old suffixes
! 964: * a .PRECIOUS line makes all targets precious
! 965: * a .IGNORE line ignores errors for all targets
! 966: * a .SILENT line creates silence when making all targets
! 967: * a .PATH removes all directories from the search path(s).
! 968: */
! 969: if (!*line) {
! 970: switch (specType) {
! 971: case Suffixes:
! 972: Suff_ClearSuffixes ();
! 973: break;
! 974: case Precious:
! 975: allPrecious = TRUE;
! 976: break;
! 977: case Ignore:
! 978: ignoreErrors = TRUE;
! 979: break;
! 980: case Silent:
! 981: beSilent = TRUE;
! 982: break;
! 983: case ExPath:
! 984: Lst_ForEach(paths, ParseClearPath, (ClientData)NULL);
! 985: break;
! 986: default:
! 987: break;
! 988: }
! 989: } else if (specType == MFlags) {
! 990: /*
! 991: * Call on functions in main.c to deal with these arguments and
! 992: * set the initial character to a null-character so the loop to
! 993: * get sources won't get anything
! 994: */
! 995: Main_ParseArgLine (line);
! 996: *line = '\0';
! 997: } else if (specType == ExShell) {
! 998: if (Job_ParseShell (line) != SUCCESS) {
! 999: Parse_Error (PARSE_FATAL, "improper shell specification");
! 1000: return;
! 1001: }
! 1002: *line = '\0';
! 1003: } else if ((specType == NotParallel) || (specType == SingleShell)) {
! 1004: *line = '\0';
! 1005: }
! 1006:
! 1007: /*
! 1008: * NOW GO FOR THE SOURCES
! 1009: */
! 1010: if ((specType == Suffixes) || (specType == ExPath) ||
! 1011: (specType == Includes) || (specType == Libs) ||
! 1012: (specType == Null))
! 1013: {
! 1014: while (*line) {
! 1015: /*
! 1016: * If the target was one that doesn't take files as its sources
! 1017: * but takes something like suffixes, we take each
! 1018: * space-separated word on the line as a something and deal
! 1019: * with it accordingly.
! 1020: *
! 1021: * If the target was .SUFFIXES, we take each source as a
! 1022: * suffix and add it to the list of suffixes maintained by the
! 1023: * Suff module.
! 1024: *
! 1025: * If the target was a .PATH, we add the source as a directory
! 1026: * to search on the search path.
! 1027: *
! 1028: * If it was .INCLUDES, the source is taken to be the suffix of
! 1029: * files which will be #included and whose search path should
! 1030: * be present in the .INCLUDES variable.
! 1031: *
! 1032: * If it was .LIBS, the source is taken to be the suffix of
! 1033: * files which are considered libraries and whose search path
! 1034: * should be present in the .LIBS variable.
! 1035: *
! 1036: * If it was .NULL, the source is the suffix to use when a file
! 1037: * has no valid suffix.
! 1038: */
! 1039: char savec;
! 1040: while (*cp && !isspace (*cp)) {
! 1041: cp++;
! 1042: }
! 1043: savec = *cp;
! 1044: *cp = '\0';
! 1045: switch (specType) {
! 1046: case Suffixes:
! 1047: Suff_AddSuffix (line);
! 1048: break;
! 1049: case ExPath:
! 1050: Lst_ForEach(paths, ParseAddDir, (ClientData)line);
! 1051: break;
! 1052: case Includes:
! 1053: Suff_AddInclude (line);
! 1054: break;
! 1055: case Libs:
! 1056: Suff_AddLib (line);
! 1057: break;
! 1058: case Null:
! 1059: Suff_SetNull (line);
! 1060: break;
! 1061: default:
! 1062: break;
! 1063: }
! 1064: *cp = savec;
! 1065: if (savec != '\0') {
! 1066: cp++;
! 1067: }
! 1068: while (*cp && isspace (*cp)) {
! 1069: cp++;
! 1070: }
! 1071: line = cp;
! 1072: }
! 1073: if (paths) {
! 1074: Lst_Destroy(paths, NOFREE);
! 1075: }
! 1076: } else {
! 1077: while (*line) {
! 1078: /*
! 1079: * The targets take real sources, so we must beware of archive
! 1080: * specifications (i.e. things with left parentheses in them)
! 1081: * and handle them accordingly.
! 1082: */
! 1083: while (*cp && !isspace (*cp)) {
! 1084: if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) {
! 1085: /*
! 1086: * Only stop for a left parenthesis if it isn't at the
! 1087: * start of a word (that'll be for variable changes
! 1088: * later) and isn't preceded by a dollar sign (a dynamic
! 1089: * source).
! 1090: */
! 1091: break;
! 1092: } else {
! 1093: cp++;
! 1094: }
! 1095: }
! 1096:
! 1097: if (*cp == '(') {
! 1098: GNode *gn;
! 1099:
! 1100: sources = Lst_Init (FALSE);
! 1101: if (Arch_ParseArchive (&line, sources, VAR_CMD) != SUCCESS) {
! 1102: Parse_Error (PARSE_FATAL,
! 1103: "Error in source archive spec \"%s\"", line);
! 1104: return;
! 1105: }
! 1106:
! 1107: while (!Lst_IsEmpty (sources)) {
! 1108: gn = (GNode *) Lst_DeQueue (sources);
! 1109: ParseDoSrc (tOp, gn->name);
! 1110: }
! 1111: Lst_Destroy (sources, NOFREE);
! 1112: cp = line;
! 1113: } else {
! 1114: if (*cp) {
! 1115: *cp = '\0';
! 1116: cp += 1;
! 1117: }
! 1118:
! 1119: ParseDoSrc (tOp, line);
! 1120: }
! 1121: while (*cp && isspace (*cp)) {
! 1122: cp++;
! 1123: }
! 1124: line = cp;
! 1125: }
! 1126: }
! 1127:
! 1128: if (mainNode == NILGNODE) {
! 1129: /*
! 1130: * If we have yet to decide on a main target to make, in the
! 1131: * absence of any user input, we want the first target on
! 1132: * the first dependency line that is actually a real target
! 1133: * (i.e. isn't a .USE or .EXEC rule) to be made.
! 1134: */
! 1135: Lst_ForEach (targets, ParseFindMain, (ClientData)0);
! 1136: }
! 1137:
! 1138: }
! 1139:
! 1140: /*-
! 1141: *---------------------------------------------------------------------
! 1142: * Parse_IsVar --
! 1143: * Return TRUE if the passed line is a variable assignment. A variable
! 1144: * assignment consists of a single word followed by optional whitespace
! 1145: * followed by either a += or an = operator.
! 1146: * This function is used both by the Parse_File function and main when
! 1147: * parsing the command-line arguments.
! 1148: *
! 1149: * Results:
! 1150: * TRUE if it is. FALSE if it ain't
! 1151: *
! 1152: * Side Effects:
! 1153: * none
! 1154: *---------------------------------------------------------------------
! 1155: */
! 1156: Boolean
! 1157: Parse_IsVar (line)
! 1158: register char *line; /* the line to check */
! 1159: {
! 1160: register Boolean wasSpace = FALSE; /* set TRUE if found a space */
! 1161: register Boolean haveName = FALSE; /* Set TRUE if have a variable name */
! 1162: int level = 0;
! 1163: #define ISEQOPERATOR(c) \
! 1164: (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!'))
! 1165:
! 1166: /*
! 1167: * Skip to variable name
! 1168: */
! 1169: for (;(*line == ' ') || (*line == '\t'); line++)
! 1170: continue;
! 1171:
! 1172: for (; *line != '=' || level != 0; line++)
! 1173: switch (*line) {
! 1174: case '\0':
! 1175: /*
! 1176: * end-of-line -- can't be a variable assignment.
! 1177: */
! 1178: return FALSE;
! 1179:
! 1180: case ' ':
! 1181: case '\t':
! 1182: /*
! 1183: * there can be as much white space as desired so long as there is
! 1184: * only one word before the operator
! 1185: */
! 1186: wasSpace = TRUE;
! 1187: break;
! 1188:
! 1189: case '(':
! 1190: case '{':
! 1191: level++;
! 1192: break;
! 1193:
! 1194: case '}':
! 1195: case ')':
! 1196: level--;
! 1197: break;
! 1198:
! 1199: default:
! 1200: if (wasSpace && haveName) {
! 1201: if (ISEQOPERATOR(*line)) {
! 1202: /*
! 1203: * When an = operator [+?!:] is found, the next
! 1204: * character * must be an = or it ain't a valid
! 1205: * assignment.
! 1206: */
! 1207: if (line[1] != '=' && level == 0)
! 1208: return FALSE;
! 1209: else
! 1210: return haveName;
! 1211: }
! 1212: else {
! 1213: /*
! 1214: * This is the start of another word, so not assignment.
! 1215: */
! 1216: return FALSE;
! 1217: }
! 1218: }
! 1219: else {
! 1220: haveName = TRUE;
! 1221: wasSpace = FALSE;
! 1222: }
! 1223: break;
! 1224: }
! 1225:
! 1226: return haveName;
! 1227: }
! 1228:
! 1229: /*-
! 1230: *---------------------------------------------------------------------
! 1231: * Parse_DoVar --
! 1232: * Take the variable assignment in the passed line and do it in the
! 1233: * global context.
! 1234: *
! 1235: * Note: There is a lexical ambiguity with assignment modifier characters
! 1236: * in variable names. This routine interprets the character before the =
! 1237: * as a modifier. Therefore, an assignment like
! 1238: * C++=/usr/bin/CC
! 1239: * is interpreted as "C+ +=" instead of "C++ =".
! 1240: *
! 1241: * Results:
! 1242: * none
! 1243: *
! 1244: * Side Effects:
! 1245: * the variable structure of the given variable name is altered in the
! 1246: * global context.
! 1247: *---------------------------------------------------------------------
! 1248: */
! 1249: void
! 1250: Parse_DoVar (line, ctxt)
! 1251: char *line; /* a line guaranteed to be a variable
! 1252: * assignment. This reduces error checks */
! 1253: GNode *ctxt; /* Context in which to do the assignment */
! 1254: {
! 1255: char *cp; /* pointer into line */
! 1256: enum {
! 1257: VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL
! 1258: } type; /* Type of assignment */
! 1259: char *opc; /* ptr to operator character to
! 1260: * null-terminate the variable name */
! 1261: /*
! 1262: * Avoid clobbered variable warnings by forcing the compiler
! 1263: * to ``unregister'' variables
! 1264: */
! 1265: #if __GNUC__
! 1266: (void) &cp;
! 1267: (void) &line;
! 1268: #endif
! 1269:
! 1270: /*
! 1271: * Skip to variable name
! 1272: */
! 1273: while ((*line == ' ') || (*line == '\t')) {
! 1274: line++;
! 1275: }
! 1276:
! 1277: /*
! 1278: * Skip to operator character, nulling out whitespace as we go
! 1279: */
! 1280: for (cp = line + 1; *cp != '='; cp++) {
! 1281: if (isspace (*cp)) {
! 1282: *cp = '\0';
! 1283: }
! 1284: }
! 1285: opc = cp-1; /* operator is the previous character */
! 1286: *cp++ = '\0'; /* nuke the = */
! 1287:
! 1288: /*
! 1289: * Check operator type
! 1290: */
! 1291: switch (*opc) {
! 1292: case '+':
! 1293: type = VAR_APPEND;
! 1294: *opc = '\0';
! 1295: break;
! 1296:
! 1297: case '?':
! 1298: /*
! 1299: * If the variable already has a value, we don't do anything.
! 1300: */
! 1301: *opc = '\0';
! 1302: if (Var_Exists(line, ctxt)) {
! 1303: return;
! 1304: } else {
! 1305: type = VAR_NORMAL;
! 1306: }
! 1307: break;
! 1308:
! 1309: case ':':
! 1310: type = VAR_SUBST;
! 1311: *opc = '\0';
! 1312: break;
! 1313:
! 1314: case '!':
! 1315: type = VAR_SHELL;
! 1316: *opc = '\0';
! 1317: break;
! 1318:
! 1319: default:
! 1320: type = VAR_NORMAL;
! 1321: break;
! 1322: }
! 1323:
! 1324: while (isspace (*cp)) {
! 1325: cp++;
! 1326: }
! 1327:
! 1328: if (type == VAR_APPEND) {
! 1329: Var_Append (line, cp, ctxt);
! 1330: } else if (type == VAR_SUBST) {
! 1331: /*
! 1332: * Allow variables in the old value to be undefined, but leave their
! 1333: * invocation alone -- this is done by forcing oldVars to be false.
! 1334: * XXX: This can cause recursive variables, but that's not hard to do,
! 1335: * and this allows someone to do something like
! 1336: *
! 1337: * CFLAGS = $(.INCLUDES)
! 1338: * CFLAGS := -I.. $(CFLAGS)
! 1339: *
! 1340: * And not get an error.
! 1341: */
! 1342: Boolean oldOldVars = oldVars;
! 1343:
! 1344: oldVars = FALSE;
! 1345: cp = Var_Subst(NULL, cp, ctxt, FALSE);
! 1346: oldVars = oldOldVars;
! 1347:
! 1348: Var_Set(line, cp, ctxt);
! 1349: free(cp);
! 1350: } else if (type == VAR_SHELL) {
! 1351: char *args[4]; /* Args for invoking the shell */
! 1352: int fds[2]; /* Pipe streams */
! 1353: int cpid; /* Child PID */
! 1354: int pid; /* PID from wait() */
! 1355: Boolean freeCmd; /* TRUE if the command needs to be freed, i.e.
! 1356: * if any variable expansion was performed */
! 1357:
! 1358: /*
! 1359: * Avoid clobbered variable warnings by forcing the compiler
! 1360: * to ``unregister'' variables
! 1361: */
! 1362: #if __GNUC__
! 1363: (void) &freeCmd;
! 1364: #endif
! 1365:
! 1366: /*
! 1367: * Set up arguments for shell
! 1368: */
! 1369: args[0] = "sh";
! 1370: args[1] = "-c";
! 1371: if (strchr(cp, '$') != (char *)NULL) {
! 1372: /*
! 1373: * There's a dollar sign in the command, so perform variable
! 1374: * expansion on the whole thing. The resulting string will need
! 1375: * freeing when we're done, so set freeCmd to TRUE.
! 1376: */
! 1377: args[2] = Var_Subst(NULL, cp, VAR_CMD, TRUE);
! 1378: freeCmd = TRUE;
! 1379: } else {
! 1380: args[2] = cp;
! 1381: freeCmd = FALSE;
! 1382: }
! 1383: args[3] = (char *)NULL;
! 1384:
! 1385: /*
! 1386: * Open a pipe for fetching its output
! 1387: */
! 1388: pipe(fds);
! 1389:
! 1390: /*
! 1391: * Fork
! 1392: */
! 1393: cpid = vfork();
! 1394: if (cpid == 0) {
! 1395: /*
! 1396: * Close input side of pipe
! 1397: */
! 1398: close(fds[0]);
! 1399:
! 1400: /*
! 1401: * Duplicate the output stream to the shell's output, then
! 1402: * shut the extra thing down. Note we don't fetch the error
! 1403: * stream...why not? Why?
! 1404: */
! 1405: dup2(fds[1], 1);
! 1406: close(fds[1]);
! 1407:
! 1408: execv("/bin/sh", args);
! 1409: _exit(1);
! 1410: } else if (cpid < 0) {
! 1411: /*
! 1412: * Couldn't fork -- tell the user and make the variable null
! 1413: */
! 1414: Parse_Error(PARSE_WARNING, "Couldn't exec \"%s\"", cp);
! 1415: Var_Set(line, "", ctxt);
! 1416: } else {
! 1417: int status;
! 1418: int cc;
! 1419: Buffer buf;
! 1420: char *res;
! 1421:
! 1422: /*
! 1423: * No need for the writing half
! 1424: */
! 1425: close(fds[1]);
! 1426:
! 1427: buf = Buf_Init (MAKE_BSIZE);
! 1428:
! 1429: do {
! 1430: char result[BUFSIZ];
! 1431: cc = read(fds[0], result, sizeof(result));
! 1432: if (cc > 0)
! 1433: Buf_AddBytes(buf, cc, (Byte *) result);
! 1434: }
! 1435: while (cc > 0 || (cc == -1 && errno == EINTR));
! 1436:
! 1437: /*
! 1438: * Close the input side of the pipe.
! 1439: */
! 1440: close(fds[0]);
! 1441:
! 1442: /*
! 1443: * Wait for the process to exit.
! 1444: */
! 1445: while(((pid = wait(&status)) != cpid) && (pid >= 0))
! 1446: continue;
! 1447:
! 1448: res = (char *)Buf_GetAll (buf, &cc);
! 1449: Buf_Destroy (buf, FALSE);
! 1450:
! 1451: if (cc == 0) {
! 1452: /*
! 1453: * Couldn't read the child's output -- tell the user and
! 1454: * set the variable to null
! 1455: */
! 1456: Parse_Error(PARSE_WARNING, "Couldn't read shell's output");
! 1457: }
! 1458:
! 1459: if (status) {
! 1460: /*
! 1461: * Child returned an error -- tell the user but still use
! 1462: * the result.
! 1463: */
! 1464: Parse_Error(PARSE_WARNING, "\"%s\" returned non-zero", cp);
! 1465: }
! 1466:
! 1467: /*
! 1468: * Null-terminate the result, convert newlines to spaces and
! 1469: * install it in the variable.
! 1470: */
! 1471: res[cc] = '\0';
! 1472: cp = &res[cc] - 1;
! 1473:
! 1474: if (*cp == '\n') {
! 1475: /*
! 1476: * A final newline is just stripped
! 1477: */
! 1478: *cp-- = '\0';
! 1479: }
! 1480: while (cp >= res) {
! 1481: if (*cp == '\n') {
! 1482: *cp = ' ';
! 1483: }
! 1484: cp--;
! 1485: }
! 1486: Var_Set(line, res, ctxt);
! 1487: free(res);
! 1488:
! 1489: }
! 1490: if (freeCmd) {
! 1491: free(args[2]);
! 1492: }
! 1493: } else {
! 1494: /*
! 1495: * Normal assignment -- just do it.
! 1496: */
! 1497: Var_Set (line, cp, ctxt);
! 1498: }
! 1499: }
! 1500:
! 1501: /*-
! 1502: * ParseAddCmd --
! 1503: * Lst_ForEach function to add a command line to all targets
! 1504: *
! 1505: * Results:
! 1506: * Always 0
! 1507: *
! 1508: * Side Effects:
! 1509: * A new element is added to the commands list of the node.
! 1510: */
! 1511: static int
! 1512: ParseAddCmd(gnp, cmd)
! 1513: ClientData gnp; /* the node to which the command is to be added */
! 1514: ClientData cmd; /* the command to add */
! 1515: {
! 1516: GNode *gn = (GNode *) gnp;
! 1517: /* if target already supplied, ignore commands */
! 1518: if (!(gn->type & OP_HAS_COMMANDS))
! 1519: (void)Lst_AtEnd(gn->commands, cmd);
! 1520: return(0);
! 1521: }
! 1522:
! 1523: /*-
! 1524: *-----------------------------------------------------------------------
! 1525: * ParseHasCommands --
! 1526: * Callback procedure for Parse_File when destroying the list of
! 1527: * targets on the last dependency line. Marks a target as already
! 1528: * having commands if it does, to keep from having shell commands
! 1529: * on multiple dependency lines.
! 1530: *
! 1531: * Results:
! 1532: * None
! 1533: *
! 1534: * Side Effects:
! 1535: * OP_HAS_COMMANDS may be set for the target.
! 1536: *
! 1537: *-----------------------------------------------------------------------
! 1538: */
! 1539: static void
! 1540: ParseHasCommands(gnp)
! 1541: ClientData gnp; /* Node to examine */
! 1542: {
! 1543: GNode *gn = (GNode *) gnp;
! 1544: if (!Lst_IsEmpty(gn->commands)) {
! 1545: gn->type |= OP_HAS_COMMANDS;
! 1546: }
! 1547: }
! 1548:
! 1549: /*-
! 1550: *-----------------------------------------------------------------------
! 1551: * Parse_AddIncludeDir --
! 1552: * Add a directory to the path searched for included makefiles
! 1553: * bracketed by double-quotes. Used by functions in main.c
! 1554: *
! 1555: * Results:
! 1556: * None.
! 1557: *
! 1558: * Side Effects:
! 1559: * The directory is appended to the list.
! 1560: *
! 1561: *-----------------------------------------------------------------------
! 1562: */
! 1563: void
! 1564: Parse_AddIncludeDir (dir)
! 1565: char *dir; /* The name of the directory to add */
! 1566: {
! 1567: Dir_AddDir (parseIncPath, dir);
! 1568: }
! 1569:
! 1570: /*-
! 1571: *---------------------------------------------------------------------
! 1572: * ParseDoInclude --
! 1573: * Push to another file.
! 1574: *
! 1575: * The input is the line minus the #include. A file spec is a string
! 1576: * enclosed in <> or "". The former is looked for only in sysIncPath.
! 1577: * The latter in . and the directories specified by -I command line
! 1578: * options
! 1579: *
! 1580: * Results:
! 1581: * None
! 1582: *
! 1583: * Side Effects:
! 1584: * A structure is added to the includes Lst and readProc, lineno,
! 1585: * fname and curFILE are altered for the new file
! 1586: *---------------------------------------------------------------------
! 1587: */
! 1588: static void
! 1589: ParseDoInclude (file)
! 1590: char *file; /* file specification */
! 1591: {
! 1592: char *fullname; /* full pathname of file */
! 1593: IFile *oldFile; /* state associated with current file */
! 1594: char endc; /* the character which ends the file spec */
! 1595: char *cp; /* current position in file spec */
! 1596: Boolean isSystem; /* TRUE if makefile is a system makefile */
! 1597:
! 1598: /*
! 1599: * Skip to delimiter character so we know where to look
! 1600: */
! 1601: while ((*file == ' ') || (*file == '\t')) {
! 1602: file++;
! 1603: }
! 1604:
! 1605: if ((*file != '"') && (*file != '<')) {
! 1606: Parse_Error (PARSE_FATAL,
! 1607: ".include filename must be delimited by '\"' or '<'");
! 1608: return;
! 1609: }
! 1610:
! 1611: /*
! 1612: * Set the search path on which to find the include file based on the
! 1613: * characters which bracket its name. Angle-brackets imply it's
! 1614: * a system Makefile while double-quotes imply it's a user makefile
! 1615: */
! 1616: if (*file == '<') {
! 1617: isSystem = TRUE;
! 1618: endc = '>';
! 1619: } else {
! 1620: isSystem = FALSE;
! 1621: endc = '"';
! 1622: }
! 1623:
! 1624: /*
! 1625: * Skip to matching delimiter
! 1626: */
! 1627: for (cp = ++file; *cp && *cp != endc; cp++) {
! 1628: continue;
! 1629: }
! 1630:
! 1631: if (*cp != endc) {
! 1632: Parse_Error (PARSE_FATAL,
! 1633: "Unclosed %cinclude filename. '%c' expected",
! 1634: '.', endc);
! 1635: return;
! 1636: }
! 1637: *cp = '\0';
! 1638:
! 1639: /*
! 1640: * Substitute for any variables in the file name before trying to
! 1641: * find the thing.
! 1642: */
! 1643: file = Var_Subst (NULL, file, VAR_CMD, FALSE);
! 1644:
! 1645: /*
! 1646: * Now we know the file's name and its search path, we attempt to
! 1647: * find the durn thing. A return of NULL indicates the file don't
! 1648: * exist.
! 1649: */
! 1650: if (!isSystem) {
! 1651: /*
! 1652: * Include files contained in double-quotes are first searched for
! 1653: * relative to the including file's location. We don't want to
! 1654: * cd there, of course, so we just tack on the old file's
! 1655: * leading path components and call Dir_FindFile to see if
! 1656: * we can locate the beast.
! 1657: */
! 1658: char *prefEnd;
! 1659:
! 1660: prefEnd = strrchr (fname, '/');
! 1661: if (prefEnd != (char *)NULL) {
! 1662: char *newName;
! 1663:
! 1664: *prefEnd = '\0';
! 1665: if (file[0] == '/')
! 1666: newName = strdup(file);
! 1667: else
! 1668: newName = str_concat (fname, file, STR_ADDSLASH);
! 1669: fullname = Dir_FindFile (newName, parseIncPath);
! 1670: if (fullname == (char *)NULL) {
! 1671: fullname = Dir_FindFile(newName, dirSearchPath);
! 1672: }
! 1673: free (newName);
! 1674: *prefEnd = '/';
! 1675: } else {
! 1676: fullname = (char *)NULL;
! 1677: }
! 1678: } else {
! 1679: fullname = (char *)NULL;
! 1680: }
! 1681:
! 1682: if (fullname == (char *)NULL) {
! 1683: /*
! 1684: * System makefile or makefile wasn't found in same directory as
! 1685: * included makefile. Search for it first on the -I search path,
! 1686: * then on the .PATH search path, if not found in a -I directory.
! 1687: * XXX: Suffix specific?
! 1688: */
! 1689: fullname = Dir_FindFile (file, parseIncPath);
! 1690: if (fullname == (char *)NULL) {
! 1691: fullname = Dir_FindFile(file, dirSearchPath);
! 1692: }
! 1693: }
! 1694:
! 1695: if (fullname == (char *)NULL) {
! 1696: /*
! 1697: * Still haven't found the makefile. Look for it on the system
! 1698: * path as a last resort.
! 1699: */
! 1700: fullname = Dir_FindFile(file, sysIncPath);
! 1701: }
! 1702:
! 1703: if (fullname == (char *) NULL) {
! 1704: *cp = endc;
! 1705: Parse_Error (PARSE_FATAL, "Could not find %s", file);
! 1706: return;
! 1707: }
! 1708:
! 1709: free(file);
! 1710:
! 1711: /*
! 1712: * Once we find the absolute path to the file, we get to save all the
! 1713: * state from the current file before we can start reading this
! 1714: * include file. The state is stored in an IFile structure which
! 1715: * is placed on a list with other IFile structures. The list makes
! 1716: * a very nice stack to track how we got here...
! 1717: */
! 1718: oldFile = (IFile *) emalloc (sizeof (IFile));
! 1719: oldFile->fname = fname;
! 1720:
! 1721: oldFile->F = curFILE;
! 1722: oldFile->p = curPTR;
! 1723: oldFile->lineno = lineno;
! 1724:
! 1725: (void) Lst_AtFront (includes, (ClientData)oldFile);
! 1726:
! 1727: /*
! 1728: * Once the previous state has been saved, we can get down to reading
! 1729: * the new file. We set up the name of the file to be the absolute
! 1730: * name of the include file so error messages refer to the right
! 1731: * place. Naturally enough, we start reading at line number 0.
! 1732: */
! 1733: fname = fullname;
! 1734: lineno = 0;
! 1735:
! 1736: curFILE = fopen (fullname, "r");
! 1737: curPTR = NULL;
! 1738: if (curFILE == (FILE * ) NULL) {
! 1739: Parse_Error (PARSE_FATAL, "Cannot open %s", fullname);
! 1740: /*
! 1741: * Pop to previous file
! 1742: */
! 1743: (void) ParseEOF(0);
! 1744: }
! 1745: }
! 1746:
! 1747:
! 1748: /*-
! 1749: *---------------------------------------------------------------------
! 1750: * Parse_FromString --
! 1751: * Start Parsing from the given string
! 1752: *
! 1753: * Results:
! 1754: * None
! 1755: *
! 1756: * Side Effects:
! 1757: * A structure is added to the includes Lst and readProc, lineno,
! 1758: * fname and curFILE are altered for the new file
! 1759: *---------------------------------------------------------------------
! 1760: */
! 1761: void
! 1762: Parse_FromString(str)
! 1763: char *str;
! 1764: {
! 1765: IFile *oldFile; /* state associated with this file */
! 1766:
! 1767: if (DEBUG(FOR))
! 1768: (void) fprintf(stderr, "%s\n----\n", str);
! 1769:
! 1770: oldFile = (IFile *) emalloc (sizeof (IFile));
! 1771: oldFile->lineno = lineno;
! 1772: oldFile->fname = fname;
! 1773: oldFile->F = curFILE;
! 1774: oldFile->p = curPTR;
! 1775:
! 1776: (void) Lst_AtFront (includes, (ClientData)oldFile);
! 1777:
! 1778: curFILE = NULL;
! 1779: curPTR = (PTR *) emalloc (sizeof (PTR));
! 1780: curPTR->str = curPTR->ptr = str;
! 1781: lineno = 0;
! 1782: fname = strdup(fname);
! 1783: }
! 1784:
! 1785:
! 1786: #ifdef SYSVINCLUDE
! 1787: /*-
! 1788: *---------------------------------------------------------------------
! 1789: * ParseTraditionalInclude --
! 1790: * Push to another file.
! 1791: *
! 1792: * The input is the line minus the "include". The file name is
! 1793: * the string following the "include".
! 1794: *
! 1795: * Results:
! 1796: * None
! 1797: *
! 1798: * Side Effects:
! 1799: * A structure is added to the includes Lst and readProc, lineno,
! 1800: * fname and curFILE are altered for the new file
! 1801: *---------------------------------------------------------------------
! 1802: */
! 1803: static void
! 1804: ParseTraditionalInclude (file)
! 1805: char *file; /* file specification */
! 1806: {
! 1807: char *fullname; /* full pathname of file */
! 1808: IFile *oldFile; /* state associated with current file */
! 1809: char *cp; /* current position in file spec */
! 1810: char *prefEnd;
! 1811:
! 1812: /*
! 1813: * Skip over whitespace
! 1814: */
! 1815: while ((*file == ' ') || (*file == '\t')) {
! 1816: file++;
! 1817: }
! 1818:
! 1819: if (*file == '\0') {
! 1820: Parse_Error (PARSE_FATAL,
! 1821: "Filename missing from \"include\"");
! 1822: return;
! 1823: }
! 1824:
! 1825: /*
! 1826: * Skip to end of line or next whitespace
! 1827: */
! 1828: for (cp = file; *cp && *cp != '\n' && *cp != '\t' && *cp != ' '; cp++) {
! 1829: continue;
! 1830: }
! 1831:
! 1832: *cp = '\0';
! 1833:
! 1834: /*
! 1835: * Substitute for any variables in the file name before trying to
! 1836: * find the thing.
! 1837: */
! 1838: file = Var_Subst (NULL, file, VAR_CMD, FALSE);
! 1839:
! 1840: /*
! 1841: * Now we know the file's name, we attempt to find the durn thing.
! 1842: * A return of NULL indicates the file don't exist.
! 1843: *
! 1844: * Include files are first searched for relative to the including
! 1845: * file's location. We don't want to cd there, of course, so we
! 1846: * just tack on the old file's leading path components and call
! 1847: * Dir_FindFile to see if we can locate the beast.
! 1848: * XXX - this *does* search in the current directory, right?
! 1849: */
! 1850:
! 1851: prefEnd = strrchr (fname, '/');
! 1852: if (prefEnd != (char *)NULL) {
! 1853: char *newName;
! 1854:
! 1855: *prefEnd = '\0';
! 1856: newName = str_concat (fname, file, STR_ADDSLASH);
! 1857: fullname = Dir_FindFile (newName, parseIncPath);
! 1858: if (fullname == (char *)NULL) {
! 1859: fullname = Dir_FindFile(newName, dirSearchPath);
! 1860: }
! 1861: free (newName);
! 1862: *prefEnd = '/';
! 1863: } else {
! 1864: fullname = (char *)NULL;
! 1865: }
! 1866:
! 1867: if (fullname == (char *)NULL) {
! 1868: /*
! 1869: * System makefile or makefile wasn't found in same directory as
! 1870: * included makefile. Search for it first on the -I search path,
! 1871: * then on the .PATH search path, if not found in a -I directory.
! 1872: * XXX: Suffix specific?
! 1873: */
! 1874: fullname = Dir_FindFile (file, parseIncPath);
! 1875: if (fullname == (char *)NULL) {
! 1876: fullname = Dir_FindFile(file, dirSearchPath);
! 1877: }
! 1878: }
! 1879:
! 1880: if (fullname == (char *)NULL) {
! 1881: /*
! 1882: * Still haven't found the makefile. Look for it on the system
! 1883: * path as a last resort.
! 1884: */
! 1885: fullname = Dir_FindFile(file, sysIncPath);
! 1886: }
! 1887:
! 1888: if (fullname == (char *) NULL) {
! 1889: Parse_Error (PARSE_FATAL, "Could not find %s", file);
! 1890: return;
! 1891: }
! 1892:
! 1893: /*
! 1894: * Once we find the absolute path to the file, we get to save all the
! 1895: * state from the current file before we can start reading this
! 1896: * include file. The state is stored in an IFile structure which
! 1897: * is placed on a list with other IFile structures. The list makes
! 1898: * a very nice stack to track how we got here...
! 1899: */
! 1900: oldFile = (IFile *) emalloc (sizeof (IFile));
! 1901: oldFile->fname = fname;
! 1902:
! 1903: oldFile->F = curFILE;
! 1904: oldFile->p = curPTR;
! 1905: oldFile->lineno = lineno;
! 1906:
! 1907: (void) Lst_AtFront (includes, (ClientData)oldFile);
! 1908:
! 1909: /*
! 1910: * Once the previous state has been saved, we can get down to reading
! 1911: * the new file. We set up the name of the file to be the absolute
! 1912: * name of the include file so error messages refer to the right
! 1913: * place. Naturally enough, we start reading at line number 0.
! 1914: */
! 1915: fname = fullname;
! 1916: lineno = 0;
! 1917:
! 1918: curFILE = fopen (fullname, "r");
! 1919: curPTR = NULL;
! 1920: if (curFILE == (FILE * ) NULL) {
! 1921: Parse_Error (PARSE_FATAL, "Cannot open %s", fullname);
! 1922: /*
! 1923: * Pop to previous file
! 1924: */
! 1925: (void) ParseEOF(1);
! 1926: }
! 1927: }
! 1928: #endif
! 1929:
! 1930: /*-
! 1931: *---------------------------------------------------------------------
! 1932: * ParseEOF --
! 1933: * Called when EOF is reached in the current file. If we were reading
! 1934: * an include file, the includes stack is popped and things set up
! 1935: * to go back to reading the previous file at the previous location.
! 1936: *
! 1937: * Results:
! 1938: * CONTINUE if there's more to do. DONE if not.
! 1939: *
! 1940: * Side Effects:
! 1941: * The old curFILE, is closed. The includes list is shortened.
! 1942: * lineno, curFILE, and fname are changed if CONTINUE is returned.
! 1943: *---------------------------------------------------------------------
! 1944: */
! 1945: static int
! 1946: ParseEOF (opened)
! 1947: int opened;
! 1948: {
! 1949: IFile *ifile; /* the state on the top of the includes stack */
! 1950:
! 1951: if (Lst_IsEmpty (includes)) {
! 1952: return (DONE);
! 1953: }
! 1954:
! 1955: ifile = (IFile *) Lst_DeQueue (includes);
! 1956: free ((Address) fname);
! 1957: fname = ifile->fname;
! 1958: lineno = ifile->lineno;
! 1959: if (opened && curFILE)
! 1960: (void) fclose (curFILE);
! 1961: if (curPTR) {
! 1962: free((Address) curPTR->str);
! 1963: free((Address) curPTR);
! 1964: }
! 1965: curFILE = ifile->F;
! 1966: curPTR = ifile->p;
! 1967: free ((Address)ifile);
! 1968: return (CONTINUE);
! 1969: }
! 1970:
! 1971: /*-
! 1972: *---------------------------------------------------------------------
! 1973: * ParseReadc --
! 1974: * Read a character from the current file
! 1975: *
! 1976: * Results:
! 1977: * The character that was read
! 1978: *
! 1979: * Side Effects:
! 1980: *---------------------------------------------------------------------
! 1981: */
! 1982: static int
! 1983: ParseReadc()
! 1984: {
! 1985: if (curFILE)
! 1986: return fgetc(curFILE);
! 1987:
! 1988: if (curPTR && *curPTR->ptr)
! 1989: return *curPTR->ptr++;
! 1990: return EOF;
! 1991: }
! 1992:
! 1993:
! 1994: /*-
! 1995: *---------------------------------------------------------------------
! 1996: * ParseUnreadc --
! 1997: * Put back a character to the current file
! 1998: *
! 1999: * Results:
! 2000: * None.
! 2001: *
! 2002: * Side Effects:
! 2003: *---------------------------------------------------------------------
! 2004: */
! 2005: static void
! 2006: ParseUnreadc(c)
! 2007: int c;
! 2008: {
! 2009: if (curFILE) {
! 2010: ungetc(c, curFILE);
! 2011: return;
! 2012: }
! 2013: if (curPTR) {
! 2014: *--(curPTR->ptr) = c;
! 2015: return;
! 2016: }
! 2017: }
! 2018:
! 2019:
! 2020: /* ParseSkipLine():
! 2021: * Grab the next line
! 2022: */
! 2023: static char *
! 2024: ParseSkipLine(skip)
! 2025: int skip; /* Skip lines that don't start with . */
! 2026: {
! 2027: char *line;
! 2028: int c, lastc = '\0', lineLength;
! 2029: Buffer buf;
! 2030:
! 2031: c = ParseReadc();
! 2032:
! 2033: if (skip) {
! 2034: /*
! 2035: * Skip lines until get to one that begins with a
! 2036: * special char.
! 2037: */
! 2038: while ((c != '.') && (c != EOF)) {
! 2039: while (((c != '\n') || (lastc == '\\')) && (c != EOF))
! 2040: {
! 2041: /*
! 2042: * Advance to next unescaped newline
! 2043: */
! 2044: if ((lastc = c) == '\n') {
! 2045: lineno++;
! 2046: }
! 2047: c = ParseReadc();
! 2048: }
! 2049: lineno++;
! 2050:
! 2051: lastc = c;
! 2052: c = ParseReadc ();
! 2053: }
! 2054: }
! 2055:
! 2056: if (c == EOF) {
! 2057: Parse_Error (PARSE_FATAL, "Unclosed conditional/for loop");
! 2058: return ((char *)NULL);
! 2059: }
! 2060:
! 2061: /*
! 2062: * Read the entire line into buf
! 2063: */
! 2064: buf = Buf_Init (MAKE_BSIZE);
! 2065: if (c != '\n') {
! 2066: do {
! 2067: Buf_AddByte (buf, (Byte)c);
! 2068: c = ParseReadc();
! 2069: } while ((c != '\n') && (c != EOF));
! 2070: }
! 2071: lineno++;
! 2072:
! 2073: Buf_AddByte (buf, (Byte)'\0');
! 2074: line = (char *)Buf_GetAll (buf, &lineLength);
! 2075: Buf_Destroy (buf, FALSE);
! 2076: return line;
! 2077: }
! 2078:
! 2079:
! 2080: /*-
! 2081: *---------------------------------------------------------------------
! 2082: * ParseReadLine --
! 2083: * Read an entire line from the input file. Called only by Parse_File.
! 2084: * To facilitate escaped newlines and what have you, a character is
! 2085: * buffered in 'lastc', which is '\0' when no characters have been
! 2086: * read. When we break out of the loop, c holds the terminating
! 2087: * character and lastc holds a character that should be added to
! 2088: * the line (unless we don't read anything but a terminator).
! 2089: *
! 2090: * Results:
! 2091: * A line w/o its newline
! 2092: *
! 2093: * Side Effects:
! 2094: * Only those associated with reading a character
! 2095: *---------------------------------------------------------------------
! 2096: */
! 2097: static char *
! 2098: ParseReadLine ()
! 2099: {
! 2100: Buffer buf; /* Buffer for current line */
! 2101: register int c; /* the current character */
! 2102: register int lastc; /* The most-recent character */
! 2103: Boolean semiNL; /* treat semi-colons as newlines */
! 2104: Boolean ignDepOp; /* TRUE if should ignore dependency operators
! 2105: * for the purposes of setting semiNL */
! 2106: Boolean ignComment; /* TRUE if should ignore comments (in a
! 2107: * shell command */
! 2108: char *line; /* Result */
! 2109: char *ep; /* to strip trailing blanks */
! 2110: int lineLength; /* Length of result */
! 2111:
! 2112: semiNL = FALSE;
! 2113: ignDepOp = FALSE;
! 2114: ignComment = FALSE;
! 2115:
! 2116: /*
! 2117: * Handle special-characters at the beginning of the line. Either a
! 2118: * leading tab (shell command) or pound-sign (possible conditional)
! 2119: * forces us to ignore comments and dependency operators and treat
! 2120: * semi-colons as semi-colons (by leaving semiNL FALSE). This also
! 2121: * discards completely blank lines.
! 2122: */
! 2123: for (;;) {
! 2124: c = ParseReadc();
! 2125:
! 2126: if (c == '\t') {
! 2127: ignComment = ignDepOp = TRUE;
! 2128: break;
! 2129: } else if (c == '\n') {
! 2130: lineno++;
! 2131: } else if (c == '#') {
! 2132: ParseUnreadc(c);
! 2133: break;
! 2134: } else {
! 2135: /*
! 2136: * Anything else breaks out without doing anything
! 2137: */
! 2138: break;
! 2139: }
! 2140: }
! 2141:
! 2142: if (c != EOF) {
! 2143: lastc = c;
! 2144: buf = Buf_Init(MAKE_BSIZE);
! 2145:
! 2146: while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) &&
! 2147: (c != EOF))
! 2148: {
! 2149: test_char:
! 2150: switch(c) {
! 2151: case '\n':
! 2152: /*
! 2153: * Escaped newline: read characters until a non-space or an
! 2154: * unescaped newline and replace them all by a single space.
! 2155: * This is done by storing the space over the backslash and
! 2156: * dropping through with the next nonspace. If it is a
! 2157: * semi-colon and semiNL is TRUE, it will be recognized as a
! 2158: * newline in the code below this...
! 2159: */
! 2160: lineno++;
! 2161: lastc = ' ';
! 2162: while ((c = ParseReadc ()) == ' ' || c == '\t') {
! 2163: continue;
! 2164: }
! 2165: if (c == EOF || c == '\n') {
! 2166: goto line_read;
! 2167: } else {
! 2168: /*
! 2169: * Check for comments, semiNL's, etc. -- easier than
! 2170: * ParseUnreadc(c); continue;
! 2171: */
! 2172: goto test_char;
! 2173: }
! 2174: /*NOTREACHED*/
! 2175: break;
! 2176:
! 2177: case ';':
! 2178: /*
! 2179: * Semi-colon: Need to see if it should be interpreted as a
! 2180: * newline
! 2181: */
! 2182: if (semiNL) {
! 2183: /*
! 2184: * To make sure the command that may be following this
! 2185: * semi-colon begins with a tab, we push one back into the
! 2186: * input stream. This will overwrite the semi-colon in the
! 2187: * buffer. If there is no command following, this does no
! 2188: * harm, since the newline remains in the buffer and the
! 2189: * whole line is ignored.
! 2190: */
! 2191: ParseUnreadc('\t');
! 2192: goto line_read;
! 2193: }
! 2194: break;
! 2195: case '=':
! 2196: if (!semiNL) {
! 2197: /*
! 2198: * Haven't seen a dependency operator before this, so this
! 2199: * must be a variable assignment -- don't pay attention to
! 2200: * dependency operators after this.
! 2201: */
! 2202: ignDepOp = TRUE;
! 2203: } else if (lastc == ':' || lastc == '!') {
! 2204: /*
! 2205: * Well, we've seen a dependency operator already, but it
! 2206: * was the previous character, so this is really just an
! 2207: * expanded variable assignment. Revert semi-colons to
! 2208: * being just semi-colons again and ignore any more
! 2209: * dependency operators.
! 2210: *
! 2211: * XXX: Note that a line like "foo : a:=b" will blow up,
! 2212: * but who'd write a line like that anyway?
! 2213: */
! 2214: ignDepOp = TRUE; semiNL = FALSE;
! 2215: }
! 2216: break;
! 2217: case '#':
! 2218: if (!ignComment) {
! 2219: if (compatMake && (lastc != '\\')) {
! 2220: /*
! 2221: * If the character is a hash mark and it isn't escaped
! 2222: * (or we're being compatible), the thing is a comment.
! 2223: * Skip to the end of the line.
! 2224: */
! 2225: do {
! 2226: c = ParseReadc();
! 2227: } while ((c != '\n') && (c != EOF));
! 2228: goto line_read;
! 2229: } else {
! 2230: /*
! 2231: * Don't add the backslash. Just let the # get copied
! 2232: * over.
! 2233: */
! 2234: lastc = c;
! 2235: continue;
! 2236: }
! 2237: }
! 2238: break;
! 2239: case ':':
! 2240: case '!':
! 2241: if (!ignDepOp && (c == ':' || c == '!')) {
! 2242: /*
! 2243: * A semi-colon is recognized as a newline only on
! 2244: * dependency lines. Dependency lines are lines with a
! 2245: * colon or an exclamation point. Ergo...
! 2246: */
! 2247: semiNL = TRUE;
! 2248: }
! 2249: break;
! 2250: }
! 2251: /*
! 2252: * Copy in the previous character and save this one in lastc.
! 2253: */
! 2254: Buf_AddByte (buf, (Byte)lastc);
! 2255: lastc = c;
! 2256:
! 2257: }
! 2258: line_read:
! 2259: lineno++;
! 2260:
! 2261: if (lastc != '\0') {
! 2262: Buf_AddByte (buf, (Byte)lastc);
! 2263: }
! 2264: Buf_AddByte (buf, (Byte)'\0');
! 2265: line = (char *)Buf_GetAll (buf, &lineLength);
! 2266: Buf_Destroy (buf, FALSE);
! 2267:
! 2268: /*
! 2269: * Strip trailing blanks and tabs from the line.
! 2270: * Do not strip a blank or tab that is preceeded by
! 2271: * a '\'
! 2272: */
! 2273: ep = line;
! 2274: while (*ep)
! 2275: ++ep;
! 2276: while (ep > line && (ep[-1] == ' ' || ep[-1] == '\t')) {
! 2277: if (ep > line + 1 && ep[-2] == '\\')
! 2278: break;
! 2279: --ep;
! 2280: }
! 2281: *ep = 0;
! 2282:
! 2283: if (line[0] == '.') {
! 2284: /*
! 2285: * The line might be a conditional. Ask the conditional module
! 2286: * about it and act accordingly
! 2287: */
! 2288: switch (Cond_Eval (line)) {
! 2289: case COND_SKIP:
! 2290: /*
! 2291: * Skip to next conditional that evaluates to COND_PARSE.
! 2292: */
! 2293: do {
! 2294: free (line);
! 2295: line = ParseSkipLine(1);
! 2296: } while (line && Cond_Eval(line) != COND_PARSE);
! 2297: if (line == NULL)
! 2298: break;
! 2299: /*FALLTHRU*/
! 2300: case COND_PARSE:
! 2301: free ((Address) line);
! 2302: line = ParseReadLine();
! 2303: break;
! 2304: case COND_INVALID:
! 2305: if (For_Eval(line)) {
! 2306: int ok;
! 2307: free(line);
! 2308: do {
! 2309: /*
! 2310: * Skip after the matching end
! 2311: */
! 2312: line = ParseSkipLine(0);
! 2313: if (line == NULL) {
! 2314: Parse_Error (PARSE_FATAL,
! 2315: "Unexpected end of file in for loop.\n");
! 2316: break;
! 2317: }
! 2318: ok = For_Eval(line);
! 2319: free(line);
! 2320: }
! 2321: while (ok);
! 2322: if (line != NULL)
! 2323: For_Run();
! 2324: line = ParseReadLine();
! 2325: }
! 2326: break;
! 2327: }
! 2328: }
! 2329: return (line);
! 2330:
! 2331: } else {
! 2332: /*
! 2333: * Hit end-of-file, so return a NULL line to indicate this.
! 2334: */
! 2335: return((char *)NULL);
! 2336: }
! 2337: }
! 2338:
! 2339: /*-
! 2340: *-----------------------------------------------------------------------
! 2341: * ParseFinishLine --
! 2342: * Handle the end of a dependency group.
! 2343: *
! 2344: * Results:
! 2345: * Nothing.
! 2346: *
! 2347: * Side Effects:
! 2348: * inLine set FALSE. 'targets' list destroyed.
! 2349: *
! 2350: *-----------------------------------------------------------------------
! 2351: */
! 2352: static void
! 2353: ParseFinishLine()
! 2354: {
! 2355: if (inLine) {
! 2356: Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL);
! 2357: Lst_Destroy (targets, ParseHasCommands);
! 2358: targets = NULL;
! 2359: inLine = FALSE;
! 2360: }
! 2361: }
! 2362:
! 2363:
! 2364: /*-
! 2365: *---------------------------------------------------------------------
! 2366: * Parse_File --
! 2367: * Parse a file into its component parts, incorporating it into the
! 2368: * current dependency graph. This is the main function and controls
! 2369: * almost every other function in this module
! 2370: *
! 2371: * Results:
! 2372: * None
! 2373: *
! 2374: * Side Effects:
! 2375: * Loads. Nodes are added to the list of all targets, nodes and links
! 2376: * are added to the dependency graph. etc. etc. etc.
! 2377: *---------------------------------------------------------------------
! 2378: */
! 2379: void
! 2380: Parse_File(name, stream)
! 2381: char *name; /* the name of the file being read */
! 2382: FILE * stream; /* Stream open to makefile to parse */
! 2383: {
! 2384: register char *cp, /* pointer into the line */
! 2385: *line; /* the line we're working on */
! 2386:
! 2387: inLine = FALSE;
! 2388: fname = name;
! 2389: curFILE = stream;
! 2390: lineno = 0;
! 2391: fatals = 0;
! 2392:
! 2393: do {
! 2394: while ((line = ParseReadLine ()) != NULL) {
! 2395: if (*line == '.') {
! 2396: /*
! 2397: * Lines that begin with the special character are either
! 2398: * include or undef directives.
! 2399: */
! 2400: for (cp = line + 1; isspace (*cp); cp++) {
! 2401: continue;
! 2402: }
! 2403: if (strncmp (cp, "include", 7) == 0) {
! 2404: ParseDoInclude (cp + 7);
! 2405: goto nextLine;
! 2406: } else if (strncmp(cp, "undef", 5) == 0) {
! 2407: char *cp2;
! 2408: for (cp += 5; isspace((unsigned char) *cp); cp++) {
! 2409: continue;
! 2410: }
! 2411:
! 2412: for (cp2 = cp; !isspace((unsigned char) *cp2) &&
! 2413: (*cp2 != '\0'); cp2++) {
! 2414: continue;
! 2415: }
! 2416:
! 2417: *cp2 = '\0';
! 2418:
! 2419: Var_Delete(cp, VAR_GLOBAL);
! 2420: goto nextLine;
! 2421: }
! 2422: }
! 2423: if (*line == '#' || *line == '\0') {
! 2424: /* If we're this far, the line must be a comment.
! 2425: (Empty lines are ignored as well) */
! 2426: goto nextLine;
! 2427: }
! 2428:
! 2429: if (*line == '\t') {
! 2430: /*
! 2431: * If a line starts with a tab, it can only hope to be
! 2432: * a creation command.
! 2433: */
! 2434: #ifndef POSIX
! 2435: shellCommand:
! 2436: #endif
! 2437: for (cp = line + 1; isspace (*cp); cp++) {
! 2438: continue;
! 2439: }
! 2440: if (*cp) {
! 2441: if (inLine) {
! 2442: /*
! 2443: * So long as it's not a blank line and we're actually
! 2444: * in a dependency spec, add the command to the list of
! 2445: * commands of all targets in the dependency spec
! 2446: */
! 2447: Lst_ForEach (targets, ParseAddCmd, cp);
! 2448: Lst_AtEnd(targCmds, (ClientData) line);
! 2449: continue;
! 2450: } else {
! 2451: Parse_Error (PARSE_FATAL,
! 2452: "Unassociated shell command \"%.20s\"",
! 2453: cp);
! 2454: }
! 2455: }
! 2456: #ifdef SYSVINCLUDE
! 2457: } else if (strncmp (line, "include", 7) == 0 &&
! 2458: strchr(line, ':') == NULL) {
! 2459: /*
! 2460: * It's an S3/S5-style "include".
! 2461: */
! 2462: ParseTraditionalInclude (line + 7);
! 2463: goto nextLine;
! 2464: #endif
! 2465: } else if (Parse_IsVar (line)) {
! 2466: ParseFinishLine();
! 2467: Parse_DoVar (line, VAR_GLOBAL);
! 2468: } else {
! 2469: /*
! 2470: * We now know it's a dependency line so it needs to have all
! 2471: * variables expanded before being parsed. Tell the variable
! 2472: * module to complain if some variable is undefined...
! 2473: * To make life easier on novices, if the line is indented we
! 2474: * first make sure the line has a dependency operator in it.
! 2475: * If it doesn't have an operator and we're in a dependency
! 2476: * line's script, we assume it's actually a shell command
! 2477: * and add it to the current list of targets.
! 2478: */
! 2479: #ifndef POSIX
! 2480: Boolean nonSpace = FALSE;
! 2481: #endif
! 2482:
! 2483: cp = line;
! 2484: if (isspace((unsigned char) line[0])) {
! 2485: while ((*cp != '\0') && isspace((unsigned char) *cp)) {
! 2486: cp++;
! 2487: }
! 2488: if (*cp == '\0') {
! 2489: goto nextLine;
! 2490: }
! 2491: #ifndef POSIX
! 2492: while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) {
! 2493: nonSpace = TRUE;
! 2494: cp++;
! 2495: }
! 2496: #endif
! 2497: }
! 2498:
! 2499: #ifndef POSIX
! 2500: if (*cp == '\0') {
! 2501: if (inLine) {
! 2502: Parse_Error (PARSE_WARNING,
! 2503: "Shell command needs a leading tab");
! 2504: goto shellCommand;
! 2505: } else if (nonSpace) {
! 2506: Parse_Error (PARSE_FATAL, "Missing operator");
! 2507: }
! 2508: } else {
! 2509: #endif
! 2510: ParseFinishLine();
! 2511:
! 2512: cp = Var_Subst (NULL, line, VAR_CMD, TRUE);
! 2513: free (line);
! 2514: line = cp;
! 2515:
! 2516: /*
! 2517: * Need a non-circular list for the target nodes
! 2518: */
! 2519: if (targets)
! 2520: Lst_Destroy(targets, NOFREE);
! 2521:
! 2522: targets = Lst_Init (FALSE);
! 2523: inLine = TRUE;
! 2524:
! 2525: ParseDoDependency (line);
! 2526: #ifndef POSIX
! 2527: }
! 2528: #endif
! 2529: }
! 2530:
! 2531: nextLine:
! 2532:
! 2533: free (line);
! 2534: }
! 2535: /*
! 2536: * Reached EOF, but it may be just EOF of an include file...
! 2537: */
! 2538: } while (ParseEOF(1) == CONTINUE);
! 2539:
! 2540: /*
! 2541: * Make sure conditionals are clean
! 2542: */
! 2543: Cond_End();
! 2544:
! 2545: if (fatals) {
! 2546: fprintf (stderr, "Fatal errors encountered -- cannot continue\n");
! 2547: exit (1);
! 2548: }
! 2549: }
! 2550:
! 2551: /*-
! 2552: *---------------------------------------------------------------------
! 2553: * Parse_Init --
! 2554: * initialize the parsing module
! 2555: *
! 2556: * Results:
! 2557: * none
! 2558: *
! 2559: * Side Effects:
! 2560: * the parseIncPath list is initialized...
! 2561: *---------------------------------------------------------------------
! 2562: */
! 2563: void
! 2564: Parse_Init ()
! 2565: {
! 2566: char *cp = NULL, *start;
! 2567: /* avoid faults on read-only strings */
! 2568: static char syspath[] = _PATH_DEFSYSPATH;
! 2569:
! 2570: mainNode = NILGNODE;
! 2571: parseIncPath = Lst_Init (FALSE);
! 2572: sysIncPath = Lst_Init (FALSE);
! 2573: includes = Lst_Init (FALSE);
! 2574: targCmds = Lst_Init (FALSE);
! 2575:
! 2576: /*
! 2577: * Add the directories from the DEFSYSPATH (more than one may be given
! 2578: * as dir1:...:dirn) to the system include path.
! 2579: */
! 2580: for (start = syspath; *start != '\0'; start = cp) {
! 2581: for (cp = start; *cp != '\0' && *cp != ':'; cp++)
! 2582: continue;
! 2583: if (*cp == '\0') {
! 2584: Dir_AddDir(sysIncPath, start);
! 2585: } else {
! 2586: *cp++ = '\0';
! 2587: Dir_AddDir(sysIncPath, start);
! 2588: }
! 2589: }
! 2590: }
! 2591:
! 2592: void
! 2593: Parse_End()
! 2594: {
! 2595: Lst_Destroy(targCmds, (void (*) __P((ClientData))) free);
! 2596: if (targets)
! 2597: Lst_Destroy(targets, NOFREE);
! 2598: Lst_Destroy(sysIncPath, Dir_Destroy);
! 2599: Lst_Destroy(parseIncPath, Dir_Destroy);
! 2600: Lst_Destroy(includes, NOFREE); /* Should be empty now */
! 2601: }
! 2602:
! 2603:
! 2604: /*-
! 2605: *-----------------------------------------------------------------------
! 2606: * Parse_MainName --
! 2607: * Return a Lst of the main target to create for main()'s sake. If
! 2608: * no such target exists, we Punt with an obnoxious error message.
! 2609: *
! 2610: * Results:
! 2611: * A Lst of the single node to create.
! 2612: *
! 2613: * Side Effects:
! 2614: * None.
! 2615: *
! 2616: *-----------------------------------------------------------------------
! 2617: */
! 2618: Lst
! 2619: Parse_MainName()
! 2620: {
! 2621: Lst main; /* result list */
! 2622:
! 2623: main = Lst_Init (FALSE);
! 2624:
! 2625: if (mainNode == NILGNODE) {
! 2626: Punt ("make: no target to make.\n");
! 2627: /*NOTREACHED*/
! 2628: } else if (mainNode->type & OP_DOUBLEDEP) {
! 2629: (void) Lst_AtEnd (main, (ClientData)mainNode);
! 2630: Lst_Concat(main, mainNode->cohorts, LST_CONCNEW);
! 2631: }
! 2632: else
! 2633: (void) Lst_AtEnd (main, (ClientData)mainNode);
! 2634: return (main);
! 2635: }