cond.c
来自「早期freebsd实现」· C语言 代码 · 共 1,248 行 · 第 1/2 页
C
1,248 行
/* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94";#endif /* not lint *//*- * cond.c -- * Functions to handle conditionals in a makefile. * * Interface: * Cond_Eval Evaluate the conditional in the passed line. * */#include <ctype.h>#include <math.h>#include "make.h"#include "hash.h"#include "dir.h"#include "buf.h"/* * The parsing of conditional expressions is based on this grammar: * E -> F || E * E -> F * F -> T && F * F -> T * T -> defined(variable) * T -> make(target) * T -> exists(file) * T -> empty(varspec) * T -> target(name) * T -> symbol * T -> $(varspec) op value * T -> $(varspec) == "string" * T -> $(varspec) != "string" * T -> ( E ) * T -> ! T * op -> == | != | > | < | >= | <= * * 'symbol' is some other symbol to which the default function (condDefProc) * is applied. * * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) * will return And for '&' and '&&', Or for '|' and '||', Not for '!', * LParen for '(', RParen for ')' and will evaluate the other terminal * symbols, using either the default function or the function given in the * terminal, and return the result as either True or False. * * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. */typedef enum { And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err} Token;/*- * Structures to handle elegantly the different forms of #if's. The * last two fields are stored in condInvert and condDefProc, respectively. */static int CondGetArg __P((char **, char **, char *, Boolean));static Boolean CondDoDefined __P((int, char *));static int CondStrMatch __P((char *, char *));static Boolean CondDoMake __P((int, char *));static Boolean CondDoExists __P((int, char *));static Boolean CondDoTarget __P((int, char *));static Boolean CondCvtArg __P((char *, double *));static Token CondToken __P((Boolean));static Token CondT __P((Boolean));static Token CondF __P((Boolean));static Token CondE __P((Boolean));static struct If { char *form; /* Form of if */ int formlen; /* Length of form */ Boolean doNot; /* TRUE if default function should be negated */ Boolean (*defProc)(); /* Default function to apply */} ifs[] = { { "ifdef", 5, FALSE, CondDoDefined }, { "ifndef", 6, TRUE, CondDoDefined }, { "ifmake", 6, FALSE, CondDoMake }, { "ifnmake", 7, TRUE, CondDoMake }, { "if", 2, FALSE, CondDoDefined }, { (char *)0, 0, FALSE, (Boolean (*)())0 }};static Boolean condInvert; /* Invert the default function */static Boolean (*condDefProc)(); /* Default function to apply */static char *condExpr; /* The expression to parse */static Token condPushBack=None; /* Single push-back token used in * parsing */#define MAXIF 30 /* greatest depth of #if'ing */static Boolean condStack[MAXIF]; /* Stack of conditionals's values */static int condTop = MAXIF; /* Top-most conditional */static int skipIfLevel=0; /* Depth of skipped conditionals */static Boolean skipLine = FALSE; /* Whether the parse module is skipping * lines *//*- *----------------------------------------------------------------------- * CondPushBack -- * Push back the most recent token read. We only need one level of * this, so the thing is just stored in 'condPushback'. * * Results: * None. * * Side Effects: * condPushback is overwritten. * *----------------------------------------------------------------------- */static voidCondPushBack (t) Token t; /* Token to push back into the "stream" */{ condPushBack = t;}/*- *----------------------------------------------------------------------- * CondGetArg -- * Find the argument of a built-in function. * * Results: * The length of the argument and the address of the argument. * * Side Effects: * The pointer is set to point to the closing parenthesis of the * function call. * *----------------------------------------------------------------------- */static intCondGetArg (linePtr, argPtr, func, parens) char **linePtr; char **argPtr; char *func; Boolean parens; /* TRUE if arg should be bounded by parens */{ register char *cp; int argLen; register Buffer buf; cp = *linePtr; if (parens) { while (*cp != '(' && *cp != '\0') { cp++; } if (*cp == '(') { cp++; } } if (*cp == '\0') { /* * No arguments whatsoever. Because 'make' and 'defined' aren't really * "reserved words", we don't print a message. I think this is better * than hitting the user with a warning message every time s/he uses * the word 'make' or 'defined' at the beginning of a symbol... */ *argPtr = cp; return (0); } while (*cp == ' ' || *cp == '\t') { cp++; } /* * Create a buffer for the argument and start it out at 16 characters * long. Why 16? Why not? */ buf = Buf_Init(16); while ((strchr(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) { if (*cp == '$') { /* * Parse the variable spec and install it as part of the argument * if it's valid. We tell Var_Parse to complain on an undefined * variable, so we don't do it too. Nor do we return an error, * though perhaps we should... */ char *cp2; int len; Boolean doFree; cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); if (doFree) { free(cp2); } cp += len; } else { Buf_AddByte(buf, (Byte)*cp); cp++; } } Buf_AddByte(buf, (Byte)'\0'); *argPtr = (char *)Buf_GetAll(buf, &argLen); Buf_Destroy(buf, FALSE); while (*cp == ' ' || *cp == '\t') { cp++; } if (parens && *cp != ')') { Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()", func); return (0); } else if (parens) { /* * Advance pointer past close parenthesis. */ cp++; } *linePtr = cp; return (argLen);}/*- *----------------------------------------------------------------------- * CondDoDefined -- * Handle the 'defined' function for conditionals. * * Results: * TRUE if the given variable is defined. * * Side Effects: * None. * *----------------------------------------------------------------------- */static BooleanCondDoDefined (argLen, arg) int argLen; char *arg;{ char savec = arg[argLen]; Boolean result; arg[argLen] = '\0'; if (Var_Value (arg, VAR_CMD) != (char *)NULL) { result = TRUE; } else { result = FALSE; } arg[argLen] = savec; return (result);}/*- *----------------------------------------------------------------------- * CondStrMatch -- * Front-end for Str_Match so it returns 0 on match and non-zero * on mismatch. Callback function for CondDoMake via Lst_Find * * Results: * 0 if string matches pattern * * Side Effects: * None * *----------------------------------------------------------------------- */static intCondStrMatch(string, pattern) char *string; char *pattern;{ return(!Str_Match(string,pattern));}/*- *----------------------------------------------------------------------- * CondDoMake -- * Handle the 'make' function for conditionals. * * Results: * TRUE if the given target is being made. * * Side Effects: * None. * *----------------------------------------------------------------------- */static BooleanCondDoMake (argLen, arg) int argLen; char *arg;{ char savec = arg[argLen]; Boolean result; arg[argLen] = '\0'; if (Lst_Find (create, (ClientData)arg, CondStrMatch) == NILLNODE) { result = FALSE; } else { result = TRUE; } arg[argLen] = savec; return (result);}/*- *----------------------------------------------------------------------- * CondDoExists -- * See if the given file exists. * * Results: * TRUE if the file exists and FALSE if it does not. * * Side Effects: * None. * *----------------------------------------------------------------------- */static BooleanCondDoExists (argLen, arg) int argLen; char *arg;{ char savec = arg[argLen]; Boolean result; char *path; arg[argLen] = '\0'; path = Dir_FindFile(arg, dirSearchPath); if (path != (char *)NULL) { result = TRUE; free(path); } else { result = FALSE; } arg[argLen] = savec; return (result);}/*- *----------------------------------------------------------------------- * CondDoTarget -- * See if the given node exists and is an actual target. * * Results: * TRUE if the node exists as a target and FALSE if it does not. * * Side Effects: * None. * *----------------------------------------------------------------------- */static BooleanCondDoTarget (argLen, arg) int argLen; char *arg;{ char savec = arg[argLen]; Boolean result; GNode *gn; arg[argLen] = '\0'; gn = Targ_FindNode(arg, TARG_NOCREATE); if ((gn != NILGNODE) && !OP_NOP(gn->type)) { result = TRUE; } else { result = FALSE; } arg[argLen] = savec; return (result);}/*- *----------------------------------------------------------------------- * CondCvtArg -- * Convert the given number into a double. If the number begins * with 0x, it is interpreted as a hexadecimal integer * and converted to a double from there. All other strings just have * strtod called on them. * * Results: * Sets 'value' to double value of string. * Returns true if the string was a valid number, false o.w. * * Side Effects: * Can change 'value' even if string is not a valid number. * * *----------------------------------------------------------------------- */static BooleanCondCvtArg(str, value) register char *str; double *value;{ if ((*str == '0') && (str[1] == 'x')) { register long i; for (str += 2, i = 0; *str; str++) { int x; if (isdigit((unsigned char) *str)) x = *str - '0'; else if (isxdigit((unsigned char) *str)) x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a'; else return FALSE; i = (i << 4) + x; } *value = (double) i; return TRUE; } else { char *eptr; *value = strtod(str, &eptr); return *eptr == '\0'; }}/*- *----------------------------------------------------------------------- * CondToken -- * Return the next token from the input. * * Results: * A Token for the next lexical token in the stream. * * Side Effects: * condPushback will be set back to None if it is used. * *----------------------------------------------------------------------- */static TokenCondToken(doEval) Boolean doEval;{ Token t; if (condPushBack == None) { while (*condExpr == ' ' || *condExpr == '\t') { condExpr++; } switch (*condExpr) { case '(': t = LParen; condExpr++; break; case ')': t = RParen; condExpr++; break; case '|': if (condExpr[1] == '|') { condExpr++; } condExpr++; t = Or; break; case '&': if (condExpr[1] == '&') { condExpr++; } condExpr++; t = And; break; case '!': t = Not; condExpr++; break; case '\n': case '\0': t = EndOfFile; break; case '$': { char *lhs; char *rhs; char *op; int varSpecLen; Boolean doFree; /* * Parse the variable spec and skip over it, saving its * value in lhs. */ t = Err; lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree); if (lhs == var_Error) { /* * Even if !doEval, we still report syntax errors, which * is what getting var_Error back with !doEval means. */ return(Err); } condExpr += varSpecLen; if (!isspace(*condExpr) && strchr("!=><", *condExpr) == NULL) { Buffer buf; char *cp; buf = Buf_Init(0); for (cp = lhs; *cp; cp++) Buf_AddByte(buf, (Byte)*cp); if (doFree) free(lhs); for (;*condExpr && !isspace(*condExpr); condExpr++) Buf_AddByte(buf, (Byte)*condExpr); Buf_AddByte(buf, (Byte)'\0'); lhs = (char *)Buf_GetAll(buf, &varSpecLen); Buf_Destroy(buf, FALSE); doFree = TRUE; } /* * Skip whitespace to get to the operator */ while (isspace(*condExpr)) condExpr++; /* * Make sure the operator is a valid one. If it isn't a * known relational operator, pretend we got a * != 0 comparison. */ op = condExpr; switch (*condExpr) { case '!': case '=': case '<': case '>': if (condExpr[1] == '=') { condExpr += 2; } else { condExpr += 1; } break; default: op = "!="; rhs = "0"; goto do_compare; } while (isspace(*condExpr)) { condExpr++; } if (*condExpr == '\0') { Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator"); goto error; } rhs = condExpr;do_compare: if (*rhs == '"') { /* * Doing a string comparison. Only allow == and != for * operators. */ char *string; char *cp, *cp2; int qt; Buffer buf;do_string_compare: if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { Parse_Error(PARSE_WARNING, "String comparison operator should be either == or !="); goto error; } buf = Buf_Init(0); qt = *rhs == '"' ? 1 : 0; for (cp = &rhs[qt]; ((qt && (*cp != '"')) || (!qt && strchr(" \t)", *cp) == NULL)) && (*cp != '\0'); cp++) { if ((*cp == '\\') && (cp[1] != '\0')) { /* * Backslash escapes things -- skip over next * character, if it exists. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?