VirtualBox

source: kBuild/trunk/src/kmk/expreval.c@ 3398

Last change on this file since 3398 was 3398, checked in by bird, 5 years ago

kmk/expreval.c: Extended the expressions with version string comparison operators: vle, vlt, vgt, vge, veq, vne

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.1 KB
Line 
1#ifdef CONFIG_WITH_IF_CONDITIONALS
2/* $Id: expreval.c 3398 2020-07-02 09:16:27Z bird $ */
3/** @file
4 * expreval - Expressions evaluator, C / BSD make / nmake style.
5 */
6
7/*
8 * Copyright (c) 2008-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
9 *
10 * This file is part of kBuild.
11 *
12 * kBuild is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * kBuild is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
24 *
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include "makeint.h"
31#include <assert.h>
32
33#include <glob.h>
34
35#include "filedef.h"
36#include "dep.h"
37#include "job.h"
38#include "commands.h"
39#include "variable.h"
40#include "rule.h"
41#include "debug.h"
42#include "hash.h"
43#include "version_compare.h"
44#include <ctype.h>
45#ifndef _MSC_VER
46# include <stdint.h>
47#endif
48#include <stdarg.h>
49
50
51/*******************************************************************************
52* Defined Constants And Macros *
53*******************************************************************************/
54/** The max length of a string representation of a number. */
55#define EXPR_NUM_LEN ((sizeof("-9223372036854775802") + 4) & ~3)
56
57/** The max operator stack depth. */
58#define EXPR_MAX_OPERATORS 72
59/** The max operand depth. */
60#define EXPR_MAX_OPERANDS 128
61
62
63/*******************************************************************************
64* Structures and Typedefs *
65*******************************************************************************/
66/** The 64-bit signed integer type we're using. */
67#ifdef _MSC_VER
68typedef __int64 EXPRINT64;
69#else
70# include <stdint.h>
71typedef int64_t EXPRINT64;
72#endif
73
74/** Pointer to a evaluator instance. */
75typedef struct EXPR *PEXPR;
76
77
78/**
79 * Operand variable type.
80 */
81typedef enum
82{
83 /** Invalid zero entry. */
84 kExprVar_Invalid = 0,
85 /** A number. */
86 kExprVar_Num,
87 /** A string in need of expanding (perhaps). */
88 kExprVar_String,
89 /** A simple string that doesn't need expanding. */
90 kExprVar_SimpleString,
91 /** A quoted string in need of expanding (perhaps). */
92 kExprVar_QuotedString,
93 /** A simple quoted string that doesn't need expanding. */
94 kExprVar_QuotedSimpleString,
95 /** The end of the valid variable types. */
96 kExprVar_End
97} EXPRVARTYPE;
98
99/**
100 * Operand variable.
101 */
102typedef struct
103{
104 /** The variable type. */
105 EXPRVARTYPE enmType;
106 /** The variable. */
107 union
108 {
109 /** Pointer to the string. */
110 char *psz;
111 /** The variable. */
112 EXPRINT64 i;
113 } uVal;
114} EXPRVAR;
115/** Pointer to a operand variable. */
116typedef EXPRVAR *PEXPRVAR;
117/** Pointer to a const operand variable. */
118typedef EXPRVAR const *PCEXPRVAR;
119
120/**
121 * Operator return statuses.
122 */
123typedef enum
124{
125 kExprRet_Error = -1,
126 kExprRet_Ok = 0,
127 kExprRet_Operator,
128 kExprRet_Operand,
129 kExprRet_EndOfExpr,
130 kExprRet_End
131} EXPRRET;
132
133/**
134 * Operator.
135 */
136typedef struct
137{
138 /** The operator. */
139 char szOp[11];
140 /** The length of the operator string. */
141 char cchOp;
142 /** The pair operator.
143 * This is used with '(' and '?'. */
144 char chPair;
145 /** The precedence. Higher means higher. */
146 char iPrecedence;
147 /** The number of arguments it takes. */
148 signed char cArgs;
149 /** Pointer to the method implementing the operator. */
150 EXPRRET (*pfn)(PEXPR pThis);
151} EXPROP;
152/** Pointer to a const operator. */
153typedef EXPROP const *PCEXPROP;
154
155/**
156 * Expression evaluator instance.
157 */
158typedef struct EXPR
159{
160 /** The full expression. */
161 const char *pszExpr;
162 /** The current location. */
163 const char *psz;
164 /** The current file location, used for errors. */
165 const floc *pFileLoc;
166 /** Pending binary operator. */
167 PCEXPROP pPending;
168 /** Top of the operator stack. */
169 int iOp;
170 /** Top of the operand stack. */
171 int iVar;
172 /** The operator stack. */
173 PCEXPROP apOps[EXPR_MAX_OPERATORS];
174 /** The operand stack. */
175 EXPRVAR aVars[EXPR_MAX_OPERANDS];
176} EXPR;
177
178
179/*******************************************************************************
180* Global Variables *
181*******************************************************************************/
182/** Operator start character map.
183 * This indicates which characters that are starting operators and which aren't. */
184static unsigned char g_auchOpStartCharMap[256];
185/** Whether we've initialized the map. */
186static int g_fExprInitializedMap = 0;
187
188
189/*******************************************************************************
190* Internal Functions *
191*******************************************************************************/
192static void expr_unget_op(PEXPR pThis);
193static EXPRRET expr_get_binary_or_eoe_or_rparen(PEXPR pThis);
194
195
196
197
198/**
199 * Displays an error message.
200 *
201 * The total string length must not exceed 256 bytes.
202 *
203 * @param pThis The evaluator instance.
204 * @param pszError The message format string.
205 * @param ... The message format args.
206 */
207static void expr_error(PEXPR pThis, const char *pszError, ...)
208{
209 char szTmp[256];
210 va_list va;
211
212 va_start(va, pszError);
213 vsprintf(szTmp, pszError, va);
214 va_end(va);
215
216 OS(fatal,pThis->pFileLoc, "%s", szTmp);
217}
218
219
220/**
221 * Converts a number to a string.
222 *
223 * @returns pszDst.
224 * @param pszDst The string buffer to write into. Assumes length of EXPR_NUM_LEN.
225 * @param iSrc The number to convert.
226 */
227static char *expr_num_to_string(char *pszDst, EXPRINT64 iSrc)
228{
229 static const char s_szDigits[17] = "0123456789abcdef";
230 char szTmp[EXPR_NUM_LEN];
231 char *psz = &szTmp[EXPR_NUM_LEN - 1];
232 int fNegative;
233
234 fNegative = iSrc < 0;
235 if (fNegative)
236 {
237 /** @todo this isn't right for INT64_MIN. */
238 iSrc = -iSrc;
239 }
240
241 *psz = '\0';
242 do
243 {
244#if 0
245 *--psz = s_szDigits[iSrc & 0xf];
246 iSrc >>= 4;
247#else
248 *--psz = s_szDigits[iSrc % 10];
249 iSrc /= 10;
250#endif
251 } while (iSrc);
252
253#if 0
254 *--psz = 'x';
255 *--psz = '0';
256#endif
257
258 if (fNegative)
259 *--psz = '-';
260
261 /* copy it into the output buffer. */
262 return (char *)memcpy(pszDst, psz, &szTmp[EXPR_NUM_LEN] - psz);
263}
264
265
266/**
267 * Attempts to convert a (simple) string into a number.
268 *
269 * @returns status code.
270 * @param pThis The evaluator instance. This is optional when fQuiet is true.
271 * @param piSrc Where to store the numeric value on success.
272 * @param pszSrc The string to try convert.
273 * @param fQuiet Whether we should be quiet or grumpy on failure.
274 */
275static EXPRRET expr_string_to_num(PEXPR pThis, EXPRINT64 *piDst, const char *pszSrc, int fQuiet)
276{
277 EXPRRET rc = kExprRet_Ok;
278 char const *psz = pszSrc;
279 EXPRINT64 i;
280 unsigned uBase;
281 int fNegative;
282
283
284 /*
285 * Skip blanks.
286 */
287 while (ISBLANK(*psz))
288 psz++;
289
290 /*
291 * Check for '-'.
292 *
293 * At this point we will not need to deal with operators, this is
294 * just an indicator of negative numbers. If some operator ends up
295 * here it's because it came from a string expansion and thus shall
296 * not be interpreted. If this turns out to be an stupid restriction
297 * it can be fixed, but for now it stays like this.
298 */
299 fNegative = *psz == '-';
300 if (fNegative)
301 psz++;
302
303 /*
304 * Determin base .
305 * .
306 * Recognize some exsotic prefixes here in addition to the two standard ones.
307 */
308 if (*psz != '0' || psz[1] == '\0' || ISBLANK(psz[1]))
309 uBase = 10;
310 else if (psz[1] == 'x' || psz[1] == 'X')
311 {
312 uBase = 16;
313 psz += 2;
314 }
315 else if (psz[1] == 'b' || psz[1] == 'B')
316 {
317 uBase = 2;
318 psz += 2;
319 }
320 else if (psz[1] == 'd' || psz[1] == 'D')
321 {
322 uBase = 10;
323 psz += 2;
324 }
325 else if (psz[1] == 'o' || psz[1] == 'O')
326 {
327 uBase = 8;
328 psz += 2;
329 }
330 else if (isdigit(psz[1]) && psz[1] != '9' && psz[1] != '8')
331 {
332 uBase = 8;
333 psz++;
334 }
335 else
336 uBase = 10;
337
338 /*
339 * Convert until we hit a non-digit.
340 */
341 i = 0;
342 for (;;)
343 {
344 unsigned iDigit;
345 int ch = *psz;
346 switch (ch)
347 {
348 case '0': iDigit = 0; break;
349 case '1': iDigit = 1; break;
350 case '2': iDigit = 2; break;
351 case '3': iDigit = 3; break;
352 case '4': iDigit = 4; break;
353 case '5': iDigit = 5; break;
354 case '6': iDigit = 6; break;
355 case '7': iDigit = 7; break;
356 case '8': iDigit = 8; break;
357 case '9': iDigit = 9; break;
358 case 'a':
359 case 'A': iDigit = 10; break;
360 case 'b':
361 case 'B': iDigit = 11; break;
362 case 'c':
363 case 'C': iDigit = 12; break;
364 case 'd':
365 case 'D': iDigit = 13; break;
366 case 'e':
367 case 'E': iDigit = 14; break;
368 case 'f':
369 case 'F': iDigit = 15; break;
370
371 default:
372 /* is the rest white space? */
373 while (ISSPACE(*psz))
374 psz++;
375 if (*psz != '\0')
376 {
377 iDigit = uBase;
378 break;
379 }
380 /* fall thru */
381
382 case '\0':
383 if (fNegative)
384 i = -i;
385 *piDst = i;
386 return rc;
387 }
388 if (iDigit >= uBase)
389 {
390 if (fNegative)
391 i = -i;
392 *piDst = i;
393 if (!fQuiet)
394 expr_error(pThis, "Invalid number \"%.80s\"", pszSrc);
395 return kExprRet_Error;
396 }
397
398 /* add the digit and advance */
399 i *= uBase;
400 i += iDigit;
401 psz++;
402 }
403 /* not reached */
404}
405
406
407/**
408 * Checks if the variable is a string or not.
409 *
410 * @returns 1 if it's a string, 0 otherwise.
411 * @param pVar The variable.
412 */
413static int expr_var_is_string(PCEXPRVAR pVar)
414{
415 return pVar->enmType >= kExprVar_String;
416}
417
418
419/**
420 * Checks if the variable contains a string that was quoted
421 * in the expression.
422 *
423 * @returns 1 if if was a quoted string, otherwise 0.
424 * @param pVar The variable.
425 */
426static int expr_var_was_quoted(PCEXPRVAR pVar)
427{
428 return pVar->enmType >= kExprVar_QuotedString;
429}
430
431
432/**
433 * Deletes a variable.
434 *
435 * @param pVar The variable.
436 */
437static void expr_var_delete(PEXPRVAR pVar)
438{
439 if (expr_var_is_string(pVar))
440 {
441 free(pVar->uVal.psz);
442 pVar->uVal.psz = NULL;
443 }
444 pVar->enmType = kExprVar_Invalid;
445}
446
447
448/**
449 * Initializes a new variables with a sub-string value.
450 *
451 * @param pVar The new variable.
452 * @param psz The start of the string value.
453 * @param cch The number of chars to copy.
454 * @param enmType The string type.
455 */
456static void expr_var_init_substring(PEXPRVAR pVar, const char *psz, size_t cch, EXPRVARTYPE enmType)
457{
458 /* convert string needing expanding into simple ones if possible. */
459 if ( enmType == kExprVar_String
460 && !memchr(psz, '$', cch))
461 enmType = kExprVar_SimpleString;
462 else if ( enmType == kExprVar_QuotedString
463 && !memchr(psz, '$', cch))
464 enmType = kExprVar_QuotedSimpleString;
465
466 pVar->enmType = enmType;
467 pVar->uVal.psz = xmalloc(cch + 1);
468 memcpy(pVar->uVal.psz, psz, cch);
469 pVar->uVal.psz[cch] = '\0';
470}
471
472
473#if 0 /* unused */
474/**
475 * Initializes a new variables with a string value.
476 *
477 * @param pVar The new variable.
478 * @param psz The string value.
479 * @param enmType The string type.
480 */
481static void expr_var_init_string(PEXPRVAR pVar, const char *psz, EXPRVARTYPE enmType)
482{
483 expr_var_init_substring(pVar, psz, strlen(psz), enmType);
484}
485
486
487/**
488 * Assigns a sub-string value to a variable.
489 *
490 * @param pVar The new variable.
491 * @param psz The start of the string value.
492 * @param cch The number of chars to copy.
493 * @param enmType The string type.
494 */
495static void expr_var_assign_substring(PEXPRVAR pVar, const char *psz, size_t cch, EXPRVARTYPE enmType)
496{
497 expr_var_delete(pVar);
498 expr_var_init_substring(pVar, psz, cch, enmType);
499}
500
501
502/**
503 * Assignes a string value to a variable.
504 *
505 * @param pVar The variable.
506 * @param psz The string value.
507 * @param enmType The string type.
508 */
509static void expr_var_assign_string(PEXPRVAR pVar, const char *psz, EXPRVARTYPE enmType)
510{
511 expr_var_delete(pVar);
512 expr_var_init_string(pVar, psz, enmType);
513}
514#endif /* unused */
515
516
517/**
518 * Simplifies a string variable.
519 *
520 * @param pVar The variable.
521 */
522static void expr_var_make_simple_string(PEXPRVAR pVar)
523{
524 switch (pVar->enmType)
525 {
526 case kExprVar_Num:
527 {
528 char *psz = (char *)xmalloc(EXPR_NUM_LEN);
529 expr_num_to_string(psz, pVar->uVal.i);
530 pVar->uVal.psz = psz;
531 pVar->enmType = kExprVar_SimpleString;
532 break;
533 }
534
535 case kExprVar_String:
536 case kExprVar_QuotedString:
537 {
538 char *psz;
539 assert(strchr(pVar->uVal.psz, '$'));
540
541 psz = allocated_variable_expand(pVar->uVal.psz);
542 free(pVar->uVal.psz);
543 pVar->uVal.psz = psz;
544
545 pVar->enmType = pVar->enmType == kExprVar_String
546 ? kExprVar_SimpleString
547 : kExprVar_QuotedSimpleString;
548 break;
549 }
550
551 case kExprVar_SimpleString:
552 case kExprVar_QuotedSimpleString:
553 /* nothing to do. */
554 break;
555
556 default:
557 assert(0);
558 }
559}
560
561
562#if 0 /* unused */
563/**
564 * Turns a variable into a string value.
565 *
566 * @param pVar The variable.
567 */
568static void expr_var_make_string(PEXPRVAR pVar)
569{
570 switch (pVar->enmType)
571 {
572 case kExprVar_Num:
573 expr_var_make_simple_string(pVar);
574 break;
575
576 case kExprVar_String:
577 case kExprVar_SimpleString:
578 case kExprVar_QuotedString:
579 case kExprVar_QuotedSimpleString:
580 /* nothing to do. */
581 break;
582
583 default:
584 assert(0);
585 }
586}
587#endif /* unused */
588
589
590/**
591 * Initializes a new variables with a integer value.
592 *
593 * @param pVar The new variable.
594 * @param i The integer value.
595 */
596static void expr_var_init_num(PEXPRVAR pVar, EXPRINT64 i)
597{
598 pVar->enmType = kExprVar_Num;
599 pVar->uVal.i = i;
600}
601
602
603/**
604 * Assigns a integer value to a variable.
605 *
606 * @param pVar The variable.
607 * @param i The integer value.
608 */
609static void expr_var_assign_num(PEXPRVAR pVar, EXPRINT64 i)
610{
611 expr_var_delete(pVar);
612 expr_var_init_num(pVar, i);
613}
614
615
616/**
617 * Turns the variable into a number.
618 *
619 * @returns status code.
620 * @param pThis The evaluator instance.
621 * @param pVar The variable.
622 */
623static EXPRRET expr_var_make_num(PEXPR pThis, PEXPRVAR pVar)
624{
625 switch (pVar->enmType)
626 {
627 case kExprVar_Num:
628 /* nothing to do. */
629 break;
630
631 case kExprVar_String:
632 expr_var_make_simple_string(pVar);
633 /* fall thru */
634 case kExprVar_SimpleString:
635 {
636 EXPRINT64 i;
637 EXPRRET rc = expr_string_to_num(pThis, &i, pVar->uVal.psz, 0 /* fQuiet */);
638 if (rc < kExprRet_Ok)
639 return rc;
640 expr_var_assign_num(pVar, i);
641 break;
642 }
643
644 case kExprVar_QuotedString:
645 case kExprVar_QuotedSimpleString:
646 expr_error(pThis, "Cannot convert a quoted string to a number");
647 return kExprRet_Error;
648
649 default:
650 assert(0);
651 return kExprRet_Error;
652 }
653
654 return kExprRet_Ok;
655}
656
657
658/**
659 * Try to turn the variable into a number.
660 *
661 * @returns status code.
662 * @param pVar The variable.
663 */
664static EXPRRET expr_var_try_make_num(PEXPRVAR pVar)
665{
666 switch (pVar->enmType)
667 {
668 case kExprVar_Num:
669 /* nothing to do. */
670 break;
671
672 case kExprVar_String:
673 expr_var_make_simple_string(pVar);
674 /* fall thru */
675 case kExprVar_SimpleString:
676 {
677 EXPRINT64 i;
678 EXPRRET rc = expr_string_to_num(NULL, &i, pVar->uVal.psz, 1 /* fQuiet */);
679 if (rc < kExprRet_Ok)
680 return rc;
681 expr_var_assign_num(pVar, i);
682 break;
683 }
684
685 default:
686 assert(0);
687 case kExprVar_QuotedString:
688 case kExprVar_QuotedSimpleString:
689 /* can't do this */
690 return kExprRet_Error;
691 }
692
693 return kExprRet_Ok;
694}
695
696
697/**
698 * Initializes a new variables with a boolean value.
699 *
700 * @param pVar The new variable.
701 * @param f The boolean value.
702 */
703static void expr_var_init_bool(PEXPRVAR pVar, int f)
704{
705 pVar->enmType = kExprVar_Num;
706 pVar->uVal.i = !!f;
707}
708
709
710/**
711 * Assigns a boolean value to a variable.
712 *
713 * @param pVar The variable.
714 * @param f The boolean value.
715 */
716static void expr_var_assign_bool(PEXPRVAR pVar, int f)
717{
718 expr_var_delete(pVar);
719 expr_var_init_bool(pVar, f);
720}
721
722
723/**
724 * Turns the variable into an boolean.
725 *
726 * @returns the boolean interpretation.
727 * @param pVar The variable.
728 */
729static int expr_var_make_bool(PEXPRVAR pVar)
730{
731 switch (pVar->enmType)
732 {
733 case kExprVar_Num:
734 pVar->uVal.i = !!pVar->uVal.i;
735 break;
736
737 case kExprVar_String:
738 expr_var_make_simple_string(pVar);
739 /* fall thru */
740 case kExprVar_SimpleString:
741 {
742 /*
743 * Try convert it to a number. If that fails, use the
744 * GNU make boolean logic - not empty string means true.
745 */
746 EXPRINT64 iVal;
747 char const *psz = pVar->uVal.psz;
748 while (ISBLANK(*psz))
749 psz++;
750 if ( *psz
751 && expr_string_to_num(NULL, &iVal, psz, 1 /* fQuiet */) >= kExprRet_Ok)
752 expr_var_assign_bool(pVar, iVal != 0);
753 else
754 expr_var_assign_bool(pVar, *psz != '\0');
755 break;
756 }
757
758 case kExprVar_QuotedString:
759 expr_var_make_simple_string(pVar);
760 /* fall thru */
761 case kExprVar_QuotedSimpleString:
762 /*
763 * Use GNU make boolean logic (not empty string means true).
764 * No stripping here, the string is quoted.
765 */
766 expr_var_assign_bool(pVar, *pVar->uVal.psz != '\0');
767 break;
768
769 default:
770 assert(0);
771 break;
772 }
773
774 return pVar->uVal.i;
775}
776
777
778/**
779 * Pops a varable off the stack and deletes it.
780 * @param pThis The evaluator instance.
781 */
782static void expr_pop_and_delete_var(PEXPR pThis)
783{
784 expr_var_delete(&pThis->aVars[pThis->iVar]);
785 pThis->iVar--;
786}
787
788
789
790/**
791 * Tries to make the variables the same type.
792 *
793 * This will not convert numbers to strings, unless one of them
794 * is a quoted string.
795 *
796 * this will try convert both to numbers if neither is quoted. Both
797 * conversions will have to suceed for this to be commited.
798 *
799 * All strings will be simplified.
800 *
801 * @returns status code. Done complaining on failure.
802 *
803 * @param pThis The evaluator instance.
804 * @param pVar1 The first variable.
805 * @param pVar2 The second variable.
806 */
807static EXPRRET expr_var_unify_types(PEXPR pThis, PEXPRVAR pVar1, PEXPRVAR pVar2, const char *pszOp)
808{
809 /*
810 * Try make the variables the same type before comparing.
811 */
812 if ( !expr_var_was_quoted(pVar1)
813 && !expr_var_was_quoted(pVar2))
814 {
815 if ( expr_var_is_string(pVar1)
816 || expr_var_is_string(pVar2))
817 {
818 if (!expr_var_is_string(pVar1))
819 expr_var_try_make_num(pVar2);
820 else if (!expr_var_is_string(pVar2))
821 expr_var_try_make_num(pVar1);
822 else
823 {
824 /*
825 * Both are strings, simplify them then see if both can be made into numbers.
826 */
827 EXPRINT64 iVar1;
828 EXPRINT64 iVar2;
829
830 expr_var_make_simple_string(pVar1);
831 expr_var_make_simple_string(pVar2);
832
833 if ( expr_string_to_num(NULL, &iVar1, pVar1->uVal.psz, 1 /* fQuiet */) >= kExprRet_Ok
834 && expr_string_to_num(NULL, &iVar2, pVar2->uVal.psz, 1 /* fQuiet */) >= kExprRet_Ok)
835 {
836 expr_var_assign_num(pVar1, iVar1);
837 expr_var_assign_num(pVar2, iVar2);
838 }
839 }
840 }
841 }
842 else
843 {
844 expr_var_make_simple_string(pVar1);
845 expr_var_make_simple_string(pVar2);
846 }
847
848 /*
849 * Complain if they aren't the same type now.
850 */
851 if (expr_var_is_string(pVar1) != expr_var_is_string(pVar2))
852 {
853 expr_error(pThis, "Unable to unify types for \"%s\"", pszOp);
854 return kExprRet_Error;
855 }
856 return kExprRet_Ok;
857}
858
859
860/**
861 * Is variable defined, unary.
862 *
863 * @returns Status code.
864 * @param pThis The instance.
865 */
866static EXPRRET expr_op_defined(PEXPR pThis)
867{
868 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
869 struct variable *pMakeVar;
870
871 expr_var_make_simple_string(pVar);
872 pMakeVar = lookup_variable(pVar->uVal.psz, strlen(pVar->uVal.psz));
873 expr_var_assign_bool(pVar, pMakeVar && *pMakeVar->value != '\0');
874
875 return kExprRet_Ok;
876}
877
878
879/**
880 * Does file(/dir/whatever) exist, unary.
881 *
882 * @returns Status code.
883 * @param pThis The instance.
884 */
885static EXPRRET expr_op_exists(PEXPR pThis)
886{
887 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
888 struct stat st;
889
890 expr_var_make_simple_string(pVar);
891 expr_var_assign_bool(pVar, stat(pVar->uVal.psz, &st) == 0);
892
893 return kExprRet_Ok;
894}
895
896
897/**
898 * Is target defined, unary.
899 *
900 * @returns Status code.
901 * @param pThis The instance.
902 */
903static EXPRRET expr_op_target(PEXPR pThis)
904{
905 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
906 struct file *pFile = NULL;
907
908 /*
909 * Because of secondary target expansion, lookup the unexpanded
910 * name first.
911 */
912#ifdef CONFIG_WITH_2ND_TARGET_EXPANSION
913 if ( pVar->enmType == kExprVar_String
914 || pVar->enmType == kExprVar_QuotedString)
915 {
916 pFile = lookup_file(pVar->uVal.psz);
917 if ( pFile
918 && !pFile->need_2nd_target_expansion)
919 pFile = NULL;
920 }
921 if (!pFile)
922#endif
923 {
924 expr_var_make_simple_string(pVar);
925 pFile = lookup_file(pVar->uVal.psz);
926 }
927
928 /*
929 * Always inspect the head of a multiple target rule
930 * and look for a file with commands.
931 */
932#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET
933 if (pFile && pFile->multi_head)
934 pFile = pFile->multi_head;
935#endif
936
937 while (pFile && !pFile->cmds)
938 pFile = pFile->prev;
939
940 expr_var_assign_bool(pVar, pFile != NULL && pFile->is_target);
941
942 return kExprRet_Ok;
943}
944
945
946/**
947 * Convert to boolean.
948 *
949 * @returns Status code.
950 * @param pThis The instance.
951 */
952static EXPRRET expr_op_bool(PEXPR pThis)
953{
954 expr_var_make_bool(&pThis->aVars[pThis->iVar]);
955 return kExprRet_Ok;
956}
957
958
959/**
960 * Convert to number, works on quoted strings too.
961 *
962 * @returns Status code.
963 * @param pThis The instance.
964 */
965static EXPRRET expr_op_num(PEXPR pThis)
966{
967 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
968
969 /* unquote the string */
970 if (pVar->enmType == kExprVar_QuotedSimpleString)
971 pVar->enmType = kExprVar_SimpleString;
972 else if (pVar->enmType == kExprVar_QuotedString)
973 pVar->enmType = kExprVar_String;
974
975 return expr_var_make_num(pThis, pVar);
976}
977
978
979/**
980 * Convert to string (simplified and quoted)
981 *
982 * @returns Status code.
983 * @param pThis The instance.
984 */
985static EXPRRET expr_op_str(PEXPR pThis)
986{
987 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
988
989 expr_var_make_simple_string(pVar);
990 pVar->enmType = kExprVar_QuotedSimpleString;
991
992 return kExprRet_Ok;
993}
994
995
996/**
997 * Pluss (dummy / make_integer)
998 *
999 * @returns Status code.
1000 * @param pThis The instance.
1001 */
1002static EXPRRET expr_op_pluss(PEXPR pThis)
1003{
1004 return expr_var_make_num(pThis, &pThis->aVars[pThis->iVar]);
1005}
1006
1007
1008/**
1009 * Minus (negate)
1010 *
1011 * @returns Status code.
1012 * @param pThis The instance.
1013 */
1014static EXPRRET expr_op_minus(PEXPR pThis)
1015{
1016 EXPRRET rc;
1017 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1018
1019 rc = expr_var_make_num(pThis, pVar);
1020 if (rc >= kExprRet_Ok)
1021 pVar->uVal.i = -pVar->uVal.i;
1022
1023 return rc;
1024}
1025
1026
1027
1028/**
1029 * Bitwise NOT.
1030 *
1031 * @returns Status code.
1032 * @param pThis The instance.
1033 */
1034static EXPRRET expr_op_bitwise_not(PEXPR pThis)
1035{
1036 EXPRRET rc;
1037 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1038
1039 rc = expr_var_make_num(pThis, pVar);
1040 if (rc >= kExprRet_Ok)
1041 pVar->uVal.i = ~pVar->uVal.i;
1042
1043 return rc;
1044}
1045
1046
1047/**
1048 * Logical NOT.
1049 *
1050 * @returns Status code.
1051 * @param pThis The instance.
1052 */
1053static EXPRRET expr_op_logical_not(PEXPR pThis)
1054{
1055 PEXPRVAR pVar = &pThis->aVars[pThis->iVar];
1056
1057 expr_var_make_bool(pVar);
1058 pVar->uVal.i = !pVar->uVal.i;
1059
1060 return kExprRet_Ok;
1061}
1062
1063
1064/**
1065 * Multiplication.
1066 *
1067 * @returns Status code.
1068 * @param pThis The instance.
1069 */
1070static EXPRRET expr_op_multiply(PEXPR pThis)
1071{
1072 EXPRRET rc = kExprRet_Ok;
1073 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1074 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1075
1076 rc = expr_var_make_num(pThis, pVar1);
1077 if (rc >= kExprRet_Ok)
1078 {
1079 rc = expr_var_make_num(pThis, pVar2);
1080 if (rc >= kExprRet_Ok)
1081 pVar1->uVal.i *= pVar2->uVal.i;
1082 }
1083
1084 expr_pop_and_delete_var(pThis);
1085 return rc;
1086}
1087
1088
1089
1090/**
1091 * Division.
1092 *
1093 * @returns Status code.
1094 * @param pThis The instance.
1095 */
1096static EXPRRET expr_op_divide(PEXPR pThis)
1097{
1098 EXPRRET rc = kExprRet_Ok;
1099 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1100 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1101
1102 rc = expr_var_make_num(pThis, pVar1);
1103 if (rc >= kExprRet_Ok)
1104 {
1105 rc = expr_var_make_num(pThis, pVar2);
1106 if (rc >= kExprRet_Ok)
1107 pVar1->uVal.i /= pVar2->uVal.i;
1108 }
1109
1110 expr_pop_and_delete_var(pThis);
1111 return rc;
1112}
1113
1114
1115
1116/**
1117 * Modulus.
1118 *
1119 * @returns Status code.
1120 * @param pThis The instance.
1121 */
1122static EXPRRET expr_op_modulus(PEXPR pThis)
1123{
1124 EXPRRET rc = kExprRet_Ok;
1125 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1126 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1127
1128 rc = expr_var_make_num(pThis, pVar1);
1129 if (rc >= kExprRet_Ok)
1130 {
1131 rc = expr_var_make_num(pThis, pVar2);
1132 if (rc >= kExprRet_Ok)
1133 pVar1->uVal.i %= pVar2->uVal.i;
1134 }
1135
1136 expr_pop_and_delete_var(pThis);
1137 return rc;
1138}
1139
1140
1141
1142/**
1143 * Addition (numeric).
1144 *
1145 * @returns Status code.
1146 * @param pThis The instance.
1147 */
1148static EXPRRET expr_op_add(PEXPR pThis)
1149{
1150 EXPRRET rc = kExprRet_Ok;
1151 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1152 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1153
1154 rc = expr_var_make_num(pThis, pVar1);
1155 if (rc >= kExprRet_Ok)
1156 {
1157 rc = expr_var_make_num(pThis, pVar2);
1158 if (rc >= kExprRet_Ok)
1159 pVar1->uVal.i += pVar2->uVal.i;
1160 }
1161
1162 expr_pop_and_delete_var(pThis);
1163 return rc;
1164}
1165
1166
1167/**
1168 * Subtract (numeric).
1169 *
1170 * @returns Status code.
1171 * @param pThis The instance.
1172 */
1173static EXPRRET expr_op_sub(PEXPR pThis)
1174{
1175 EXPRRET rc = kExprRet_Ok;
1176 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1177 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1178
1179 rc = expr_var_make_num(pThis, pVar1);
1180 if (rc >= kExprRet_Ok)
1181 {
1182 rc = expr_var_make_num(pThis, pVar2);
1183 if (rc >= kExprRet_Ok)
1184 pVar1->uVal.i -= pVar2->uVal.i;
1185 }
1186
1187 expr_pop_and_delete_var(pThis);
1188 return rc;
1189}
1190
1191/**
1192 * Bitwise left shift.
1193 *
1194 * @returns Status code.
1195 * @param pThis The instance.
1196 */
1197static EXPRRET expr_op_shift_left(PEXPR pThis)
1198{
1199 EXPRRET rc = kExprRet_Ok;
1200 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1201 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1202
1203 rc = expr_var_make_num(pThis, pVar1);
1204 if (rc >= kExprRet_Ok)
1205 {
1206 rc = expr_var_make_num(pThis, pVar2);
1207 if (rc >= kExprRet_Ok)
1208 pVar1->uVal.i <<= pVar2->uVal.i;
1209 }
1210
1211 expr_pop_and_delete_var(pThis);
1212 return rc;
1213}
1214
1215
1216/**
1217 * Bitwise right shift.
1218 *
1219 * @returns Status code.
1220 * @param pThis The instance.
1221 */
1222static EXPRRET expr_op_shift_right(PEXPR pThis)
1223{
1224 EXPRRET rc = kExprRet_Ok;
1225 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1226 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1227
1228 rc = expr_var_make_num(pThis, pVar1);
1229 if (rc >= kExprRet_Ok)
1230 {
1231 rc = expr_var_make_num(pThis, pVar2);
1232 if (rc >= kExprRet_Ok)
1233 pVar1->uVal.i >>= pVar2->uVal.i;
1234 }
1235
1236 expr_pop_and_delete_var(pThis);
1237 return rc;
1238}
1239
1240
1241/**
1242 * Less than or equal, version string.
1243 *
1244 * @returns Status code.
1245 * @param pThis The instance.
1246 */
1247static EXPRRET expr_op_ver_less_or_equal_than(PEXPR pThis)
1248{
1249 EXPRRET rc = kExprRet_Ok;
1250 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1251 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1252
1253 rc = expr_var_unify_types(pThis, pVar1, pVar2, "vle");
1254 if (rc >= kExprRet_Ok)
1255 {
1256 if (!expr_var_is_string(pVar1))
1257 expr_var_assign_bool(pVar1, pVar1->uVal.i <= pVar2->uVal.i);
1258 else
1259 expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) <= 0);
1260 }
1261
1262 expr_pop_and_delete_var(pThis);
1263 return rc;
1264}
1265
1266
1267/**
1268 * Less than or equal.
1269 *
1270 * @returns Status code.
1271 * @param pThis The instance.
1272 */
1273static EXPRRET expr_op_less_or_equal_than(PEXPR pThis)
1274{
1275 EXPRRET rc = kExprRet_Ok;
1276 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1277 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1278
1279 rc = expr_var_unify_types(pThis, pVar1, pVar2, "<=");
1280 if (rc >= kExprRet_Ok)
1281 {
1282 if (!expr_var_is_string(pVar1))
1283 expr_var_assign_bool(pVar1, pVar1->uVal.i <= pVar2->uVal.i);
1284 else
1285 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) <= 0);
1286 }
1287
1288 expr_pop_and_delete_var(pThis);
1289 return rc;
1290}
1291
1292
1293/**
1294 * Less than, version string.
1295 *
1296 * @returns Status code.
1297 * @param pThis The instance.
1298 */
1299static EXPRRET expr_op_ver_less_than(PEXPR pThis)
1300{
1301 EXPRRET rc = kExprRet_Ok;
1302 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1303 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1304
1305 rc = expr_var_unify_types(pThis, pVar1, pVar2, "vlt");
1306 if (rc >= kExprRet_Ok)
1307 {
1308 if (!expr_var_is_string(pVar1))
1309 expr_var_assign_bool(pVar1, pVar1->uVal.i < pVar2->uVal.i);
1310 else
1311 expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) < 0);
1312 }
1313
1314 expr_pop_and_delete_var(pThis);
1315 return rc;
1316}
1317
1318
1319/**
1320 * Less than.
1321 *
1322 * @returns Status code.
1323 * @param pThis The instance.
1324 */
1325static EXPRRET expr_op_less_than(PEXPR pThis)
1326{
1327 EXPRRET rc = kExprRet_Ok;
1328 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1329 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1330
1331 rc = expr_var_unify_types(pThis, pVar1, pVar2, "<");
1332 if (rc >= kExprRet_Ok)
1333 {
1334 if (!expr_var_is_string(pVar1))
1335 expr_var_assign_bool(pVar1, pVar1->uVal.i < pVar2->uVal.i);
1336 else
1337 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) < 0);
1338 }
1339
1340 expr_pop_and_delete_var(pThis);
1341 return rc;
1342}
1343
1344
1345/**
1346 * Greater or equal than, version string.
1347 *
1348 * @returns Status code.
1349 * @param pThis The instance.
1350 */
1351static EXPRRET expr_op_ver_greater_or_equal_than(PEXPR pThis)
1352{
1353 EXPRRET rc = kExprRet_Ok;
1354 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1355 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1356
1357 rc = expr_var_unify_types(pThis, pVar1, pVar2, "vge");
1358 if (rc >= kExprRet_Ok)
1359 {
1360 if (!expr_var_is_string(pVar1))
1361 expr_var_assign_bool(pVar1, pVar1->uVal.i >= pVar2->uVal.i);
1362 else
1363 expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) >= 0);
1364 }
1365
1366 expr_pop_and_delete_var(pThis);
1367 return rc;
1368}
1369
1370
1371/**
1372 * Greater or equal than.
1373 *
1374 * @returns Status code.
1375 * @param pThis The instance.
1376 */
1377static EXPRRET expr_op_greater_or_equal_than(PEXPR pThis)
1378{
1379 EXPRRET rc = kExprRet_Ok;
1380 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1381 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1382
1383 rc = expr_var_unify_types(pThis, pVar1, pVar2, ">=");
1384 if (rc >= kExprRet_Ok)
1385 {
1386 if (!expr_var_is_string(pVar1))
1387 expr_var_assign_bool(pVar1, pVar1->uVal.i >= pVar2->uVal.i);
1388 else
1389 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) >= 0);
1390 }
1391
1392 expr_pop_and_delete_var(pThis);
1393 return rc;
1394}
1395
1396
1397/**
1398 * Greater than, version string.
1399 *
1400 * @returns Status code.
1401 * @param pThis The instance.
1402 */
1403static EXPRRET expr_op_ver_greater_than(PEXPR pThis)
1404{
1405 EXPRRET rc = kExprRet_Ok;
1406 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1407 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1408
1409 rc = expr_var_unify_types(pThis, pVar1, pVar2, "vgt");
1410 if (rc >= kExprRet_Ok)
1411 {
1412 if (!expr_var_is_string(pVar1))
1413 expr_var_assign_bool(pVar1, pVar1->uVal.i > pVar2->uVal.i);
1414 else
1415 expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) > 0);
1416 }
1417
1418 expr_pop_and_delete_var(pThis);
1419 return rc;
1420}
1421
1422
1423/**
1424 * Greater than.
1425 *
1426 * @returns Status code.
1427 * @param pThis The instance.
1428 */
1429static EXPRRET expr_op_greater_than(PEXPR pThis)
1430{
1431 EXPRRET rc = kExprRet_Ok;
1432 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1433 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1434
1435 rc = expr_var_unify_types(pThis, pVar1, pVar2, ">");
1436 if (rc >= kExprRet_Ok)
1437 {
1438 if (!expr_var_is_string(pVar1))
1439 expr_var_assign_bool(pVar1, pVar1->uVal.i > pVar2->uVal.i);
1440 else
1441 expr_var_assign_bool(pVar1, strcmp(pVar1->uVal.psz, pVar2->uVal.psz) > 0);
1442 }
1443
1444 expr_pop_and_delete_var(pThis);
1445 return rc;
1446}
1447
1448
1449/**
1450 * Equal, version strings.
1451 *
1452 * @returns Status code.
1453 * @param pThis The instance.
1454 */
1455static EXPRRET expr_op_ver_equal(PEXPR pThis)
1456{
1457 EXPRRET rc = kExprRet_Ok;
1458 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1459 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1460 int const fIsString1 = expr_var_is_string(pVar1);
1461
1462 /*
1463 * The same type?
1464 */
1465 if (fIsString1 == expr_var_is_string(pVar2))
1466 {
1467 if (!fIsString1)
1468 /* numbers are simple */
1469 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1470 else
1471 {
1472 /* try a normal string compare. */
1473 expr_var_make_simple_string(pVar1);
1474 expr_var_make_simple_string(pVar2);
1475 if (!version_compare(pVar1->uVal.psz, pVar2->uVal.psz))
1476 expr_var_assign_bool(pVar1, 1);
1477 /* try convert and compare as number instead. */
1478 else if ( expr_var_try_make_num(pVar1) >= kExprRet_Ok
1479 && expr_var_try_make_num(pVar2) >= kExprRet_Ok)
1480 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1481 /* ok, they really aren't equal. */
1482 else
1483 expr_var_assign_bool(pVar1, 0);
1484 }
1485 }
1486 else
1487 {
1488 /*
1489 * If the type differs, there are now two options:
1490 * 1. Try convert the string to a valid number and compare the numbers.
1491 * 2. Convert the non-string to a number and compare the strings.
1492 */
1493 if ( expr_var_try_make_num(pVar1) >= kExprRet_Ok
1494 && expr_var_try_make_num(pVar2) >= kExprRet_Ok)
1495 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1496 else
1497 {
1498 expr_var_make_simple_string(pVar1);
1499 expr_var_make_simple_string(pVar2);
1500 expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) == 0);
1501 }
1502 }
1503
1504 expr_pop_and_delete_var(pThis);
1505 return rc;
1506}
1507
1508
1509/**
1510 * Not equal, version string.
1511 *
1512 * @returns Status code.
1513 * @param pThis The instance.
1514 */
1515static EXPRRET expr_op_ver_not_equal(PEXPR pThis)
1516{
1517 EXPRRET rc = expr_op_ver_equal(pThis);
1518 if (rc >= kExprRet_Ok)
1519 rc = expr_op_logical_not(pThis);
1520 return rc;
1521}
1522
1523
1524/**
1525 * Equal.
1526 *
1527 * @returns Status code.
1528 * @param pThis The instance.
1529 */
1530static EXPRRET expr_op_equal(PEXPR pThis)
1531{
1532 EXPRRET rc = kExprRet_Ok;
1533 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1534 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1535 int const fIsString1 = expr_var_is_string(pVar1);
1536
1537 /*
1538 * The same type?
1539 */
1540 if (fIsString1 == expr_var_is_string(pVar2))
1541 {
1542 if (!fIsString1)
1543 /* numbers are simple */
1544 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1545 else
1546 {
1547 /* try a normal string compare. */
1548 expr_var_make_simple_string(pVar1);
1549 expr_var_make_simple_string(pVar2);
1550 if (!strcmp(pVar1->uVal.psz, pVar2->uVal.psz))
1551 expr_var_assign_bool(pVar1, 1);
1552 /* try convert and compare as number instead. */
1553 else if ( expr_var_try_make_num(pVar1) >= kExprRet_Ok
1554 && expr_var_try_make_num(pVar2) >= kExprRet_Ok)
1555 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1556 /* ok, they really aren't equal. */
1557 else
1558 expr_var_assign_bool(pVar1, 0);
1559 }
1560 }
1561 else
1562 {
1563 /*
1564 * If the type differs, there are now two options:
1565 * 1. Convert the string to a valid number and compare the numbers.
1566 * 2. Convert an empty string to a 'false' boolean value and compare
1567 * numerically. This one is a bit questionable, so we don't try this.
1568 */
1569 if ( expr_var_try_make_num(pVar1) >= kExprRet_Ok
1570 && expr_var_try_make_num(pVar2) >= kExprRet_Ok)
1571 expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
1572 else
1573 {
1574 expr_error(pThis, "Cannot compare strings and numbers");
1575 rc = kExprRet_Error;
1576 }
1577 }
1578
1579 expr_pop_and_delete_var(pThis);
1580 return rc;
1581}
1582
1583
1584/**
1585 * Not equal.
1586 *
1587 * @returns Status code.
1588 * @param pThis The instance.
1589 */
1590static EXPRRET expr_op_not_equal(PEXPR pThis)
1591{
1592 EXPRRET rc = expr_op_equal(pThis);
1593 if (rc >= kExprRet_Ok)
1594 rc = expr_op_logical_not(pThis);
1595 return rc;
1596}
1597
1598
1599/**
1600 * Bitwise AND.
1601 *
1602 * @returns Status code.
1603 * @param pThis The instance.
1604 */
1605static EXPRRET expr_op_bitwise_and(PEXPR pThis)
1606{
1607 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1608 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1609 EXPRRET rc;
1610
1611 rc = expr_var_make_num(pThis, pVar1);
1612 if (rc >= kExprRet_Ok)
1613 {
1614 rc = expr_var_make_num(pThis, pVar2);
1615 if (rc >= kExprRet_Ok)
1616 pVar1->uVal.i &= pVar2->uVal.i;
1617 }
1618
1619 expr_pop_and_delete_var(pThis);
1620 return kExprRet_Ok;
1621}
1622
1623
1624/**
1625 * Bitwise XOR.
1626 *
1627 * @returns Status code.
1628 * @param pThis The instance.
1629 */
1630static EXPRRET expr_op_bitwise_xor(PEXPR pThis)
1631{
1632 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1633 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1634 EXPRRET rc;
1635
1636 rc = expr_var_make_num(pThis, pVar1);
1637 if (rc >= kExprRet_Ok)
1638 {
1639 rc = expr_var_make_num(pThis, pVar2);
1640 if (rc >= kExprRet_Ok)
1641 pVar1->uVal.i ^= pVar2->uVal.i;
1642 }
1643
1644 expr_pop_and_delete_var(pThis);
1645 return kExprRet_Ok;
1646}
1647
1648
1649/**
1650 * Bitwise OR.
1651 *
1652 * @returns Status code.
1653 * @param pThis The instance.
1654 */
1655static EXPRRET expr_op_bitwise_or(PEXPR pThis)
1656{
1657 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1658 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1659 EXPRRET rc;
1660
1661 rc = expr_var_make_num(pThis, pVar1);
1662 if (rc >= kExprRet_Ok)
1663 {
1664 rc = expr_var_make_num(pThis, pVar2);
1665 if (rc >= kExprRet_Ok)
1666 pVar1->uVal.i |= pVar2->uVal.i;
1667 }
1668
1669 expr_pop_and_delete_var(pThis);
1670 return kExprRet_Ok;
1671}
1672
1673
1674/**
1675 * Logical AND.
1676 *
1677 * @returns Status code.
1678 * @param pThis The instance.
1679 */
1680static EXPRRET expr_op_logical_and(PEXPR pThis)
1681{
1682 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1683 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1684
1685 if ( expr_var_make_bool(pVar1)
1686 && expr_var_make_bool(pVar2))
1687 expr_var_assign_bool(pVar1, 1);
1688 else
1689 expr_var_assign_bool(pVar1, 0);
1690
1691 expr_pop_and_delete_var(pThis);
1692 return kExprRet_Ok;
1693}
1694
1695
1696/**
1697 * Logical OR.
1698 *
1699 * @returns Status code.
1700 * @param pThis The instance.
1701 */
1702static EXPRRET expr_op_logical_or(PEXPR pThis)
1703{
1704 PEXPRVAR pVar1 = &pThis->aVars[pThis->iVar - 1];
1705 PEXPRVAR pVar2 = &pThis->aVars[pThis->iVar];
1706
1707 if ( expr_var_make_bool(pVar1)
1708 || expr_var_make_bool(pVar2))
1709 expr_var_assign_bool(pVar1, 1);
1710 else
1711 expr_var_assign_bool(pVar1, 0);
1712
1713 expr_pop_and_delete_var(pThis);
1714 return kExprRet_Ok;
1715}
1716
1717
1718/**
1719 * Left parenthesis.
1720 *
1721 * @returns Status code.
1722 * @param pThis The instance.
1723 */
1724static EXPRRET expr_op_left_parenthesis(PEXPR pThis)
1725{
1726 /*
1727 * There should be a right parenthesis operator lined up for us now,
1728 * eat it. If not found there is an inbalance.
1729 */
1730 EXPRRET rc = expr_get_binary_or_eoe_or_rparen(pThis);
1731 if ( rc == kExprRet_Operator
1732 && pThis->apOps[pThis->iOp]->szOp[0] == ')')
1733 {
1734 /* pop it and get another one which we can leave pending. */
1735 pThis->iOp--;
1736 rc = expr_get_binary_or_eoe_or_rparen(pThis);
1737 if (rc >= kExprRet_Ok)
1738 expr_unget_op(pThis);
1739 }
1740 else
1741 {
1742 expr_error(pThis, "Missing ')'");
1743 rc = kExprRet_Error;
1744 }
1745
1746 return rc;
1747}
1748
1749
1750/**
1751 * Right parenthesis, dummy that's never actually called.
1752 *
1753 * @returns Status code.
1754 * @param pThis The instance.
1755 */
1756static EXPRRET expr_op_right_parenthesis(PEXPR pThis)
1757{
1758 assert(0);
1759 (void)pThis;
1760 return kExprRet_Ok;
1761}
1762
1763
1764
1765
1766
1767/**
1768 * The operator table.
1769 *
1770 * This table is NOT ordered by precedence, but for linear search
1771 * allowing for first match to return the correct operator. This
1772 * means that || must come before |, or else | will match all.
1773 */
1774static const EXPROP g_aExprOps[] =
1775{
1776#define EXPR_OP(szOp, iPrecedence, cArgs, pfn) { szOp, sizeof(szOp) - 1, '\0', iPrecedence, cArgs, pfn }
1777 /* Name, iPrecedence, cArgs, pfn */
1778 EXPR_OP("defined", 90, 1, expr_op_defined),
1779 EXPR_OP("exists", 90, 1, expr_op_exists),
1780 EXPR_OP("target", 90, 1, expr_op_target),
1781 EXPR_OP("bool", 90, 1, expr_op_bool),
1782 EXPR_OP("num", 90, 1, expr_op_num),
1783 EXPR_OP("str", 90, 1, expr_op_str),
1784 EXPR_OP("+", 80, 1, expr_op_pluss),
1785 EXPR_OP("-", 80, 1, expr_op_minus),
1786 EXPR_OP("~", 80, 1, expr_op_bitwise_not),
1787 EXPR_OP("*", 75, 2, expr_op_multiply),
1788 EXPR_OP("/", 75, 2, expr_op_divide),
1789 EXPR_OP("%", 75, 2, expr_op_modulus),
1790 EXPR_OP("+", 70, 2, expr_op_add),
1791 EXPR_OP("-", 70, 2, expr_op_sub),
1792 EXPR_OP("<<", 65, 2, expr_op_shift_left),
1793 EXPR_OP(">>", 65, 2, expr_op_shift_right),
1794 EXPR_OP("<=", 60, 2, expr_op_less_or_equal_than),
1795 EXPR_OP("<", 60, 2, expr_op_less_than),
1796 EXPR_OP(">=", 60, 2, expr_op_greater_or_equal_than),
1797 EXPR_OP(">", 60, 2, expr_op_greater_than),
1798 EXPR_OP("vle", 60, 2, expr_op_ver_less_or_equal_than),
1799 EXPR_OP("vlt", 60, 2, expr_op_ver_less_than),
1800 EXPR_OP("vge", 60, 2, expr_op_ver_greater_or_equal_than),
1801 EXPR_OP("vgt", 60, 2, expr_op_ver_greater_than),
1802 EXPR_OP("==", 55, 2, expr_op_equal),
1803 EXPR_OP("veq", 55, 2, expr_op_ver_equal),
1804 EXPR_OP("!=", 55, 2, expr_op_not_equal),
1805 EXPR_OP("vne", 55, 2, expr_op_ver_not_equal),
1806 EXPR_OP("!", 80, 1, expr_op_logical_not),
1807 EXPR_OP("^", 45, 2, expr_op_bitwise_xor),
1808 EXPR_OP("&&", 35, 2, expr_op_logical_and),
1809 EXPR_OP("&", 50, 2, expr_op_bitwise_and),
1810 EXPR_OP("||", 30, 2, expr_op_logical_or),
1811 EXPR_OP("|", 40, 2, expr_op_bitwise_or),
1812 { "(", 1, ')', 10, 1, expr_op_left_parenthesis },
1813 { ")", 1, '(', 10, 0, expr_op_right_parenthesis },
1814 /* { "?", 1, ':', 5, 2, expr_op_question },
1815 { ":", 1, '?', 5, 2, expr_op_colon }, -- too weird for now. */
1816#undef EXPR_OP
1817};
1818
1819/** Dummy end of expression fake. */
1820static const EXPROP g_ExprEndOfExpOp =
1821{
1822 "", 0, '\0', 0, 0, NULL
1823};
1824
1825
1826/**
1827 * Initializes the opcode character map if necessary.
1828 */
1829static void expr_map_init(void)
1830{
1831 unsigned i;
1832 if (g_fExprInitializedMap)
1833 return;
1834
1835 /*
1836 * Initialize it.
1837 */
1838 memset(&g_auchOpStartCharMap, 0, sizeof(g_auchOpStartCharMap));
1839 for (i = 0; i < sizeof(g_aExprOps) / sizeof(g_aExprOps[0]); i++)
1840 {
1841 unsigned int ch = (unsigned int)g_aExprOps[i].szOp[0];
1842 if (!g_auchOpStartCharMap[ch])
1843 g_auchOpStartCharMap[ch] = (i << 1) | 1;
1844 }
1845
1846 g_fExprInitializedMap = 1;
1847}
1848
1849
1850/**
1851 * Looks up a character in the map.
1852 *
1853 * @returns the value for that char.
1854 * @retval 0 if not a potential opcode start char.
1855 * @retval non-zero if it's a potential operator. The low bit is always set
1856 * while the remaining 7 bits is the index into the operator table
1857 * of the first match.
1858 *
1859 * @param ch The character.
1860 */
1861static unsigned char expr_map_get(char ch)
1862{
1863 return g_auchOpStartCharMap[(unsigned int)ch];
1864}
1865
1866
1867/**
1868 * Searches the operator table given a potential operator start char.
1869 *
1870 * @returns Pointer to the matching operator. NULL if not found.
1871 * @param psz Pointer to what can be an operator.
1872 * @param uchVal The expr_map_get value.
1873 * @param fUnary Whether it must be an unary operator or not.
1874 */
1875static PCEXPROP expr_lookup_op(char const *psz, unsigned char uchVal, int fUnary)
1876{
1877 char ch = *psz;
1878 unsigned i;
1879
1880 for (i = uchVal >> 1; i < sizeof(g_aExprOps) / sizeof(g_aExprOps[0]); i++)
1881 {
1882 /* compare the string... */
1883 if (g_aExprOps[i].szOp[0] != ch)
1884 continue;
1885 switch (g_aExprOps[i].cchOp)
1886 {
1887 case 1:
1888 break;
1889 case 2:
1890 if (g_aExprOps[i].szOp[1] != psz[1])
1891 continue;
1892 break;
1893 default:
1894 if (strncmp(&g_aExprOps[i].szOp[1], psz + 1, g_aExprOps[i].cchOp - 1))
1895 continue;
1896 break;
1897 }
1898
1899 /* ... and the operator type. */
1900 if (fUnary == (g_aExprOps[i].cArgs == 1))
1901 {
1902 /* got a match! */
1903 return &g_aExprOps[i];
1904 }
1905 }
1906
1907 return NULL;
1908}
1909
1910
1911/**
1912 * Ungets a binary operator.
1913 *
1914 * The operator is poped from the stack and put in the pending position.
1915 *
1916 * @param pThis The evaluator instance.
1917 */
1918static void expr_unget_op(PEXPR pThis)
1919{
1920 assert(pThis->pPending == NULL);
1921 assert(pThis->iOp >= 0);
1922
1923 pThis->pPending = pThis->apOps[pThis->iOp];
1924 pThis->apOps[pThis->iOp] = NULL;
1925 pThis->iOp--;
1926}
1927
1928
1929
1930/**
1931 * Get the next token, it should be a binary operator, or the end of
1932 * the expression, or a right parenthesis.
1933 *
1934 * The operator is pushed onto the stack and the status code indicates
1935 * which of the two we found.
1936 *
1937 * @returns status code. Will grumble on failure.
1938 * @retval kExprRet_EndOfExpr if we encountered the end of the expression.
1939 * @retval kExprRet_Operator if we encountered a binary operator or right
1940 * parenthesis. It's on the operator stack.
1941 *
1942 * @param pThis The evaluator instance.
1943 */
1944static EXPRRET expr_get_binary_or_eoe_or_rparen(PEXPR pThis)
1945{
1946 /*
1947 * See if there is anything pending first.
1948 */
1949 PCEXPROP pOp = pThis->pPending;
1950 if (pOp)
1951 pThis->pPending = NULL;
1952 else
1953 {
1954 /*
1955 * Eat more of the expression.
1956 */
1957 char const *psz = pThis->psz;
1958
1959 /* spaces */
1960 while (ISSPACE(*psz))
1961 psz++;
1962 /* see what we've got. */
1963 if (*psz)
1964 {
1965 unsigned char uchVal = expr_map_get(*psz);
1966 if (uchVal)
1967 pOp = expr_lookup_op(psz, uchVal, 0 /* fUnary */);
1968 if (!pOp)
1969 {
1970 expr_error(pThis, "Expected binary operator, found \"%.42s\"...", psz);
1971 return kExprRet_Error;
1972 }
1973 psz += pOp->cchOp;
1974 }
1975 else
1976 pOp = &g_ExprEndOfExpOp;
1977 pThis->psz = psz;
1978 }
1979
1980 /*
1981 * Push it.
1982 */
1983 if (pThis->iOp >= EXPR_MAX_OPERATORS - 1)
1984 {
1985 expr_error(pThis, "Operator stack overflow");
1986 return kExprRet_Error;
1987 }
1988 pThis->apOps[++pThis->iOp] = pOp;
1989
1990 return pOp->iPrecedence
1991 ? kExprRet_Operator
1992 : kExprRet_EndOfExpr;
1993}
1994
1995
1996
1997/**
1998 * Get the next token, it should be an unary operator or an operand.
1999 *
2000 * This will fail if encountering the end of the expression since
2001 * it is implied that there should be something more.
2002 *
2003 * The token is pushed onto the respective stack and the status code
2004 * indicates which it is.
2005 *
2006 * @returns status code. On failure we'll be done bitching already.
2007 * @retval kExprRet_Operator if we encountered an unary operator.
2008 * It's on the operator stack.
2009 * @retval kExprRet_Operand if we encountered an operand operator.
2010 * It's on the operand stack.
2011 *
2012 * @param This The evaluator instance.
2013 */
2014static EXPRRET expr_get_unary_or_operand(PEXPR pThis)
2015{
2016 EXPRRET rc;
2017 unsigned char uchVal;
2018 PCEXPROP pOp;
2019 char const *psz = pThis->psz;
2020
2021 /*
2022 * Eat white space and make sure there is something after it.
2023 */
2024 while (ISSPACE(*psz))
2025 psz++;
2026 if (!*psz)
2027 {
2028 expr_error(pThis, "Unexpected end of expression");
2029 return kExprRet_Error;
2030 }
2031
2032 /*
2033 * Is it an operator?
2034 */
2035 pOp = NULL;
2036 uchVal = expr_map_get(*psz);
2037 if (uchVal)
2038 pOp = expr_lookup_op(psz, uchVal, 1 /* fUnary */);
2039 if (pOp)
2040 {
2041 /*
2042 * Push the operator onto the stack.
2043 */
2044 if (pThis->iVar < EXPR_MAX_OPERANDS - 1)
2045 {
2046 pThis->apOps[++pThis->iOp] = pOp;
2047 rc = kExprRet_Operator;
2048 }
2049 else
2050 {
2051 expr_error(pThis, "Operator stack overflow");
2052 rc = kExprRet_Error;
2053 }
2054 psz += pOp->cchOp;
2055 }
2056 else if (pThis->iVar < EXPR_MAX_OPERANDS - 1)
2057 {
2058 /*
2059 * It's an operand. Figure out where it ends and
2060 * push it onto the stack.
2061 */
2062 const char *pszStart;
2063
2064 rc = kExprRet_Ok;
2065 if (*psz == '"')
2066 {
2067 pszStart = ++psz;
2068 while (*psz && *psz != '"')
2069 psz++;
2070 expr_var_init_substring(&pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kExprVar_QuotedString);
2071 if (*psz)
2072 psz++;
2073 }
2074 else if (*psz == '\'')
2075 {
2076 pszStart = ++psz;
2077 while (*psz && *psz != '\'')
2078 psz++;
2079 expr_var_init_substring(&pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kExprVar_QuotedSimpleString);
2080 if (*psz)
2081 psz++;
2082 }
2083 else
2084 {
2085 char achPars[20];
2086 int iPar = -1;
2087 char chEndPar = '\0';
2088 char ch, ch2;
2089
2090 pszStart = psz;
2091 while ((ch = *psz) != '\0')
2092 {
2093 /* $(adsf) or ${asdf} needs special handling. */
2094 if ( ch == '$'
2095 && ( (ch2 = psz[1]) == '('
2096 || ch2 == '{'))
2097 {
2098 psz++;
2099 if (iPar > (int)(sizeof(achPars) / sizeof(achPars[0])))
2100 {
2101 expr_error(pThis, "Too deep nesting of variable expansions");
2102 rc = kExprRet_Error;
2103 break;
2104 }
2105 achPars[++iPar] = chEndPar = ch2 == '(' ? ')' : '}';
2106 }
2107 else if (ch == chEndPar)
2108 {
2109 iPar--;
2110 chEndPar = iPar >= 0 ? achPars[iPar] : '\0';
2111 }
2112 else if (!chEndPar)
2113 {
2114 /** @todo combine isspace and expr_map_get! */
2115 unsigned chVal = expr_map_get(ch);
2116 if (chVal)
2117 {
2118 pOp = expr_lookup_op(psz, uchVal, 0 /* fUnary */);
2119 if (pOp)
2120 break;
2121 }
2122 if (ISSPACE(ch))
2123 break;
2124 }
2125
2126 /* next */
2127 psz++;
2128 }
2129
2130 if (rc == kExprRet_Ok)
2131 expr_var_init_substring(&pThis->aVars[++pThis->iVar], pszStart, psz - pszStart, kExprVar_String);
2132 }
2133 }
2134 else
2135 {
2136 expr_error(pThis, "Operand stack overflow");
2137 rc = kExprRet_Error;
2138 }
2139 pThis->psz = psz;
2140
2141 return rc;
2142}
2143
2144
2145/**
2146 * Evaluates the current expression.
2147 *
2148 * @returns status code.
2149 *
2150 * @param pThis The instance.
2151 */
2152static EXPRRET expr_eval(PEXPR pThis)
2153{
2154 EXPRRET rc;
2155 PCEXPROP pOp;
2156
2157 /*
2158 * The main loop.
2159 */
2160 for (;;)
2161 {
2162 /*
2163 * Eat unary operators until we hit an operand.
2164 */
2165 do rc = expr_get_unary_or_operand(pThis);
2166 while (rc == kExprRet_Operator);
2167 if (rc < kExprRet_Ok)
2168 break;
2169
2170 /*
2171 * Look for a binary operator, right parenthesis or end of expression.
2172 */
2173 rc = expr_get_binary_or_eoe_or_rparen(pThis);
2174 if (rc < kExprRet_Ok)
2175 break;
2176 expr_unget_op(pThis);
2177
2178 /*
2179 * Pop operators and apply them.
2180 *
2181 * Parenthesis will be handed via precedence, where the left parenthesis
2182 * will go pop the right one and make another operator pending.
2183 */
2184 while ( pThis->iOp >= 0
2185 && pThis->apOps[pThis->iOp]->iPrecedence >= pThis->pPending->iPrecedence)
2186 {
2187 pOp = pThis->apOps[pThis->iOp--];
2188 assert(pThis->iVar + 1 >= pOp->cArgs);
2189 rc = pOp->pfn(pThis);
2190 if (rc < kExprRet_Ok)
2191 break;
2192 }
2193 if (rc < kExprRet_Ok)
2194 break;
2195
2196 /*
2197 * Get the next binary operator or end of expression.
2198 * There should be no right parenthesis here.
2199 */
2200 rc = expr_get_binary_or_eoe_or_rparen(pThis);
2201 if (rc < kExprRet_Ok)
2202 break;
2203 pOp = pThis->apOps[pThis->iOp];
2204 if (!pOp->iPrecedence)
2205 break; /* end of expression */
2206 if (!pOp->cArgs)
2207 {
2208 expr_error(pThis, "Unexpected \"%s\"", pOp->szOp);
2209 rc = kExprRet_Error;
2210 break;
2211 }
2212 }
2213
2214 return rc;
2215}
2216
2217
2218/**
2219 * Destroys the given instance.
2220 *
2221 * @param pThis The instance to destroy.
2222 */
2223static void expr_destroy(PEXPR pThis)
2224{
2225 while (pThis->iVar >= 0)
2226 {
2227 expr_var_delete(pThis->aVars);
2228 pThis->iVar--;
2229 }
2230 free(pThis);
2231}
2232
2233
2234/**
2235 * Instantiates an expression evaluator.
2236 *
2237 * @returns The instance.
2238 *
2239 * @param pszExpr What to parse.
2240 * This must stick around until expr_destroy.
2241 */
2242static PEXPR expr_create(char const *pszExpr)
2243{
2244 PEXPR pThis = (PEXPR)xmalloc(sizeof(*pThis));
2245 pThis->pszExpr = pszExpr;
2246 pThis->psz = pszExpr;
2247 pThis->pFileLoc = NULL;
2248 pThis->pPending = NULL;
2249 pThis->iVar = -1;
2250 pThis->iOp = -1;
2251
2252 expr_map_init();
2253 return pThis;
2254}
2255
2256
2257/**
2258 * Evaluates the given if expression.
2259 *
2260 * @returns -1, 0 or 1. (GNU make conditional check convention, see read.c.)
2261 * @retval -1 if the expression is invalid.
2262 * @retval 0 if the expression is true
2263 * @retval 1 if the expression is false.
2264 *
2265 * @param line The expression.
2266 * @param flocp The file location, used for errors.
2267 */
2268int expr_eval_if_conditionals(const char *line, const floc *flocp)
2269{
2270 /*
2271 * Instantiate the expression evaluator and let
2272 * it have a go at it.
2273 */
2274 int rc = -1;
2275 PEXPR pExpr = expr_create(line);
2276 pExpr->pFileLoc = flocp;
2277 if (expr_eval(pExpr) >= kExprRet_Ok)
2278 {
2279 /*
2280 * Convert the result (on top of the stack) to boolean and
2281 * set our return value accordingly.
2282 */
2283 if (expr_var_make_bool(&pExpr->aVars[0]))
2284 rc = 0;
2285 else
2286 rc = 1;
2287 }
2288 expr_destroy(pExpr);
2289
2290 return rc;
2291}
2292
2293
2294/**
2295 * Evaluates the given expression and returns the result as a string.
2296 *
2297 * @returns variable buffer position.
2298 *
2299 * @param o The current variable buffer position.
2300 * @param expr The expression.
2301 */
2302char *expr_eval_to_string(char *o, const char *expr)
2303{
2304 /*
2305 * Instantiate the expression evaluator and let
2306 * it have a go at it.
2307 */
2308 PEXPR pExpr = expr_create(expr);
2309 if (expr_eval(pExpr) >= kExprRet_Ok)
2310 {
2311 /*
2312 * Convert the result (on top of the stack) to a string
2313 * and copy it out the variable buffer.
2314 */
2315 PEXPRVAR pVar = &pExpr->aVars[0];
2316 expr_var_make_simple_string(pVar);
2317 o = variable_buffer_output(o, pVar->uVal.psz, strlen(pVar->uVal.psz));
2318 }
2319 else
2320 o = variable_buffer_output(o, "<expression evaluation failed>", sizeof("<expression evaluation failed>") - 1);
2321 expr_destroy(pExpr);
2322
2323 return o;
2324}
2325
2326
2327#endif /* CONFIG_WITH_IF_CONDITIONALS */
2328
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette