1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "zbxalgo.h"
22 
23 #include "log.h"
24 
25 /******************************************************************************
26  *                                                                            *
27  *                     Module for evaluating expressions                      *
28  *                  ---------------------------------------                   *
29  *                                                                            *
30  * Global variables are used for efficiency reasons so that arguments do not  *
31  * have to be passed to each of evaluate_termX() functions. For this reason,  *
32  * too, this module is isolated into a separate file.                         *
33  *                                                                            *
34  * The priority of supported operators is as follows:                         *
35  *                                                                            *
36  *   - (unary)   evaluate_term8()                                             *
37  *   not         evaluate_term7()                                             *
38  *   * /         evaluate_term6()                                             *
39  *   + -         evaluate_term5()                                             *
40  *   < <= >= >   evaluate_term4()                                             *
41  *   = <>        evaluate_term3()                                             *
42  *   and         evaluate_term2()                                             *
43  *   or          evaluate_term1()                                             *
44  *                                                                            *
45  * Function evaluate_term9() is used for parsing tokens on the lowest level:  *
46  * those can be suffixed numbers like "12.345K" or parenthesized expressions. *
47  *                                                                            *
48  ******************************************************************************/
49 
50 static const char	*ptr;		/* character being looked at */
51 static int		level;		/* expression nesting level  */
52 
53 static char		*buffer;	/* error message buffer      */
54 static size_t		max_buffer_len;	/* error message buffer size */
55 
56 /******************************************************************************
57  *                                                                            *
58  * Purpose: check whether the character delimits a numeric token              *
59  *                                                                            *
60  ******************************************************************************/
is_number_delimiter(char c)61 static int	is_number_delimiter(char c)
62 {
63 	return 0 == isdigit(c) && '.' != c && 0 == isalpha(c) ? SUCCEED : FAIL;
64 }
65 
66 /******************************************************************************
67  *                                                                            *
68  * Purpose: check whether the character delimits a symbolic operator token    *
69  *                                                                            *
70  ******************************************************************************/
is_operator_delimiter(char c)71 static int	is_operator_delimiter(char c)
72 {
73 	return ' ' == c || '(' == c || '\r' == c || '\n' == c || '\t' == c || ')' == c || '\0' == c ? SUCCEED : FAIL;
74 }
75 
76 /******************************************************************************
77  *                                                                            *
78  * Purpose: evaluate a suffixed number like "12.345K"                         *
79  *                                                                            *
80  ******************************************************************************/
evaluate_number(int * unknown_idx)81 static double	evaluate_number(int *unknown_idx)
82 {
83 	double		result;
84 	int		len;
85 
86 	/* Is it a special token of unknown value (e.g. ZBX_UNKNOWN0, ZBX_UNKNOWN1) ? */
87 	if (0 == strncmp(ZBX_UNKNOWN_STR, ptr, ZBX_UNKNOWN_STR_LEN))
88 	{
89 		const char	*p0, *p1;
90 
91 		p0 = ptr + ZBX_UNKNOWN_STR_LEN;
92 		p1 = p0;
93 
94 		/* extract the message number which follows after 'ZBX_UNKNOWN' */
95 		while (0 != isdigit((unsigned char)*p1))
96 			p1++;
97 
98 		if (p0 < p1 && SUCCEED == is_number_delimiter(*p1))
99 		{
100 			ptr = p1;
101 
102 			/* return 'unknown' and corresponding message number about its origin */
103 			*unknown_idx = atoi(p0);
104 			return ZBX_UNKNOWN;
105 		}
106 
107 		ptr = p0;
108 
109 		return ZBX_INFINITY;
110 	}
111 
112 	if (SUCCEED == zbx_suffixed_number_parse(ptr, &len) && SUCCEED == is_number_delimiter(*(ptr + len)))
113 	{
114 		result = atof(ptr) * suffix2factor(*(ptr + len - 1));
115 		ptr += len;
116 	}
117 	else
118 		result = ZBX_INFINITY;
119 
120 	return result;
121 }
122 
123 static double	evaluate_term1(int *unknown_idx);
124 
125 /******************************************************************************
126  *                                                                            *
127  * Purpose: evaluate a suffixed number or a parenthesized expression          *
128  *                                                                            *
129  ******************************************************************************/
evaluate_term9(int * unknown_idx)130 static double	evaluate_term9(int *unknown_idx)
131 {
132 	double	result;
133 
134 	while (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr)
135 		ptr++;
136 
137 	if ('\0' == *ptr)
138 	{
139 		zbx_strlcpy(buffer, "Cannot evaluate expression: unexpected end of expression.", max_buffer_len);
140 		return ZBX_INFINITY;
141 	}
142 
143 	if ('(' == *ptr)
144 	{
145 		ptr++;
146 
147 		if (ZBX_INFINITY == (result = evaluate_term1(unknown_idx)))
148 			return ZBX_INFINITY;
149 
150 		/* if evaluate_term1() returns ZBX_UNKNOWN then continue as with regular number */
151 
152 		if (')' != *ptr)
153 		{
154 			zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
155 					" expected closing parenthesis at \"%s\".", ptr);
156 			return ZBX_INFINITY;
157 		}
158 
159 		ptr++;
160 	}
161 	else
162 	{
163 		if (ZBX_INFINITY == (result = evaluate_number(unknown_idx)))
164 		{
165 			zbx_snprintf(buffer, max_buffer_len, "Cannot evaluate expression:"
166 					" expected numeric token at \"%s\".", ptr);
167 			return ZBX_INFINITY;
168 		}
169 	}
170 
171 	while ('\0' != *ptr && (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr))
172 		ptr++;
173 
174 	return result;
175 }
176 
177 /******************************************************************************
178  *                                                                            *
179  * Purpose: evaluate "-" (unary)                                              *
180  *                                                                            *
181  * -0.0     -> -0.0                                                           *
182  * -1.2     -> -1.2                                                           *
183  * -Unknown ->  Unknown                                                       *
184  *                                                                            *
185  ******************************************************************************/
evaluate_term8(int * unknown_idx)186 static double	evaluate_term8(int *unknown_idx)
187 {
188 	double	result;
189 
190 	while (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr)
191 		ptr++;
192 
193 	if ('-' == *ptr)
194 	{
195 		ptr++;
196 
197 		if (ZBX_UNKNOWN == (result = evaluate_term9(unknown_idx)) || ZBX_INFINITY == result)
198 			return result;
199 
200 		result = -result;
201 	}
202 	else
203 		result = evaluate_term9(unknown_idx);
204 
205 	return result;
206 }
207 
208 /******************************************************************************
209  *                                                                            *
210  * Purpose: evaluate "not"                                                    *
211  *                                                                            *
212  * not 0.0     ->  1.0                                                        *
213  * not 1.2     ->  0.0                                                        *
214  * not Unknown ->  Unknown                                                    *
215  *                                                                            *
216  ******************************************************************************/
evaluate_term7(int * unknown_idx)217 static double	evaluate_term7(int *unknown_idx)
218 {
219 	double	result;
220 
221 	while (' ' == *ptr || '\r' == *ptr || '\n' == *ptr || '\t' == *ptr)
222 		ptr++;
223 
224 	if ('n' == ptr[0] && 'o' == ptr[1] && 't' == ptr[2] && SUCCEED == is_operator_delimiter(ptr[3]))
225 	{
226 		ptr += 3;
227 
228 		if (ZBX_UNKNOWN == (result = evaluate_term8(unknown_idx)) || ZBX_INFINITY == result)
229 			return result;
230 
231 		result = (SUCCEED == zbx_double_compare(result, 0.0) ? 1.0 : 0.0);
232 	}
233 	else
234 		result = evaluate_term8(unknown_idx);
235 
236 	return result;
237 }
238 
239 /******************************************************************************
240  *                                                                            *
241  * Purpose: evaluate "*" and "/"                                              *
242  *                                                                            *
243  *     0.0 * Unknown  ->  Unknown (yes, not 0 as we don't want to lose        *
244  *                        Unknown in arithmetic operations)                   *
245  *     1.2 * Unknown  ->  Unknown                                             *
246  *     0.0 / 1.2      ->  0.0                                                 *
247  *     1.2 / 0.0      ->  error (ZBX_INFINITY)                                *
248  * Unknown / 0.0      ->  error (ZBX_INFINITY)                                *
249  * Unknown / 1.2      ->  Unknown                                             *
250  * Unknown / Unknown  ->  Unknown                                             *
251  *     0.0 / Unknown  ->  Unknown                                             *
252  *     1.2 / Unknown  ->  Unknown                                             *
253  *                                                                            *
254  ******************************************************************************/
evaluate_term6(int * unknown_idx)255 static double	evaluate_term6(int *unknown_idx)
256 {
257 	char	op;
258 	double	result, operand;
259 	int	res_idx = -1, oper_idx = -2;	/* set invalid values to catch errors */
260 
261 	if (ZBX_INFINITY == (result = evaluate_term7(&res_idx)))
262 		return ZBX_INFINITY;
263 
264 	if (ZBX_UNKNOWN == result)
265 		*unknown_idx = res_idx;
266 
267 	/* if evaluate_term7() returns ZBX_UNKNOWN then continue as with regular number */
268 
269 	while ('*' == *ptr || '/' == *ptr)
270 	{
271 		op = *ptr++;
272 
273 		/* 'ZBX_UNKNOWN' in multiplication and division produces 'ZBX_UNKNOWN'. */
274 		/* Even if 1st operand is Unknown we evaluate 2nd operand too to catch fatal errors in it. */
275 
276 		if (ZBX_INFINITY == (operand = evaluate_term7(&oper_idx)))
277 			return ZBX_INFINITY;
278 
279 		if ('*' == op)
280 		{
281 			if (ZBX_UNKNOWN == operand)		/* (anything) * Unknown */
282 			{
283 				*unknown_idx = oper_idx;
284 				res_idx = oper_idx;
285 				result = ZBX_UNKNOWN;
286 			}
287 			else if (ZBX_UNKNOWN == result)		/* Unknown * known */
288 			{
289 				*unknown_idx = res_idx;
290 			}
291 			else
292 				result *= operand;
293 		}
294 		else
295 		{
296 			/* catch division by 0 even if 1st operand is Unknown */
297 
298 			if (ZBX_UNKNOWN != operand && SUCCEED == zbx_double_compare(operand, 0.0))
299 			{
300 				zbx_strlcpy(buffer, "Cannot evaluate expression: division by zero.", max_buffer_len);
301 				return ZBX_INFINITY;
302 			}
303 
304 			if (ZBX_UNKNOWN == operand)		/* (anything) / Unknown */
305 			{
306 				*unknown_idx = oper_idx;
307 				res_idx = oper_idx;
308 				result = ZBX_UNKNOWN;
309 			}
310 			else if (ZBX_UNKNOWN == result)		/* Unknown / known */
311 			{
312 				*unknown_idx = res_idx;
313 			}
314 			else
315 				result /= operand;
316 		}
317 	}
318 
319 	return result;
320 }
321 
322 /******************************************************************************
323  *                                                                            *
324  * Purpose: evaluate "+" and "-"                                              *
325  *                                                                            *
326  *     0.0 +/- Unknown  ->  Unknown                                           *
327  *     1.2 +/- Unknown  ->  Unknown                                           *
328  * Unknown +/- Unknown  ->  Unknown                                           *
329  *                                                                            *
330  ******************************************************************************/
evaluate_term5(int * unknown_idx)331 static double	evaluate_term5(int *unknown_idx)
332 {
333 	char	op;
334 	double	result, operand;
335 	int	res_idx = -3, oper_idx = -4;	/* set invalid values to catch errors */
336 
337 	if (ZBX_INFINITY == (result = evaluate_term6(&res_idx)))
338 		return ZBX_INFINITY;
339 
340 	if (ZBX_UNKNOWN == result)
341 		*unknown_idx = res_idx;
342 
343 	/* if evaluate_term6() returns ZBX_UNKNOWN then continue as with regular number */
344 
345 	while ('+' == *ptr || '-' == *ptr)
346 	{
347 		op = *ptr++;
348 
349 		/* even if 1st operand is Unknown we evaluate 2nd operand to catch fatal error if any occurs */
350 
351 		if (ZBX_INFINITY == (operand = evaluate_term6(&oper_idx)))
352 			return ZBX_INFINITY;
353 
354 		if (ZBX_UNKNOWN == operand)		/* (anything) +/- Unknown */
355 		{
356 			*unknown_idx = oper_idx;
357 			res_idx = oper_idx;
358 			result = ZBX_UNKNOWN;
359 		}
360 		else if (ZBX_UNKNOWN == result)		/* Unknown +/- known */
361 		{
362 			*unknown_idx = res_idx;
363 		}
364 		else
365 		{
366 			if ('+' == op)
367 				result += operand;
368 			else
369 				result -= operand;
370 		}
371 	}
372 
373 	return result;
374 }
375 
376 /******************************************************************************
377  *                                                                            *
378  * Purpose: evaluate "<", "<=", ">=", ">"                                     *
379  *                                                                            *
380  *     0.0 < Unknown  ->  Unknown                                             *
381  *     1.2 < Unknown  ->  Unknown                                             *
382  * Unknown < Unknown  ->  Unknown                                             *
383  *                                                                            *
384  ******************************************************************************/
evaluate_term4(int * unknown_idx)385 static double	evaluate_term4(int *unknown_idx)
386 {
387 	char	op;
388 	double	result, operand;
389 	int	res_idx = -5, oper_idx = -6;	/* set invalid values to catch errors */
390 
391 	if (ZBX_INFINITY == (result = evaluate_term5(&res_idx)))
392 		return ZBX_INFINITY;
393 
394 	if (ZBX_UNKNOWN == result)
395 		*unknown_idx = res_idx;
396 
397 	/* if evaluate_term5() returns ZBX_UNKNOWN then continue as with regular number */
398 
399 	while (1)
400 	{
401 		if ('<' == ptr[0] && '=' == ptr[1])
402 		{
403 			op = 'l';
404 			ptr += 2;
405 		}
406 		else if ('>' == ptr[0] && '=' == ptr[1])
407 		{
408 			op = 'g';
409 			ptr += 2;
410 		}
411 		else if (('<' == ptr[0] && '>' != ptr[1]) || '>' == ptr[0])
412 		{
413 			op = *ptr++;
414 		}
415 		else
416 			break;
417 
418 		/* even if 1st operand is Unknown we evaluate 2nd operand to catch fatal error if any occurs */
419 
420 		if (ZBX_INFINITY == (operand = evaluate_term5(&oper_idx)))
421 			return ZBX_INFINITY;
422 
423 		if (ZBX_UNKNOWN == operand)		/* (anything) < Unknown */
424 		{
425 			*unknown_idx = oper_idx;
426 			res_idx = oper_idx;
427 			result = ZBX_UNKNOWN;
428 		}
429 		else if (ZBX_UNKNOWN == result)		/* Unknown < known */
430 		{
431 			*unknown_idx = res_idx;
432 		}
433 		else
434 		{
435 			if ('<' == op)
436 				result = (result < operand - ZBX_DOUBLE_EPSILON);
437 			else if ('l' == op)
438 				result = (result <= operand + ZBX_DOUBLE_EPSILON);
439 			else if ('g' == op)
440 				result = (result >= operand - ZBX_DOUBLE_EPSILON);
441 			else
442 				result = (result > operand + ZBX_DOUBLE_EPSILON);
443 		}
444 	}
445 
446 	return result;
447 }
448 
449 /******************************************************************************
450  *                                                                            *
451  * Purpose: evaluate "=" and "<>"                                             *
452  *                                                                            *
453  *      0.0 = Unknown  ->  Unknown                                            *
454  *      1.2 = Unknown  ->  Unknown                                            *
455  *  Unknown = Unknown  ->  Unknown                                            *
456  *     0.0 <> Unknown  ->  Unknown                                            *
457  *     1.2 <> Unknown  ->  Unknown                                            *
458  * Unknown <> Unknown  ->  Unknown                                            *
459  *                                                                            *
460  ******************************************************************************/
evaluate_term3(int * unknown_idx)461 static double	evaluate_term3(int *unknown_idx)
462 {
463 	char	op;
464 	double	result, operand;
465 	int	res_idx = -7, oper_idx = -8;	/* set invalid values to catch errors */
466 
467 	if (ZBX_INFINITY == (result = evaluate_term4(&res_idx)))
468 		return ZBX_INFINITY;
469 
470 	if (ZBX_UNKNOWN == result)
471 		*unknown_idx = res_idx;
472 
473 	/* if evaluate_term4() returns ZBX_UNKNOWN then continue as with regular number */
474 
475 	while (1)
476 	{
477 		if ('=' == *ptr)
478 		{
479 			op = *ptr++;
480 		}
481 		else if ('<' == ptr[0] && '>' == ptr[1])
482 		{
483 			op = '#';
484 			ptr += 2;
485 		}
486 		else
487 			break;
488 
489 		/* even if 1st operand is Unknown we evaluate 2nd operand to catch fatal error if any occurs */
490 
491 		if (ZBX_INFINITY == (operand = evaluate_term4(&oper_idx)))
492 			return ZBX_INFINITY;
493 
494 		if (ZBX_UNKNOWN == operand)		/* (anything) = Unknown, (anything) <> Unknown */
495 		{
496 			*unknown_idx = oper_idx;
497 			res_idx = oper_idx;
498 			result = ZBX_UNKNOWN;
499 		}
500 		else if (ZBX_UNKNOWN == result)		/* Unknown = known, Unknown <> known */
501 		{
502 			*unknown_idx = res_idx;
503 		}
504 		else if ('=' == op)
505 		{
506 			result = (SUCCEED == zbx_double_compare(result, operand));
507 		}
508 		else
509 			result = (SUCCEED != zbx_double_compare(result, operand));
510 	}
511 
512 	return result;
513 }
514 
515 /******************************************************************************
516  *                                                                            *
517  * Purpose: evaluate "and"                                                    *
518  *                                                                            *
519  *      0.0 and Unknown  -> 0.0                                               *
520  *  Unknown and 0.0      -> 0.0                                               *
521  *      1.0 and Unknown  -> Unknown                                           *
522  *  Unknown and 1.0      -> Unknown                                           *
523  *  Unknown and Unknown  -> Unknown                                           *
524  *                                                                            *
525  ******************************************************************************/
evaluate_term2(int * unknown_idx)526 static double	evaluate_term2(int *unknown_idx)
527 {
528 	double	result, operand;
529 	int	res_idx = -9, oper_idx = -10;	/* set invalid values to catch errors */
530 
531 	if (ZBX_INFINITY == (result = evaluate_term3(&res_idx)))
532 		return ZBX_INFINITY;
533 
534 	if (ZBX_UNKNOWN == result)
535 		*unknown_idx = res_idx;
536 
537 	/* if evaluate_term3() returns ZBX_UNKNOWN then continue as with regular number */
538 
539 	while ('a' == ptr[0] && 'n' == ptr[1] && 'd' == ptr[2] && SUCCEED == is_operator_delimiter(ptr[3]))
540 	{
541 		ptr += 3;
542 
543 		if (ZBX_INFINITY == (operand = evaluate_term3(&oper_idx)))
544 			return ZBX_INFINITY;
545 
546 		if (ZBX_UNKNOWN == result)
547 		{
548 			if (ZBX_UNKNOWN == operand)				/* Unknown and Unknown */
549 			{
550 				*unknown_idx = oper_idx;
551 				res_idx = oper_idx;
552 				result = ZBX_UNKNOWN;
553 			}
554 			else if (SUCCEED == zbx_double_compare(operand, 0.0))	/* Unknown and 0 */
555 			{
556 				result = 0.0;
557 			}
558 			else							/* Unknown and 1 */
559 				*unknown_idx = res_idx;
560 		}
561 		else if (ZBX_UNKNOWN == operand)
562 		{
563 			if (SUCCEED == zbx_double_compare(result, 0.0))		/* 0 and Unknown */
564 			{
565 				result = 0.0;
566 			}
567 			else							/* 1 and Unknown */
568 			{
569 				*unknown_idx = oper_idx;
570 				res_idx = oper_idx;
571 				result = ZBX_UNKNOWN;
572 			}
573 		}
574 		else
575 		{
576 			result = (SUCCEED != zbx_double_compare(result, 0.0) &&
577 					SUCCEED != zbx_double_compare(operand, 0.0));
578 		}
579 	}
580 
581 	return result;
582 }
583 
584 /******************************************************************************
585  *                                                                            *
586  * Purpose: evaluate "or"                                                     *
587  *                                                                            *
588  *      1.0 or Unknown  -> 1.0                                                *
589  *  Unknown or 1.0      -> 1.0                                                *
590  *      0.0 or Unknown  -> Unknown                                            *
591  *  Unknown or 0.0      -> Unknown                                            *
592  *  Unknown or Unknown  -> Unknown                                            *
593  *                                                                            *
594  ******************************************************************************/
evaluate_term1(int * unknown_idx)595 static double	evaluate_term1(int *unknown_idx)
596 {
597 	double	result, operand;
598 	int	res_idx = -11, oper_idx = -12;	/* set invalid values to catch errors */
599 
600 	level++;
601 
602 	if (32 < level)
603 	{
604 		zbx_strlcpy(buffer, "Cannot evaluate expression: nesting level is too deep.", max_buffer_len);
605 		return ZBX_INFINITY;
606 	}
607 
608 	if (ZBX_INFINITY == (result = evaluate_term2(&res_idx)))
609 		return ZBX_INFINITY;
610 
611 	if (ZBX_UNKNOWN == result)
612 		*unknown_idx = res_idx;
613 
614 	/* if evaluate_term2() returns ZBX_UNKNOWN then continue as with regular number */
615 
616 	while ('o' == ptr[0] && 'r' == ptr[1] && SUCCEED == is_operator_delimiter(ptr[2]))
617 	{
618 		ptr += 2;
619 
620 		if (ZBX_INFINITY == (operand = evaluate_term2(&oper_idx)))
621 			return ZBX_INFINITY;
622 
623 		if (ZBX_UNKNOWN == result)
624 		{
625 			if (ZBX_UNKNOWN == operand)				/* Unknown or Unknown */
626 			{
627 				*unknown_idx = oper_idx;
628 				res_idx = oper_idx;
629 				result = ZBX_UNKNOWN;
630 			}
631 			else if (SUCCEED != zbx_double_compare(operand, 0.0))	/* Unknown or 1 */
632 			{
633 				result = 1;
634 			}
635 			else							/* Unknown or 0 */
636 				*unknown_idx = res_idx;
637 		}
638 		else if (ZBX_UNKNOWN == operand)
639 		{
640 			if (SUCCEED != zbx_double_compare(result, 0.0))		/* 1 or Unknown */
641 			{
642 				result = 1;
643 			}
644 			else							/* 0 or Unknown */
645 			{
646 				*unknown_idx = oper_idx;
647 				res_idx = oper_idx;
648 				result = ZBX_UNKNOWN;
649 			}
650 		}
651 		else
652 		{
653 			result = (SUCCEED != zbx_double_compare(result, 0.0) ||
654 					SUCCEED != zbx_double_compare(operand, 0.0));
655 		}
656 	}
657 
658 	level--;
659 
660 	return result;
661 }
662 
663 /******************************************************************************
664  *                                                                            *
665  * Purpose: evaluate an expression like "(26.416>10) or (0=1)"                *
666  *                                                                            *
667  ******************************************************************************/
evaluate(double * value,const char * expression,char * error,size_t max_error_len,zbx_vector_ptr_t * unknown_msgs)668 int	evaluate(double *value, const char *expression, char *error, size_t max_error_len,
669 		zbx_vector_ptr_t *unknown_msgs)
670 {
671 	const char	*__function_name = "evaluate";
672 	int		unknown_idx = -13;	/* index of message in 'unknown_msgs' vector, set to invalid value */
673 						/* to catch errors */
674 
675 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __function_name, expression);
676 
677 	ptr = expression;
678 	level = 0;
679 
680 	buffer = error;
681 	max_buffer_len = max_error_len;
682 
683 	*value = evaluate_term1(&unknown_idx);
684 
685 	if ('\0' != *ptr && ZBX_INFINITY != *value)
686 	{
687 		zbx_snprintf(error, max_error_len, "Cannot evaluate expression: unexpected token at \"%s\".", ptr);
688 		*value = ZBX_INFINITY;
689 	}
690 
691 	if (ZBX_UNKNOWN == *value)
692 	{
693 		/* Map Unknown result to error. Callers currently do not operate with ZBX_UNKNOWN. */
694 		if (NULL != unknown_msgs)
695 		{
696 			if (0 > unknown_idx)
697 			{
698 				THIS_SHOULD_NEVER_HAPPEN;
699 				zabbix_log(LOG_LEVEL_WARNING, "%s() internal error: " ZBX_UNKNOWN_STR " index:%d"
700 						" expression:'%s'", __function_name, unknown_idx, expression);
701 				zbx_snprintf(error, max_error_len, "Internal error: " ZBX_UNKNOWN_STR " index %d."
702 						" Please report this to Zabbix developers.", unknown_idx);
703 			}
704 			else if (unknown_msgs->values_num > unknown_idx)
705 			{
706 				zbx_snprintf(error, max_error_len, "Cannot evaluate expression: \"%s\".",
707 						(char *)(unknown_msgs->values[unknown_idx]));
708 			}
709 			else
710 			{
711 				zbx_snprintf(error, max_error_len, "Cannot evaluate expression: unsupported "
712 						ZBX_UNKNOWN_STR "%d value.", unknown_idx);
713 			}
714 		}
715 		else
716 		{
717 			THIS_SHOULD_NEVER_HAPPEN;
718 			/* do not leave garbage in error buffer, write something helpful */
719 			zbx_snprintf(error, max_error_len, "%s(): internal error: no message for unknown result",
720 					__function_name);
721 		}
722 
723 		*value = ZBX_INFINITY;
724 	}
725 
726 	if (ZBX_INFINITY == *value)
727 	{
728 		zabbix_log(LOG_LEVEL_DEBUG, "End of %s() error:'%s'", __function_name, error);
729 		return FAIL;
730 	}
731 
732 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() value:" ZBX_FS_DBL, __function_name, *value);
733 
734 	return SUCCEED;
735 }
736 
737 /******************************************************************************
738  *                                                                            *
739  * Function: evaluate_unknown                                                 *
740  *                                                                            *
741  * Purpose: evaluate an expression like "(26.416>10) and not(0=ZBX_UNKNOWN0)" *
742  *                                                                            *
743  * Parameters: expression    - [IN]  expression to evaluate                   *
744  *             value         - [OUT] expression evaluation result             *
745  *             error         - [OUT] error message buffer                     *
746  *             max_error_len - [IN]  error buffer size                        *
747  *                                                                            *
748  * Return value: SUCCEED - expression evaluated successfully,                 *
749  *                         or evaluation result is undefined (ZBX_UNKNOWN)    *
750  *               FAIL    - expression evaluation failed                       *
751  *                                                                            *
752  ******************************************************************************/
evaluate_unknown(const char * expression,double * value,char * error,size_t max_error_len)753 int	evaluate_unknown(const char *expression, double *value, char *error, size_t max_error_len)
754 {
755 	const char	*__function_name = "evaluate_with_unknown";
756 	int		unknown_idx = -13;	/* index of message in 'unknown_msgs' vector, set to invalid value */
757 						/* to catch errors */
758 
759 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __function_name, expression);
760 
761 	ptr = expression;
762 	level = 0;
763 
764 	buffer = error;
765 	max_buffer_len = max_error_len;
766 
767 	*value = evaluate_term1(&unknown_idx);
768 
769 	if ('\0' != *ptr && ZBX_INFINITY != *value)
770 	{
771 		zbx_snprintf(error, max_error_len, "Cannot evaluate expression: unexpected token at \"%s\".", ptr);
772 		*value = ZBX_INFINITY;
773 	}
774 
775 	if (ZBX_INFINITY == *value)
776 	{
777 		zabbix_log(LOG_LEVEL_DEBUG, "End of %s() error:'%s'", __function_name, error);
778 		return FAIL;
779 	}
780 
781 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() value:" ZBX_FS_DBL, __function_name, *value);
782 
783 	return SUCCEED;
784 }
785