1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <syntax.h>
26
27 #include <json.h>
28 #include <files_names.h>
29 #include <mod_files.h>
30 #include <mod_custom.h>
31 #include <item_lib.h>
32 #include <conversion.h>
33 #include <expand.h>
34 #include <matching.h>
35 #include <scope.h>
36 #include <fncall.h>
37 #include <string_lib.h> /* IsStrIn */
38 #include <regex.h> /* StringMatchFull */
39 #include <misc_lib.h>
40 #include <rlist.h>
41 #include <vars.h>
42 #include <eval_context.h>
43
44
45 static SyntaxTypeMatch CheckParseString(const char *lv, const char *s, const char *range);
46 static SyntaxTypeMatch CheckParseInt(const char *lv, const char *s, const char *range);
47 static SyntaxTypeMatch CheckParseReal(const char *lv, const char *s, const char *range);
48 static SyntaxTypeMatch CheckParseRealRange(const char *lval, const char *s, const char *range);
49 static SyntaxTypeMatch CheckParseIntRange(const char *lval, const char *s, const char *range);
50 static SyntaxTypeMatch CheckParseOpts(const char *s, const char *range);
51 static SyntaxTypeMatch CheckFnCallType(const char *s, DataType dtype);
52
53 /*********************************************************/
54
PromiseTypeSyntaxGetStrict(const char * bundle_type,const char * promise_type)55 static const PromiseTypeSyntax *PromiseTypeSyntaxGetStrict(const char *bundle_type, const char *promise_type)
56 {
57 assert(bundle_type != NULL);
58 assert(promise_type != NULL);
59
60 for (int module_index = 0; module_index < CF3_MODULES; module_index++)
61 {
62 for (int promise_type_index = 0; CF_ALL_PROMISE_TYPES[module_index][promise_type_index].promise_type; promise_type_index++)
63 {
64 const PromiseTypeSyntax *promise_type_syntax = &CF_ALL_PROMISE_TYPES[module_index][promise_type_index];
65
66 if (strcmp(bundle_type, promise_type_syntax->bundle_type) == 0
67 && strcmp(promise_type, promise_type_syntax->promise_type) == 0)
68 {
69 return promise_type_syntax;
70 }
71 }
72 }
73 return NULL;
74 }
75
IsBuiltInPromiseType(const char * const promise_type)76 bool IsBuiltInPromiseType(const char *const promise_type)
77 {
78 // Any built in promise type, regardless of bundle (agent) type
79 assert(promise_type != NULL);
80
81 for (int module_index = 0; module_index < CF3_MODULES; module_index++)
82 {
83 const PromiseTypeSyntax *const module = CF_ALL_PROMISE_TYPES[module_index];
84 for (int promise_type_index = 0; module[promise_type_index].promise_type; promise_type_index++)
85 {
86 const PromiseTypeSyntax *promise_type_syntax = &(module[promise_type_index]);
87 if (StringEqual(promise_type, promise_type_syntax->promise_type))
88 {
89 return true;
90 }
91 }
92 }
93 return false;
94 }
95
PromiseTypeSyntaxGet(const char * bundle_type,const char * promise_type)96 const PromiseTypeSyntax *PromiseTypeSyntaxGet(const char *bundle_type, const char *promise_type)
97 {
98 const PromiseTypeSyntax *pts = PromiseTypeSyntaxGetStrict(bundle_type, promise_type);
99 if (!pts)
100 {
101 pts = PromiseTypeSyntaxGetStrict("*", promise_type);
102 }
103 return pts;
104 }
105
GetCommonConstraint(const char * lval)106 static const ConstraintSyntax *GetCommonConstraint(const char *lval)
107 {
108 for (int i = 0; CF_COMMON_PROMISE_TYPES[i].promise_type; i++)
109 {
110 const ConstraintSyntax *constraints = CF_COMMON_PROMISE_TYPES[i].constraints;
111 for (int j = 0; constraints[j].lval != NULL; j++)
112 {
113 if (StringEqual(constraints[j].lval, lval))
114 {
115 return &(constraints[j]);
116 }
117 }
118 }
119
120 return NULL;
121 }
122
BodySyntaxGetConstraintSyntax(const ConstraintSyntax * body_syntax,const char * lval)123 const ConstraintSyntax *BodySyntaxGetConstraintSyntax(const ConstraintSyntax *body_syntax, const char *lval)
124 {
125 for (int j = 0; body_syntax[j].lval; j++)
126 {
127 if (strcmp(body_syntax[j].lval, lval) == 0)
128 {
129 return &body_syntax[j];
130 }
131 }
132 return NULL;
133 }
134
PromiseTypeSyntaxGetConstraintSyntax(const PromiseTypeSyntax * promise_type_syntax,const char * lval)135 const ConstraintSyntax *PromiseTypeSyntaxGetConstraintSyntax(const PromiseTypeSyntax *promise_type_syntax, const char *lval)
136 {
137 assert(promise_type_syntax != NULL);
138 assert(lval != NULL);
139
140 for (int i = 0; promise_type_syntax->constraints[i].lval; i++)
141 {
142 if (strcmp(promise_type_syntax->constraints[i].lval, lval) == 0)
143 {
144 return &promise_type_syntax->constraints[i];
145 }
146 }
147
148 const ConstraintSyntax *constraint_syntax = NULL;
149 if (strcmp("edit_line", promise_type_syntax->bundle_type) == 0)
150 {
151 constraint_syntax = BodySyntaxGetConstraintSyntax(CF_COMMON_EDITBODIES, lval);
152 if (constraint_syntax)
153 {
154 return constraint_syntax;
155 }
156 }
157 else if (strcmp("edit_xml", promise_type_syntax->bundle_type) == 0)
158 {
159 constraint_syntax = BodySyntaxGetConstraintSyntax(CF_COMMON_XMLBODIES, lval);
160 if (constraint_syntax)
161 {
162 return constraint_syntax;
163 }
164 }
165
166 return GetCommonConstraint(lval);
167 }
168
BodySyntaxGet(ARG_UNUSED ParserBlock block,const char * body_type)169 const BodySyntax *BodySyntaxGet(ARG_UNUSED ParserBlock block, const char *body_type)
170 {
171 if (block == PARSER_BLOCK_PROMISE)
172 {
173 // Required: promise agent <id>
174 if (!StringEqual(body_type, "agent"))
175 {
176 return NULL;
177 }
178 return &CUSTOM_PROMISE_BLOCK_SYNTAX;
179 }
180
181 assert(block == PARSER_BLOCK_BODY);
182 for (int i = 0; i < CF3_MODULES; i++)
183 {
184 const PromiseTypeSyntax *promise_type_syntax = CF_ALL_PROMISE_TYPES[i];
185
186 for (int k = 0; promise_type_syntax[k].bundle_type != NULL; k++)
187 {
188 for (int z = 0; promise_type_syntax[k].constraints[z].lval != NULL; z++)
189 {
190 const ConstraintSyntax constraint_syntax = promise_type_syntax[k].constraints[z];
191
192 if (constraint_syntax.dtype == CF_DATA_TYPE_BODY && strcmp(body_type, constraint_syntax.lval) == 0)
193 {
194 return constraint_syntax.range.body_type_syntax;
195 }
196 }
197 }
198 }
199
200 for (int i = 0; CONTROL_BODIES[i].body_type != NULL; i++)
201 {
202 const BodySyntax body_syntax = CONTROL_BODIES[i];
203
204 if (strcmp(body_type, body_syntax.body_type) == 0)
205 {
206 return &CONTROL_BODIES[i];
207 }
208 }
209
210 return NULL;
211 }
212
SyntaxStatusToString(SyntaxStatus status)213 const char *SyntaxStatusToString(SyntaxStatus status)
214 {
215 assert( status == SYNTAX_STATUS_DEPRECATED ||
216 status == SYNTAX_STATUS_NORMAL ||
217 status == SYNTAX_STATUS_REMOVED );
218 switch (status)
219 {
220 case SYNTAX_STATUS_DEPRECATED:
221 return "deprecated";
222 case SYNTAX_STATUS_NORMAL:
223 return "normal";
224 case SYNTAX_STATUS_REMOVED:
225 return "removed";
226 default:
227 break;
228 }
229 return NULL;
230 }
231
232 /****************************************************************************/
233
ExpectedDataType(const char * lvalname)234 DataType ExpectedDataType(const char *lvalname)
235 {
236 int i, j, k, l;
237 const ConstraintSyntax *bs, *bs2;
238 const PromiseTypeSyntax *ss;
239
240 for (i = 0; i < CF3_MODULES; i++)
241 {
242 if ((ss = CF_ALL_PROMISE_TYPES[i]) == NULL)
243 {
244 continue;
245 }
246
247 for (j = 0; ss[j].promise_type != NULL; j++)
248 {
249 if ((bs = ss[j].constraints) == NULL)
250 {
251 continue;
252 }
253
254 for (k = 0; bs[k].lval != NULL; k++)
255 {
256 if (strcmp(lvalname, bs[k].lval) == 0)
257 {
258 return bs[k].dtype;
259 }
260 }
261
262 for (k = 0; bs[k].lval != NULL; k++)
263 {
264 if (bs[k].dtype == CF_DATA_TYPE_BODY)
265 {
266 bs2 = bs[k].range.body_type_syntax->constraints;
267
268 if (bs2 == NULL || bs2 == (void *) CF_BUNDLE)
269 {
270 continue;
271 }
272
273 for (l = 0; bs2[l].dtype != CF_DATA_TYPE_NONE; l++)
274 {
275 if (strcmp(lvalname, bs2[l].lval) == 0)
276 {
277 return bs2[l].dtype;
278 }
279 }
280 }
281 }
282
283 }
284 }
285
286 return CF_DATA_TYPE_NONE;
287 }
288
289 /****************************************************************************/
290 /* Level 1 */
291 /****************************************************************************/
292
SyntaxTypeMatchToString(SyntaxTypeMatch result)293 const char *SyntaxTypeMatchToString(SyntaxTypeMatch result)
294 {
295 assert(result < SYNTAX_TYPE_MATCH_MAX);
296
297 static const char *const msgs[SYNTAX_TYPE_MATCH_MAX] =
298 {
299 [SYNTAX_TYPE_MATCH_OK] = "OK",
300
301 [SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED] = "Cannot check unexpanded value",
302 [SYNTAX_TYPE_MATCH_ERROR_RANGE_BRACKETED] = "Real range specification should not be enclosed in brackets - just 'a,b'",
303 [SYNTAX_TYPE_MATCH_ERROR_RANGE_MULTIPLE_ITEMS] = "Range format specifier should be of form 'a,b'' but got multiple items",
304 [SYNTAX_TYPE_MATCH_ERROR_GOT_SCALAR] = "Attempted to give a scalar to a non-scalar type",
305 [SYNTAX_TYPE_MATCH_ERROR_GOT_LIST] = "Attempted to give a list to a non-list type",
306 [SYNTAX_TYPE_MATCH_ERROR_GOT_NULL] = "Attempted to give a value of type null",
307
308 [SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION] = "Error parsing Unix permission string",
309
310 [SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE] = "Scalar value is out of range",
311 [SYNTAX_TYPE_MATCH_ERROR_EMPTY_SCALAR_OUT_OF_RANGE] = "Empty scalar value is out of range",
312
313 [SYNTAX_TYPE_MATCH_ERROR_INT_PARSE] = "Cannot parse value as integer",
314 [SYNTAX_TYPE_MATCH_ERROR_INT_OUT_OF_RANGE] = "Integer is out of range",
315
316 [SYNTAX_TYPE_MATCH_ERROR_REAL_INF] = "Keyword 'inf' has an integer value, cannot be used as real",
317 [SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE] = "Real value is out of range",
318
319 [SYNTAX_TYPE_MATCH_ERROR_OPTS_OUT_OF_RANGE] = "Selection is out of bounds",
320
321 [SYNTAX_TYPE_MATCH_ERROR_FNCALL_RETURN_TYPE] = "Function does not return the required type",
322 [SYNTAX_TYPE_MATCH_ERROR_FNCALL_UNKNOWN] = "Unknown function",
323
324 [SYNTAX_TYPE_MATCH_ERROR_CONTEXT_OUT_OF_RANGE] = "Context string is invalid/out of range",
325 [SYNTAX_TYPE_MATCH_ERROR_ABSOLUTE_PATH] = "Filename is not an absolute path",
326 };
327
328 return msgs[result];
329 }
330
CheckConstraintTypeMatch(const char * lval,Rval rval,DataType dt,const char * range,int level)331 SyntaxTypeMatch CheckConstraintTypeMatch(const char *lval, Rval rval, DataType dt, const char *range, int level)
332 {
333 Rlist *rp;
334 Item *checklist;
335
336 /* Get type of lval */
337
338 switch (rval.type)
339 {
340 case RVAL_TYPE_SCALAR:
341 switch (dt)
342 {
343 case CF_DATA_TYPE_STRING_LIST:
344 case CF_DATA_TYPE_INT_LIST:
345 case CF_DATA_TYPE_REAL_LIST:
346 case CF_DATA_TYPE_CONTEXT_LIST:
347 case CF_DATA_TYPE_OPTION_LIST:
348 if (level == 0)
349 {
350 return SYNTAX_TYPE_MATCH_ERROR_GOT_SCALAR;
351 }
352 break;
353 default:
354 /* Only lists are incompatible with scalars */
355 break;
356 }
357 break;
358
359 case RVAL_TYPE_LIST:
360
361 switch (dt)
362 {
363 case CF_DATA_TYPE_STRING_LIST:
364 case CF_DATA_TYPE_INT_LIST:
365 case CF_DATA_TYPE_REAL_LIST:
366 case CF_DATA_TYPE_CONTEXT_LIST:
367 case CF_DATA_TYPE_OPTION_LIST:
368 break;
369 default:
370 return SYNTAX_TYPE_MATCH_ERROR_GOT_LIST;
371 }
372
373 for (rp = (Rlist *) rval.item; rp != NULL; rp = rp->next)
374 {
375 SyntaxTypeMatch err = CheckConstraintTypeMatch(lval, rp->val, dt, range, 1);
376 switch (err)
377 {
378 case SYNTAX_TYPE_MATCH_OK:
379 case SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED:
380 break;
381
382 default:
383 return err;
384 }
385 }
386
387 return SYNTAX_TYPE_MATCH_OK;
388
389 case RVAL_TYPE_FNCALL:
390
391 /* Fn-like objects are assumed to be parameterized bundles in these... */
392
393 checklist = SplitString("bundlesequence,edit_line,edit_xml,usebundle,service_bundle,home_bundle", ',');
394
395 if (!IsItemIn(checklist, lval))
396 {
397 SyntaxTypeMatch err = CheckFnCallType(RvalFnCallValue(rval)->name, dt);
398 DeleteItemList(checklist);
399 return err;
400 }
401
402 DeleteItemList(checklist);
403 return SYNTAX_TYPE_MATCH_OK;
404
405 case RVAL_TYPE_CONTAINER:
406 break;
407
408 case RVAL_TYPE_NOPROMISEE:
409 return SYNTAX_TYPE_MATCH_ERROR_GOT_NULL;
410 }
411
412 /* If we get here, we have a literal scalar type */
413
414 switch (dt)
415 {
416 case CF_DATA_TYPE_STRING:
417 case CF_DATA_TYPE_STRING_LIST:
418 return CheckParseString(lval, (const char *) rval.item, range);
419
420 case CF_DATA_TYPE_INT:
421 case CF_DATA_TYPE_INT_LIST:
422 return CheckParseInt(lval, (const char *) rval.item, range);
423
424 case CF_DATA_TYPE_REAL:
425 case CF_DATA_TYPE_REAL_LIST:
426 return CheckParseReal(lval, (const char *) rval.item, range);
427
428 case CF_DATA_TYPE_BODY:
429 case CF_DATA_TYPE_BUNDLE:
430 case CF_DATA_TYPE_CONTAINER:
431 break;
432
433 case CF_DATA_TYPE_OPTION:
434 case CF_DATA_TYPE_OPTION_LIST:
435 return CheckParseOpts(RvalScalarValue(rval), range);
436
437 case CF_DATA_TYPE_CONTEXT:
438 case CF_DATA_TYPE_CONTEXT_LIST:
439 return CheckParseContext((const char *) rval.item, range);
440
441 case CF_DATA_TYPE_INT_RANGE:
442 return CheckParseIntRange(lval, (const char *) rval.item, range);
443
444 case CF_DATA_TYPE_REAL_RANGE:
445 return CheckParseRealRange(lval, (char *) rval.item, range);
446
447 default:
448 ProgrammingError("Unknown (unhandled) datatype for lval = %s (CheckConstraintTypeMatch)", lval);
449 break;
450 }
451
452 return SYNTAX_TYPE_MATCH_OK;
453 }
454
455 /****************************************************************************/
456
StringDataType(EvalContext * ctx,const char * string)457 DataType StringDataType(EvalContext *ctx, const char *string)
458 {
459 int islist = false; /* TODO something is wrong here */
460
461 /*-------------------------------------------------------
462 What happens if we embed vars in a literal string
463 "$(list)withending" - a list?
464 "$(list1)$(list2)" - not a simple list
465 Disallow these manual concatenations as ambiguous.
466 Demand this syntax to work around
467
468 vars:
469
470 "listvar" slist => EmbellishList("prefix$(list)suffix");
471 ---------------------------------------------------------*/
472
473 size_t len = strlen(string);
474
475 if (*string == '$')
476 {
477 Buffer *inner_value = BufferNew();
478 if (ExtractScalarReference(inner_value, string, len, true))
479 {
480 DataType dtype;
481 if (!IsExpandable(BufferData(inner_value)))
482 {
483 VarRef *ref = VarRefParse(BufferData(inner_value));
484 EvalContextVariableGet(ctx, ref, &dtype);
485 VarRefDestroy(ref);
486
487 if (DataTypeToRvalType(dtype) == RVAL_TYPE_LIST)
488 {
489 if (!islist)
490 {
491 islist = true;
492 }
493 else
494 {
495 islist = false;
496 }
497 }
498 }
499
500 if (BufferSize(inner_value) == strlen(string))
501 {
502 BufferDestroy(inner_value);
503 return dtype;
504 }
505 else
506 {
507 BufferDestroy(inner_value);
508 return CF_DATA_TYPE_STRING;
509 }
510 }
511
512 BufferDestroy(inner_value);
513 }
514
515 return CF_DATA_TYPE_STRING;
516 }
517
518 /****************************************************************************/
519 /* Level 1 */
520 /****************************************************************************/
521
CheckParseString(const char * lval,const char * s,const char * range)522 static SyntaxTypeMatch CheckParseString(const char *lval, const char *s, const char *range)
523 {
524 if (s == NULL)
525 {
526 return SYNTAX_TYPE_MATCH_OK;
527 }
528
529 if (strlen(range) == 0)
530 {
531 return SYNTAX_TYPE_MATCH_OK;
532 }
533
534 if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
535 {
536 return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
537 }
538
539 /* Deal with complex strings as special cases */
540
541 if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0)
542 {
543 mode_t plus, minus;
544
545 if (!ParseModeString(s, &plus, &minus))
546 {
547 return SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION;
548 }
549 }
550
551 /* FIXME: review this strcmp. Moved out from StringMatch */
552 if (!strcmp(range, s) || StringMatchFull(range, s))
553 {
554 return SYNTAX_TYPE_MATCH_OK;
555 }
556
557 if (IsCf3VarString(s))
558 {
559 return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
560 }
561 else if ('\0' == s[0])
562 {
563 return SYNTAX_TYPE_MATCH_ERROR_EMPTY_SCALAR_OUT_OF_RANGE;
564 }
565 else if (!strcmp(range, CF_ABSPATHRANGE))
566 {
567 return SYNTAX_TYPE_MATCH_ERROR_ABSOLUTE_PATH;
568 }
569 else
570 {
571 return SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE;
572 }
573
574 return SYNTAX_TYPE_MATCH_OK;
575 }
576
577 /****************************************************************************/
578
CheckParseContext(const char * context,const char * range)579 SyntaxTypeMatch CheckParseContext(const char *context, const char *range)
580 {
581 if (strlen(range) == 0)
582 {
583 return SYNTAX_TYPE_MATCH_OK;
584 }
585
586 /* FIXME: review this strcmp. Moved out from StringMatch */
587 if (!strcmp(range, context) || StringMatchFull(range, context))
588 {
589 return SYNTAX_TYPE_MATCH_OK;
590 }
591
592 return SYNTAX_TYPE_MATCH_ERROR_CONTEXT_OUT_OF_RANGE;
593 }
594
595 /****************************************************************************/
596
CheckParseInt(const char * lval,const char * s,const char * range)597 static SyntaxTypeMatch CheckParseInt(const char *lval, const char *s, const char *range)
598 {
599 Item *split;
600 int n;
601 long long max = CF_LOWINIT, min = CF_HIGHINIT;
602
603 // Numeric types are registered by range separated by comma str "min,max"
604 split = SplitString(range, ',');
605
606 if ((n = ListLen(split)) != 2)
607 {
608 ProgrammingError("INTERN: format specifier for int rvalues is not ok for lval %s - got %d items", lval, n);
609 }
610
611 sscanf(split->name, "%lld", &min);
612
613 if (strcmp(split->next->name, "inf") == 0)
614 {
615 max = CF_INFINITY;
616 }
617 else
618 {
619 sscanf(split->next->name, "%lld", &max);
620 }
621
622 DeleteItemList(split);
623
624 if (min == CF_HIGHINIT || max == CF_LOWINIT)
625 {
626 ProgrammingError("INTERN: could not parse format specifier for int rvalues for lval %s", lval);
627 }
628
629 if (IsCf3VarString(s))
630 {
631 return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
632 }
633
634 long val = IntFromString(s);
635
636 if (val == CF_NOINT)
637 {
638 return SYNTAX_TYPE_MATCH_ERROR_INT_PARSE;
639 }
640
641 if (val > max || val < min)
642 {
643 return SYNTAX_TYPE_MATCH_ERROR_INT_OUT_OF_RANGE;
644 }
645
646 return SYNTAX_TYPE_MATCH_OK;
647 }
648
649 /****************************************************************************/
650
CheckParseIntRange(const char * lval,const char * s,const char * range)651 static SyntaxTypeMatch CheckParseIntRange(const char *lval, const char *s, const char *range)
652 {
653 Item *split, *ip, *rangep;
654 int n;
655 long long max = CF_LOWINIT, min = CF_HIGHINIT;
656
657 // Numeric types are registered by range separated by comma str "min,max"
658 if (*s == '[' || *s == '(')
659 {
660 return SYNTAX_TYPE_MATCH_ERROR_RANGE_BRACKETED;
661 }
662
663 split = SplitString(range, ',');
664
665 if ((n = ListLen(split)) != 2)
666 {
667 ProgrammingError("Format specifier %s for irange rvalues is not ok for lval %s - got %d items", range, lval, n);
668 }
669
670 sscanf(split->name, "%lld", &min);
671
672 if (strcmp(split->next->name, "inf") == 0)
673 {
674 max = CF_INFINITY;
675 }
676 else
677 {
678 sscanf(split->next->name, "%lld", &max);
679 }
680
681 DeleteItemList(split);
682
683 if (min == CF_HIGHINIT || max == CF_LOWINIT)
684 {
685 ProgrammingError("Could not parse irange format specifier for int rvalues for lval %s", lval);
686 }
687
688 if (IsCf3VarString(s))
689 {
690 return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
691 }
692
693 rangep = SplitString(s, ',');
694
695 if ((n = ListLen(rangep)) != 2)
696 {
697 return SYNTAX_TYPE_MATCH_ERROR_RANGE_MULTIPLE_ITEMS;
698 }
699
700 for (ip = rangep; ip != NULL; ip = ip->next)
701 {
702 long val = IntFromString(ip->name);
703
704 if (val > max || val < min)
705 {
706 return SYNTAX_TYPE_MATCH_ERROR_INT_OUT_OF_RANGE;
707 }
708 }
709
710 DeleteItemList(rangep);
711
712 return SYNTAX_TYPE_MATCH_OK;
713 }
714
715 /****************************************************************************/
716
CheckParseReal(const char * lval,const char * s,const char * range)717 static SyntaxTypeMatch CheckParseReal(const char *lval, const char *s, const char *range)
718 {
719 Item *split;
720 double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val;
721 int n;
722
723 if (strcmp(s, "inf") == 0)
724 {
725 return SYNTAX_TYPE_MATCH_ERROR_REAL_INF;
726 }
727
728 if (IsCf3VarString(s))
729 {
730 return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
731 }
732
733 /* Numeric types are registered by range separated by comma str "min,max" */
734
735 split = SplitString(range, ',');
736
737 if ((n = ListLen(split)) != 2)
738 {
739 ProgrammingError("Format specifier for real rvalues is not ok for lval %s - %d items", lval, n);
740 }
741
742 sscanf(split->name, "%lf", &min);
743 sscanf(split->next->name, "%lf", &max);
744 DeleteItemList(split);
745
746 if (min == CF_HIGHINIT || max == CF_LOWINIT)
747 {
748 ProgrammingError("Could not parse format specifier for int rvalues for lval %s", lval);
749 }
750
751 if (!DoubleFromString(s, &val))
752 {
753 return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
754 }
755
756 if (val > max || val < min)
757 {
758 return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
759 }
760
761 return SYNTAX_TYPE_MATCH_OK;
762 }
763
764 /****************************************************************************/
765
CheckParseRealRange(const char * lval,const char * s,const char * range)766 static SyntaxTypeMatch CheckParseRealRange(const char *lval, const char *s, const char *range)
767 {
768 Item *split, *rangep, *ip;
769 double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val;
770 int n;
771
772 if (*s == '[' || *s == '(')
773 {
774 return SYNTAX_TYPE_MATCH_ERROR_RANGE_BRACKETED;
775 }
776
777 if (strcmp(s, "inf") == 0)
778 {
779 return SYNTAX_TYPE_MATCH_ERROR_REAL_INF;
780 }
781
782 if (IsCf3VarString(s))
783 {
784 return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
785 }
786
787 /* Numeric types are registered by range separated by comma str "min,max" */
788
789 split = SplitString(range, ',');
790
791 if ((n = ListLen(split)) != 2)
792 {
793 ProgrammingError("Format specifier for real rvalues is not ok for lval %s - %d items", lval, n);
794 }
795
796 sscanf(split->name, "%lf", &min);
797 sscanf(split->next->name, "%lf", &max);
798 DeleteItemList(split);
799
800 if (min == CF_HIGHINIT || max == CF_LOWINIT)
801 {
802 ProgrammingError("Could not parse format specifier for int rvalues for lval %s", lval);
803 }
804
805 rangep = SplitString(s, ',');
806
807 if ((n = ListLen(rangep)) != 2)
808 {
809 return SYNTAX_TYPE_MATCH_ERROR_RANGE_MULTIPLE_ITEMS;
810 }
811
812 for (ip = rangep; ip != NULL; ip = ip->next)
813 {
814 if (!DoubleFromString(ip->name, &val))
815 {
816 return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
817 }
818
819 if (val > max || val < min)
820 {
821 return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
822 }
823 }
824
825 DeleteItemList(rangep);
826
827 return SYNTAX_TYPE_MATCH_OK;
828 }
829
830 /****************************************************************************/
831
CheckParseOpts(const char * s,const char * range)832 static SyntaxTypeMatch CheckParseOpts(const char *s, const char *range)
833 {
834 Item *split;
835
836 /* List/menu types are separated by comma str "a,b,c,..." */
837
838 if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
839 {
840 return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
841 }
842
843 split = SplitString(range, ',');
844
845 if (!IsItemIn(split, s))
846 {
847 DeleteItemList(split);
848 return SYNTAX_TYPE_MATCH_ERROR_OPTS_OUT_OF_RANGE;
849 }
850
851 DeleteItemList(split);
852
853 return SYNTAX_TYPE_MATCH_OK;
854 }
855
856 /****************************************************************************/
857
CheckParseVariableName(const char * const name)858 bool CheckParseVariableName(const char *const name)
859 {
860 assert(name != NULL);
861
862 const char *const reserved[] = {
863 "promiser",
864 "handle",
865 "promise_filename",
866 "promise_dirname",
867 "promise_linenumber",
868 "this",
869 NULL
870 };
871
872 if (IsStrIn(name, reserved))
873 {
874 return false;
875 }
876
877 int count = 0, level = 0;
878
879 const char *const first_dot = strchr(name, '.');
880
881 if (first_dot != NULL)
882 {
883 for (const char *sp = name; *sp != '\0'; sp++)
884 {
885 switch (*sp)
886 {
887 case '.':
888 count++;
889 if (count > 1 && level != 1)
890 {
891 // Adding a second dot is not allowed,
892 // except inside 1 level of square brackets
893 return false;
894 }
895 break;
896
897 case '[':
898 level++;
899 break;
900
901 case ']':
902 level--;
903 break;
904
905 default:
906 break;
907 }
908
909 if (level > 1)
910 {
911 yyerror("Too many levels of [] reserved for array use");
912 return false;
913 }
914 }
915
916 if (count == 1)
917 {
918 // Check that there is something before and after first dot:
919 if (name[0] == '.' || first_dot[1] == '\0')
920 {
921 return false;
922 }
923 }
924 }
925
926 return true;
927 }
928
929 /****************************************************************************/
930
CheckFnCallType(const char * s,DataType dtype)931 static SyntaxTypeMatch CheckFnCallType(const char *s, DataType dtype)
932 {
933 DataType dt;
934 const FnCallType *fn;
935
936 fn = FnCallTypeGet(s);
937
938 if (fn)
939 {
940 dt = fn->dtype;
941
942 if (dtype != dt)
943 {
944 /* Ok to allow fn calls of correct element-type in lists */
945
946 if (dt == CF_DATA_TYPE_STRING && dtype == CF_DATA_TYPE_STRING_LIST)
947 {
948 return SYNTAX_TYPE_MATCH_OK;
949 }
950
951 if (dt == CF_DATA_TYPE_STRING && dtype == CF_DATA_TYPE_CONTEXT)
952 {
953 return SYNTAX_TYPE_MATCH_OK;
954 }
955
956 if (dt == CF_DATA_TYPE_INT && dtype == CF_DATA_TYPE_INT_LIST)
957 {
958 return SYNTAX_TYPE_MATCH_OK;
959 }
960
961 if (dt == CF_DATA_TYPE_REAL && dtype == CF_DATA_TYPE_REAL_LIST)
962 {
963 return SYNTAX_TYPE_MATCH_OK;
964 }
965
966 if (dt == CF_DATA_TYPE_OPTION && dtype == CF_DATA_TYPE_OPTION_LIST)
967 {
968 return SYNTAX_TYPE_MATCH_OK;
969 }
970
971 if (dt == CF_DATA_TYPE_CONTEXT && dtype == CF_DATA_TYPE_CONTEXT_LIST)
972 {
973 return SYNTAX_TYPE_MATCH_OK;
974 }
975
976 return SYNTAX_TYPE_MATCH_ERROR_FNCALL_RETURN_TYPE;
977 }
978 else
979 {
980 return SYNTAX_TYPE_MATCH_OK;
981 }
982 }
983 else
984 {
985 return SYNTAX_TYPE_MATCH_ERROR_FNCALL_UNKNOWN;
986 }
987 }
988
989
990 /****************************************************************************/
991
ConstraintSyntaxToJson(const ConstraintSyntax * constraint_syntax)992 static JsonElement *ConstraintSyntaxToJson(const ConstraintSyntax *constraint_syntax)
993 {
994 JsonElement *json_constraint = JsonObjectCreate(5);
995
996 JsonObjectAppendString(json_constraint, "attribute", constraint_syntax->lval);
997 JsonObjectAppendString(json_constraint, "status", SyntaxStatusToString(constraint_syntax->status));
998 JsonObjectAppendString(json_constraint, "type", DataTypeToString(constraint_syntax->dtype));
999
1000 if (constraint_syntax->dtype != CF_DATA_TYPE_BODY && constraint_syntax->dtype != CF_DATA_TYPE_BUNDLE)
1001 {
1002 JsonObjectAppendString(json_constraint, "range", constraint_syntax->range.validation_string);
1003 }
1004
1005 return json_constraint;
1006 }
1007
BodySyntaxToJson(const BodySyntax * body_syntax)1008 static JsonElement *BodySyntaxToJson(const BodySyntax *body_syntax)
1009 {
1010 JsonElement *json_body = JsonObjectCreate(2);
1011
1012 JsonObjectAppendString(json_body, "status", SyntaxStatusToString(body_syntax->status));
1013 {
1014 JsonElement *attributes = JsonObjectCreate(50);
1015
1016 for (int i = 0; body_syntax->constraints[i].lval; i++)
1017 {
1018 const ConstraintSyntax *constraint_syntax = &body_syntax->constraints[i];
1019 if (constraint_syntax->status != SYNTAX_STATUS_REMOVED)
1020 {
1021 JsonElement *json_constraint = ConstraintSyntaxToJson(constraint_syntax);
1022 JsonObjectAppendString(json_constraint, "visibility", "body");
1023 JsonObjectAppendObject(attributes, constraint_syntax->lval, json_constraint);
1024 }
1025 }
1026
1027 JsonObjectAppendObject(json_body, "attributes", attributes);
1028 }
1029
1030 return json_body;
1031 }
1032
JsonBundleTypeNew(void)1033 static JsonElement *JsonBundleTypeNew(void)
1034 {
1035 JsonElement *json_bundle_type = JsonObjectCreate(2);
1036
1037 JsonObjectAppendString(json_bundle_type, "status", SyntaxStatusToString(SYNTAX_STATUS_NORMAL));
1038 JsonObjectAppendArray(json_bundle_type, "promiseTypes", JsonArrayCreate(50));
1039
1040 return json_bundle_type;
1041 }
1042
BundleTypesToJson(void)1043 static JsonElement *BundleTypesToJson(void)
1044 {
1045 JsonElement *bundle_types = JsonObjectCreate(50);
1046
1047 Seq *common_promise_types = SeqNew(50, free);
1048
1049 for (int module_index = 0; module_index < CF3_MODULES; module_index++)
1050 {
1051 for (int promise_type_index = 0; CF_ALL_PROMISE_TYPES[module_index][promise_type_index].promise_type; promise_type_index++)
1052 {
1053 const PromiseTypeSyntax *promise_type_syntax = &CF_ALL_PROMISE_TYPES[module_index][promise_type_index];
1054
1055 // skip global constraints
1056 if (strcmp("*", promise_type_syntax->promise_type) == 0)
1057 {
1058 continue;
1059 }
1060
1061 // collect common promise types to be appended at the end
1062 if (strcmp("*", promise_type_syntax->bundle_type) == 0)
1063 {
1064 SeqAppend(common_promise_types, xstrdup(promise_type_syntax->promise_type));
1065 continue;
1066 }
1067
1068 if (promise_type_syntax->status == SYNTAX_STATUS_REMOVED)
1069 {
1070 continue;
1071 }
1072
1073 JsonElement *bundle_type = JsonObjectGet(bundle_types, promise_type_syntax->bundle_type);
1074 if (!bundle_type)
1075 {
1076 bundle_type = JsonBundleTypeNew();
1077 JsonObjectAppendObject(bundle_types, promise_type_syntax->bundle_type, bundle_type);
1078 }
1079 assert(bundle_type);
1080
1081 JsonElement *promise_types = JsonObjectGet(bundle_type, "promiseTypes");
1082 assert(promise_types);
1083
1084 JsonArrayAppendString(promise_types, promise_type_syntax->promise_type);
1085 }
1086 }
1087
1088 // Append the common bundle, which has only common promise types, but is not declared in syntax
1089 {
1090 JsonElement *bundle_type = JsonBundleTypeNew();
1091 JsonObjectAppendObject(bundle_types, "common", bundle_type);
1092 }
1093
1094 JsonIterator it = JsonIteratorInit(bundle_types);
1095 const char *bundle_type = NULL;
1096 while ((bundle_type = JsonIteratorNextKey(&it)))
1097 {
1098 JsonElement *promise_types = JsonObjectGetAsArray(JsonObjectGetAsObject(bundle_types, bundle_type), "promiseTypes");
1099 for (size_t i = 0; i < SeqLength(common_promise_types); i++)
1100 {
1101 const char *common_promise_type = SeqAt(common_promise_types, i);
1102 JsonArrayAppendString(promise_types, common_promise_type);
1103 }
1104 }
1105
1106 SeqDestroy(common_promise_types);
1107 return bundle_types;
1108 }
1109
JsonPromiseTypeNew(SyntaxStatus status)1110 static JsonElement *JsonPromiseTypeNew(SyntaxStatus status)
1111 {
1112 JsonElement *promise_type = JsonObjectCreate(2);
1113
1114 JsonObjectAppendString(promise_type, "status", SyntaxStatusToString(status));
1115 JsonObjectAppendObject(promise_type, "attributes", JsonObjectCreate(50));
1116
1117 return promise_type;
1118 }
1119
PromiseTypesToJson(void)1120 static JsonElement *PromiseTypesToJson(void)
1121 {
1122 JsonElement *promise_types = JsonObjectCreate(50);
1123
1124 const PromiseTypeSyntax *global_promise_type = PromiseTypeSyntaxGet("*", "*");
1125
1126 for (int module_index = 0; module_index < CF3_MODULES; module_index++)
1127 {
1128 for (int promise_type_index = 0; CF_ALL_PROMISE_TYPES[module_index][promise_type_index].promise_type; promise_type_index++)
1129 {
1130 const PromiseTypeSyntax *promise_type_syntax = &CF_ALL_PROMISE_TYPES[module_index][promise_type_index];
1131
1132 // skip global and bundle-local common constraints
1133 if (strcmp("*", promise_type_syntax->promise_type) == 0)
1134 {
1135 continue;
1136 }
1137
1138 if (promise_type_syntax->status == SYNTAX_STATUS_REMOVED)
1139 {
1140 continue;
1141 }
1142
1143 JsonElement *promise_type = JsonObjectGet(promise_types, promise_type_syntax->promise_type);
1144 if (!promise_type)
1145 {
1146 promise_type = JsonPromiseTypeNew(promise_type_syntax->status);
1147 JsonObjectAppendObject(promise_types, promise_type_syntax->promise_type, promise_type);
1148 }
1149 assert(promise_type);
1150
1151 JsonElement *attributes = JsonObjectGet(promise_type, "attributes");
1152 assert(attributes);
1153
1154 for (int i = 0; promise_type_syntax->constraints[i].lval; i++)
1155 {
1156 const ConstraintSyntax *constraint_syntax = &promise_type_syntax->constraints[i];
1157 JsonElement *json_constraint = ConstraintSyntaxToJson(constraint_syntax);
1158 JsonObjectAppendString(json_constraint, "visibility", "promiseType");
1159 JsonObjectAppendObject(attributes, constraint_syntax->lval, json_constraint);
1160 }
1161
1162 // append bundle common constraints
1163 const PromiseTypeSyntax *bundle_promise_type = PromiseTypeSyntaxGet(promise_type_syntax->bundle_type, "*");
1164 if (strcmp("*", bundle_promise_type->bundle_type) != 0)
1165 {
1166 for (int i = 0; bundle_promise_type->constraints[i].lval; i++)
1167 {
1168 const ConstraintSyntax *constraint_syntax = &bundle_promise_type->constraints[i];
1169 JsonElement *json_constraint = ConstraintSyntaxToJson(constraint_syntax);
1170 JsonObjectAppendString(json_constraint, "visibility", "bundle");
1171 JsonObjectAppendObject(attributes, constraint_syntax->lval, json_constraint);
1172 }
1173 }
1174
1175 // append global common constraints
1176 for (int i = 0; global_promise_type->constraints[i].lval; i++)
1177 {
1178 const ConstraintSyntax *constraint_syntax = &global_promise_type->constraints[i];
1179 JsonElement *json_constraint = ConstraintSyntaxToJson(constraint_syntax);
1180 JsonObjectAppendString(json_constraint, "visibility", "global");
1181 JsonObjectAppendObject(attributes, constraint_syntax->lval, json_constraint);
1182 }
1183 }
1184 }
1185
1186 return promise_types;
1187 }
1188
BodyTypesToJson(void)1189 static JsonElement *BodyTypesToJson(void)
1190 {
1191 JsonElement *body_types = JsonObjectCreate(50);
1192
1193 for (int module_index = 0; module_index < CF3_MODULES; module_index++)
1194 {
1195 for (int promise_type_index = 0; CF_ALL_PROMISE_TYPES[module_index][promise_type_index].promise_type; promise_type_index++)
1196 {
1197 const PromiseTypeSyntax *promise_type_syntax = &CF_ALL_PROMISE_TYPES[module_index][promise_type_index];
1198
1199 for (int constraint_index = 0; promise_type_syntax->constraints[constraint_index].lval; constraint_index++)
1200 {
1201 const ConstraintSyntax *constraint_syntax = &promise_type_syntax->constraints[constraint_index];
1202 if (constraint_syntax->dtype != CF_DATA_TYPE_BODY)
1203 {
1204 continue;
1205 }
1206
1207 if (constraint_syntax->status == SYNTAX_STATUS_REMOVED)
1208 {
1209 continue;
1210 }
1211
1212 const BodySyntax *body_syntax = constraint_syntax->range.body_type_syntax;
1213 JsonElement *body_type = JsonObjectGet(body_types, body_syntax->body_type);
1214 if (!body_type)
1215 {
1216 JsonElement *body_type = BodySyntaxToJson(body_syntax);
1217 JsonObjectAppendObject(body_types, body_syntax->body_type, body_type);
1218 }
1219 }
1220 }
1221 }
1222
1223 for (int i = 0; CONTROL_BODIES[i].body_type; i++)
1224 {
1225 const BodySyntax *body_syntax = &CONTROL_BODIES[i];
1226
1227 if (body_syntax->status == SYNTAX_STATUS_REMOVED)
1228 {
1229 continue;
1230 }
1231
1232 JsonElement *body_type = JsonObjectGet(body_types, body_syntax->body_type);
1233 if (!body_type)
1234 {
1235 JsonElement *body_type = BodySyntaxToJson(body_syntax);
1236 JsonObjectAppendObject(body_types, body_syntax->body_type, body_type);
1237 }
1238 }
1239
1240 return body_types;
1241 }
1242
FnCallCategoryToString(FnCallCategory category)1243 static const char *FnCallCategoryToString(FnCallCategory category)
1244 {
1245 static const char *const category_str[] =
1246 {
1247 [FNCALL_CATEGORY_COMM] = "communication",
1248 [FNCALL_CATEGORY_DATA] = "data",
1249 [FNCALL_CATEGORY_FILES] = "files",
1250 [FNCALL_CATEGORY_IO] = "io",
1251 [FNCALL_CATEGORY_SYSTEM] = "system",
1252 [FNCALL_CATEGORY_UTILS] = "utils",
1253 [FNCALL_CATEGORY_INTERNAL] = "internal"
1254 };
1255
1256 return category_str[category];
1257 }
1258
FnCallTypeToJson(const FnCallType * fn_syntax)1259 static JsonElement *FnCallTypeToJson(const FnCallType *fn_syntax)
1260 {
1261 JsonElement *json_fn = JsonObjectCreate(10);
1262
1263 JsonObjectAppendString(json_fn, "status", SyntaxStatusToString(fn_syntax->status));
1264 JsonObjectAppendString(json_fn, "returnType", DataTypeToString(fn_syntax->dtype));
1265
1266 {
1267 JsonElement *params = JsonArrayCreate(10);
1268 for (int i = 0; fn_syntax->args[i].pattern; i++)
1269 {
1270 const FnCallArg *param = &fn_syntax->args[i];
1271
1272 JsonElement *json_param = JsonObjectCreate(2);
1273 JsonObjectAppendString(json_param, "type", DataTypeToString(param->dtype));
1274 JsonObjectAppendString(json_param, "range", param->pattern);
1275 JsonObjectAppendString(json_param, "description", param->description);
1276 JsonArrayAppendObject(params, json_param);
1277 }
1278 JsonObjectAppendArray(json_fn, "parameters", params);
1279 }
1280
1281 JsonObjectAppendBool(json_fn, "variadic", fn_syntax->options & FNCALL_OPTION_VARARG);
1282 JsonObjectAppendBool(json_fn, "cached", fn_syntax->options & FNCALL_OPTION_CACHED);
1283 JsonObjectAppendBool(json_fn, "collecting", fn_syntax->options & FNCALL_OPTION_COLLECTING);
1284 JsonObjectAppendString(json_fn, "category", FnCallCategoryToString(fn_syntax->category));
1285
1286 return json_fn;
1287 }
1288
FunctionsToJson(void)1289 static JsonElement *FunctionsToJson(void)
1290 {
1291 JsonElement *functions = JsonObjectCreate(500);
1292
1293 for (int i = 0; CF_FNCALL_TYPES[i].name; i++)
1294 {
1295 const FnCallType *fn_syntax = &CF_FNCALL_TYPES[i];
1296
1297 if (fn_syntax->status == SYNTAX_STATUS_REMOVED)
1298 {
1299 continue;
1300 }
1301
1302 JsonObjectAppendObject(functions, fn_syntax->name, FnCallTypeToJson(fn_syntax));
1303 }
1304
1305 return functions;
1306 }
1307
SyntaxToJson(void)1308 JsonElement *SyntaxToJson(void)
1309 {
1310 JsonElement *syntax_tree = JsonObjectCreate(3);
1311
1312 JsonObjectAppendObject(syntax_tree, "bundleTypes", BundleTypesToJson());
1313 JsonObjectAppendObject(syntax_tree, "promiseTypes", PromiseTypesToJson());
1314 JsonObjectAppendObject(syntax_tree, "bodyTypes", BodyTypesToJson());
1315 JsonObjectAppendObject(syntax_tree, "functions", FunctionsToJson());
1316
1317 return syntax_tree;
1318 }
1319