1141104Sharti/*- 294589Sobrien * Copyright (c) 1988, 1989, 1990, 1993 394589Sobrien * The Regents of the University of California. All rights reserved. 45814Sjkh * Copyright (c) 1988, 1989 by Adam de Boor 51590Srgrimes * Copyright (c) 1989 by Berkeley Softworks 61590Srgrimes * All rights reserved. 71590Srgrimes * 81590Srgrimes * This code is derived from software contributed to Berkeley by 91590Srgrimes * Adam de Boor. 101590Srgrimes * 111590Srgrimes * Redistribution and use in source and binary forms, with or without 121590Srgrimes * modification, are permitted provided that the following conditions 131590Srgrimes * are met: 141590Srgrimes * 1. Redistributions of source code must retain the above copyright 151590Srgrimes * notice, this list of conditions and the following disclaimer. 161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171590Srgrimes * notice, this list of conditions and the following disclaimer in the 181590Srgrimes * documentation and/or other materials provided with the distribution. 191590Srgrimes * 3. All advertising materials mentioning features or use of this software 201590Srgrimes * must display the following acknowledgement: 211590Srgrimes * This product includes software developed by the University of 221590Srgrimes * California, Berkeley and its contributors. 231590Srgrimes * 4. Neither the name of the University nor the names of its contributors 241590Srgrimes * may be used to endorse or promote products derived from this software 251590Srgrimes * without specific prior written permission. 261590Srgrimes * 271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 281590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 291590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 301590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 311590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 321590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 331590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 341590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 351590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 361590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 371590Srgrimes * SUCH DAMAGE. 3862833Swsanchez * 3962833Swsanchez * @(#)cond.c 8.2 (Berkeley) 1/2/94 401590Srgrimes */ 411590Srgrimes 4262833Swsanchez#include <sys/cdefs.h> 4394587Sobrien__FBSDID("$FreeBSD$"); 441590Srgrimes 45144473Sharti/* 46144473Sharti * Functions to handle conditionals in a makefile. 471590Srgrimes * 481590Srgrimes * Interface: 49144473Sharti * Cond_Eval Evaluate the conditional in the passed line. 501590Srgrimes */ 511590Srgrimes 52141104Sharti#include <ctype.h> 53141104Sharti#include <string.h> 54141104Sharti#include <stdlib.h> 551590Srgrimes 56141133Sharti#include "buf.h" 57141104Sharti#include "cond.h" 58141104Sharti#include "dir.h" 59141104Sharti#include "globals.h" 60141104Sharti#include "GNode.h" 61141104Sharti#include "make.h" 62141104Sharti#include "parse.h" 63141104Sharti#include "str.h" 64141104Sharti#include "targ.h" 65141104Sharti#include "util.h" 66141104Sharti#include "var.h" 67141104Sharti 681590Srgrimes/* 691590Srgrimes * The parsing of conditional expressions is based on this grammar: 701590Srgrimes * E -> F || E 711590Srgrimes * E -> F 721590Srgrimes * F -> T && F 731590Srgrimes * F -> T 741590Srgrimes * T -> defined(variable) 751590Srgrimes * T -> make(target) 761590Srgrimes * T -> exists(file) 771590Srgrimes * T -> empty(varspec) 781590Srgrimes * T -> target(name) 791590Srgrimes * T -> symbol 801590Srgrimes * T -> $(varspec) op value 811590Srgrimes * T -> $(varspec) == "string" 821590Srgrimes * T -> $(varspec) != "string" 831590Srgrimes * T -> ( E ) 841590Srgrimes * T -> ! T 851590Srgrimes * op -> == | != | > | < | >= | <= 861590Srgrimes * 871590Srgrimes * 'symbol' is some other symbol to which the default function (condDefProc) 881590Srgrimes * is applied. 891590Srgrimes * 901590Srgrimes * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) 911590Srgrimes * will return And for '&' and '&&', Or for '|' and '||', Not for '!', 921590Srgrimes * LParen for '(', RParen for ')' and will evaluate the other terminal 931590Srgrimes * symbols, using either the default function or the function given in the 941590Srgrimes * terminal, and return the result as either True or False. 951590Srgrimes * 961590Srgrimes * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. 971590Srgrimes */ 981590Srgrimestypedef enum { 99144473Sharti And, 100144473Sharti Or, 101144473Sharti Not, 102144473Sharti True, 103144473Sharti False, 104144473Sharti LParen, 105144473Sharti RParen, 106144473Sharti EndOfFile, 107144473Sharti None, 108144473Sharti Err 1091590Srgrimes} Token; 1101590Srgrimes 111141254Shartitypedef Boolean CondProc(int, char *); 112141254Sharti 1131590Srgrimes/*- 1141590Srgrimes * Structures to handle elegantly the different forms of #if's. The 1151590Srgrimes * last two fields are stored in condInvert and condDefProc, respectively. 1161590Srgrimes */ 11792921Simpstatic void CondPushBack(Token); 118141192Shartistatic int CondGetArg(char **, char **, const char *, Boolean); 119141254Shartistatic CondProc CondDoDefined; 120141254Shartistatic CondProc CondDoMake; 121141254Shartistatic CondProc CondDoExists; 122141254Shartistatic CondProc CondDoTarget; 123141254Shartistatic char *CondCvtArg(char *, double *); 12492921Simpstatic Token CondToken(Boolean); 12592921Simpstatic Token CondT(Boolean); 12692921Simpstatic Token CondF(Boolean); 12792921Simpstatic Token CondE(Boolean); 1281590Srgrimes 129144894Shartistatic const struct If { 130144473Sharti Boolean doNot; /* TRUE if default function should be negated */ 131144473Sharti CondProc *defProc; /* Default function to apply */ 132144894Sharti Boolean isElse; /* actually el<XXX> */ 1331590Srgrimes} ifs[] = { 134144894Sharti [COND_IF] = { FALSE, CondDoDefined, FALSE }, 135144894Sharti [COND_IFDEF] = { FALSE, CondDoDefined, FALSE }, 136144894Sharti [COND_IFNDEF] = { TRUE, CondDoDefined, FALSE }, 137144894Sharti [COND_IFMAKE] = { FALSE, CondDoMake, FALSE }, 138144894Sharti [COND_IFNMAKE] = { TRUE, CondDoMake, FALSE }, 139144894Sharti [COND_ELIF] = { FALSE, CondDoDefined, TRUE }, 140144894Sharti [COND_ELIFDEF] = { FALSE, CondDoDefined, TRUE }, 141144894Sharti [COND_ELIFNDEF] = { TRUE, CondDoDefined, TRUE }, 142144894Sharti [COND_ELIFMAKE] = { FALSE, CondDoMake, TRUE }, 143144894Sharti [COND_ELIFNMAKE] = { TRUE, CondDoMake, TRUE }, 1441590Srgrimes}; 1451590Srgrimes 146144473Shartistatic Boolean condInvert; /* Invert the default function */ 147144473Shartistatic CondProc *condDefProc; /* default function to apply */ 148144473Shartistatic char *condExpr; /* The expression to parse */ 149144473Shartistatic Token condPushBack = None; /* Single push-back token in parsing */ 1501590Srgrimes 151144473Sharti#define MAXIF 30 /* greatest depth of #if'ing */ 1521590Srgrimes 153144473Shartistatic Boolean condStack[MAXIF]; /* Stack of conditionals's values */ 154144473Shartistatic int condLineno[MAXIF]; /* Line numbers of the opening .if */ 155144473Shartistatic int condTop = MAXIF; /* Top-most conditional */ 156144473Shartistatic int skipIfLevel = 0; /* Depth of skipped conditionals */ 157144473Shartistatic int skipIfLineno[MAXIF]; /* Line numbers of skipped .ifs */ 158144894ShartiBoolean skipLine = FALSE; /* Whether the parse module is skipping 1591590Srgrimes * lines */ 1601590Srgrimes 161144473Sharti/** 162144473Sharti * CondPushBack 1631590Srgrimes * Push back the most recent token read. We only need one level of 1641590Srgrimes * this, so the thing is just stored in 'condPushback'. 1651590Srgrimes * 1661590Srgrimes * Side Effects: 1671590Srgrimes * condPushback is overwritten. 1681590Srgrimes */ 1691590Srgrimesstatic void 170138232ShartiCondPushBack(Token t) 1711590Srgrimes{ 172138264Sharti 173144473Sharti condPushBack = t; 1741590Srgrimes} 175138232Sharti 176144473Sharti/** 177144473Sharti * CondGetArg 178104696Sjmallett * Find the argument of a built-in function. parens is set to TRUE 179104696Sjmallett * if the arguments are bounded by parens. 1801590Srgrimes * 1811590Srgrimes * Results: 1821590Srgrimes * The length of the argument and the address of the argument. 1831590Srgrimes * 1841590Srgrimes * Side Effects: 1851590Srgrimes * The pointer is set to point to the closing parenthesis of the 1861590Srgrimes * function call. 1871590Srgrimes */ 1881590Srgrimesstatic int 189141192ShartiCondGetArg(char **linePtr, char **argPtr, const char *func, Boolean parens) 1901590Srgrimes{ 191144473Sharti char *cp; 192144473Sharti size_t argLen; 193144473Sharti Buffer *buf; 1941590Srgrimes 195144473Sharti cp = *linePtr; 196144473Sharti if (parens) { 197144473Sharti while (*cp != '(' && *cp != '\0') { 198144473Sharti cp++; 199144473Sharti } 200144473Sharti if (*cp == '(') { 201144473Sharti cp++; 202144473Sharti } 2031590Srgrimes } 204144473Sharti 205144473Sharti if (*cp == '\0') { 206144473Sharti /* 207144473Sharti * No arguments whatsoever. Because 'make' and 'defined' 208144473Sharti * aren't really "reserved words", we don't print a message. 209144473Sharti * I think this is better than hitting the user with a warning 210144473Sharti * message every time s/he uses the word 'make' or 'defined' 211144473Sharti * at the beginning of a symbol... 212144473Sharti */ 213144473Sharti *argPtr = cp; 214144473Sharti return (0); 2151590Srgrimes } 2161590Srgrimes 217144473Sharti while (*cp == ' ' || *cp == '\t') { 218144473Sharti cp++; 219144473Sharti } 220144473Sharti 2211590Srgrimes /* 222144473Sharti * Create a buffer for the argument and start it out at 16 characters 223144473Sharti * long. Why 16? Why not? 2241590Srgrimes */ 225144473Sharti buf = Buf_Init(16); 2261590Srgrimes 227144473Sharti while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) { 228144473Sharti if (*cp == '$') { 229144473Sharti /* 230144473Sharti * Parse the variable spec and install it as part of 231144473Sharti * the argument if it's valid. We tell Var_Parse to 232144473Sharti * complain on an undefined variable, so we don't do 233144473Sharti * it too. Nor do we return an error, though perhaps 234144473Sharti * we should... 235144473Sharti */ 236144473Sharti char *cp2; 237144473Sharti size_t len = 0; 238144473Sharti Boolean doFree; 2391590Srgrimes 240144473Sharti cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); 2418874Srgrimes 242144473Sharti Buf_Append(buf, cp2); 243144473Sharti if (doFree) { 244144473Sharti free(cp2); 245144473Sharti } 246144473Sharti cp += len; 247144473Sharti } else { 248144473Sharti Buf_AddByte(buf, (Byte)*cp); 249144473Sharti cp++; 250144473Sharti } 251144473Sharti } 2521590Srgrimes 253144473Sharti Buf_AddByte(buf, (Byte)'\0'); 254144473Sharti *argPtr = (char *)Buf_GetAll(buf, &argLen); 255144473Sharti Buf_Destroy(buf, FALSE); 2561590Srgrimes 257144473Sharti while (*cp == ' ' || *cp == '\t') { 258144473Sharti cp++; 2591590Srgrimes } 260144473Sharti if (parens && *cp != ')') { 261144473Sharti Parse_Error(PARSE_WARNING, 262144473Sharti "Missing closing parenthesis for %s()", func); 263144473Sharti return (0); 264144473Sharti } else if (parens) { 265144473Sharti /* 266144473Sharti * Advance pointer past close parenthesis. 267144473Sharti */ 268144473Sharti cp++; 269144473Sharti } 2701590Srgrimes 271144473Sharti *linePtr = cp; 272144473Sharti return (argLen); 2731590Srgrimes} 274138232Sharti 275144473Sharti/** 276144473Sharti * CondDoDefined 2771590Srgrimes * Handle the 'defined' function for conditionals. 2781590Srgrimes * 2791590Srgrimes * Results: 2801590Srgrimes * TRUE if the given variable is defined. 2811590Srgrimes */ 2821590Srgrimesstatic Boolean 283138232ShartiCondDoDefined(int argLen, char *arg) 2841590Srgrimes{ 285144473Sharti char savec = arg[argLen]; 286144473Sharti Boolean result; 2871590Srgrimes 288144473Sharti arg[argLen] = '\0'; 289146580Sharti if (Var_Value(arg, VAR_CMD) != NULL) { 290144473Sharti result = TRUE; 291144473Sharti } else { 292144473Sharti result = FALSE; 293144473Sharti } 294144473Sharti arg[argLen] = savec; 295144473Sharti return (result); 2961590Srgrimes} 297138232Sharti 298144473Sharti/** 299144473Sharti * CondDoMake 3001590Srgrimes * Handle the 'make' function for conditionals. 3011590Srgrimes * 3021590Srgrimes * Results: 3031590Srgrimes * TRUE if the given target is being made. 3041590Srgrimes */ 3051590Srgrimesstatic Boolean 306138232ShartiCondDoMake(int argLen, char *arg) 3071590Srgrimes{ 308144473Sharti char savec = arg[argLen]; 309144473Sharti Boolean result; 310144473Sharti const LstNode *ln; 3111590Srgrimes 312144473Sharti arg[argLen] = '\0'; 313144473Sharti result = FALSE; 314144473Sharti LST_FOREACH(ln, &create) { 315144473Sharti if (Str_Match(Lst_Datum(ln), arg)) { 316144473Sharti result = TRUE; 317144473Sharti break; 318144473Sharti } 319143810Sharti } 320144473Sharti arg[argLen] = savec; 321144473Sharti return (result); 3221590Srgrimes} 323138232Sharti 324144473Sharti/** 325144473Sharti * CondDoExists 3261590Srgrimes * See if the given file exists. 3271590Srgrimes * 3281590Srgrimes * Results: 3291590Srgrimes * TRUE if the file exists and FALSE if it does not. 3301590Srgrimes */ 3311590Srgrimesstatic Boolean 332138232ShartiCondDoExists(int argLen, char *arg) 3331590Srgrimes{ 334144473Sharti char savec = arg[argLen]; 335144473Sharti Boolean result; 336144473Sharti char *path; 3371590Srgrimes 338144473Sharti arg[argLen] = '\0'; 339144473Sharti path = Path_FindFile(arg, &dirSearchPath); 340144473Sharti if (path != NULL) { 341144473Sharti result = TRUE; 342144473Sharti free(path); 343144473Sharti } else { 344144473Sharti result = FALSE; 345144473Sharti } 346144473Sharti arg[argLen] = savec; 347144473Sharti return (result); 3481590Srgrimes} 349138232Sharti 350144473Sharti/** 351144473Sharti * CondDoTarget 3521590Srgrimes * See if the given node exists and is an actual target. 3531590Srgrimes * 3541590Srgrimes * Results: 3551590Srgrimes * TRUE if the node exists as a target and FALSE if it does not. 3561590Srgrimes */ 3571590Srgrimesstatic Boolean 358138232ShartiCondDoTarget(int argLen, char *arg) 3591590Srgrimes{ 360144473Sharti char savec = arg[argLen]; 361144473Sharti Boolean result; 362144473Sharti GNode *gn; 3631590Srgrimes 364144473Sharti arg[argLen] = '\0'; 365144473Sharti gn = Targ_FindNode(arg, TARG_NOCREATE); 366144473Sharti if ((gn != NULL) && !OP_NOP(gn->type)) { 367144473Sharti result = TRUE; 368144473Sharti } else { 369144473Sharti result = FALSE; 370144473Sharti } 371144473Sharti arg[argLen] = savec; 372144473Sharti return (result); 3731590Srgrimes} 3741590Srgrimes 375144473Sharti/** 376144473Sharti * CondCvtArg 3771590Srgrimes * Convert the given number into a double. If the number begins 3781590Srgrimes * with 0x, it is interpreted as a hexadecimal integer 3791590Srgrimes * and converted to a double from there. All other strings just have 3801590Srgrimes * strtod called on them. 3811590Srgrimes * 3821590Srgrimes * Results: 3831590Srgrimes * Sets 'value' to double value of string. 38447494Shoek * Returns address of the first character after the last valid 38547494Shoek * character of the converted number. 3861590Srgrimes * 3871590Srgrimes * Side Effects: 3881590Srgrimes * Can change 'value' even if string is not a valid number. 3891590Srgrimes */ 39047494Shoekstatic char * 391104696SjmallettCondCvtArg(char *str, double *value) 3921590Srgrimes{ 3931590Srgrimes 394144473Sharti if ((*str == '0') && (str[1] == 'x')) { 395144473Sharti long i; 396144473Sharti 397144473Sharti for (str += 2, i = 0; ; str++) { 398144473Sharti int x; 399144473Sharti 400144473Sharti if (isdigit((unsigned char)*str)) 401144473Sharti x = *str - '0'; 402144473Sharti else if (isxdigit((unsigned char)*str)) 403144473Sharti x = 10 + *str - 404144473Sharti isupper((unsigned char)*str) ? 'A' : 'a'; 405144473Sharti else { 406144473Sharti *value = (double)i; 407144473Sharti return (str); 408144473Sharti } 409144473Sharti i = (i << 4) + x; 410144473Sharti } 411144473Sharti 412144473Sharti } else { 413144473Sharti char *eptr; 414144473Sharti 415144473Sharti *value = strtod(str, &eptr); 416144473Sharti return (eptr); 4171590Srgrimes } 4181590Srgrimes} 419138232Sharti 420144473Sharti/** 421144473Sharti * CondToken 4221590Srgrimes * Return the next token from the input. 4231590Srgrimes * 4241590Srgrimes * Results: 4251590Srgrimes * A Token for the next lexical token in the stream. 4261590Srgrimes * 4271590Srgrimes * Side Effects: 4281590Srgrimes * condPushback will be set back to None if it is used. 4291590Srgrimes */ 4301590Srgrimesstatic Token 431104696SjmallettCondToken(Boolean doEval) 4321590Srgrimes{ 433144473Sharti Token t; 4341590Srgrimes 435146152Sharti if (condPushBack != None) { 436146152Sharti t = condPushBack; 437146152Sharti condPushBack = None; 438146152Sharti return (t); 439146152Sharti } 440146152Sharti 441146152Sharti while (*condExpr == ' ' || *condExpr == '\t') { 442146152Sharti condExpr++; 443146152Sharti } 444146152Sharti switch (*condExpr) { 445146152Sharti case '(': 446146152Sharti t = LParen; 447146152Sharti condExpr++; 448146152Sharti break; 449146152Sharti case ')': 450146152Sharti t = RParen; 451146152Sharti condExpr++; 452146152Sharti break; 453146152Sharti case '|': 454146152Sharti if (condExpr[1] == '|') { 455144473Sharti condExpr++; 4561590Srgrimes } 457146152Sharti condExpr++; 458146152Sharti t = Or; 459146152Sharti break; 460146152Sharti case '&': 461146152Sharti if (condExpr[1] == '&') { 462144473Sharti condExpr++; 463146152Sharti } 464146152Sharti condExpr++; 465146152Sharti t = And; 466146152Sharti break; 467146152Sharti case '!': 468146152Sharti t = Not; 469146152Sharti condExpr++; 470146152Sharti break; 471146152Sharti case '\n': 472146152Sharti case '\0': 473146152Sharti t = EndOfFile; 474146152Sharti break; 475146152Sharti case '$': { 476146154Sharti char *lhs; 477146154Sharti const char *op; 478146154Sharti char *rhs; 479146154Sharti char zero[] = "0"; 480146154Sharti size_t varSpecLen = 0; 481146154Sharti Boolean doFree; 4821590Srgrimes 483146152Sharti /* 484146152Sharti * Parse the variable spec and skip over it, saving its 485146152Sharti * value in lhs. 486146152Sharti */ 487146152Sharti t = Err; 488146152Sharti lhs = Var_Parse(condExpr, VAR_CMD, doEval, 489146152Sharti &varSpecLen, &doFree); 490146152Sharti if (lhs == var_Error) { 491144473Sharti /* 492146152Sharti * Even if !doEval, we still report syntax 493146152Sharti * errors, which is what getting var_Error 494146152Sharti * back with !doEval means. 495144473Sharti */ 496146152Sharti return (Err); 497146152Sharti } 498146152Sharti condExpr += varSpecLen; 4991590Srgrimes 500146152Sharti if (!isspace((unsigned char)*condExpr) && 501146152Sharti strchr("!=><", *condExpr) == NULL) { 502146152Sharti Buffer *buf; 5031590Srgrimes 504146152Sharti buf = Buf_Init(0); 5051590Srgrimes 506146152Sharti Buf_Append(buf, lhs); 5071590Srgrimes 508146152Sharti if (doFree) 509146152Sharti free(lhs); 5101590Srgrimes 511146152Sharti for (;*condExpr && 512146152Sharti !isspace((unsigned char)*condExpr); 513146152Sharti condExpr++) 514146152Sharti Buf_AddByte(buf, (Byte)*condExpr); 5151590Srgrimes 516146152Sharti Buf_AddByte(buf, (Byte)'\0'); 517146152Sharti lhs = (char *)Buf_GetAll(buf, &varSpecLen); 518146152Sharti Buf_Destroy(buf, FALSE); 5191590Srgrimes 520146152Sharti doFree = TRUE; 521146152Sharti } 522146152Sharti 523146152Sharti /* 524146152Sharti * Skip whitespace to get to the operator 525146152Sharti */ 526146152Sharti while (isspace((unsigned char)*condExpr)) 527146152Sharti condExpr++; 528146152Sharti 529146152Sharti /* 530146152Sharti * Make sure the operator is a valid one. If it isn't a 531146152Sharti * known relational operator, pretend we got a 532146152Sharti * != 0 comparison. 533146152Sharti */ 534146152Sharti op = condExpr; 535146152Sharti switch (*condExpr) { 536146152Sharti case '!': 537146152Sharti case '=': 538146152Sharti case '<': 539146152Sharti case '>': 540146152Sharti if (condExpr[1] == '=') { 541146152Sharti condExpr += 2; 542146152Sharti } else { 543146152Sharti condExpr += 1; 544144473Sharti } 545146153Sharti while (isspace((unsigned char)*condExpr)) { 546146153Sharti condExpr++; 547146153Sharti } 548146153Sharti if (*condExpr == '\0') { 549146153Sharti Parse_Error(PARSE_WARNING, 550146153Sharti "Missing right-hand-side of operator"); 551146153Sharti goto error; 552146153Sharti } 553146153Sharti rhs = condExpr; 554146152Sharti break; 555146153Sharti 556146152Sharti default: 557146152Sharti op = "!="; 558146154Sharti rhs = zero; 559146153Sharti break; 560146152Sharti } 561146152Sharti if (*rhs == '"') { 562144473Sharti /* 563146152Sharti * Doing a string comparison. Only allow == and 564146152Sharti * != for * operators. 565144473Sharti */ 566146152Sharti char *string; 567146152Sharti char *cp, *cp2; 568146152Sharti int qt; 569146152Sharti Buffer *buf; 5701590Srgrimes 571146152Sharti do_string_compare: 572146152Sharti if (((*op != '!') && (*op != '=')) || 573146152Sharti (op[1] != '=')) { 574144473Sharti Parse_Error(PARSE_WARNING, 575146152Sharti "String comparison operator should " 576146152Sharti "be either == or !="); 577144473Sharti goto error; 578144473Sharti } 5791590Srgrimes 580146152Sharti buf = Buf_Init(0); 581146152Sharti qt = *rhs == '"' ? 1 : 0; 5821590Srgrimes 583146152Sharti for (cp = &rhs[qt]; 584146152Sharti ((qt && (*cp != '"')) || 585146152Sharti (!qt && strchr(" \t)", *cp) == NULL)) && 586146152Sharti (*cp != '\0'); cp++) { 587146152Sharti if ((*cp == '\\') && (cp[1] != '\0')) { 588146152Sharti /* 589146152Sharti * Backslash escapes things -- 590146152Sharti * skip over next character, * if it exists. 591146152Sharti */ 592146152Sharti cp++; 593146152Sharti Buf_AddByte(buf, (Byte)*cp); 5941590Srgrimes 595146152Sharti } else if (*cp == '$') { 596146152Sharti size_t len = 0; 597146152Sharti Boolean freeIt; 5988874Srgrimes 599146152Sharti cp2 = Var_Parse(cp, VAR_CMD, 600146152Sharti doEval, &len, &freeIt); 601146152Sharti if (cp2 != var_Error) { 602146152Sharti Buf_Append(buf, cp2); 603146152Sharti if (freeIt) { 604146152Sharti free(cp2); 605144473Sharti } 606146152Sharti cp += len - 1; 607144473Sharti } else { 608146152Sharti Buf_AddByte(buf, 609146152Sharti (Byte)*cp); 610144473Sharti } 611146152Sharti } else { 612146152Sharti Buf_AddByte(buf, (Byte)*cp); 6131590Srgrimes } 614146152Sharti } 615144473Sharti 616146152Sharti string = Buf_Peel(buf); 617144473Sharti 618146152Sharti DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", " 619146152Sharti "op = %.2s\n", lhs, string, op)); 620146152Sharti /* 621146152Sharti * Null-terminate rhs and perform the 622146152Sharti * comparison. t is set to the result. 623146152Sharti */ 624146152Sharti if (*op == '=') { 625146152Sharti t = strcmp(lhs, string) ? False : True; 6261590Srgrimes } else { 627146152Sharti t = strcmp(lhs, string) ? True : False; 628146152Sharti } 629146152Sharti free(string); 630146152Sharti if (rhs == condExpr) { 631146625Sharti if (*cp == '\0' || (!qt && *cp == ')')) 632146152Sharti condExpr = cp; 633146152Sharti else 634146152Sharti condExpr = cp + 1; 635146152Sharti } 636146152Sharti } else { 637146152Sharti /* 638146152Sharti * rhs is either a float or an integer. 639146152Sharti * Convert both the lhs and the rhs to a 640146152Sharti * double and compare the two. 641146152Sharti */ 642146152Sharti double left, right; 643146152Sharti char *string; 6441590Srgrimes 645146152Sharti if (*CondCvtArg(lhs, &left) != '\0') 646146152Sharti goto do_string_compare; 647146152Sharti if (*rhs == '$') { 648146152Sharti size_t len = 0; 649146152Sharti Boolean freeIt; 6501590Srgrimes 651146152Sharti string = Var_Parse(rhs, VAR_CMD, doEval, 652146152Sharti &len, &freeIt); 653146152Sharti if (string == var_Error) { 654146152Sharti right = 0.0; 655146152Sharti } else { 656146152Sharti if (*CondCvtArg(string, 657146152Sharti &right) != '\0') { 658144473Sharti if (freeIt) 659144473Sharti free(string); 660144473Sharti goto do_string_compare; 661144473Sharti } 662146152Sharti if (freeIt) 663146152Sharti free(string); 664146152Sharti if (rhs == condExpr) 665146152Sharti condExpr += len; 666144473Sharti } 667146152Sharti } else { 668146152Sharti char *c = CondCvtArg(rhs, &right); 6698874Srgrimes 670146152Sharti if (c == rhs) 671146152Sharti goto do_string_compare; 672146152Sharti if (rhs == condExpr) { 673146152Sharti /* 674146152Sharti * Skip over the right-hand side 675146152Sharti */ 676146152Sharti condExpr = c; 677144473Sharti } 6781590Srgrimes } 679146152Sharti 680146152Sharti DEBUGF(COND, ("left = %f, right = %f, " 681146152Sharti "op = %.2s\n", left, right, op)); 682146152Sharti switch (op[0]) { 683146152Sharti case '!': 684146152Sharti if (op[1] != '=') { 685146152Sharti Parse_Error(PARSE_WARNING, 686146152Sharti "Unknown operator"); 687146152Sharti goto error; 688146152Sharti } 689146152Sharti t = (left != right ? True : False); 690146152Sharti break; 691146152Sharti case '=': 692146152Sharti if (op[1] != '=') { 693146152Sharti Parse_Error(PARSE_WARNING, 694146152Sharti "Unknown operator"); 695146152Sharti goto error; 696146152Sharti } 697146152Sharti t = (left == right ? True : False); 698146152Sharti break; 699146152Sharti case '<': 700146152Sharti if (op[1] == '=') { 701146152Sharti t = (left <= right?True:False); 702146152Sharti } else { 703146152Sharti t = (left < right?True:False); 704146152Sharti } 705146152Sharti break; 706146152Sharti case '>': 707146152Sharti if (op[1] == '=') { 708146152Sharti t = (left >= right?True:False); 709146152Sharti } else { 710146152Sharti t = (left > right?True:False); 711146152Sharti } 712146152Sharti break; 713146152Sharti default: 714146152Sharti break; 7151590Srgrimes } 716146152Sharti } 717146152Sharti error: 718146152Sharti if (doFree) 719146152Sharti free(lhs); 720146152Sharti break; 721146152Sharti } 7228874Srgrimes 723146152Sharti default: { 724146152Sharti CondProc *evalProc; 725146152Sharti Boolean invert = FALSE; 726146152Sharti char *arg; 727146152Sharti int arglen; 7281590Srgrimes 729146152Sharti if (strncmp(condExpr, "defined", 7) == 0) { 730146152Sharti /* 731146152Sharti * Use CondDoDefined to evaluate the argument 732146152Sharti * and CondGetArg to extract the argument from 733146152Sharti * the 'function call'. 734146152Sharti */ 735146152Sharti evalProc = CondDoDefined; 736146152Sharti condExpr += 7; 737146152Sharti arglen = CondGetArg(&condExpr, &arg, 738146152Sharti "defined", TRUE); 739146152Sharti if (arglen == 0) { 740146152Sharti condExpr -= 7; 741146152Sharti goto use_default; 742146152Sharti } 7431590Srgrimes 744146152Sharti } else if (strncmp(condExpr, "make", 4) == 0) { 745146152Sharti /* 746146152Sharti * Use CondDoMake to evaluate the argument and 747146152Sharti * CondGetArg to extract the argument from the 748146152Sharti * 'function call'. 749146152Sharti */ 750146152Sharti evalProc = CondDoMake; 751146152Sharti condExpr += 4; 752146152Sharti arglen = CondGetArg(&condExpr, &arg, 753146152Sharti "make", TRUE); 754146152Sharti if (arglen == 0) { 755146152Sharti condExpr -= 4; 756146152Sharti goto use_default; 757146152Sharti } 7585814Sjkh 759146152Sharti } else if (strncmp(condExpr, "exists", 6) == 0) { 760146152Sharti /* 761146152Sharti * Use CondDoExists to evaluate the argument and 762146152Sharti * CondGetArg to extract the argument from the 763146152Sharti * 'function call'. 764146152Sharti */ 765146152Sharti evalProc = CondDoExists; 766146152Sharti condExpr += 6; 767146152Sharti arglen = CondGetArg(&condExpr, &arg, 768146152Sharti "exists", TRUE); 769146152Sharti if (arglen == 0) { 770146152Sharti condExpr -= 6; 771146152Sharti goto use_default; 772146152Sharti } 773144473Sharti 774146152Sharti } else if (strncmp(condExpr, "empty", 5) == 0) { 775146152Sharti /* 776146152Sharti * Use Var_Parse to parse the spec in parens and 777146152Sharti * return True if the resulting string is empty. 778146152Sharti */ 779146152Sharti size_t length; 780146152Sharti Boolean doFree; 781146152Sharti char *val; 782144473Sharti 783146152Sharti condExpr += 5; 784144473Sharti 785146152Sharti for (arglen = 0; 786146152Sharti condExpr[arglen] != '(' && 787146152Sharti condExpr[arglen] != '\0'; arglen += 1) 788146152Sharti continue; 789144473Sharti 790146152Sharti if (condExpr[arglen] != '\0') { 791146152Sharti length = 0; 792146152Sharti val = Var_Parse(&condExpr[arglen - 1], 793146152Sharti VAR_CMD, FALSE, &length, &doFree); 794146152Sharti if (val == var_Error) { 795146152Sharti t = Err; 796146152Sharti } else { 797144473Sharti /* 798146152Sharti * A variable is empty when it 799146152Sharti * just contains spaces... 800146152Sharti * 4/15/92, christos 801144473Sharti */ 802146152Sharti char *p; 803146152Sharti 804146152Sharti for (p = val; 805146152Sharti *p && 806146152Sharti isspace((unsigned char)*p); 807146152Sharti p++) 808146152Sharti continue; 809146152Sharti t = (*p == '\0') ? True : False; 810144473Sharti } 811146152Sharti if (doFree) { 812146152Sharti free(val); 813146152Sharti } 814144473Sharti /* 815146152Sharti * Advance condExpr to beyond the 816146152Sharti * closing ). Note that we subtract 817146152Sharti * one from arglen + length b/c length 818146152Sharti * is calculated from 819146152Sharti * condExpr[arglen - 1]. 820144473Sharti */ 821146152Sharti condExpr += arglen + length - 1; 8221590Srgrimes } else { 823146152Sharti condExpr -= 5; 824146152Sharti goto use_default; 8251590Srgrimes } 826146152Sharti break; 827144473Sharti 828146152Sharti } else if (strncmp(condExpr, "target", 6) == 0) { 8291590Srgrimes /* 830146152Sharti * Use CondDoTarget to evaluate the argument and 831146152Sharti * CondGetArg to extract the argument from the 832146152Sharti * 'function call'. 8331590Srgrimes */ 834146152Sharti evalProc = CondDoTarget; 835146152Sharti condExpr += 6; 836146152Sharti arglen = CondGetArg(&condExpr, &arg, 837146152Sharti "target", TRUE); 838146152Sharti if (arglen == 0) { 839146152Sharti condExpr -= 6; 840146152Sharti goto use_default; 841144473Sharti } 842146152Sharti 843146152Sharti } else { 844146152Sharti /* 845146152Sharti * The symbol is itself the argument to the 846146152Sharti * default function. We advance condExpr to 847146152Sharti * the end of the symbol by hand (the next 848146152Sharti * whitespace, closing paren or binary operator) 849146152Sharti * and set to invert the evaluation 850146152Sharti * function if condInvert is TRUE. 851146152Sharti */ 852146152Sharti use_default: 853146152Sharti invert = condInvert; 854146152Sharti evalProc = condDefProc; 855146152Sharti arglen = CondGetArg(&condExpr, &arg, "", FALSE); 8561590Srgrimes } 857146152Sharti 858146152Sharti /* 859146152Sharti * Evaluate the argument using the set function. If 860146152Sharti * invert is TRUE, we invert the sense of the function. 861146152Sharti */ 862146152Sharti t = (!doEval || (* evalProc) (arglen, arg) ? 863146152Sharti (invert ? False : True) : 864146152Sharti (invert ? True : False)); 865146152Sharti free(arg); 866146152Sharti break; 867146152Sharti } 8681590Srgrimes } 869144473Sharti return (t); 8701590Srgrimes} 871138232Sharti 872144473Sharti/** 873144473Sharti * CondT 8741590Srgrimes * Parse a single term in the expression. This consists of a terminal 8751590Srgrimes * symbol or Not and a terminal symbol (not including the binary 8761590Srgrimes * operators): 8771590Srgrimes * T -> defined(variable) | make(target) | exists(file) | symbol 8781590Srgrimes * T -> ! T | ( E ) 8791590Srgrimes * 8801590Srgrimes * Results: 8811590Srgrimes * True, False or Err. 8821590Srgrimes * 8831590Srgrimes * Side Effects: 8841590Srgrimes * Tokens are consumed. 8851590Srgrimes */ 8861590Srgrimesstatic Token 887104696SjmallettCondT(Boolean doEval) 8881590Srgrimes{ 889144473Sharti Token t; 8901590Srgrimes 891144473Sharti t = CondToken(doEval); 892144473Sharti if (t == EndOfFile) { 893144473Sharti /* 894144473Sharti * If we reached the end of the expression, the expression 895144473Sharti * is malformed... 896144473Sharti */ 8971590Srgrimes t = Err; 898144473Sharti } else if (t == LParen) { 899144473Sharti /* 900144473Sharti * T -> ( E ) 901144473Sharti */ 902144473Sharti t = CondE(doEval); 903144473Sharti if (t != Err) { 904144473Sharti if (CondToken(doEval) != RParen) { 905144473Sharti t = Err; 906144473Sharti } 907144473Sharti } 908144473Sharti } else if (t == Not) { 909144473Sharti t = CondT(doEval); 910144473Sharti if (t == True) { 911144473Sharti t = False; 912144473Sharti } else if (t == False) { 913144473Sharti t = True; 914144473Sharti } 9151590Srgrimes } 916144473Sharti return (t); 9171590Srgrimes} 918138232Sharti 919144473Sharti/** 9201590Srgrimes * CondF -- 9211590Srgrimes * Parse a conjunctive factor (nice name, wot?) 9221590Srgrimes * F -> T && F | T 9231590Srgrimes * 9241590Srgrimes * Results: 9251590Srgrimes * True, False or Err 9261590Srgrimes * 9271590Srgrimes * Side Effects: 9281590Srgrimes * Tokens are consumed. 9291590Srgrimes */ 9301590Srgrimesstatic Token 931104696SjmallettCondF(Boolean doEval) 9321590Srgrimes{ 933144473Sharti Token l, o; 9341590Srgrimes 935144473Sharti l = CondT(doEval); 936144473Sharti if (l != Err) { 937144473Sharti o = CondToken(doEval); 9381590Srgrimes 939144473Sharti if (o == And) { 940144473Sharti /* 941144473Sharti * F -> T && F 942144473Sharti * 943144473Sharti * If T is False, the whole thing will be False, but 944144473Sharti * we have to parse the r.h.s. anyway (to throw it 945144473Sharti * away). If T is True, the result is the r.h.s., 946144473Sharti * be it an Err or no. 947144473Sharti */ 948144473Sharti if (l == True) { 949144473Sharti l = CondF(doEval); 950144473Sharti } else { 951144473Sharti CondF(FALSE); 952144473Sharti } 953144473Sharti } else { 954144473Sharti /* 955144473Sharti * F -> T 956144473Sharti */ 957144473Sharti CondPushBack(o); 958144473Sharti } 9591590Srgrimes } 960144473Sharti return (l); 9611590Srgrimes} 962138232Sharti 963144473Sharti/** 9641590Srgrimes * CondE -- 9651590Srgrimes * Main expression production. 9661590Srgrimes * E -> F || E | F 9671590Srgrimes * 9681590Srgrimes * Results: 9691590Srgrimes * True, False or Err. 9701590Srgrimes * 9711590Srgrimes * Side Effects: 9721590Srgrimes * Tokens are, of course, consumed. 9731590Srgrimes */ 9741590Srgrimesstatic Token 975104696SjmallettCondE(Boolean doEval) 9761590Srgrimes{ 977144473Sharti Token l, o; 9781590Srgrimes 979144473Sharti l = CondF(doEval); 980144473Sharti if (l != Err) { 981144473Sharti o = CondToken(doEval); 9821590Srgrimes 983144473Sharti if (o == Or) { 984144473Sharti /* 985144473Sharti * E -> F || E 986144473Sharti * 987144473Sharti * A similar thing occurs for ||, except that here we 988144473Sharti * make sure the l.h.s. is False before we bother to 989144473Sharti * evaluate the r.h.s. Once again, if l is False, the 990144473Sharti * result is the r.h.s. and once again if l is True, 991144473Sharti * we parse the r.h.s. to throw it away. 992144473Sharti */ 993144473Sharti if (l == False) { 994144473Sharti l = CondE(doEval); 995144473Sharti } else { 996144473Sharti CondE(FALSE); 997144473Sharti } 998144473Sharti } else { 999144473Sharti /* 1000144473Sharti * E -> F 1001144473Sharti */ 1002144473Sharti CondPushBack(o); 1003144473Sharti } 10041590Srgrimes } 1005144473Sharti return (l); 10061590Srgrimes} 1007138232Sharti 1008144473Sharti/** 1009144894Sharti * Cond_If 1010144894Sharti * Handle .if<X> and .elif<X> directives. 1011144894Sharti * This function is called even when we're skipping. 10121590Srgrimes */ 1013144894Shartivoid 1014144894ShartiCond_If(char *line, int code, int lineno) 10151590Srgrimes{ 1016144894Sharti const struct If *ifp; 1017144894Sharti Boolean value; 10181590Srgrimes 1019144894Sharti ifp = &ifs[code]; 10201590Srgrimes 1021144894Sharti if (ifp->isElse) { 1022144894Sharti if (condTop == MAXIF) { 1023144894Sharti Parse_Error(PARSE_FATAL, "if-less elif"); 1024144894Sharti return; 1025144894Sharti } 1026144473Sharti if (skipIfLevel != 0) { 1027144473Sharti /* 1028144894Sharti * If skipping this conditional, just ignore 1029144894Sharti * the whole thing. If we don't, the user 1030144894Sharti * might be employing a variable that's 1031144894Sharti * undefined, for which there's an enclosing 1032144894Sharti * ifdef that we're skipping... 1033144473Sharti */ 1034144894Sharti skipIfLineno[skipIfLevel - 1] = lineno; 1035144894Sharti return; 1036144473Sharti } 10371590Srgrimes 1038144894Sharti } else if (skipLine) { 1039144473Sharti /* 1040144894Sharti * Don't even try to evaluate a conditional that's 1041144894Sharti * not an else if we're skipping things... 1042144473Sharti */ 1043144894Sharti skipIfLineno[skipIfLevel] = lineno; 1044144894Sharti skipIfLevel += 1; 1045144894Sharti return; 1046144894Sharti } 10478874Srgrimes 1048144894Sharti /* 1049144894Sharti * Initialize file-global variables for parsing 1050144894Sharti */ 1051144894Sharti condDefProc = ifp->defProc; 1052144894Sharti condInvert = ifp->doNot; 10538874Srgrimes 1054144894Sharti while (*line == ' ' || *line == '\t') { 1055144894Sharti line++; 1056144894Sharti } 1057144473Sharti 1058144894Sharti condExpr = line; 1059144894Sharti condPushBack = None; 1060144473Sharti 1061144894Sharti switch (CondE(TRUE)) { 1062144894Sharti case True: 1063144894Sharti if (CondToken(TRUE) != EndOfFile) 1064144473Sharti goto err; 1065144894Sharti value = TRUE; 1066144894Sharti break; 1067144473Sharti 1068144894Sharti case False: 1069144894Sharti if (CondToken(TRUE) != EndOfFile) 1070144894Sharti goto err; 1071144894Sharti value = FALSE; 1072144894Sharti break; 1073144473Sharti 1074144894Sharti case Err: 1075144894Sharti err: Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); 1076144894Sharti return; 1077144894Sharti 1078144894Sharti default: 1079144894Sharti abort(); 1080144473Sharti } 1081144894Sharti 1082144894Sharti if (!ifp->isElse) { 1083144894Sharti /* push this value */ 1084144473Sharti condTop -= 1; 1085144473Sharti 1086144894Sharti } else if (skipIfLevel != 0 || condStack[condTop]) { 1087144473Sharti /* 1088144473Sharti * If this is an else-type conditional, it should only take 1089144473Sharti * effect if its corresponding if was evaluated and FALSE. 1090144473Sharti * If its if was TRUE or skipped, we return COND_SKIP (and 1091144473Sharti * start skipping in case we weren't already), leaving the 1092144473Sharti * stack unmolested so later elif's don't screw up... 1093144473Sharti */ 1094144473Sharti skipLine = TRUE; 1095144894Sharti return; 1096144473Sharti } 1097144473Sharti 1098144473Sharti if (condTop < 0) { 1099144473Sharti /* 1100144473Sharti * This is the one case where we can definitely proclaim a fatal 1101144473Sharti * error. If we don't, we're hosed. 1102144473Sharti */ 1103144894Sharti Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",MAXIF); 1104144894Sharti return; 11051590Srgrimes } 1106144894Sharti 1107144894Sharti /* push */ 1108144894Sharti condStack[condTop] = value; 1109144894Sharti condLineno[condTop] = lineno; 1110144894Sharti skipLine = !value; 11111590Srgrimes} 1112138232Sharti 1113144473Sharti/** 1114144894Sharti * Cond_Else 1115144894Sharti * Handle .else statement. 1116144894Sharti */ 1117144894Shartivoid 1118144894ShartiCond_Else(char *line __unused, int code __unused, int lineno __unused) 1119144894Sharti{ 1120144894Sharti 1121144894Sharti while (isspace((u_char)*line)) 1122144894Sharti line++; 1123144894Sharti 1124145679Sharti if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) { 1125145627Sharti Parse_Error(PARSE_WARNING, "junk after .else ignored '%s'", 1126145627Sharti line); 1127144894Sharti } 1128144894Sharti 1129144894Sharti if (condTop == MAXIF) { 1130144894Sharti Parse_Error(PARSE_FATAL, "if-less else"); 1131144894Sharti return; 1132144894Sharti } 1133144894Sharti if (skipIfLevel != 0) 1134144894Sharti return; 1135144894Sharti 1136144894Sharti if (skipIfLevel != 0 || condStack[condTop]) { 1137144894Sharti /* 1138144894Sharti * An else should only take effect if its corresponding if was 1139144894Sharti * evaluated and FALSE. 1140144894Sharti * If its if was TRUE or skipped, we return COND_SKIP (and 1141144894Sharti * start skipping in case we weren't already), leaving the 1142144894Sharti * stack unmolested so later elif's don't screw up... 1143144894Sharti * XXX How does this work with two .else's? 1144144894Sharti */ 1145144894Sharti skipLine = TRUE; 1146144894Sharti return; 1147144894Sharti } 1148144894Sharti 1149144894Sharti /* inverse value */ 1150144894Sharti condStack[condTop] = !condStack[condTop]; 1151144894Sharti skipLine = !condStack[condTop]; 1152144894Sharti} 1153144894Sharti 1154144894Sharti/** 1155144894Sharti * Cond_Endif 1156144894Sharti * Handle .endif statement. 1157144894Sharti */ 1158144894Shartivoid 1159144894ShartiCond_Endif(char *line __unused, int code __unused, int lineno __unused) 1160144894Sharti{ 1161144894Sharti 1162144894Sharti while (isspace((u_char)*line)) 1163144894Sharti line++; 1164144894Sharti 1165145679Sharti if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) { 1166145627Sharti Parse_Error(PARSE_WARNING, "junk after .endif ignored '%s'", 1167145627Sharti line); 1168144894Sharti } 1169145627Sharti 1170144894Sharti /* 1171144894Sharti * End of a conditional section. If skipIfLevel is non-zero, 1172144894Sharti * that conditional was skipped, so lines following it should 1173144894Sharti * also be skipped. Hence, we return COND_SKIP. Otherwise, 1174144894Sharti * the conditional was read so succeeding lines should be 1175144894Sharti * parsed (think about it...) so we return COND_PARSE, unless 1176144894Sharti * this endif isn't paired with a decent if. 1177144894Sharti */ 1178144894Sharti if (skipIfLevel != 0) { 1179144894Sharti skipIfLevel -= 1; 1180144894Sharti return; 1181144894Sharti } 1182144894Sharti 1183144894Sharti if (condTop == MAXIF) { 1184144894Sharti Parse_Error(PARSE_FATAL, "if-less endif"); 1185144894Sharti return; 1186144894Sharti } 1187144894Sharti 1188144894Sharti /* pop */ 1189144894Sharti skipLine = FALSE; 1190144894Sharti condTop += 1; 1191144894Sharti} 1192144894Sharti 1193144894Sharti/** 1194144473Sharti * Cond_End 11951590Srgrimes * Make sure everything's clean at the end of a makefile. 11961590Srgrimes * 11971590Srgrimes * Side Effects: 11981590Srgrimes * Parse_Error will be called if open conditionals are around. 11991590Srgrimes */ 12001590Srgrimesvoid 1201104696SjmallettCond_End(void) 12021590Srgrimes{ 1203144473Sharti int level; 1204132439Sharti 1205144473Sharti if (condTop != MAXIF) { 1206144473Sharti Parse_Error(PARSE_FATAL, "%d open conditional%s:", 1207144473Sharti MAXIF - condTop + skipIfLevel, 1208144473Sharti MAXIF - condTop + skipIfLevel== 1 ? "" : "s"); 1209132439Sharti 1210144473Sharti for (level = skipIfLevel; level > 0; level--) 1211144473Sharti Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)", 1212144473Sharti MAXIF - condTop + level + 1, "", 1213144473Sharti skipIfLineno[level - 1]); 1214144473Sharti for (level = condTop; level < MAXIF; level++) 1215144473Sharti Parse_Error(PARSE_FATAL, "\t%*sat line %d " 1216144473Sharti "(evaluated to %s)", MAXIF - level + skipIfLevel, 1217144473Sharti "", condLineno[level], 1218144473Sharti condStack[level] ? "true" : "false"); 1219144473Sharti } 1220144473Sharti condTop = MAXIF; 12211590Srgrimes} 1222