cond.c revision 141104
1118611Snjl/*- 2118611Snjl * Copyright (c) 1988, 1989, 1990, 1993 3118611Snjl * The Regents of the University of California. All rights reserved. 4118611Snjl * Copyright (c) 1988, 1989 by Adam de Boor 5118611Snjl * Copyright (c) 1989 by Berkeley Softworks 6118611Snjl * All rights reserved. 7118611Snjl * 8118611Snjl * This code is derived from software contributed to Berkeley by 9118611Snjl * Adam de Boor. 10118611Snjl * 11118611Snjl * Redistribution and use in source and binary forms, with or without 12118611Snjl * modification, are permitted provided that the following conditions 13118611Snjl * are met: 14126372Snjl * 1. Redistributions of source code must retain the above copyright 15118611Snjl * notice, this list of conditions and the following disclaimer. 16118611Snjl * 2. Redistributions in binary form must reproduce the above copyright 17118611Snjl * notice, this list of conditions and the following disclaimer in the 18118611Snjl * documentation and/or other materials provided with the distribution. 19118611Snjl * 3. All advertising materials mentioning features or use of this software 20118611Snjl * must display the following acknowledgement: 21118611Snjl * This product includes software developed by the University of 22118611Snjl * California, Berkeley and its contributors. 23118611Snjl * 4. Neither the name of the University nor the names of its contributors 24118611Snjl * may be used to endorse or promote products derived from this software 25118611Snjl * without specific prior written permission. 26118611Snjl * 27118611Snjl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28118611Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29118611Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30118611Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32118611Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33118611Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34118611Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36118611Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37118611Snjl * SUCH DAMAGE. 38118611Snjl * 39118611Snjl * @(#)cond.c 8.2 (Berkeley) 1/2/94 40118611Snjl */ 41118611Snjl 42118611Snjl#include <sys/cdefs.h> 43118611Snjl__FBSDID("$FreeBSD: head/usr.bin/make/cond.c 141104 2005-02-01 10:50:37Z harti $"); 44118611Snjl 45118611Snjl/*- 46118611Snjl * cond.c -- 47118611Snjl * Functions to handle conditionals in a makefile. 48118611Snjl * 49118611Snjl * Interface: 50118611Snjl * Cond_Eval Evaluate the conditional in the passed line. 51118611Snjl * 52118611Snjl */ 53118611Snjl 54118611Snjl#include <ctype.h> 55118611Snjl#include <string.h> 56118611Snjl#include <stdlib.h> 57118611Snjl 58118611Snjl#include "cond.h" 59118611Snjl#include "dir.h" 60118611Snjl#include "globals.h" 61118611Snjl#include "GNode.h" 62118611Snjl#include "make.h" 63118611Snjl#include "parse.h" 64118611Snjl#include "sprite.h" 65118611Snjl#include "str.h" 66118611Snjl#include "targ.h" 67118611Snjl#include "util.h" 68118611Snjl#include "var.h" 69118611Snjl 70118611Snjl/* 71118611Snjl * The parsing of conditional expressions is based on this grammar: 72118611Snjl * E -> F || E 73118611Snjl * E -> F 74118611Snjl * F -> T && F 75118611Snjl * F -> T 76118611Snjl * T -> defined(variable) 77118611Snjl * T -> make(target) 78118611Snjl * T -> exists(file) 79118611Snjl * T -> empty(varspec) 80118611Snjl * T -> target(name) 81118611Snjl * T -> symbol 82118611Snjl * T -> $(varspec) op value 83118611Snjl * T -> $(varspec) == "string" 84118611Snjl * T -> $(varspec) != "string" 85118611Snjl * T -> ( E ) 86118611Snjl * T -> ! T 87118611Snjl * op -> == | != | > | < | >= | <= 88118611Snjl * 89118611Snjl * 'symbol' is some other symbol to which the default function (condDefProc) 90118611Snjl * is applied. 91118611Snjl * 92118611Snjl * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) 93118611Snjl * will return And for '&' and '&&', Or for '|' and '||', Not for '!', 94118611Snjl * LParen for '(', RParen for ')' and will evaluate the other terminal 95118611Snjl * symbols, using either the default function or the function given in the 96118611Snjl * terminal, and return the result as either True or False. 97118611Snjl * 98118611Snjl * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. 99118611Snjl */ 100118611Snjltypedef enum { 101118611Snjl And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err 102118611Snjl} Token; 103118611Snjl 104118611Snjl/*- 105118611Snjl * Structures to handle elegantly the different forms of #if's. The 106118611Snjl * last two fields are stored in condInvert and condDefProc, respectively. 107118611Snjl */ 108118611Snjlstatic void CondPushBack(Token); 109118611Snjlstatic int CondGetArg(char **, char **, char *, Boolean); 110118611Snjlstatic Boolean CondDoDefined(int, char *); 111118611Snjlstatic Boolean CondDoMake(int, char *); 112118611Snjlstatic Boolean CondDoExists(int, char *); 113118611Snjlstatic Boolean CondDoTarget(int, char *); 114118611Snjlstatic char * CondCvtArg(char *, double *); 115118611Snjlstatic Token CondToken(Boolean); 116118611Snjlstatic Token CondT(Boolean); 117118611Snjlstatic Token CondF(Boolean); 118118611Snjlstatic Token CondE(Boolean); 119118611Snjl 120118611Snjlstatic struct If { 121118611Snjl char *form; /* Form of if */ 122118611Snjl int formlen; /* Length of form */ 123118611Snjl Boolean doNot; /* TRUE if default function should be negated */ 124118611Snjl Boolean (*defProc)(int, char *); /* Default function to apply */ 125118611Snjl} ifs[] = { 126118611Snjl { "ifdef", 5, FALSE, CondDoDefined }, 127118611Snjl { "ifndef", 6, TRUE, CondDoDefined }, 128118611Snjl { "ifmake", 6, FALSE, CondDoMake }, 129118611Snjl { "ifnmake", 7, TRUE, CondDoMake }, 130118611Snjl { "if", 2, FALSE, CondDoDefined }, 131118611Snjl { NULL, 0, FALSE, NULL } 132118611Snjl}; 133118611Snjl 134118611Snjlstatic Boolean condInvert; /* Invert the default function */ 135118611Snjlstatic Boolean (*condDefProc) /* Default function to apply */ 136118611Snjl(int, char *); 137118611Snjlstatic char *condExpr; /* The expression to parse */ 138118611Snjlstatic Token condPushBack=None; /* Single push-back token used in 139118611Snjl * parsing */ 140118611Snjl 141118611Snjl#define MAXIF 30 /* greatest depth of #if'ing */ 142118611Snjl 143118611Snjlstatic Boolean condStack[MAXIF]; /* Stack of conditionals's values */ 144118611Snjlstatic int condLineno[MAXIF]; /* Line numbers of the opening .if */ 145118611Snjlstatic int condTop = MAXIF; /* Top-most conditional */ 146118611Snjlstatic int skipIfLevel=0; /* Depth of skipped conditionals */ 147118611Snjlstatic int skipIfLineno[MAXIF]; /* Line numbers of skipped .ifs */ 148118611Snjlstatic Boolean skipLine = FALSE; /* Whether the parse module is skipping 149118611Snjl * lines */ 150118611Snjl 151118611Snjl/*- 152118611Snjl *----------------------------------------------------------------------- 153118611Snjl * CondPushBack -- 154118611Snjl * Push back the most recent token read. We only need one level of 155118611Snjl * this, so the thing is just stored in 'condPushback'. 156118611Snjl * 157118611Snjl * Results: 158118611Snjl * None. 159118611Snjl * 160118611Snjl * Side Effects: 161118611Snjl * condPushback is overwritten. 162118611Snjl * 163118611Snjl *----------------------------------------------------------------------- 164118611Snjl */ 165118611Snjlstatic void 166118611SnjlCondPushBack(Token t) 167118611Snjl{ 168118611Snjl 169118611Snjl condPushBack = t; 170118611Snjl} 171118611Snjl 172118611Snjl/*- 173118611Snjl *----------------------------------------------------------------------- 174118611Snjl * CondGetArg -- 175118611Snjl * Find the argument of a built-in function. parens is set to TRUE 176118611Snjl * if the arguments are bounded by parens. 177118611Snjl * 178118611Snjl * Results: 179118611Snjl * The length of the argument and the address of the argument. 180118611Snjl * 181118611Snjl * Side Effects: 182118611Snjl * The pointer is set to point to the closing parenthesis of the 183118611Snjl * function call. 184118611Snjl * 185118611Snjl *----------------------------------------------------------------------- 186118611Snjl */ 187118611Snjlstatic int 188118611SnjlCondGetArg(char **linePtr, char **argPtr, char *func, Boolean parens) 189118611Snjl{ 190118611Snjl char *cp; 191118611Snjl size_t argLen; 192118611Snjl Buffer buf; 193118611Snjl 194118611Snjl cp = *linePtr; 195118611Snjl if (parens) { 196118611Snjl while (*cp != '(' && *cp != '\0') { 197118611Snjl cp++; 198118611Snjl } 199118611Snjl if (*cp == '(') { 200118611Snjl cp++; 201118611Snjl } 202118611Snjl } 203118611Snjl 204118611Snjl if (*cp == '\0') { 205118611Snjl /* 206118611Snjl * No arguments whatsoever. Because 'make' and 'defined' aren't really 207118611Snjl * "reserved words", we don't print a message. I think this is better 208118611Snjl * than hitting the user with a warning message every time s/he uses 209118611Snjl * the word 'make' or 'defined' at the beginning of a symbol... 210138287Smarks */ 211118611Snjl *argPtr = cp; 212118611Snjl return (0); 213118611Snjl } 214118611Snjl 215118611Snjl while (*cp == ' ' || *cp == '\t') { 216118611Snjl cp++; 217118611Snjl } 218118611Snjl 219118611Snjl /* 220118611Snjl * Create a buffer for the argument and start it out at 16 characters 221118611Snjl * long. Why 16? Why not? 222118611Snjl */ 223118611Snjl buf = Buf_Init(16); 224118611Snjl 225118611Snjl while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) { 226118611Snjl if (*cp == '$') { 227118611Snjl /* 228118611Snjl * Parse the variable spec and install it as part of the argument 229118611Snjl * if it's valid. We tell Var_Parse to complain on an undefined 230118611Snjl * variable, so we don't do it too. Nor do we return an error, 231118611Snjl * though perhaps we should... 232118611Snjl */ 233118611Snjl char *cp2; 234118611Snjl size_t len; 235118611Snjl Boolean doFree; 236118611Snjl 237118611Snjl cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); 238118611Snjl 239118611Snjl Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); 240118611Snjl if (doFree) { 241118611Snjl free(cp2); 242118611Snjl } 243118611Snjl cp += len; 244118611Snjl } else { 245118611Snjl Buf_AddByte(buf, (Byte)*cp); 246118611Snjl cp++; 247118611Snjl } 248118611Snjl } 249118611Snjl 250118611Snjl Buf_AddByte(buf, (Byte)'\0'); 251118611Snjl *argPtr = (char *)Buf_GetAll(buf, &argLen); 252118611Snjl Buf_Destroy(buf, FALSE); 253118611Snjl 254118611Snjl while (*cp == ' ' || *cp == '\t') { 255118611Snjl cp++; 256118611Snjl } 257118611Snjl if (parens && *cp != ')') { 258118611Snjl Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", 259118611Snjl func); 260118611Snjl return (0); 261118611Snjl } else if (parens) { 262118611Snjl /* 263118611Snjl * Advance pointer past close parenthesis. 264118611Snjl */ 265118611Snjl cp++; 266118611Snjl } 267118611Snjl 268118611Snjl *linePtr = cp; 269118611Snjl return (argLen); 270118611Snjl} 271118611Snjl 272118611Snjl/*- 273118611Snjl *----------------------------------------------------------------------- 274118611Snjl * CondDoDefined -- 275118611Snjl * Handle the 'defined' function for conditionals. 276118611Snjl * 277118611Snjl * Results: 278118611Snjl * TRUE if the given variable is defined. 279118611Snjl * 280118611Snjl * Side Effects: 281118611Snjl * None. 282118611Snjl * 283138287Smarks *----------------------------------------------------------------------- 284118611Snjl */ 285118611Snjlstatic Boolean 286118611SnjlCondDoDefined(int argLen, char *arg) 287118611Snjl{ 288118611Snjl char savec = arg[argLen]; 289118611Snjl char *p1; 290118611Snjl Boolean result; 291118611Snjl 292118611Snjl arg[argLen] = '\0'; 293118611Snjl if (Var_Value(arg, VAR_CMD, &p1) != NULL) { 294118611Snjl result = TRUE; 295118611Snjl } else { 296118611Snjl result = FALSE; 297118611Snjl } 298118611Snjl free(p1); 299118611Snjl arg[argLen] = savec; 300118611Snjl return (result); 301118611Snjl} 302118611Snjl 303118611Snjl/*- 304118611Snjl *----------------------------------------------------------------------- 305118611Snjl * CondStrMatch -- 306118611Snjl * Front-end for Str_Match so it returns 0 on match and non-zero 307118611Snjl * on mismatch. Callback function for CondDoMake via Lst_Find 308118611Snjl * 309118611Snjl * Results: 310118611Snjl * 0 if string matches pattern 311118611Snjl * 312118611Snjl * Side Effects: 313118611Snjl * None 314118611Snjl * 315118611Snjl *----------------------------------------------------------------------- 316118611Snjl */ 317118611Snjlstatic int 318138287SmarksCondStrMatch(const void *string, const void *pattern) 319118611Snjl{ 320118611Snjl 321118611Snjl return (!Str_Match(string, pattern)); 322118611Snjl} 323138287Smarks 324118611Snjl/*- 325138287Smarks *----------------------------------------------------------------------- 326138287Smarks * CondDoMake -- 327138287Smarks * Handle the 'make' function for conditionals. 328118611Snjl * 329118611Snjl * Results: 330118611Snjl * TRUE if the given target is being made. 331118611Snjl * 332118611Snjl * Side Effects: 333118611Snjl * None. 334118611Snjl * 335118611Snjl *----------------------------------------------------------------------- 336118611Snjl */ 337118611Snjlstatic Boolean 338138287SmarksCondDoMake(int argLen, char *arg) 339118611Snjl{ 340118611Snjl char savec = arg[argLen]; 341118611Snjl Boolean result; 342118611Snjl 343118611Snjl arg[argLen] = '\0'; 344118611Snjl if (Lst_Find(&create, arg, CondStrMatch) == NULL) { 345118611Snjl result = FALSE; 346138287Smarks } else { 347118611Snjl result = TRUE; 348118611Snjl } 349118611Snjl arg[argLen] = savec; 350118611Snjl return (result); 351118611Snjl} 352118611Snjl 353118611Snjl/*- 354118611Snjl *----------------------------------------------------------------------- 355118611Snjl * CondDoExists -- 356118611Snjl * See if the given file exists. 357118611Snjl * 358118611Snjl * Results: 359118611Snjl * TRUE if the file exists and FALSE if it does not. 360118611Snjl * 361118611Snjl * Side Effects: 362118611Snjl * None. 363118611Snjl * 364118611Snjl *----------------------------------------------------------------------- 365118611Snjl */ 366118611Snjlstatic Boolean 367118611SnjlCondDoExists(int argLen, char *arg) 368118611Snjl{ 369118611Snjl char savec = arg[argLen]; 370118611Snjl Boolean result; 371118611Snjl char *path; 372118611Snjl 373118611Snjl arg[argLen] = '\0'; 374118611Snjl path = Dir_FindFile(arg, &dirSearchPath); 375118611Snjl if (path != NULL) { 376118611Snjl result = TRUE; 377118611Snjl free(path); 378118611Snjl } else { 379118611Snjl result = FALSE; 380118611Snjl } 381118611Snjl arg[argLen] = savec; 382118611Snjl return (result); 383118611Snjl} 384118611Snjl 385118611Snjl/*- 386118611Snjl *----------------------------------------------------------------------- 387118611Snjl * CondDoTarget -- 388118611Snjl * See if the given node exists and is an actual target. 389118611Snjl * 390118611Snjl * Results: 391118611Snjl * TRUE if the node exists as a target and FALSE if it does not. 392118611Snjl * 393118611Snjl * Side Effects: 394118611Snjl * None. 395118611Snjl * 396118611Snjl *----------------------------------------------------------------------- 397118611Snjl */ 398118611Snjlstatic Boolean 399118611SnjlCondDoTarget(int argLen, char *arg) 400118611Snjl{ 401118611Snjl char savec = arg[argLen]; 402118611Snjl Boolean result; 403118611Snjl GNode *gn; 404118611Snjl 405118611Snjl arg[argLen] = '\0'; 406118611Snjl gn = Targ_FindNode(arg, TARG_NOCREATE); 407118611Snjl if ((gn != NULL) && !OP_NOP(gn->type)) { 408118611Snjl result = TRUE; 409118611Snjl } else { 410118611Snjl result = FALSE; 411118611Snjl } 412118611Snjl arg[argLen] = savec; 413118611Snjl return (result); 414118611Snjl} 415118611Snjl 416118611Snjl/*- 417118611Snjl *----------------------------------------------------------------------- 418118611Snjl * CondCvtArg -- 419118611Snjl * Convert the given number into a double. If the number begins 420118611Snjl * with 0x, it is interpreted as a hexadecimal integer 421118611Snjl * and converted to a double from there. All other strings just have 422118611Snjl * strtod called on them. 423118611Snjl * 424118611Snjl * Results: 425118611Snjl * Sets 'value' to double value of string. 426118611Snjl * Returns address of the first character after the last valid 427118611Snjl * character of the converted number. 428118611Snjl * 429118611Snjl * Side Effects: 430118611Snjl * Can change 'value' even if string is not a valid number. 431118611Snjl * 432118611Snjl * 433118611Snjl *----------------------------------------------------------------------- 434118611Snjl */ 435118611Snjlstatic char * 436118611SnjlCondCvtArg(char *str, double *value) 437118611Snjl{ 438118611Snjl if ((*str == '0') && (str[1] == 'x')) { 439118611Snjl long i; 440118611Snjl 441118611Snjl for (str += 2, i = 0; ; str++) { 442118611Snjl int x; 443118611Snjl if (isdigit((unsigned char)*str)) 444118611Snjl x = *str - '0'; 445118611Snjl else if (isxdigit((unsigned char)*str)) 446118611Snjl x = 10 + *str - isupper((unsigned char)*str) ? 'A' : 'a'; 447118611Snjl else { 448118611Snjl *value = (double)i; 449118611Snjl return (str); 450118611Snjl } 451118611Snjl i = (i << 4) + x; 452118611Snjl } 453118611Snjl } 454118611Snjl else { 455118611Snjl char *eptr; 456118611Snjl *value = strtod(str, &eptr); 457118611Snjl return (eptr); 458118611Snjl } 459118611Snjl} 460118611Snjl 461118611Snjl/*- 462118611Snjl *----------------------------------------------------------------------- 463118611Snjl * CondToken -- 464118611Snjl * Return the next token from the input. 465118611Snjl * 466118611Snjl * Results: 467118611Snjl * A Token for the next lexical token in the stream. 468118611Snjl * 469118611Snjl * Side Effects: 470118611Snjl * condPushback will be set back to None if it is used. 471118611Snjl * 472118611Snjl *----------------------------------------------------------------------- 473118611Snjl */ 474118611Snjlstatic Token 475118611SnjlCondToken(Boolean doEval) 476118611Snjl{ 477118611Snjl Token t; 478118611Snjl 479118611Snjl if (condPushBack == None) { 480118611Snjl while (*condExpr == ' ' || *condExpr == '\t') { 481118611Snjl condExpr++; 482118611Snjl } 483118611Snjl switch (*condExpr) { 484118611Snjl case '(': 485118611Snjl t = LParen; 486118611Snjl condExpr++; 487118611Snjl break; 488118611Snjl case ')': 489118611Snjl t = RParen; 490118611Snjl condExpr++; 491118611Snjl break; 492118611Snjl case '|': 493118611Snjl if (condExpr[1] == '|') { 494118611Snjl condExpr++; 495118611Snjl } 496118611Snjl condExpr++; 497118611Snjl t = Or; 498118611Snjl break; 499118611Snjl case '&': 500118611Snjl if (condExpr[1] == '&') { 501118611Snjl condExpr++; 502118611Snjl } 503118611Snjl condExpr++; 504118611Snjl t = And; 505118611Snjl break; 506118611Snjl case '!': 507118611Snjl t = Not; 508118611Snjl condExpr++; 509118611Snjl break; 510118611Snjl case '\n': 511118611Snjl case '\0': 512118611Snjl t = EndOfFile; 513118611Snjl break; 514118611Snjl case '$': { 515118611Snjl char *lhs; 516118611Snjl char *rhs; 517118611Snjl char *op; 518118611Snjl size_t varSpecLen; 519118611Snjl Boolean doFree; 520118611Snjl 521118611Snjl /* 522118611Snjl * Parse the variable spec and skip over it, saving its 523118611Snjl * value in lhs. 524118611Snjl */ 525118611Snjl t = Err; 526118611Snjl lhs = Var_Parse(condExpr, VAR_CMD, doEval, 527118611Snjl &varSpecLen, &doFree); 528118611Snjl if (lhs == var_Error) { 529118611Snjl /* 530118611Snjl * Even if !doEval, we still report syntax errors, which 531118611Snjl * is what getting var_Error back with !doEval means. 532118611Snjl */ 533118611Snjl return (Err); 534118611Snjl } 535118611Snjl condExpr += varSpecLen; 536118611Snjl 537118611Snjl if (!isspace((unsigned char)*condExpr) && 538118611Snjl strchr("!=><", *condExpr) == NULL) { 539118611Snjl Buffer buf; 540118611Snjl char *cp; 541118611Snjl 542118611Snjl buf = Buf_Init(0); 543118611Snjl 544118611Snjl for (cp = lhs; *cp; cp++) 545118611Snjl Buf_AddByte(buf, (Byte)*cp); 546118611Snjl 547118611Snjl if (doFree) 548118611Snjl free(lhs); 549118611Snjl 550118611Snjl for (;*condExpr && !isspace((unsigned char) *condExpr); 551118611Snjl condExpr++) 552118611Snjl Buf_AddByte(buf, (Byte)*condExpr); 553118611Snjl 554118611Snjl Buf_AddByte(buf, (Byte)'\0'); 555118611Snjl lhs = (char *)Buf_GetAll(buf, &varSpecLen); 556118611Snjl Buf_Destroy(buf, FALSE); 557118611Snjl 558118611Snjl doFree = TRUE; 559118611Snjl } 560118611Snjl 561118611Snjl /* 562118611Snjl * Skip whitespace to get to the operator 563118611Snjl */ 564118611Snjl while (isspace((unsigned char)*condExpr)) 565118611Snjl condExpr++; 566118611Snjl 567118611Snjl /* 568118611Snjl * Make sure the operator is a valid one. If it isn't a 569118611Snjl * known relational operator, pretend we got a 570118611Snjl * != 0 comparison. 571118611Snjl */ 572118611Snjl op = condExpr; 573118611Snjl switch (*condExpr) { 574118611Snjl case '!': 575118611Snjl case '=': 576118611Snjl case '<': 577118611Snjl case '>': 578118611Snjl if (condExpr[1] == '=') { 579118611Snjl condExpr += 2; 580118611Snjl } else { 581118611Snjl condExpr += 1; 582118611Snjl } 583118611Snjl break; 584118611Snjl default: 585118611Snjl op = "!="; 586118611Snjl rhs = "0"; 587118611Snjl 588118611Snjl goto do_compare; 589118611Snjl } 590118611Snjl while (isspace((unsigned char)*condExpr)) { 591118611Snjl condExpr++; 592118611Snjl } 593118611Snjl if (*condExpr == '\0') { 594118611Snjl Parse_Error(PARSE_WARNING, 595118611Snjl "Missing right-hand-side of operator"); 596118611Snjl goto error; 597118611Snjl } 598118611Snjl rhs = condExpr; 599118611Snjldo_compare: 600118611Snjl if (*rhs == '"') { 601118611Snjl /* 602118611Snjl * Doing a string comparison. Only allow == and != for 603118611Snjl * operators. 604118611Snjl */ 605118611Snjl char *string; 606118611Snjl char *cp, *cp2; 607118611Snjl int qt; 608118611Snjl Buffer buf; 609118611Snjl 610118611Snjldo_string_compare: 611118611Snjl if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { 612118611Snjl Parse_Error(PARSE_WARNING, 613118611Snjl "String comparison operator should be either == or !="); 614118611Snjl goto error; 615118611Snjl } 616118611Snjl 617118611Snjl buf = Buf_Init(0); 618118611Snjl qt = *rhs == '"' ? 1 : 0; 619118611Snjl 620118611Snjl for (cp = &rhs[qt]; 621118611Snjl ((qt && (*cp != '"')) || 622118611Snjl (!qt && strchr(" \t)", *cp) == NULL)) && 623118611Snjl (*cp != '\0'); cp++) { 624118611Snjl if ((*cp == '\\') && (cp[1] != '\0')) { 625118611Snjl /* 626118611Snjl * Backslash escapes things -- skip over next 627118611Snjl * character, if it exists. 628118611Snjl */ 629118611Snjl cp++; 630118611Snjl Buf_AddByte(buf, (Byte)*cp); 631118611Snjl } else if (*cp == '$') { 632118611Snjl size_t len; 633118611Snjl Boolean freeIt; 634118611Snjl 635118611Snjl cp2 = Var_Parse(cp, VAR_CMD, doEval, &len, &freeIt); 636118611Snjl if (cp2 != var_Error) { 637118611Snjl Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); 638118611Snjl if (freeIt) { 639118611Snjl free(cp2); 640118611Snjl } 641118611Snjl cp += len - 1; 642118611Snjl } else { 643118611Snjl Buf_AddByte(buf, (Byte)*cp); 644118611Snjl } 645118611Snjl } else { 646118611Snjl Buf_AddByte(buf, (Byte)*cp); 647118611Snjl } 648118611Snjl } 649118611Snjl 650118611Snjl Buf_AddByte(buf, (Byte)0); 651118611Snjl 652118611Snjl string = (char *)Buf_GetAll(buf, (size_t *)NULL); 653118611Snjl Buf_Destroy(buf, FALSE); 654118611Snjl 655118611Snjl DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", 656118611Snjl lhs, string, op)); 657118611Snjl /* 658118611Snjl * Null-terminate rhs and perform the comparison. 659118611Snjl * t is set to the result. 660118611Snjl */ 661118611Snjl if (*op == '=') { 662118611Snjl t = strcmp(lhs, string) ? False : True; 663118611Snjl } else { 664118611Snjl t = strcmp(lhs, string) ? True : False; 665118611Snjl } 666118611Snjl free(string); 667118611Snjl if (rhs == condExpr) { 668118611Snjl if (!qt && *cp == ')') 669118611Snjl condExpr = cp; 670118611Snjl else 671118611Snjl condExpr = cp + 1; 672118611Snjl } 673118611Snjl } else { 674118611Snjl /* 675118611Snjl * rhs is either a float or an integer. Convert both the 676118611Snjl * lhs and the rhs to a double and compare the two. 677118611Snjl */ 678118611Snjl double left, right; 679118611Snjl char *string; 680118611Snjl 681118611Snjl if (*CondCvtArg(lhs, &left) != '\0') 682118611Snjl goto do_string_compare; 683118611Snjl if (*rhs == '$') { 684118611Snjl size_t len; 685118611Snjl Boolean freeIt; 686118611Snjl 687118611Snjl string = Var_Parse(rhs, VAR_CMD, doEval, &len, &freeIt); 688118611Snjl if (string == var_Error) { 689118611Snjl right = 0.0; 690118611Snjl } else { 691118611Snjl if (*CondCvtArg(string, &right) != '\0') { 692118611Snjl if (freeIt) 693118611Snjl free(string); 694118611Snjl goto do_string_compare; 695118611Snjl } 696118611Snjl if (freeIt) 697118611Snjl free(string); 698118611Snjl if (rhs == condExpr) 699118611Snjl condExpr += len; 700118611Snjl } 701118611Snjl } else { 702118611Snjl char *c = CondCvtArg(rhs, &right); 703118611Snjl if (c == rhs) 704118611Snjl goto do_string_compare; 705118611Snjl if (rhs == condExpr) { 706118611Snjl /* 707118611Snjl * Skip over the right-hand side 708118611Snjl */ 709118611Snjl condExpr = c; 710118611Snjl } 711118611Snjl } 712118611Snjl 713118611Snjl DEBUGF(COND, ("left = %f, right = %f, op = %.2s\n", left, 714118611Snjl right, op)); 715118611Snjl switch (op[0]) { 716118611Snjl case '!': 717118611Snjl if (op[1] != '=') { 718118611Snjl Parse_Error(PARSE_WARNING, 719118611Snjl "Unknown operator"); 720118611Snjl goto error; 721118611Snjl } 722118611Snjl t = (left != right ? True : False); 723118611Snjl break; 724118611Snjl case '=': 725118611Snjl if (op[1] != '=') { 726118611Snjl Parse_Error(PARSE_WARNING, 727118611Snjl "Unknown operator"); 728118611Snjl goto error; 729118611Snjl } 730118611Snjl t = (left == right ? True : False); 731118611Snjl break; 732118611Snjl case '<': 733118611Snjl if (op[1] == '=') { 734118611Snjl t = (left <= right ? True : False); 735118611Snjl } else { 736118611Snjl t = (left < right ? True : False); 737118611Snjl } 738118611Snjl break; 739118611Snjl case '>': 740118611Snjl if (op[1] == '=') { 741118611Snjl t = (left >= right ? True : False); 742118611Snjl } else { 743118611Snjl t = (left > right ? True : False); 744118611Snjl } 745118611Snjl break; 746118611Snjl default: 747118611Snjl break; 748118611Snjl } 749118611Snjl } 750118611Snjlerror: 751118611Snjl if (doFree) 752118611Snjl free(lhs); 753118611Snjl break; 754118611Snjl } 755118611Snjl default: { 756118611Snjl Boolean (*evalProc)(int, char *); 757118611Snjl Boolean invert = FALSE; 758118611Snjl char *arg; 759118611Snjl int arglen; 760118611Snjl 761118611Snjl if (strncmp(condExpr, "defined", 7) == 0) { 762118611Snjl /* 763118611Snjl * Use CondDoDefined to evaluate the argument and 764118611Snjl * CondGetArg to extract the argument from the 'function 765118611Snjl * call'. 766118611Snjl */ 767118611Snjl evalProc = CondDoDefined; 768118611Snjl condExpr += 7; 769118611Snjl arglen = CondGetArg(&condExpr, &arg, "defined", TRUE); 770118611Snjl if (arglen == 0) { 771118611Snjl condExpr -= 7; 772118611Snjl goto use_default; 773118611Snjl } 774118611Snjl } else if (strncmp(condExpr, "make", 4) == 0) { 775118611Snjl /* 776118611Snjl * Use CondDoMake to evaluate the argument and 777118611Snjl * CondGetArg to extract the argument from the 'function 778118611Snjl * call'. 779118611Snjl */ 780118611Snjl evalProc = CondDoMake; 781118611Snjl condExpr += 4; 782118611Snjl arglen = CondGetArg(&condExpr, &arg, "make", TRUE); 783118611Snjl if (arglen == 0) { 784118611Snjl condExpr -= 4; 785118611Snjl goto use_default; 786118611Snjl } 787118611Snjl } else if (strncmp(condExpr, "exists", 6) == 0) { 788118611Snjl /* 789118611Snjl * Use CondDoExists to evaluate the argument and 790118611Snjl * CondGetArg to extract the argument from the 791118611Snjl * 'function call'. 792118611Snjl */ 793118611Snjl evalProc = CondDoExists; 794118611Snjl condExpr += 6; 795118611Snjl arglen = CondGetArg(&condExpr, &arg, "exists", TRUE); 796118611Snjl if (arglen == 0) { 797118611Snjl condExpr -= 6; 798118611Snjl goto use_default; 799118611Snjl } 800118611Snjl } else if (strncmp(condExpr, "empty", 5) == 0) { 801118611Snjl /* 802118611Snjl * Use Var_Parse to parse the spec in parens and return 803118611Snjl * True if the resulting string is empty. 804118611Snjl */ 805118611Snjl size_t length; 806118611Snjl Boolean doFree; 807118611Snjl char *val; 808118611Snjl 809118611Snjl condExpr += 5; 810118611Snjl 811118611Snjl for (arglen = 0; 812118611Snjl condExpr[arglen] != '(' && condExpr[arglen] != '\0'; 813118611Snjl arglen += 1) 814118611Snjl continue; 815118611Snjl 816118611Snjl if (condExpr[arglen] != '\0') { 817118611Snjl val = Var_Parse(&condExpr[arglen - 1], VAR_CMD, 818118611Snjl FALSE, &length, &doFree); 819118611Snjl if (val == var_Error) { 820118611Snjl t = Err; 821118611Snjl } else { 822118611Snjl /* 823118611Snjl * A variable is empty when it just contains 824118611Snjl * spaces... 4/15/92, christos 825118611Snjl */ 826118611Snjl char *p; 827118611Snjl for (p = val; *p && isspace((unsigned char)*p); p++) 828118611Snjl continue; 829118611Snjl t = (*p == '\0') ? True : False; 830118611Snjl } 831118611Snjl if (doFree) { 832118611Snjl free(val); 833118611Snjl } 834118611Snjl /* 835118611Snjl * Advance condExpr to beyond the closing ). Note that 836118611Snjl * we subtract one from arglen + length b/c length 837118611Snjl * is calculated from condExpr[arglen - 1]. 838118611Snjl */ 839118611Snjl condExpr += arglen + length - 1; 840118611Snjl } else { 841118611Snjl condExpr -= 5; 842118611Snjl goto use_default; 843118611Snjl } 844118611Snjl break; 845118611Snjl } else if (strncmp(condExpr, "target", 6) == 0) { 846118611Snjl /* 847118611Snjl * Use CondDoTarget to evaluate the argument and 848118611Snjl * CondGetArg to extract the argument from the 849118611Snjl * 'function call'. 850118611Snjl */ 851118611Snjl evalProc = CondDoTarget; 852118611Snjl condExpr += 6; 853118611Snjl arglen = CondGetArg(&condExpr, &arg, "target", TRUE); 854118611Snjl if (arglen == 0) { 855118611Snjl condExpr -= 6; 856118611Snjl goto use_default; 857118611Snjl } 858118611Snjl } else { 859118611Snjl /* 860118611Snjl * The symbol is itself the argument to the default 861118611Snjl * function. We advance condExpr to the end of the symbol 862118611Snjl * by hand (the next whitespace, closing paren or 863118611Snjl * binary operator) and set to invert the evaluation 864118611Snjl * function if condInvert is TRUE. 865118611Snjl */ 866118611Snjl use_default: 867118611Snjl invert = condInvert; 868118611Snjl evalProc = condDefProc; 869118611Snjl arglen = CondGetArg(&condExpr, &arg, "", FALSE); 870118611Snjl } 871118611Snjl 872118611Snjl /* 873118611Snjl * Evaluate the argument using the set function. If invert 874118611Snjl * is TRUE, we invert the sense of the function. 875118611Snjl */ 876118611Snjl t = (!doEval || (* evalProc) (arglen, arg) ? 877118611Snjl (invert ? False : True) : 878118611Snjl (invert ? True : False)); 879118611Snjl free(arg); 880118611Snjl break; 881118611Snjl } 882118611Snjl } 883118611Snjl } else { 884118611Snjl t = condPushBack; 885118611Snjl condPushBack = None; 886118611Snjl } 887118611Snjl return (t); 888118611Snjl} 889118611Snjl 890118611Snjl/*- 891118611Snjl *----------------------------------------------------------------------- 892118611Snjl * CondT -- 893118611Snjl * Parse a single term in the expression. This consists of a terminal 894118611Snjl * symbol or Not and a terminal symbol (not including the binary 895118611Snjl * operators): 896118611Snjl * T -> defined(variable) | make(target) | exists(file) | symbol 897118611Snjl * T -> ! T | ( E ) 898138287Smarks * 899138287Smarks * Results: 900138287Smarks * True, False or Err. 901118611Snjl * 902118611Snjl * Side Effects: 903118611Snjl * Tokens are consumed. 904118611Snjl * 905118611Snjl *----------------------------------------------------------------------- 906118611Snjl */ 907118611Snjlstatic Token 908118611SnjlCondT(Boolean doEval) 909118611Snjl{ 910118611Snjl Token t; 911118611Snjl 912118611Snjl t = CondToken(doEval); 913118611Snjl 914118611Snjl if (t == EndOfFile) { 915118611Snjl /* 916118611Snjl * If we reached the end of the expression, the expression 917118611Snjl * is malformed... 918118611Snjl */ 919118611Snjl t = Err; 920118611Snjl } else if (t == LParen) { 921118611Snjl /* 922118611Snjl * T -> ( E ) 923118611Snjl */ 924118611Snjl t = CondE(doEval); 925118611Snjl if (t != Err) { 926118611Snjl if (CondToken(doEval) != RParen) { 927118611Snjl t = Err; 928118611Snjl } 929118611Snjl } 930118611Snjl } else if (t == Not) { 931118611Snjl t = CondT(doEval); 932118611Snjl if (t == True) { 933118611Snjl t = False; 934118611Snjl } else if (t == False) { 935118611Snjl t = True; 936118611Snjl } 937118611Snjl } 938118611Snjl return (t); 939118611Snjl} 940118611Snjl 941118611Snjl/*- 942118611Snjl *----------------------------------------------------------------------- 943118611Snjl * CondF -- 944118611Snjl * Parse a conjunctive factor (nice name, wot?) 945118611Snjl * F -> T && F | T 946118611Snjl * 947118611Snjl * Results: 948118611Snjl * True, False or Err 949118611Snjl * 950118611Snjl * Side Effects: 951118611Snjl * Tokens are consumed. 952118611Snjl * 953118611Snjl *----------------------------------------------------------------------- 954118611Snjl */ 955118611Snjlstatic Token 956118611SnjlCondF(Boolean doEval) 957118611Snjl{ 958118611Snjl Token l, o; 959118611Snjl 960118611Snjl l = CondT(doEval); 961118611Snjl if (l != Err) { 962118611Snjl o = CondToken(doEval); 963118611Snjl 964118611Snjl if (o == And) { 965118611Snjl /* 966118611Snjl * F -> T && F 967118611Snjl * 968118611Snjl * If T is False, the whole thing will be False, but we have to 969118611Snjl * parse the r.h.s. anyway (to throw it away). 970118611Snjl * If T is True, the result is the r.h.s., be it an Err or no. 971118611Snjl */ 972118611Snjl if (l == True) { 973118611Snjl l = CondF(doEval); 974118611Snjl } else { 975118611Snjl CondF(FALSE); 976118611Snjl } 977118611Snjl } else { 978118611Snjl /* 979118611Snjl * F -> T 980118611Snjl */ 981118611Snjl CondPushBack(o); 982118611Snjl } 983118611Snjl } 984118611Snjl return (l); 985118611Snjl} 986118611Snjl 987118611Snjl/*- 988118611Snjl *----------------------------------------------------------------------- 989118611Snjl * CondE -- 990118611Snjl * Main expression production. 991118611Snjl * E -> F || E | F 992118611Snjl * 993118611Snjl * Results: 994118611Snjl * True, False or Err. 995118611Snjl * 996118611Snjl * Side Effects: 997118611Snjl * Tokens are, of course, consumed. 998118611Snjl * 999118611Snjl *----------------------------------------------------------------------- 1000118611Snjl */ 1001118611Snjlstatic Token 1002118611SnjlCondE(Boolean doEval) 1003118611Snjl{ 1004118611Snjl Token l, o; 1005118611Snjl 1006118611Snjl l = CondF(doEval); 1007118611Snjl if (l != Err) { 1008118611Snjl o = CondToken(doEval); 1009118611Snjl 1010118611Snjl if (o == Or) { 1011118611Snjl /* 1012118611Snjl * E -> F || E 1013118611Snjl * 1014118611Snjl * A similar thing occurs for ||, except that here we make sure 1015118611Snjl * the l.h.s. is False before we bother to evaluate the r.h.s. 1016118611Snjl * Once again, if l is False, the result is the r.h.s. and once 1017118611Snjl * again if l is True, we parse the r.h.s. to throw it away. 1018118611Snjl */ 1019118611Snjl if (l == False) { 1020118611Snjl l = CondE(doEval); 1021118611Snjl } else { 1022118611Snjl CondE(FALSE); 1023118611Snjl } 1024118611Snjl } else { 1025118611Snjl /* 1026118611Snjl * E -> F 1027118611Snjl */ 1028118611Snjl CondPushBack(o); 1029118611Snjl } 1030118611Snjl } 1031118611Snjl return (l); 1032118611Snjl} 1033118611Snjl 1034118611Snjl/*- 1035118611Snjl *----------------------------------------------------------------------- 1036118611Snjl * Cond_Eval -- 1037118611Snjl * Evaluate the conditional in the passed line. The line 1038118611Snjl * looks like this: 1039118611Snjl * #<cond-type> <expr> 1040118611Snjl * where <cond-type> is any of if, ifmake, ifnmake, ifdef, 1041118611Snjl * ifndef, elif, elifmake, elifnmake, elifdef, elifndef 1042118611Snjl * and <expr> consists of &&, ||, !, make(target), defined(variable) 1043118611Snjl * and parenthetical groupings thereof. 1044118611Snjl * 1045118611Snjl * Results: 1046118611Snjl * COND_PARSE if should parse lines after the conditional 1047118611Snjl * COND_SKIP if should skip lines after the conditional 1048118611Snjl * COND_INVALID if not a valid conditional. 1049118611Snjl * 1050118611Snjl * Side Effects: 1051118611Snjl * None. 1052118611Snjl * 1053118611Snjl *----------------------------------------------------------------------- 1054118611Snjl */ 1055118611Snjlint 1056118611SnjlCond_Eval(char *line) 1057118611Snjl{ 1058118611Snjl struct If *ifp; 1059118611Snjl Boolean isElse; 1060118611Snjl Boolean value = FALSE; 1061118611Snjl int level; /* Level at which to report errors. */ 1062118611Snjl int lineno; 1063118611Snjl 1064118611Snjl level = PARSE_FATAL; 1065118611Snjl lineno = curFile.lineno; 1066118611Snjl 1067118611Snjl for (line++; *line == ' ' || *line == '\t'; line++) { 1068118611Snjl continue; 1069118611Snjl } 1070118611Snjl 1071118611Snjl /* 1072118611Snjl * Find what type of if we're dealing with. The result is left 1073118611Snjl * in ifp and isElse is set TRUE if it's an elif line. 1074118611Snjl */ 1075118611Snjl if (line[0] == 'e' && line[1] == 'l') { 1076118611Snjl line += 2; 1077118611Snjl isElse = TRUE; 1078118611Snjl } else if (strncmp(line, "endif", 5) == 0) { 1079118611Snjl /* 1080118611Snjl * End of a conditional section. If skipIfLevel is non-zero, that 1081118611Snjl * conditional was skipped, so lines following it should also be 1082118611Snjl * skipped. Hence, we return COND_SKIP. Otherwise, the conditional 1083118611Snjl * was read so succeeding lines should be parsed (think about it...) 1084118611Snjl * so we return COND_PARSE, unless this endif isn't paired with 1085118611Snjl * a decent if. 1086118611Snjl */ 1087118611Snjl if (skipIfLevel != 0) { 1088118611Snjl skipIfLevel -= 1; 1089118611Snjl return (COND_SKIP); 1090118611Snjl } else { 1091118611Snjl if (condTop == MAXIF) { 1092118611Snjl Parse_Error(level, "if-less endif"); 1093118611Snjl return (COND_INVALID); 1094118611Snjl } else { 1095118611Snjl skipLine = FALSE; 1096118611Snjl condTop += 1; 1097118611Snjl return (COND_PARSE); 1098118611Snjl } 1099118611Snjl } 1100118611Snjl } else { 1101118611Snjl isElse = FALSE; 1102118611Snjl } 1103118611Snjl 1104118611Snjl /* 1105118611Snjl * Figure out what sort of conditional it is -- what its default 1106118611Snjl * function is, etc. -- by looking in the table of valid "ifs" 1107118611Snjl */ 1108118611Snjl for (ifp = ifs; ifp->form != NULL; ifp++) { 1109118611Snjl if (strncmp(ifp->form, line, ifp->formlen) == 0) { 1110118611Snjl break; 1111118611Snjl } 1112118611Snjl } 1113118611Snjl 1114118611Snjl if (ifp->form == NULL) { 1115118611Snjl /* 1116118611Snjl * Nothing fit. If the first word on the line is actually 1117118611Snjl * "else", it's a valid conditional whose value is the inverse 1118118611Snjl * of the previous if we parsed. 1119118611Snjl */ 1120118611Snjl if (isElse && (line[0] == 's') && (line[1] == 'e')) { 1121118611Snjl if (condTop == MAXIF) { 1122118611Snjl Parse_Error(level, "if-less else"); 1123118611Snjl return (COND_INVALID); 1124118611Snjl } else if (skipIfLevel == 0) { 1125118611Snjl value = !condStack[condTop]; 1126118611Snjl lineno = condLineno[condTop]; 1127118611Snjl } else { 1128118611Snjl return (COND_SKIP); 1129118611Snjl } 1130118611Snjl } else { 1131118611Snjl /* 1132118611Snjl * Not a valid conditional type. No error... 1133118611Snjl */ 1134118611Snjl return (COND_INVALID); 1135118611Snjl } 1136118611Snjl } else { 1137118611Snjl if (isElse) { 1138118611Snjl if (condTop == MAXIF) { 1139118611Snjl Parse_Error(level, "if-less elif"); 1140118611Snjl return (COND_INVALID); 1141118611Snjl } else if (skipIfLevel != 0) { 1142118611Snjl /* 1143118611Snjl * If skipping this conditional, just ignore the whole thing. 1144118611Snjl * If we don't, the user might be employing a variable that's 1145118611Snjl * undefined, for which there's an enclosing ifdef that 1146118611Snjl * we're skipping... 1147118611Snjl */ 1148118611Snjl skipIfLineno[skipIfLevel - 1] = lineno; 1149118611Snjl return (COND_SKIP); 1150118611Snjl } 1151118611Snjl } else if (skipLine) { 1152118611Snjl /* 1153118611Snjl * Don't even try to evaluate a conditional that's not an else if 1154118611Snjl * we're skipping things... 1155118611Snjl */ 1156118611Snjl skipIfLineno[skipIfLevel] = lineno; 1157118611Snjl skipIfLevel += 1; 1158 return (COND_SKIP); 1159 } 1160 1161 /* 1162 * Initialize file-global variables for parsing 1163 */ 1164 condDefProc = ifp->defProc; 1165 condInvert = ifp->doNot; 1166 1167 line += ifp->formlen; 1168 1169 while (*line == ' ' || *line == '\t') { 1170 line++; 1171 } 1172 1173 condExpr = line; 1174 condPushBack = None; 1175 1176 switch (CondE(TRUE)) { 1177 case True: 1178 if (CondToken(TRUE) == EndOfFile) { 1179 value = TRUE; 1180 break; 1181 } 1182 goto err; 1183 /*FALLTHRU*/ 1184 case False: 1185 if (CondToken(TRUE) == EndOfFile) { 1186 value = FALSE; 1187 break; 1188 } 1189 /*FALLTHRU*/ 1190 case Err: 1191 err: 1192 Parse_Error(level, "Malformed conditional (%s)", 1193 line); 1194 return (COND_INVALID); 1195 default: 1196 break; 1197 } 1198 } 1199 if (!isElse) { 1200 condTop -= 1; 1201 } else if ((skipIfLevel != 0) || condStack[condTop]) { 1202 /* 1203 * If this is an else-type conditional, it should only take effect 1204 * if its corresponding if was evaluated and FALSE. If its if was 1205 * TRUE or skipped, we return COND_SKIP (and start skipping in case 1206 * we weren't already), leaving the stack unmolested so later elif's 1207 * don't screw up... 1208 */ 1209 skipLine = TRUE; 1210 return (COND_SKIP); 1211 } 1212 1213 if (condTop < 0) { 1214 /* 1215 * This is the one case where we can definitely proclaim a fatal 1216 * error. If we don't, we're hosed. 1217 */ 1218 Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); 1219 return (COND_INVALID); 1220 } else { 1221 condStack[condTop] = value; 1222 condLineno[condTop] = lineno; 1223 skipLine = !value; 1224 return (value ? COND_PARSE : COND_SKIP); 1225 } 1226} 1227 1228/*- 1229 *----------------------------------------------------------------------- 1230 * Cond_End -- 1231 * Make sure everything's clean at the end of a makefile. 1232 * 1233 * Results: 1234 * None. 1235 * 1236 * Side Effects: 1237 * Parse_Error will be called if open conditionals are around. 1238 * 1239 *----------------------------------------------------------------------- 1240 */ 1241void 1242Cond_End(void) 1243{ 1244 int level; 1245 1246 if (condTop != MAXIF) { 1247 Parse_Error(PARSE_FATAL, "%d open conditional%s:", 1248 MAXIF - condTop + skipIfLevel, 1249 MAXIF - condTop + skipIfLevel== 1 ? "" : "s"); 1250 1251 for (level = skipIfLevel; level > 0; level--) 1252 Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)", 1253 MAXIF - condTop + level + 1, "", skipIfLineno[level - 1]); 1254 for (level = condTop; level < MAXIF; level++) 1255 Parse_Error(PARSE_FATAL, "\t%*sat line %d " 1256 "(evaluated to %s)", MAXIF - level + skipIfLevel, "", 1257 condLineno[level], condStack[level] ? "true" : "false"); 1258 } 1259 condTop = MAXIF; 1260} 1261