1207536Smav/*- 2207536Smav * Copyright (c) 1988, 1989, 1990, 1993 3207536Smav * The Regents of the University of California. All rights reserved. 4207536Smav * Copyright (c) 1988, 1989 by Adam de Boor 5207536Smav * Copyright (c) 1989 by Berkeley Softworks 6207536Smav * All rights reserved. 7207536Smav * 8207536Smav * This code is derived from software contributed to Berkeley by 9207536Smav * Adam de Boor. 10207536Smav * 11207536Smav * Redistribution and use in source and binary forms, with or without 12207536Smav * modification, are permitted provided that the following conditions 13207536Smav * are met: 14207536Smav * 1. Redistributions of source code must retain the above copyright 15207536Smav * notice, this list of conditions and the following disclaimer. 16207536Smav * 2. Redistributions in binary form must reproduce the above copyright 17207536Smav * notice, this list of conditions and the following disclaimer in the 18207536Smav * documentation and/or other materials provided with the distribution. 19207536Smav * 3. All advertising materials mentioning features or use of this software 20207536Smav * must display the following acknowledgement: 21207536Smav * This product includes software developed by the University of 22207536Smav * California, Berkeley and its contributors. 23207536Smav * 4. Neither the name of the University nor the names of its contributors 24207536Smav * may be used to endorse or promote products derived from this software 25207536Smav * without specific prior written permission. 26207536Smav * 27207536Smav * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28207536Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29207536Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30207536Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31207536Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32207536Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33207536Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34207536Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35207536Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36207536Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37207536Smav * SUCH DAMAGE. 38207536Smav * 39207536Smav * @(#)cond.c 8.2 (Berkeley) 1/2/94 40207536Smav */ 41207536Smav 42207536Smav#include <sys/cdefs.h> 43207536Smav__FBSDID("$FreeBSD$"); 44207536Smav 45210471Smav/* 46207536Smav * Functions to handle conditionals in a makefile. 47207536Smav * 48207536Smav * Interface: 49207536Smav * Cond_Eval Evaluate the conditional in the passed line. 50207536Smav */ 51207536Smav 52207536Smav#include <ctype.h> 53207536Smav#include <string.h> 54207536Smav#include <stdlib.h> 55208393Smav 56208393Smav#include "buf.h" 57207536Smav#include "cond.h" 58207536Smav#include "dir.h" 59207536Smav#include "globals.h" 60214099Smav#include "GNode.h" 61214099Smav#include "make.h" 62207536Smav#include "parse.h" 63207536Smav#include "str.h" 64207536Smav#include "targ.h" 65207536Smav#include "util.h" 66207536Smav#include "var.h" 67207536Smav 68207536Smav/* 69207536Smav * The parsing of conditional expressions is based on this grammar: 70207536Smav * E -> F || E 71207536Smav * E -> F 72207536Smav * F -> T && F 73207536Smav * F -> T 74207536Smav * T -> defined(variable) 75207536Smav * T -> make(target) 76207536Smav * T -> exists(file) 77207536Smav * T -> empty(varspec) 78207536Smav * T -> target(name) 79207536Smav * T -> symbol 80207536Smav * T -> $(varspec) op value 81207536Smav * T -> $(varspec) == "string" 82207536Smav * T -> $(varspec) != "string" 83214099Smav * T -> ( E ) 84214099Smav * T -> ! T 85207536Smav * op -> == | != | > | < | >= | <= 86207536Smav * 87207536Smav * 'symbol' is some other symbol to which the default function (condDefProc) 88207536Smav * is applied. 89220569Smav * 90207536Smav * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) 91220569Smav * will return And for '&' and '&&', Or for '|' and '||', Not for '!', 92207536Smav * LParen for '(', RParen for ')' and will evaluate the other terminal 93207536Smav * symbols, using either the default function or the function given in the 94207536Smav * terminal, and return the result as either True or False. 95207536Smav * 96207536Smav * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. 97207536Smav */ 98220569Smavtypedef enum { 99220569Smav And, 100220569Smav Or, 101220569Smav Not, 102220569Smav True, 103220569Smav False, 104207536Smav LParen, 105207536Smav RParen, 106207536Smav EndOfFile, 107207536Smav None, 108207536Smav Err 109207536Smav} Token; 110207536Smav 111207536Smavtypedef Boolean CondProc(int, char *); 112207536Smav 113207536Smav/*- 114207536Smav * Structures to handle elegantly the different forms of #if's. The 115207536Smav * last two fields are stored in condInvert and condDefProc, respectively. 116207536Smav */ 117207536Smavstatic void CondPushBack(Token); 118207536Smavstatic int CondGetArg(char **, char **, const char *, Boolean); 119207536Smavstatic CondProc CondDoDefined; 120207536Smavstatic CondProc CondDoMake; 121207536Smavstatic CondProc CondDoExists; 122207536Smavstatic CondProc CondDoTarget; 123207536Smavstatic char *CondCvtArg(char *, double *); 124207536Smavstatic Token CondToken(Boolean); 125207536Smavstatic Token CondT(Boolean); 126207536Smavstatic Token CondF(Boolean); 127207536Smavstatic Token CondE(Boolean); 128207536Smav 129207536Smavstatic const struct If { 130207536Smav Boolean doNot; /* TRUE if default function should be negated */ 131207536Smav CondProc *defProc; /* Default function to apply */ 132207536Smav Boolean isElse; /* actually el<XXX> */ 133207536Smav} ifs[] = { 134207536Smav [COND_IF] = { FALSE, CondDoDefined, FALSE }, 135207536Smav [COND_IFDEF] = { FALSE, CondDoDefined, FALSE }, 136207536Smav [COND_IFNDEF] = { TRUE, CondDoDefined, FALSE }, 137207536Smav [COND_IFMAKE] = { FALSE, CondDoMake, FALSE }, 138207536Smav [COND_IFNMAKE] = { TRUE, CondDoMake, FALSE }, 139207536Smav [COND_ELIF] = { FALSE, CondDoDefined, TRUE }, 140207536Smav [COND_ELIFDEF] = { FALSE, CondDoDefined, TRUE }, 141207536Smav [COND_ELIFNDEF] = { TRUE, CondDoDefined, TRUE }, 142207536Smav [COND_ELIFMAKE] = { FALSE, CondDoMake, TRUE }, 143207536Smav [COND_ELIFNMAKE] = { TRUE, CondDoMake, TRUE }, 144207536Smav}; 145207536Smav 146207536Smavstatic Boolean condInvert; /* Invert the default function */ 147207536Smavstatic CondProc *condDefProc; /* default function to apply */ 148208393Smavstatic char *condExpr; /* The expression to parse */ 149207536Smavstatic Token condPushBack = None; /* Single push-back token in parsing */ 150207536Smav 151207536Smav#define MAXIF 30 /* greatest depth of #if'ing */ 152207536Smav 153207536Smavstatic Boolean condStack[MAXIF]; /* Stack of conditionals's values */ 154207536Smavstatic int condLineno[MAXIF]; /* Line numbers of the opening .if */ 155207536Smavstatic int condTop = MAXIF; /* Top-most conditional */ 156207536Smavstatic int skipIfLevel = 0; /* Depth of skipped conditionals */ 157207536Smavstatic int skipIfLineno[MAXIF]; /* Line numbers of skipped .ifs */ 158207536SmavBoolean skipLine = FALSE; /* Whether the parse module is skipping 159207536Smav * lines */ 160207536Smav 161207536Smav/** 162207536Smav * CondPushBack 163207536Smav * Push back the most recent token read. We only need one level of 164207536Smav * this, so the thing is just stored in 'condPushback'. 165207536Smav * 166207536Smav * Side Effects: 167207536Smav * condPushback is overwritten. 168207536Smav */ 169207536Smavstatic void 170207536SmavCondPushBack(Token t) 171207536Smav{ 172207536Smav 173207536Smav condPushBack = t; 174207536Smav} 175207536Smav 176207536Smav/** 177207536Smav * CondGetArg 178207536Smav * Find the argument of a built-in function. parens is set to TRUE 179207536Smav * if the arguments are bounded by parens. 180207536Smav * 181207536Smav * Results: 182207536Smav * The length of the argument and the address of the argument. 183207536Smav * 184207536Smav * Side Effects: 185207536Smav * The pointer is set to point to the closing parenthesis of the 186207536Smav * function call. 187207536Smav */ 188207536Smavstatic int 189207536SmavCondGetArg(char **linePtr, char **argPtr, const char *func, Boolean parens) 190207536Smav{ 191207536Smav char *cp; 192207536Smav size_t argLen; 193207536Smav Buffer *buf; 194207536Smav 195207536Smav cp = *linePtr; 196207536Smav if (parens) { 197207536Smav while (*cp != '(' && *cp != '\0') { 198207536Smav cp++; 199207536Smav } 200207536Smav if (*cp == '(') { 201207536Smav cp++; 202207536Smav } 203207536Smav } 204207536Smav 205207536Smav if (*cp == '\0') { 206207536Smav /* 207207536Smav * No arguments whatsoever. Because 'make' and 'defined' 208207536Smav * aren't really "reserved words", we don't print a message. 209207536Smav * I think this is better than hitting the user with a warning 210207536Smav * message every time s/he uses the word 'make' or 'defined' 211207536Smav * at the beginning of a symbol... 212207536Smav */ 213207536Smav *argPtr = cp; 214207536Smav return (0); 215207536Smav } 216207536Smav 217207536Smav while (*cp == ' ' || *cp == '\t') { 218207536Smav cp++; 219207536Smav } 220207536Smav 221207536Smav /* 222207536Smav * Create a buffer for the argument and start it out at 16 characters 223207536Smav * long. Why 16? Why not? 224207536Smav */ 225207536Smav buf = Buf_Init(16); 226207536Smav 227207536Smav while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) { 228207536Smav if (*cp == '$') { 229207536Smav /* 230208393Smav * Parse the variable spec and install it as part of 231207536Smav * the argument if it's valid. We tell Var_Parse to 232207536Smav * complain on an undefined variable, so we don't do 233207536Smav * it too. Nor do we return an error, though perhaps 234207536Smav * we should... 235207536Smav */ 236207536Smav char *cp2; 237207536Smav size_t len = 0; 238207536Smav Boolean doFree; 239207536Smav 240208393Smav cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); 241207536Smav 242207536Smav Buf_Append(buf, cp2); 243207536Smav if (doFree) { 244207536Smav free(cp2); 245207536Smav } 246207536Smav cp += len; 247207536Smav } else { 248207536Smav Buf_AddByte(buf, (Byte)*cp); 249207536Smav cp++; 250207536Smav } 251207536Smav } 252207536Smav 253207536Smav Buf_AddByte(buf, (Byte)'\0'); 254207536Smav *argPtr = (char *)Buf_GetAll(buf, &argLen); 255207536Smav Buf_Destroy(buf, FALSE); 256207536Smav 257207536Smav while (*cp == ' ' || *cp == '\t') { 258207536Smav cp++; 259207536Smav } 260207536Smav if (parens && *cp != ')') { 261207536Smav Parse_Error(PARSE_WARNING, 262207536Smav "Missing closing parenthesis for %s()", func); 263207536Smav return (0); 264207536Smav } else if (parens) { 265207536Smav /* 266207536Smav * Advance pointer past close parenthesis. 267208393Smav */ 268208393Smav cp++; 269208393Smav } 270208393Smav 271208393Smav *linePtr = cp; 272208393Smav return (argLen); 273208393Smav} 274208393Smav 275208393Smav/** 276208393Smav * CondDoDefined 277208393Smav * Handle the 'defined' function for conditionals. 278208393Smav * 279208393Smav * Results: 280208393Smav * TRUE if the given variable is defined. 281208393Smav */ 282208393Smavstatic Boolean 283208393SmavCondDoDefined(int argLen, char *arg) 284208393Smav{ 285208393Smav char savec = arg[argLen]; 286208393Smav Boolean result; 287208393Smav 288208393Smav arg[argLen] = '\0'; 289208393Smav if (Var_Value(arg, VAR_CMD) != NULL) { 290208393Smav result = TRUE; 291208393Smav } else { 292208393Smav result = FALSE; 293208393Smav } 294208393Smav arg[argLen] = savec; 295208393Smav return (result); 296208393Smav} 297208393Smav 298208393Smav/** 299208393Smav * CondDoMake 300208393Smav * Handle the 'make' function for conditionals. 301208393Smav * 302208393Smav * Results: 303208393Smav * TRUE if the given target is being made. 304208393Smav */ 305208393Smavstatic Boolean 306207536SmavCondDoMake(int argLen, char *arg) 307207536Smav{ 308207536Smav char savec = arg[argLen]; 309207536Smav Boolean result; 310207536Smav const LstNode *ln; 311207536Smav 312207536Smav arg[argLen] = '\0'; 313207536Smav result = FALSE; 314207536Smav LST_FOREACH(ln, &create) { 315207536Smav if (Str_Match(Lst_Datum(ln), arg)) { 316207536Smav result = TRUE; 317207536Smav break; 318207536Smav } 319207536Smav } 320207536Smav arg[argLen] = savec; 321207536Smav return (result); 322207536Smav} 323207536Smav 324207536Smav/** 325207536Smav * CondDoExists 326214099Smav * See if the given file exists. 327214099Smav * 328214099Smav * Results: 329214099Smav * TRUE if the file exists and FALSE if it does not. 330214099Smav */ 331207536Smavstatic Boolean 332207536SmavCondDoExists(int argLen, char *arg) 333207536Smav{ 334207536Smav char savec = arg[argLen]; 335207536Smav Boolean result; 336207536Smav char *path; 337207536Smav 338207536Smav arg[argLen] = '\0'; 339207536Smav path = Path_FindFile(arg, &dirSearchPath); 340207536Smav if (path != NULL) { 341207536Smav result = TRUE; 342207536Smav free(path); 343214099Smav } else { 344214099Smav result = FALSE; 345214099Smav } 346214099Smav arg[argLen] = savec; 347214099Smav return (result); 348207536Smav} 349207536Smav 350207536Smav/** 351207536Smav * CondDoTarget 352207536Smav * See if the given node exists and is an actual target. 353207536Smav * 354207536Smav * Results: 355207536Smav * TRUE if the node exists as a target and FALSE if it does not. 356207536Smav */ 357207536Smavstatic Boolean 358207536SmavCondDoTarget(int argLen, char *arg) 359207536Smav{ 360207536Smav char savec = arg[argLen]; 361207536Smav Boolean result; 362207536Smav GNode *gn; 363207536Smav 364207536Smav arg[argLen] = '\0'; 365207536Smav gn = Targ_FindNode(arg, TARG_NOCREATE); 366207536Smav if ((gn != NULL) && !OP_NOP(gn->type)) { 367207536Smav result = TRUE; 368207536Smav } else { 369207536Smav result = FALSE; 370207536Smav } 371207536Smav arg[argLen] = savec; 372207536Smav return (result); 373207536Smav} 374207536Smav 375207536Smav/** 376207536Smav * CondCvtArg 377207536Smav * Convert the given number into a double. If the number begins 378207536Smav * with 0x, it is interpreted as a hexadecimal integer 379207536Smav * and converted to a double from there. All other strings just have 380207536Smav * strtod called on them. 381207536Smav * 382207536Smav * Results: 383207536Smav * Sets 'value' to double value of string. 384207536Smav * Returns address of the first character after the last valid 385207536Smav * character of the converted number. 386207536Smav * 387214099Smav * Side Effects: 388214099Smav * Can change 'value' even if string is not a valid number. 389207536Smav */ 390207536Smavstatic char * 391207536SmavCondCvtArg(char *str, double *value) 392207536Smav{ 393207536Smav 394207536Smav if ((*str == '0') && (str[1] == 'x')) { 395207536Smav long i; 396207536Smav 397207536Smav for (str += 2, i = 0; ; str++) { 398207536Smav int x; 399214099Smav 400214099Smav if (isdigit((unsigned char)*str)) 401207536Smav x = *str - '0'; 402207536Smav else if (isxdigit((unsigned char)*str)) 403207536Smav x = 10 + *str - 404207536Smav isupper((unsigned char)*str) ? 'A' : 'a'; 405207536Smav else { 406207536Smav *value = (double)i; 407207536Smav return (str); 408207536Smav } 409207536Smav i = (i << 4) + x; 410207536Smav } 411207536Smav 412207536Smav } else { 413207536Smav char *eptr; 414207536Smav 415207536Smav *value = strtod(str, &eptr); 416207536Smav return (eptr); 417207536Smav } 418207536Smav} 419207536Smav 420207536Smav/** 421207536Smav * CondToken 422207536Smav * Return the next token from the input. 423207536Smav * 424207536Smav * Results: 425207536Smav * A Token for the next lexical token in the stream. 426207536Smav * 427207536Smav * Side Effects: 428207536Smav * condPushback will be set back to None if it is used. 429207536Smav */ 430207536Smavstatic Token 431207536SmavCondToken(Boolean doEval) 432207536Smav{ 433207536Smav Token t; 434207536Smav 435207536Smav if (condPushBack != None) { 436207536Smav t = condPushBack; 437207536Smav condPushBack = None; 438207536Smav return (t); 439207536Smav } 440207536Smav 441207536Smav while (*condExpr == ' ' || *condExpr == '\t') { 442207536Smav condExpr++; 443207536Smav } 444207536Smav switch (*condExpr) { 445207536Smav case '(': 446207536Smav t = LParen; 447207536Smav condExpr++; 448207536Smav break; 449207536Smav case ')': 450207536Smav t = RParen; 451207536Smav condExpr++; 452207536Smav break; 453207536Smav case '|': 454207536Smav if (condExpr[1] == '|') { 455207536Smav condExpr++; 456207536Smav } 457207536Smav condExpr++; 458207536Smav t = Or; 459207536Smav break; 460207536Smav case '&': 461207536Smav if (condExpr[1] == '&') { 462214099Smav condExpr++; 463214099Smav } 464207536Smav condExpr++; 465214102Smav t = And; 466207536Smav break; 467207536Smav case '!': 468207536Smav t = Not; 469207536Smav condExpr++; 470214099Smav break; 471214099Smav case '\n': 472207536Smav case '\0': 473207536Smav t = EndOfFile; 474207536Smav break; 475207536Smav case '$': { 476207536Smav char *lhs; 477207536Smav const char *op; 478207536Smav char *rhs; 479207536Smav char zero[] = "0"; 480207536Smav size_t varSpecLen = 0; 481207536Smav Boolean doFree; 482207536Smav 483207536Smav /* 484207536Smav * Parse the variable spec and skip over it, saving its 485207536Smav * value in lhs. 486207536Smav */ 487207536Smav t = Err; 488207536Smav lhs = Var_Parse(condExpr, VAR_CMD, doEval, 489207536Smav &varSpecLen, &doFree); 490207536Smav if (lhs == var_Error) { 491207536Smav /* 492207536Smav * Even if !doEval, we still report syntax 493207536Smav * errors, which is what getting var_Error 494207536Smav * back with !doEval means. 495207536Smav */ 496207536Smav return (Err); 497207536Smav } 498207536Smav condExpr += varSpecLen; 499207536Smav 500207536Smav if (!isspace((unsigned char)*condExpr) && 501207536Smav strchr("!=><", *condExpr) == NULL) { 502207536Smav Buffer *buf; 503207536Smav 504207536Smav buf = Buf_Init(0); 505207536Smav 506207536Smav Buf_Append(buf, lhs); 507207536Smav 508207536Smav if (doFree) 509207536Smav free(lhs); 510207536Smav 511207536Smav for (;*condExpr && 512207536Smav !isspace((unsigned char)*condExpr); 513207536Smav condExpr++) 514207536Smav Buf_AddByte(buf, (Byte)*condExpr); 515207536Smav 516207536Smav Buf_AddByte(buf, (Byte)'\0'); 517207536Smav lhs = (char *)Buf_GetAll(buf, &varSpecLen); 518207536Smav Buf_Destroy(buf, FALSE); 519207536Smav 520207536Smav doFree = TRUE; 521207536Smav } 522207536Smav 523207536Smav /* 524207536Smav * Skip whitespace to get to the operator 525207536Smav */ 526207536Smav while (isspace((unsigned char)*condExpr)) 527207536Smav condExpr++; 528207536Smav 529207536Smav /* 530207536Smav * Make sure the operator is a valid one. If it isn't a 531207536Smav * known relational operator, pretend we got a 532207536Smav * != 0 comparison. 533207536Smav */ 534207536Smav op = condExpr; 535207536Smav switch (*condExpr) { 536207536Smav case '!': 537207536Smav case '=': 538207536Smav case '<': 539207536Smav case '>': 540207536Smav if (condExpr[1] == '=') { 541207536Smav condExpr += 2; 542207536Smav } else { 543207536Smav condExpr += 1; 544207536Smav } 545207536Smav while (isspace((unsigned char)*condExpr)) { 546207536Smav condExpr++; 547207536Smav } 548207536Smav if (*condExpr == '\0') { 549207536Smav Parse_Error(PARSE_WARNING, 550207536Smav "Missing right-hand-side of operator"); 551207536Smav goto error; 552207536Smav } 553207536Smav rhs = condExpr; 554207536Smav break; 555207536Smav 556207536Smav default: 557207536Smav op = "!="; 558207536Smav rhs = zero; 559207536Smav break; 560207536Smav } 561207536Smav if (*rhs == '"') { 562207536Smav /* 563207536Smav * Doing a string comparison. Only allow == and 564207536Smav * != for * operators. 565207536Smav */ 566207536Smav char *string; 567207536Smav char *cp, *cp2; 568207536Smav int qt; 569207536Smav Buffer *buf; 570207536Smav 571207536Smav do_string_compare: 572207536Smav if (((*op != '!') && (*op != '=')) || 573207536Smav (op[1] != '=')) { 574207536Smav Parse_Error(PARSE_WARNING, 575207536Smav "String comparison operator should " 576207536Smav "be either == or !="); 577207536Smav goto error; 578207536Smav } 579207536Smav 580207536Smav buf = Buf_Init(0); 581207536Smav qt = *rhs == '"' ? 1 : 0; 582207536Smav 583207536Smav for (cp = &rhs[qt]; 584207536Smav ((qt && (*cp != '"')) || 585207536Smav (!qt && strchr(" \t)", *cp) == NULL)) && 586207536Smav (*cp != '\0'); cp++) { 587207536Smav if ((*cp == '\\') && (cp[1] != '\0')) { 588207536Smav /* 589207536Smav * Backslash escapes things -- 590207536Smav * skip over next character, * if it exists. 591207536Smav */ 592207536Smav cp++; 593207536Smav Buf_AddByte(buf, (Byte)*cp); 594207536Smav 595207536Smav } else if (*cp == '$') { 596207536Smav size_t len = 0; 597207536Smav Boolean freeIt; 598207536Smav 599207536Smav cp2 = Var_Parse(cp, VAR_CMD, 600207536Smav doEval, &len, &freeIt); 601207536Smav if (cp2 != var_Error) { 602207536Smav Buf_Append(buf, cp2); 603207536Smav if (freeIt) { 604207536Smav free(cp2); 605207536Smav } 606207536Smav cp += len - 1; 607207536Smav } else { 608207536Smav Buf_AddByte(buf, 609207536Smav (Byte)*cp); 610207536Smav } 611207536Smav } else { 612207536Smav Buf_AddByte(buf, (Byte)*cp); 613207536Smav } 614207536Smav } 615207536Smav 616207536Smav string = Buf_Peel(buf); 617207536Smav 618207536Smav DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", " 619207536Smav "op = %.2s\n", lhs, string, op)); 620207536Smav /* 621207536Smav * Null-terminate rhs and perform the 622207536Smav * comparison. t is set to the result. 623207536Smav */ 624207536Smav if (*op == '=') { 625207536Smav t = strcmp(lhs, string) ? False : True; 626207536Smav } else { 627207536Smav t = strcmp(lhs, string) ? True : False; 628207536Smav } 629207536Smav free(string); 630207536Smav if (rhs == condExpr) { 631207536Smav if (*cp == '\0' || (!qt && *cp == ')')) 632207536Smav condExpr = cp; 633207536Smav else 634207536Smav condExpr = cp + 1; 635207536Smav } 636207536Smav } else { 637207536Smav /* 638207536Smav * rhs is either a float or an integer. 639207536Smav * Convert both the lhs and the rhs to a 640207536Smav * double and compare the two. 641207536Smav */ 642207536Smav double left, right; 643207536Smav char *string; 644207536Smav 645207536Smav if (*CondCvtArg(lhs, &left) != '\0') 646207536Smav goto do_string_compare; 647207536Smav if (*rhs == '$') { 648207536Smav size_t len = 0; 649207536Smav Boolean freeIt; 650207536Smav 651207536Smav string = Var_Parse(rhs, VAR_CMD, doEval, 652207536Smav &len, &freeIt); 653207536Smav if (string == var_Error) { 654207536Smav right = 0.0; 655207536Smav } else { 656207536Smav if (*CondCvtArg(string, 657207536Smav &right) != '\0') { 658207536Smav if (freeIt) 659207536Smav free(string); 660207536Smav goto do_string_compare; 661207536Smav } 662207536Smav if (freeIt) 663207536Smav free(string); 664207536Smav if (rhs == condExpr) 665207536Smav condExpr += len; 666207536Smav } 667207536Smav } else { 668207536Smav char *c = CondCvtArg(rhs, &right); 669207536Smav 670207536Smav if (c == rhs) 671207536Smav goto do_string_compare; 672207536Smav if (rhs == condExpr) { 673207536Smav /* 674207536Smav * Skip over the right-hand side 675207536Smav */ 676207536Smav condExpr = c; 677207536Smav } 678207536Smav } 679207536Smav 680207536Smav DEBUGF(COND, ("left = %f, right = %f, " 681207536Smav "op = %.2s\n", left, right, op)); 682207536Smav switch (op[0]) { 683207536Smav case '!': 684207536Smav if (op[1] != '=') { 685207536Smav Parse_Error(PARSE_WARNING, 686207536Smav "Unknown operator"); 687207536Smav goto error; 688207536Smav } 689207536Smav t = (left != right ? True : False); 690207536Smav break; 691207536Smav case '=': 692207536Smav if (op[1] != '=') { 693207536Smav Parse_Error(PARSE_WARNING, 694207536Smav "Unknown operator"); 695207536Smav goto error; 696207536Smav } 697207536Smav t = (left == right ? True : False); 698207536Smav break; 699207536Smav case '<': 700207536Smav if (op[1] == '=') { 701207536Smav t = (left <= right?True:False); 702207536Smav } else { 703207536Smav t = (left < right?True:False); 704207536Smav } 705207536Smav break; 706207536Smav case '>': 707207536Smav if (op[1] == '=') { 708207536Smav t = (left >= right?True:False); 709207536Smav } else { 710207536Smav t = (left > right?True:False); 711207536Smav } 712207536Smav break; 713207536Smav default: 714207536Smav break; 715207536Smav } 716207536Smav } 717207536Smav error: 718207536Smav if (doFree) 719207536Smav free(lhs); 720207536Smav break; 721207536Smav } 722207536Smav 723207536Smav default: { 724207536Smav CondProc *evalProc; 725207536Smav Boolean invert = FALSE; 726207536Smav char *arg; 727207536Smav int arglen; 728207536Smav 729207536Smav if (strncmp(condExpr, "defined", 7) == 0) { 730207536Smav /* 731207536Smav * Use CondDoDefined to evaluate the argument 732207536Smav * and CondGetArg to extract the argument from 733207536Smav * the 'function call'. 734207536Smav */ 735207536Smav evalProc = CondDoDefined; 736207536Smav condExpr += 7; 737207536Smav arglen = CondGetArg(&condExpr, &arg, 738207536Smav "defined", TRUE); 739207536Smav if (arglen == 0) { 740207536Smav condExpr -= 7; 741207536Smav goto use_default; 742207536Smav } 743207536Smav 744207536Smav } else if (strncmp(condExpr, "make", 4) == 0) { 745207536Smav /* 746207536Smav * Use CondDoMake to evaluate the argument and 747207536Smav * CondGetArg to extract the argument from the 748207536Smav * 'function call'. 749207536Smav */ 750207536Smav evalProc = CondDoMake; 751207536Smav condExpr += 4; 752207536Smav arglen = CondGetArg(&condExpr, &arg, 753207536Smav "make", TRUE); 754207536Smav if (arglen == 0) { 755207536Smav condExpr -= 4; 756207536Smav goto use_default; 757207536Smav } 758207536Smav 759207536Smav } else if (strncmp(condExpr, "exists", 6) == 0) { 760207536Smav /* 761207536Smav * Use CondDoExists to evaluate the argument and 762207536Smav * CondGetArg to extract the argument from the 763207536Smav * 'function call'. 764207536Smav */ 765207536Smav evalProc = CondDoExists; 766207536Smav condExpr += 6; 767207536Smav arglen = CondGetArg(&condExpr, &arg, 768207536Smav "exists", TRUE); 769207536Smav if (arglen == 0) { 770207536Smav condExpr -= 6; 771207536Smav goto use_default; 772207536Smav } 773207536Smav 774207536Smav } else if (strncmp(condExpr, "empty", 5) == 0) { 775207536Smav /* 776207536Smav * Use Var_Parse to parse the spec in parens and 777207536Smav * return True if the resulting string is empty. 778207536Smav */ 779207536Smav size_t length; 780207536Smav Boolean doFree; 781207536Smav char *val; 782207536Smav 783207536Smav condExpr += 5; 784214099Smav 785214099Smav for (arglen = 0; 786207536Smav condExpr[arglen] != '(' && 787207536Smav condExpr[arglen] != '\0'; arglen += 1) 788207536Smav continue; 789207536Smav 790207536Smav if (condExpr[arglen] != '\0') { 791207536Smav length = 0; 792207536Smav val = Var_Parse(&condExpr[arglen - 1], 793207536Smav VAR_CMD, FALSE, &length, &doFree); 794207536Smav if (val == var_Error) { 795207536Smav t = Err; 796207536Smav } else { 797207536Smav /* 798207536Smav * A variable is empty when it 799207536Smav * just contains spaces... 800207536Smav * 4/15/92, christos 801207536Smav */ 802207536Smav char *p; 803207536Smav 804207536Smav for (p = val; 805207536Smav *p && 806207536Smav isspace((unsigned char)*p); 807207536Smav p++) 808207536Smav continue; 809207536Smav t = (*p == '\0') ? True : False; 810207536Smav } 811207536Smav if (doFree) { 812207536Smav free(val); 813207536Smav } 814207536Smav /* 815207536Smav * Advance condExpr to beyond the 816207536Smav * closing ). Note that we subtract 817207536Smav * one from arglen + length b/c length 818207536Smav * is calculated from 819207536Smav * condExpr[arglen - 1]. 820207536Smav */ 821207536Smav condExpr += arglen + length - 1; 822207536Smav } else { 823207536Smav condExpr -= 5; 824207536Smav goto use_default; 825207536Smav } 826207536Smav break; 827207536Smav 828207536Smav } else if (strncmp(condExpr, "target", 6) == 0) { 829207536Smav /* 830207536Smav * Use CondDoTarget to evaluate the argument and 831207536Smav * CondGetArg to extract the argument from the 832220569Smav * 'function call'. 833220569Smav */ 834207536Smav evalProc = CondDoTarget; 835207536Smav condExpr += 6; 836207536Smav arglen = CondGetArg(&condExpr, &arg, 837207536Smav "target", TRUE); 838207536Smav if (arglen == 0) { 839207536Smav condExpr -= 6; 840207536Smav goto use_default; 841207536Smav } 842207536Smav 843207536Smav } else { 844207536Smav /* 845207536Smav * The symbol is itself the argument to the 846207536Smav * default function. We advance condExpr to 847207536Smav * the end of the symbol by hand (the next 848207536Smav * whitespace, closing paren or binary operator) 849207536Smav * and set to invert the evaluation 850207536Smav * function if condInvert is TRUE. 851207536Smav */ 852207536Smav use_default: 853207536Smav invert = condInvert; 854207536Smav evalProc = condDefProc; 855207536Smav arglen = CondGetArg(&condExpr, &arg, "", FALSE); 856207536Smav } 857207536Smav 858207536Smav /* 859207536Smav * Evaluate the argument using the set function. If 860207536Smav * invert is TRUE, we invert the sense of the function. 861207536Smav */ 862207536Smav t = (!doEval || (* evalProc) (arglen, arg) ? 863207536Smav (invert ? False : True) : 864207536Smav (invert ? True : False)); 865207536Smav free(arg); 866207536Smav break; 867207536Smav } 868207536Smav } 869207536Smav return (t); 870207536Smav} 871207536Smav 872207536Smav/** 873207536Smav * CondT 874207536Smav * Parse a single term in the expression. This consists of a terminal 875207536Smav * symbol or Not and a terminal symbol (not including the binary 876207536Smav * operators): 877207536Smav * T -> defined(variable) | make(target) | exists(file) | symbol 878207536Smav * T -> ! T | ( E ) 879207536Smav * 880214099Smav * Results: 881214099Smav * True, False or Err. 882207536Smav * 883207536Smav * Side Effects: 884207536Smav * Tokens are consumed. 885207536Smav */ 886207536Smavstatic Token 887207536SmavCondT(Boolean doEval) 888207536Smav{ 889207536Smav Token t; 890207536Smav 891207536Smav t = CondToken(doEval); 892207536Smav if (t == EndOfFile) { 893207536Smav /* 894207536Smav * If we reached the end of the expression, the expression 895207536Smav * is malformed... 896207536Smav */ 897207536Smav t = Err; 898207536Smav } else if (t == LParen) { 899207536Smav /* 900207536Smav * T -> ( E ) 901207536Smav */ 902207536Smav t = CondE(doEval); 903207536Smav if (t != Err) { 904214099Smav if (CondToken(doEval) != RParen) { 905214099Smav t = Err; 906220569Smav } 907207536Smav } 908207536Smav } else if (t == Not) { 909207536Smav t = CondT(doEval); 910207536Smav if (t == True) { 911207536Smav t = False; 912214099Smav } else if (t == False) { 913207536Smav t = True; 914207536Smav } 915207536Smav } 916207536Smav return (t); 917207536Smav} 918207536Smav 919207536Smav/** 920207536Smav * CondF -- 921207536Smav * Parse a conjunctive factor (nice name, wot?) 922207536Smav * F -> T && F | T 923207536Smav * 924207536Smav * Results: 925220569Smav * True, False or Err 926220569Smav * 927220569Smav * Side Effects: 928220569Smav * Tokens are consumed. 929207536Smav */ 930207536Smavstatic Token 931207536SmavCondF(Boolean doEval) 932207536Smav{ 933207536Smav Token l, o; 934207536Smav 935207536Smav l = CondT(doEval); 936207536Smav if (l != Err) { 937207536Smav o = CondToken(doEval); 938207536Smav 939207536Smav if (o == And) { 940207536Smav /* 941207536Smav * F -> T && F 942220569Smav * 943220569Smav * If T is False, the whole thing will be False, but 944220569Smav * we have to parse the r.h.s. anyway (to throw it 945220569Smav * away). If T is True, the result is the r.h.s., 946220569Smav * be it an Err or no. 947220569Smav */ 948220569Smav if (l == True) { 949220569Smav l = CondF(doEval); 950220569Smav } else { 951220569Smav CondF(FALSE); 952220569Smav } 953220569Smav } else { 954220569Smav /* 955207536Smav * F -> T 956207536Smav */ 957207536Smav CondPushBack(o); 958207536Smav } 959207536Smav } 960207536Smav return (l); 961207536Smav} 962207536Smav 963207536Smav/** 964207536Smav * CondE -- 965207536Smav * Main expression production. 966207536Smav * E -> F || E | F 967207536Smav * 968207536Smav * Results: 969207536Smav * True, False or Err. 970207536Smav * 971207536Smav * Side Effects: 972207536Smav * Tokens are, of course, consumed. 973207536Smav */ 974207536Smavstatic Token 975207536SmavCondE(Boolean doEval) 976207536Smav{ 977207536Smav Token l, o; 978207536Smav 979207536Smav l = CondF(doEval); 980207536Smav if (l != Err) { 981207536Smav o = CondToken(doEval); 982207536Smav 983207536Smav if (o == Or) { 984207536Smav /* 985207536Smav * E -> F || E 986207536Smav * 987207536Smav * A similar thing occurs for ||, except that here we 988214099Smav * make sure the l.h.s. is False before we bother to 989214099Smav * evaluate the r.h.s. Once again, if l is False, the 990207536Smav * result is the r.h.s. and once again if l is True, 991207536Smav * we parse the r.h.s. to throw it away. 992207536Smav */ 993207536Smav if (l == False) { 994207536Smav l = CondE(doEval); 995207536Smav } else { 996207536Smav CondE(FALSE); 997207536Smav } 998207536Smav } else { 999207536Smav /* 1000207536Smav * E -> F 1001207536Smav */ 1002207536Smav CondPushBack(o); 1003207536Smav } 1004207536Smav } 1005214102Smav return (l); 1006214102Smav} 1007207536Smav 1008207536Smav/** 1009214102Smav * Cond_If 1010214102Smav * Handle .if<X> and .elif<X> directives. 1011214102Smav * This function is called even when we're skipping. 1012214102Smav */ 1013207536Smavvoid 1014207536SmavCond_If(char *line, int code, int lineno) 1015207536Smav{ 1016214102Smav const struct If *ifp; 1017207536Smav Boolean value; 1018207536Smav 1019207536Smav ifp = &ifs[code]; 1020214102Smav 1021214102Smav if (ifp->isElse) { 1022207536Smav if (condTop == MAXIF) { 1023207536Smav Parse_Error(PARSE_FATAL, "if-less elif"); 1024207536Smav return; 1025207536Smav } 1026207536Smav if (skipIfLevel != 0) { 1027207536Smav /* 1028214102Smav * If skipping this conditional, just ignore 1029214102Smav * the whole thing. If we don't, the user 1030214102Smav * might be employing a variable that's 1031214102Smav * undefined, for which there's an enclosing 1032214102Smav * ifdef that we're skipping... 1033214102Smav */ 1034214102Smav skipIfLineno[skipIfLevel - 1] = lineno; 1035214102Smav return; 1036207536Smav } 1037207536Smav 1038214102Smav } else if (skipLine) { 1039214102Smav /* 1040207536Smav * Don't even try to evaluate a conditional that's 1041207536Smav * not an else if we're skipping things... 1042214102Smav */ 1043214102Smav skipIfLineno[skipIfLevel] = lineno; 1044214102Smav skipIfLevel += 1; 1045214102Smav return; 1046214102Smav } 1047214102Smav 1048214102Smav /* 1049214102Smav * Initialize file-global variables for parsing 1050214102Smav */ 1051214102Smav condDefProc = ifp->defProc; 1052214102Smav condInvert = ifp->doNot; 1053207536Smav 1054207536Smav while (*line == ' ' || *line == '\t') { 1055207536Smav line++; 1056207536Smav } 1057207536Smav 1058207536Smav condExpr = line; 1059207536Smav condPushBack = None; 1060207536Smav 1061207536Smav switch (CondE(TRUE)) { 1062207536Smav case True: 1063207536Smav if (CondToken(TRUE) != EndOfFile) 1064207536Smav goto err; 1065207536Smav value = TRUE; 1066207536Smav break; 1067207536Smav 1068207536Smav case False: 1069207536Smav if (CondToken(TRUE) != EndOfFile) 1070207536Smav goto err; 1071207536Smav value = FALSE; 1072207536Smav break; 1073207536Smav 1074207536Smav case Err: 1075207536Smav err: Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); 1076207536Smav return; 1077207536Smav 1078207536Smav default: 1079207536Smav abort(); 1080207536Smav } 1081207536Smav 1082207536Smav if (!ifp->isElse) { 1083207536Smav /* push this value */ 1084207536Smav condTop -= 1; 1085207536Smav 1086207536Smav } else if (skipIfLevel != 0 || condStack[condTop]) { 1087207536Smav /* 1088207536Smav * If this is an else-type conditional, it should only take 1089207536Smav * effect if its corresponding if was evaluated and FALSE. 1090207536Smav * If its if was TRUE or skipped, we return COND_SKIP (and 1091207536Smav * start skipping in case we weren't already), leaving the 1092207536Smav * stack unmolested so later elif's don't screw up... 1093207536Smav */ 1094207536Smav skipLine = TRUE; 1095207536Smav return; 1096207536Smav } 1097207536Smav 1098207536Smav if (condTop < 0) { 1099207536Smav /* 1100207536Smav * This is the one case where we can definitely proclaim a fatal 1101207536Smav * error. If we don't, we're hosed. 1102207536Smav */ 1103207536Smav Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",MAXIF); 1104207536Smav return; 1105207536Smav } 1106207536Smav 1107207536Smav /* push */ 1108207536Smav condStack[condTop] = value; 1109207536Smav condLineno[condTop] = lineno; 1110207536Smav skipLine = !value; 1111207536Smav} 1112207536Smav 1113207536Smav/** 1114207536Smav * Cond_Else 1115207536Smav * Handle .else statement. 1116207536Smav */ 1117207536Smavvoid 1118207536SmavCond_Else(char *line __unused, int code __unused, int lineno __unused) 1119207536Smav{ 1120207536Smav 1121207536Smav while (isspace((u_char)*line)) 1122207536Smav line++; 1123207536Smav 1124207536Smav if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) { 1125207536Smav Parse_Error(PARSE_WARNING, "junk after .else ignored '%s'", 1126207536Smav line); 1127207536Smav } 1128207536Smav 1129207536Smav if (condTop == MAXIF) { 1130207536Smav Parse_Error(PARSE_FATAL, "if-less else"); 1131207536Smav return; 1132207536Smav } 1133207536Smav if (skipIfLevel != 0) 1134207536Smav return; 1135207536Smav 1136207536Smav if (skipIfLevel != 0 || condStack[condTop]) { 1137207536Smav /* 1138207536Smav * An else should only take effect if its corresponding if was 1139207536Smav * evaluated and FALSE. 1140207536Smav * If its if was TRUE or skipped, we return COND_SKIP (and 1141207536Smav * start skipping in case we weren't already), leaving the 1142207536Smav * stack unmolested so later elif's don't screw up... 1143207536Smav * XXX How does this work with two .else's? 1144207536Smav */ 1145207536Smav skipLine = TRUE; 1146207536Smav return; 1147207536Smav } 1148207536Smav 1149207536Smav /* inverse value */ 1150207536Smav condStack[condTop] = !condStack[condTop]; 1151207536Smav skipLine = !condStack[condTop]; 1152207536Smav} 1153207536Smav 1154207536Smav/** 1155207536Smav * Cond_Endif 1156207536Smav * Handle .endif statement. 1157207536Smav */ 1158207536Smavvoid 1159207536SmavCond_Endif(char *line __unused, int code __unused, int lineno __unused) 1160207536Smav{ 1161207536Smav 1162207536Smav while (isspace((u_char)*line)) 1163207536Smav line++; 1164207536Smav 1165207536Smav if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) { 1166207536Smav Parse_Error(PARSE_WARNING, "junk after .endif ignored '%s'", 1167207536Smav line); 1168207536Smav } 1169207536Smav 1170207536Smav /* 1171207536Smav * End of a conditional section. If skipIfLevel is non-zero, 1172207536Smav * that conditional was skipped, so lines following it should 1173207536Smav * also be skipped. Hence, we return COND_SKIP. Otherwise, 1174207536Smav * the conditional was read so succeeding lines should be 1175207536Smav * parsed (think about it...) so we return COND_PARSE, unless 1176207536Smav * this endif isn't paired with a decent if. 1177207536Smav */ 1178207536Smav if (skipIfLevel != 0) { 1179207536Smav skipIfLevel -= 1; 1180207536Smav return; 1181207536Smav } 1182207536Smav 1183207536Smav if (condTop == MAXIF) { 1184207536Smav Parse_Error(PARSE_FATAL, "if-less endif"); 1185207536Smav return; 1186207536Smav } 1187207536Smav 1188207536Smav /* pop */ 1189207536Smav skipLine = FALSE; 1190207536Smav condTop += 1; 1191207536Smav} 1192207536Smav 1193207536Smav/** 1194207536Smav * Cond_End 1195207536Smav * Make sure everything's clean at the end of a makefile. 1196207536Smav * 1197207536Smav * Side Effects: 1198207536Smav * Parse_Error will be called if open conditionals are around. 1199207536Smav */ 1200207536Smavvoid 1201207536SmavCond_End(void) 1202207536Smav{ 1203207536Smav int level; 1204207536Smav 1205207536Smav if (condTop != MAXIF) { 1206207536Smav Parse_Error(PARSE_FATAL, "%d open conditional%s:", 1207207536Smav MAXIF - condTop + skipIfLevel, 1208207536Smav MAXIF - condTop + skipIfLevel== 1 ? "" : "s"); 1209207536Smav 1210207536Smav for (level = skipIfLevel; level > 0; level--) 1211207536Smav Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)", 1212207536Smav MAXIF - condTop + level + 1, "", 1213207536Smav skipIfLineno[level - 1]); 1214207536Smav for (level = condTop; level < MAXIF; level++) 1215207536Smav Parse_Error(PARSE_FATAL, "\t%*sat line %d " 1216207536Smav "(evaluated to %s)", MAXIF - level + skipIfLevel, 1217207536Smav "", condLineno[level], 1218207536Smav condStack[level] ? "true" : "false"); 1219207536Smav } 1220207536Smav condTop = MAXIF; 1221207536Smav} 1222207536Smav