]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/esi/Expression.cc
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 86 ESI processing */
14 #include "esi/Expression.h"
15 #include "profiler/Profiler.h"
20 /* stack precedence rules:
21 * before pushing an operator onto the stack, the
22 * top 2 elements are checked. if either has a higher
23 * or equal precedence than the current operator, they
25 * Start of expression has 5 precedence,
26 * end of expression has 0 precedence
27 * literal has 1 as does expression results
31 * == != < > <= >= has 5
36 typedef struct _stackmember stackmember
;
38 typedef int evaluate(stackmember
* stack
, int *depth
, int whereAmI
,
39 stackmember
* candidate
);
55 ESI_EXPR_EXPR
/* the result of an expr PRI 1 */
73 literalhint valuestored
;
78 static void cleanmember(stackmember
*);
79 static void stackpop(stackmember
* s
, int *depth
);
82 cleanmember(stackmember
* s
)
84 if (s
->valuetype
== ESI_EXPR_LITERAL
85 && s
->valuestored
== ESI_LITERAL_STRING
) {
86 safe_free(s
->value
.string
);
87 s
->value
.string
= NULL
;
93 stackpop(stackmember
* s
, int *depth
)
98 cleanmember(&s
[*depth
]);
102 stackpush(stackmember
*stack
, stackmember
&item
, int *depth
)
105 throw Esi::Error("ESIExpression stack has negative size");
106 if (*depth
>= ESI_STACK_DEPTH_LIMIT
)
107 throw Esi::Error("ESIExpression stack is full, cannot push");
109 stack
[(*depth
)++] = item
;
112 static evaluate evalnegate
;
113 static evaluate evalliteral
;
114 static evaluate evalor
;
115 static evaluate evaland
;
116 static evaluate evallesseq
;
117 static evaluate evallessthan
;
118 static evaluate evalmoreeq
;
119 static evaluate evalmorethan
;
120 static evaluate evalequals
;
121 static evaluate evalnotequals
;
122 static evaluate evalstartexpr
;
123 static evaluate evalendexpr
;
124 static evaluate evalexpr
;
125 static void dumpstack(stackmember
* stack
, int depth
);
126 static int addmember(stackmember
* stack
, int *stackdepth
,
127 stackmember
* candidate
);
128 static int membercompare(stackmember a
, stackmember b
);
129 static char const *trim(char const *s
);
130 static stackmember
getsymbol(const char *s
, char const **endptr
);
132 /* -2 = failed to compate
138 membercompare(stackmember a
, stackmember b
)
140 /* we can compare: sub expressions to sub expressions ,
141 * literals to literals
144 if (!((a
.valuetype
== ESI_EXPR_LITERAL
&& b
.valuetype
== ESI_EXPR_LITERAL
&&
145 a
.valuestored
!= ESI_LITERAL_INVALID
&& b
.valuestored
!= ESI_LITERAL_INVALID
) ||
146 (a
.valuetype
== ESI_EXPR_EXPR
&& b
.valuetype
== ESI_EXPR_EXPR
)))
149 if (a
.valuetype
== ESI_EXPR_EXPR
) {
150 if (a
.value
.integral
== b
.value
.integral
)
154 } else if (a
.valuestored
== ESI_LITERAL_STRING
) {
155 if (b
.valuestored
== ESI_LITERAL_STRING
) {
156 int i
=strcmp(a
.value
.string
, b
.value
.string
);
166 /* TODO: numeric to string conversion ? */
167 debugs(86, DBG_IMPORTANT
, "strcmp with non-string");
170 } else if (a
.valuestored
== ESI_LITERAL_FLOAT
) {
171 if (b
.valuestored
== ESI_LITERAL_INT
) {
172 if (fabs(a
.value
.floating
- b
.value
.integral
) < 0.00001)
174 else if (a
.value
.floating
< b
.value
.integral
)
178 } else if (b
.valuestored
== ESI_LITERAL_FLOAT
) {
179 if (a
.value
.floating
== b
.value
.floating
)
181 else if (a
.value
.floating
< b
.value
.floating
)
186 /* TODO: attempt numeric converson again? */
187 debugs(86, DBG_IMPORTANT
, "floatcomp with non float or int");
190 } else if (a
.valuestored
== ESI_LITERAL_INT
) {
191 if (b
.valuestored
== ESI_LITERAL_INT
) {
192 if (a
.value
.integral
== b
.value
.integral
)
194 else if (a
.value
.integral
< b
.value
.integral
)
198 } else if (b
.valuestored
== ESI_LITERAL_FLOAT
) {
199 if (fabs(a
.value
.integral
- b
.value
.floating
) < 0.00001)
201 else if (a
.value
.integral
< b
.value
.floating
)
206 /* TODO: attempt numeric converson again? */
207 debugs(86, DBG_IMPORTANT
, "intcomp vs non float non int");
215 /* return 0 on success, 1 on failure */
217 evalnegate(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
219 if (whereAmI
!= *depth
- 2)
224 throw Esi::Error("negate expression location too small");
225 if (*depth
>= ESI_STACK_DEPTH_LIMIT
)
226 throw Esi::Error("negate expression too complex");
228 if (stack
[whereAmI
+ 1].valuetype
!= ESI_EXPR_EXPR
)
229 /* invalid operand */
235 stack
[whereAmI
] = stack
[(*depth
)];
237 cleanmember(candidate
);
239 if (stack
[whereAmI
].value
.integral
== 1)
240 stack
[whereAmI
].value
.integral
= 0;
242 stack
[whereAmI
].value
.integral
= 1;
248 evalliteral(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
250 debugs(86, DBG_IMPORTANT
, "attempt to evaluate a literal");
251 /* literals can't be evaluated */
256 evalexpr(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
258 debugs(86, DBG_IMPORTANT
, "attempt to evaluate a sub-expression result");
259 /* sub-scpr's can't be evaluated */
264 evalor(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
270 /* Not enough operands */
273 if (whereAmI
!= *depth
- 2)
277 if (stack
[whereAmI
+ 1].valuetype
!= ESI_EXPR_EXPR
||
278 stack
[whereAmI
- 1].valuetype
!= ESI_EXPR_EXPR
)
279 /* invalid operand */
282 rv
= stack
[whereAmI
- 1].value
.integral
|| stack
[whereAmI
+ 1].value
.integral
;
284 stackpop(stack
, depth
); /* arg rhs */
286 stackpop(stack
, depth
); /* me */
288 stackpop(stack
, depth
); /* arg lhs */
290 srv
.valuetype
= ESI_EXPR_EXPR
;
292 srv
.eval
= evalliteral
;
294 srv
.valuestored
= ESI_LITERAL_BOOL
;
296 srv
.value
.integral
= rv
? 1 : 0;
300 stackpush(stack
, srv
, depth
);
302 /* we're out of way, try adding now */
303 if (!addmember(stack
, depth
, candidate
))
304 /* Something wrong upstream */
311 evaland(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
317 /* Not enough operands */
320 if (whereAmI
!= *depth
- 2)
324 if (stack
[whereAmI
+ 1].valuetype
!= ESI_EXPR_EXPR
||
325 stack
[whereAmI
- 1].valuetype
!= ESI_EXPR_EXPR
)
326 /* invalid operand */
329 rv
= stack
[whereAmI
- 1].value
.integral
&& stack
[whereAmI
+ 1].value
.integral
;
331 stackpop(stack
, depth
); /* arg rhs */
333 stackpop(stack
, depth
); /* me */
335 stackpop(stack
, depth
); /* arg lhs */
337 srv
.valuetype
= ESI_EXPR_EXPR
;
341 srv
.valuestored
= ESI_LITERAL_BOOL
;
343 srv
.value
.integral
= rv
? 1 : 0;
347 stackpush(stack
, srv
, depth
);
349 /* we're out of way, try adding now */
350 if (!addmember(stack
, depth
, candidate
))
351 /* Something wrong upstream */
358 evallesseq(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
364 /* Not enough operands */
367 if (whereAmI
!= *depth
- 2)
371 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
374 /* invalid comparison */
377 stackpop(stack
, depth
); /* arg rhs */
379 stackpop(stack
, depth
); /* me */
381 stackpop(stack
, depth
); /* arg lhs */
383 srv
.valuetype
= ESI_EXPR_EXPR
;
387 srv
.valuestored
= ESI_LITERAL_BOOL
;
389 srv
.value
.integral
= rv
<= 0 ? 1 : 0;
393 stackpush(stack
, srv
, depth
);
395 /* we're out of way, try adding now */
396 if (!addmember(stack
, depth
, candidate
))
397 /* Something wrong upstream */
400 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
406 evallessthan(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
412 /* Not enough operands */
415 if (whereAmI
!= *depth
- 2)
419 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
422 /* invalid comparison */
425 stackpop(stack
, depth
); /* arg rhs */
427 stackpop(stack
, depth
); /* me */
429 stackpop(stack
, depth
); /* arg lhs */
431 srv
.valuetype
= ESI_EXPR_EXPR
;
435 srv
.valuestored
= ESI_LITERAL_BOOL
;
437 srv
.value
.integral
= rv
< 0 ? 1 : 0;
441 stackpush(stack
, srv
, depth
);
443 /* we're out of way, try adding now */
444 if (!addmember(stack
, depth
, candidate
))
445 /* Something wrong upstream */
448 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
454 evalmoreeq(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
460 /* Not enough operands */
463 if (whereAmI
!= *depth
- 2)
467 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
470 /* invalid comparison */
473 stackpop(stack
, depth
); /* arg rhs */
475 stackpop(stack
, depth
); /* me */
477 stackpop(stack
, depth
); /* arg lhs */
479 srv
.valuetype
= ESI_EXPR_EXPR
;
483 srv
.valuestored
= ESI_LITERAL_BOOL
;
485 srv
.value
.integral
= rv
>= 0 ? 1 : 0;
489 stackpush(stack
, srv
, depth
);
491 /* we're out of way, try adding now */
492 if (!addmember(stack
, depth
, candidate
))
493 /* Something wrong upstream */
496 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
502 evalmorethan(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
508 /* Not enough operands */
511 if (whereAmI
!= *depth
- 2)
515 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
518 /* invalid comparison */
521 stackpop(stack
, depth
); /* arg rhs */
523 stackpop(stack
, depth
); /* me */
525 stackpop(stack
, depth
); /* arg lhs */
527 srv
.valuetype
= ESI_EXPR_EXPR
;
531 srv
.valuestored
= ESI_LITERAL_BOOL
;
533 srv
.value
.integral
= rv
> 0 ? 1 : 0;
537 stackpush(stack
, srv
, depth
);
539 /* we're out of way, try adding now */
540 if (!addmember(stack
, depth
, candidate
))
541 /* Something wrong upstream */
544 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
550 evalequals(stackmember
* stack
, int *depth
, int whereAmI
,
551 stackmember
* candidate
)
557 /* Not enough operands */
560 if (whereAmI
!= *depth
- 2)
564 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
567 /* invalid comparison */
570 stackpop(stack
, depth
); /* arg rhs */
572 stackpop(stack
, depth
); /* me */
574 stackpop(stack
, depth
); /* arg lhs */
576 srv
.valuetype
= ESI_EXPR_EXPR
;
580 srv
.valuestored
= ESI_LITERAL_BOOL
;
582 srv
.value
.integral
= rv
? 0 : 1;
586 stackpush(stack
, srv
, depth
);
588 /* we're out of way, try adding now */
589 if (!addmember(stack
, depth
, candidate
))
590 /* Something wrong upstream */
593 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
598 evalnotequals(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
604 /* Not enough operands */
607 if (whereAmI
!= *depth
- 2)
611 rv
= membercompare(stack
[whereAmI
- 1], stack
[whereAmI
+ 1]);
614 /* invalid comparison */
617 stackpop(stack
, depth
); /* arg rhs */
619 stackpop(stack
, depth
); /* me */
621 stackpop(stack
, depth
); /* arg lhs */
623 srv
.valuetype
= ESI_EXPR_EXPR
;
627 srv
.valuestored
= ESI_LITERAL_BOOL
;
629 srv
.value
.integral
= rv
? 1 : 0;
633 stackpush(stack
, srv
, depth
);
635 /* we're out of way, try adding now */
636 if (!addmember(stack
, depth
, candidate
))
637 /* Something wrong upstream */
640 /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
645 evalstartexpr(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
647 /* debugs(86, DBG_IMPORTANT, "?("); */
649 if (whereAmI
!= *depth
- 2)
653 /* Only valid when RHS is an end bracket */
654 if (candidate
->valuetype
!= ESI_EXPR_END
)
659 stack
[whereAmI
] = stack
[(*depth
)];
661 cleanmember(candidate
);
667 evalendexpr(stackmember
* stack
, int *depth
, int whereAmI
, stackmember
* candidate
)
669 /* Can't evaluate ) brackets */
683 getsymbol(const char *s
, char const **endptr
)
687 char const *origs
= s
;
688 /* trim whitespace */
690 rv
.eval
= NULL
; /* A literal */
691 rv
.valuetype
= ESI_EXPR_INVALID
;
692 rv
.valuestored
= ESI_LITERAL_INVALID
;
693 rv
.precedence
= 1; /* A literal */
695 if (('0' <= *s
&& *s
<= '9') || *s
== '-') {
696 size_t length
= strspn(s
, "0123456789.");
699 if ((point
= strchr(s
, '.')) && point
- s
< (ssize_t
)length
) {
701 errno
=0; /* reset errno */
702 rv
.value
.floating
= strtod(s
, &end
);
704 if (s
== end
|| errno
) {
705 /* Couldn't convert to float */
706 debugs(86, DBG_IMPORTANT
, "failed to convert '" << s
<< "' to float ");
709 debugs(86,6, "found " << rv
.value
.floating
<< " of length " << end
- s
);
711 rv
.eval
= evalliteral
;
712 rv
.valuestored
= ESI_LITERAL_FLOAT
;
713 rv
.valuetype
= ESI_EXPR_LITERAL
;
718 errno
=0; /* reset errno */
719 rv
.value
.integral
= strtol(s
, &end
, 0);
721 if (s
== end
|| errno
) {
722 /* Couldn't convert to int */
723 debugs(86, DBG_IMPORTANT
, "failed to convert '" << s
<< "' to int ");
726 debugs(86,6, "found " << rv
.value
.integral
<< " of length " << end
- s
);
728 rv
.eval
= evalliteral
;
729 rv
.valuestored
= ESI_LITERAL_INT
;
730 rv
.valuetype
= ESI_EXPR_LITERAL
;
734 } else if ('!' == *s
) {
735 if ('=' == *(s
+ 1)) {
736 debugs(86, 6, "found !=");
738 rv
.eval
= evalnotequals
;
739 rv
.valuetype
= ESI_EXPR_NOTEQ
;
742 debugs(86, 6, "found !");
744 rv
.valuetype
= ESI_EXPR_NOT
;
746 rv
.eval
= evalnegate
;
748 } else if ('\'' == *s
) {
749 char const *t
= s
+ 1;
750 debugs(86, 6, "found \'");
752 while (*t
!= '\'' && *t
)
756 debugs(86, DBG_IMPORTANT
, "missing end \' in '" << s
<< "'");
760 /* Special case for zero length strings */
763 rv
.value
.string
= xstrndup(s
+ 1, t
- (s
+ 1) + 1);
765 rv
.value
.string
= static_cast<char *>(xcalloc(1,1));
767 rv
.eval
= evalliteral
;
769 rv
.valuestored
= ESI_LITERAL_STRING
;
771 rv
.valuetype
= ESI_EXPR_LITERAL
;
775 debugs(86, 6, "found string '" << rv
.value
.string
<< "'");
777 } else if ('(' == *s
) {
778 debugs(86, 6, "found subexpr start");
780 rv
.valuetype
= ESI_EXPR_START
;
782 rv
.eval
= evalstartexpr
;
783 } else if (')' == *s
) {
784 debugs(86, 6, "found subexpr end");
786 rv
.valuetype
= ESI_EXPR_END
;
788 rv
.eval
= evalendexpr
;
789 } else if ('&' == *s
) {
790 debugs(86, 6, "found AND");
792 rv
.valuetype
= ESI_EXPR_AND
;
795 } else if ('|' == *s
) {
796 debugs(86, 6, "found OR");
798 rv
.valuetype
= ESI_EXPR_OR
;
801 } else if ('=' == *s
) {
802 if ('=' == *(s
+ 1)) {
803 debugs(86, 6, "found equals");
805 rv
.valuetype
= ESI_EXPR_EQ
;
807 rv
.eval
= evalequals
;
809 debugs(86, DBG_IMPORTANT
, "invalid expr '" << s
<< "'");
812 } else if ('<' == *s
) {
813 if ('=' == *(s
+ 1)) {
814 debugs(86, 6, "found less-equals");
816 rv
.valuetype
= ESI_EXPR_LESSEQ
;
818 rv
.eval
= evallesseq
;
820 debugs(86, 6, "found less than");
822 rv
.valuetype
= ESI_EXPR_LESS
;
824 rv
.eval
= evallessthan
;
826 } else if ('>' == *s
) {
827 if ('=' == *(s
+ 1)) {
828 debugs(86, 6, "found more-equals");
830 rv
.valuetype
= ESI_EXPR_MOREEQ
;
832 rv
.eval
= evalmoreeq
;
834 debugs(86, 6, "found more than");
836 rv
.valuetype
= ESI_EXPR_MORE
;
838 rv
.eval
= evalmorethan
;
840 } else if (!strncmp(s
, "false", 5)) {
841 debugs(86, 5, "getsymbol: found variable result 'false'");
843 rv
.valuetype
= ESI_EXPR_EXPR
;
844 rv
.valuestored
= ESI_LITERAL_BOOL
;
845 rv
.value
.integral
= 0;
848 } else if (!strncmp(s
, "true", 4)) {
849 debugs(86, 5, "getsymbol: found variable result 'true'");
851 rv
.valuetype
= ESI_EXPR_EXPR
;
852 rv
.valuestored
= ESI_LITERAL_BOOL
;
853 rv
.value
.integral
= 1;
857 debugs(86, DBG_IMPORTANT
, "invalid expr '" << s
<< "'");
865 printLiteral(std::ostream
&os
, const stackmember
&s
)
867 switch (s
.valuestored
) {
869 case ESI_LITERAL_INVALID
:
873 case ESI_LITERAL_FLOAT
:
874 os
<< s
.value
.floating
;
877 case ESI_LITERAL_STRING
:
878 os
<< '\'' << s
.value
.string
<< '\'';
881 case ESI_LITERAL_INT
:
882 os
<< s
.value
.integral
;
885 case ESI_LITERAL_BOOL
:
886 os
<< (s
.value
.integral
? "true" : "false");
890 static std::ostream
&
891 operator <<(std::ostream
&os
, const stackmember
&s
)
893 switch (s
.valuetype
) {
895 case ESI_EXPR_INVALID
:
899 case ESI_EXPR_LITERAL
:
904 os
<< (s
.value
.integral
? "true" : "false");
939 case ESI_EXPR_LESSEQ
:
947 case ESI_EXPR_MOREEQ
:
956 dumpstack(stackmember
* stack
, int depth
)
959 std::ostringstream buf
;
960 for (int i
= 0; i
< depth
; ++i
)
962 debugs(86,1, buf
.str());
967 addmember(stackmember
* stack
, int *stackdepth
, stackmember
* candidate
)
969 if (candidate
->valuetype
!= ESI_EXPR_LITERAL
&& *stackdepth
> 1) {
970 /* !(!(a==b))) is why that's safe */
971 /* strictly less than until we unwind */
973 if (*stackdepth
>= ESI_STACK_DEPTH_LIMIT
)
974 throw Esi::Error("ESI expression too complex to add member");
976 if (candidate
->precedence
< stack
[*stackdepth
- 1].precedence
||
977 candidate
->precedence
< stack
[*stackdepth
- 2].precedence
) {
978 /* must be an operator */
980 if (stack
[*stackdepth
- 2].valuetype
== ESI_EXPR_LITERAL
||
981 stack
[*stackdepth
- 2].valuetype
== ESI_EXPR_INVALID
||
982 stack
[*stackdepth
- 2].eval(stack
, stackdepth
,
983 *stackdepth
- 2, candidate
)) {
984 /* cleanup candidate and stack */
985 dumpstack(stack
, *stackdepth
);
986 cleanmember(candidate
);
987 debugs(86, DBG_IMPORTANT
, "invalid expression");
991 stackpush(stack
, *candidate
, stackdepth
);
993 } else if (candidate
->valuetype
!= ESI_EXPR_INVALID
)
994 stackpush(stack
, *candidate
, stackdepth
);
1000 ESIExpression::Evaluate(char const *s
)
1002 stackmember stack
[ESI_STACK_DEPTH_LIMIT
];
1005 PROF_start(esiExpressionEval
);
1008 stackmember candidate
= getsymbol(s
, &end
);
1010 if (candidate
.valuetype
!= ESI_EXPR_INVALID
) {
1013 if (!addmember(stack
, &stackdepth
, &candidate
)) {
1014 PROF_stop(esiExpressionEval
);
1021 debugs(86, DBG_IMPORTANT
, "failed parsing expression");
1022 PROF_stop(esiExpressionEval
);
1027 if (stackdepth
> 1) {
1029 rv
.valuetype
= ESI_EXPR_INVALID
;
1032 if (stack
[stackdepth
- 2].
1033 eval(stack
, &stackdepth
, stackdepth
- 2, &rv
)) {
1034 /* special case - leading operator failed */
1035 debugs(86, DBG_IMPORTANT
, "invalid expression");
1036 PROF_stop(esiExpressionEval
);
1041 if (stackdepth
== 0) {
1042 /* Empty expression - evaluate to false */
1043 PROF_stop(esiExpressionEval
);
1047 /* if we hit here, we think we have a valid result */
1048 assert(stackdepth
== 1);
1050 assert(stack
[0].valuetype
== ESI_EXPR_EXPR
);
1052 PROF_stop(esiExpressionEval
);
1054 return stack
[0].value
.integral
? 1 : 0;