add validation code
[project/ubox.git] / validate / validate.c
1 /*
2 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdbool.h>
18 #include <ctype.h>
19
20 #include <arpa/inet.h>
21 #include <netinet/ether.h>
22 #include <sys/stat.h>
23
24 #include "libvalidate.h"
25
26 enum dt_optype {
27 OP_UNKNOWN,
28 OP_BOOL,
29 OP_NUMBER,
30 OP_STRING,
31 OP_FUNCTION
32 };
33
34 struct dt_fun;
35
36 struct dt_op {
37 enum dt_optype type;
38 const char *next;
39 int length;
40 int nextop;
41 union {
42 bool boolean;
43 double number;
44 const char *string;
45 struct dt_fun *function;
46 } value;
47 };
48
49 struct dt_state {
50 int pos;
51 int depth;
52 const char *value;
53 struct dt_op stack[32];
54 };
55
56 struct dt_fun {
57 const char *name;
58 bool (*call)(struct dt_state *s, int nargs);
59 };
60
61 static bool
62 dt_test_number(double number, const char *value)
63 {
64 char *e;
65 double n;
66
67 n = strtod(value, &e);
68
69 return (e > value && *e == 0 && n == number);
70 }
71
72 static bool
73 dt_test_string(const char *s, const char *end, const char *value)
74 {
75 bool esc = false;
76
77 while (*value)
78 {
79 if (s > end)
80 return false;
81
82 if (!esc && *s == '\\')
83 {
84 s++;
85
86 if (s >= end)
87 break;
88
89 esc = true;
90 continue;
91 }
92
93 if (*s != *value)
94 return false;
95
96 esc = false;
97 value++;
98 s++;
99 }
100
101 return (*s == *value || (s > end && *value == 0));
102 }
103
104 static bool
105 dt_step(struct dt_state *s);
106
107 static bool
108 dt_call(struct dt_state *s);
109
110 static bool
111 dt_type_or(struct dt_state *s, int nargs)
112 {
113 while (nargs--)
114 if (dt_step(s))
115 return true;
116
117 return false;
118 }
119
120 static bool
121 dt_type_and(struct dt_state *s, int nargs)
122 {
123 while (nargs--)
124 if (!dt_step(s))
125 return false;
126
127 return true;
128 }
129
130 static bool
131 dt_type_not(struct dt_state *s, int nargs)
132 {
133 if (!nargs)
134 return false;
135
136 return !dt_step(s);
137 }
138
139 static bool
140 dt_type_neg(struct dt_state *s, int nargs)
141 {
142 bool rv;
143 const char *value = s->value;
144
145 if (!nargs)
146 return false;
147
148 if (*s->value == '!')
149 while (isspace(*++s->value));
150
151 rv = dt_step(s);
152 s->value = value;
153
154 return rv;
155 }
156
157 static bool
158 dt_type_list(struct dt_state *s, int nargs)
159 {
160 bool rv = true;
161 int pos = s->pos;
162 char *p, *str = strdup(s->value);
163 const char *value = s->value;
164
165 if (!str || !nargs)
166 return false;
167
168 for (p = strtok(str, " \t"); p; p = strtok(NULL, " \t"))
169 {
170 s->value = p;
171
172 if (!dt_step(s))
173 {
174 rv = false;
175 break;
176 }
177
178 s->pos = pos;
179 }
180
181 s->value = value;
182 free(str);
183
184 return rv;
185 }
186
187 static bool
188 dt_type_min(struct dt_state *s, int nargs)
189 {
190 int n;
191 char *e;
192
193 if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
194 {
195 n = strtol(s->value, &e, 0);
196
197 return (e > s->value && *e == 0 &&
198 n >= s->stack[s->pos].value.number);
199 }
200
201 return false;
202 }
203
204 static bool
205 dt_type_max(struct dt_state *s, int nargs)
206 {
207 int n;
208 char *e;
209
210 if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
211 {
212 n = strtol(s->value, &e, 0);
213
214 return (e > s->value && *e == 0 &&
215 n <= s->stack[s->pos].value.number);
216 }
217
218 return false;
219 }
220
221 static bool
222 dt_type_range(struct dt_state *s, int nargs)
223 {
224 int n;
225 char *e;
226
227 if (nargs >= 2 &&
228 s->stack[s->pos].type == OP_NUMBER &&
229 s->stack[s->pos + 1].type == OP_NUMBER)
230 {
231 n = strtol(s->value, &e, 0);
232
233 return (e > s->value && *e == 0 &&
234 n >= s->stack[s->pos].value.number &&
235 n <= s->stack[s->pos + 1].value.number);
236 }
237
238 return false;
239 }
240
241 static bool
242 dt_type_minlen(struct dt_state *s, int nargs)
243 {
244 if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
245 return (strlen(s->value) >= s->stack[s->pos].value.number);
246
247 return false;
248 }
249
250 static bool
251 dt_type_maxlen(struct dt_state *s, int nargs)
252 {
253 if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
254 return (strlen(s->value) <= s->stack[s->pos].value.number);
255
256 return false;
257 }
258
259 static bool
260 dt_type_rangelen(struct dt_state *s, int nargs)
261 {
262 if (nargs >= 2 &&
263 s->stack[s->pos].type == OP_NUMBER &&
264 s->stack[s->pos + 1].type == OP_NUMBER)
265 return (strlen(s->value) >= s->stack[s->pos].value.number &&
266 strlen(s->value) <= s->stack[s->pos + 1].value.number);
267
268 return false;
269 }
270
271 static bool
272 dt_type_int(struct dt_state *s, int nargs)
273 {
274 char *e;
275
276 strtol(s->value, &e, 0);
277
278 return (e > s->value && *e == 0);
279 }
280
281 static bool
282 dt_type_uint(struct dt_state *s, int nargs)
283 {
284 int n;
285 char *e;
286
287 n = strtol(s->value, &e, 0);
288
289 return (e > s->value && *e == 0 && n >= 0);
290 }
291
292 static bool
293 dt_type_float(struct dt_state *s, int nargs)
294 {
295 char *e;
296
297 strtod(s->value, &e);
298
299 return (e > s->value && *e == 0);
300 }
301
302 static bool
303 dt_type_ufloat(struct dt_state *s, int nargs)
304 {
305 int n;
306 char *e;
307
308 n = strtod(s->value, &e);
309
310 return (e > s->value && *e == 0 && n >= 0.0);
311 }
312
313 static bool
314 dt_type_bool(struct dt_state *s, int nargs)
315 {
316 int i;
317 const char *values[] = {
318 "0", "off", "false", "no",
319 "1", "on", "true", "yes"
320 };
321
322 for (i = 0; i < sizeof(values) / sizeof(values[0]); i++)
323 if (!strcasecmp(values[i], s->value))
324 return true;
325
326 return false;
327 }
328
329 static bool
330 dt_type_string(struct dt_state *s, int nargs)
331 {
332 return true;
333 }
334
335 static bool
336 dt_type_ip4addr(struct dt_state *s, int nargs)
337 {
338 struct in6_addr a;
339 return inet_pton(AF_INET, s->value, &a);
340 }
341
342 static bool
343 dt_type_ip6addr(struct dt_state *s, int nargs)
344 {
345 struct in6_addr a;
346 return inet_pton(AF_INET6, s->value, &a);
347 }
348
349 static bool
350 dt_type_ipaddr(struct dt_state *s, int nargs)
351 {
352 return (dt_type_ip4addr(s, 0) || dt_type_ip6addr(s, 0));
353 }
354
355 static bool
356 dt_type_netmask4(struct dt_state *s, int nargs)
357 {
358 int i;
359 struct in_addr a;
360
361 if (!inet_pton(AF_INET, s->value, &a))
362 return false;
363
364 if (a.s_addr == 0)
365 return true;
366
367 a.s_addr = ntohl(a.s_addr);
368
369 for (i = 0; (i < 32) && !(a.s_addr & (1 << i)); i++);
370
371 return ((uint32_t)(~((1 << i) - 1)) == a.s_addr);
372 }
373
374 static bool
375 dt_type_netmask6(struct dt_state *s, int nargs)
376 {
377 int i;
378 struct in6_addr a;
379
380 if (!inet_pton(AF_INET6, s->value, &a))
381 return false;
382
383 for (i = 0; (i < 16) && (a.s6_addr[i] == 0xFF); i++);
384
385 if (i == 16)
386 return true;
387
388 if ((a.s6_addr[i] != 255) && (a.s6_addr[i] != 254) &&
389 (a.s6_addr[i] != 252) && (a.s6_addr[i] != 248) &&
390 (a.s6_addr[i] != 240) && (a.s6_addr[i] != 224) &&
391 (a.s6_addr[i] != 192) && (a.s6_addr[i] != 128) &&
392 (a.s6_addr[i] != 0))
393 return false;
394
395 for (; (i < 16) && (a.s6_addr[i] == 0); i++);
396
397 return (i == 16);
398 }
399
400 static bool
401 dt_type_cidr4(struct dt_state *s, int nargs)
402 {
403 int n;
404 struct in_addr a;
405 char *p, buf[sizeof("255.255.255.255/32\0")];
406
407 if (strlen(s->value) >= sizeof(buf))
408 return false;
409
410 strcpy(buf, s->value);
411 p = strchr(buf, '/');
412
413 if (p)
414 {
415 *p++ = 0;
416
417 n = strtoul(p, &p, 10);
418
419 if ((*p != 0) || (n > 32))
420 return false;
421 }
422
423 return inet_pton(AF_INET, buf, &a);
424 }
425
426 static bool
427 dt_type_cidr6(struct dt_state *s, int nargs)
428 {
429 int n;
430 struct in6_addr a;
431 char *p, buf[sizeof("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255/128\0")];
432
433 if (strlen(s->value) >= sizeof(buf))
434 return false;
435
436 strcpy(buf, s->value);
437 p = strchr(buf, '/');
438
439 if (p)
440 {
441 *p++ = 0;
442
443 n = strtoul(p, &p, 10);
444
445 if ((*p != 0) || (n > 128))
446 return false;
447 }
448
449 return inet_pton(AF_INET6, buf, &a);
450 }
451
452 static bool
453 dt_type_cidr(struct dt_state *s, int nargs)
454 {
455 return (dt_type_cidr4(s, 0) || dt_type_cidr6(s, 0));
456 }
457
458 static bool
459 dt_type_ipmask4(struct dt_state *s, int nargs)
460 {
461 bool rv;
462 struct in_addr a;
463 const char *value;
464 char *p, buf[sizeof("255.255.255.255/255.255.255.255\0")];
465
466 if (strlen(s->value) >= sizeof(buf))
467 return false;
468
469 strcpy(buf, s->value);
470 p = strchr(buf, '/');
471
472 if (p)
473 {
474 *p++ = 0;
475
476 value = s->value;
477 s->value = p;
478 rv = dt_type_netmask4(s, 0);
479 s->value = value;
480
481 if (!rv)
482 return false;
483 }
484
485 return inet_pton(AF_INET, buf, &a);
486 }
487
488 static bool
489 dt_type_ipmask6(struct dt_state *s, int nargs)
490 {
491 bool rv;
492 struct in6_addr a;
493 const char *value;
494 char *p, buf[sizeof("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255/"
495 "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255\0")];
496
497 if (strlen(s->value) >= sizeof(buf))
498 return false;
499
500 strcpy(buf, s->value);
501 p = strchr(buf, '/');
502
503 if (p)
504 {
505 *p++ = 0;
506
507 value = s->value;
508 s->value = p;
509 rv = dt_type_netmask6(s, 0);
510 s->value = value;
511
512 if (!rv)
513 return false;
514 }
515
516 return inet_pton(AF_INET6, buf, &a);
517 }
518
519 static bool
520 dt_type_ipmask(struct dt_state *s, int nargs)
521 {
522 return (dt_type_ipmask4(s, 0) || dt_type_ipmask6(s, 0));
523 }
524
525 static bool
526 dt_type_port(struct dt_state *s, int nargs)
527 {
528 int n;
529 char *e;
530
531 n = strtoul(s->value, &e, 10);
532
533 return (e > s->value && *e == 0 && n <= 65535);
534 }
535
536 static bool
537 dt_type_portrange(struct dt_state *s, int nargs)
538 {
539 int n, m;
540 char *e;
541
542 n = strtoul(s->value, &e, 10);
543
544 if (e == s->value || *e != '-')
545 return false;
546
547 m = strtoul(e + 1, &e, 10);
548
549 return (*e == 0 && n <= 65535 && m <= 65535 && n <= m);
550 }
551
552 static bool
553 dt_type_macaddr(struct dt_state *s, int nargs)
554 {
555 return !!ether_aton(s->value);
556 }
557
558 static bool
559 dt_type_uciname(struct dt_state *s, int nargs)
560 {
561 const char *p;
562
563 for (p = s->value;
564 *p && ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
565 (*p >= '0' && *p <= '9') || (*p == '_'));
566 p++);
567
568 return (*p == 0);
569 }
570
571 static bool
572 dt_type_wpakey(struct dt_state *s, int nargs)
573 {
574 int len = strlen(s->value);
575 const char *p = s->value;
576
577 if (len == 64)
578 {
579 while (isxdigit(*p))
580 p++;
581
582 return (*p == 0);
583 }
584
585 return (len >= 8 && len <= 63);
586 }
587
588 static bool
589 dt_type_wepkey(struct dt_state *s, int nargs)
590 {
591 int len = strlen(s->value);
592 const char *p = s->value;
593
594 if (!strncmp(p, "s:", 2))
595 {
596 len -= 2;
597 p += 2;
598 }
599
600 if (len == 10 || len == 26)
601 {
602 while (isxdigit(*p))
603 p++;
604
605 return (*p == 0);
606 }
607
608 return (len == 5 || len == 13);
609 }
610
611 static bool
612 dt_type_hostname(struct dt_state *s, int nargs)
613 {
614 const char *p, *last;
615
616 for (p = last = s->value; *p; p++)
617 {
618 if (*p == '.')
619 {
620 if ((p - last) == 0 || (p - last) > 63)
621 return false;
622
623 last = p + 1;
624 continue;
625 }
626 else if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
627 (*p >= '0' && *p <= '9') || (*p == '_') || (*p == '-'))
628 {
629 continue;
630 }
631
632 return false;
633 }
634
635 return ((p - last) > 0 && (p - last) <= 255);
636 }
637
638 static bool
639 dt_type_host(struct dt_state *s, int nargs)
640 {
641 return (dt_type_hostname(s, 0) || dt_type_ipaddr(s, 0));
642 }
643
644 static bool
645 dt_type_network(struct dt_state *s, int nargs)
646 {
647 return (dt_type_uciname(s, 0) || dt_type_host(s, 0));
648 }
649
650 static bool
651 dt_type_phonedigit(struct dt_state *s, int nargs)
652 {
653 const char *p;
654
655 for (p = s->value;
656 *p && ((*p >= '0' && *p <= '9') || (*p == '*') || (*p == '#') ||
657 (*p == '!') || (*p == '.'));
658 p++);
659
660 return (*p == 0);
661 }
662
663 static bool
664 dt_type_directory(struct dt_state *s, int nargs)
665 {
666 struct stat st;
667 return (!stat(s->value, &st) && S_ISDIR(st.st_mode));
668 }
669
670
671 static bool
672 dt_type_device(struct dt_state *s, int nargs)
673 {
674 struct stat st;
675 return (!stat(s->value, &st) &&
676 (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)));
677 }
678
679 static bool
680 dt_type_file(struct dt_state *s, int nargs)
681 {
682 struct stat st;
683 return (!stat(s->value, &st) && S_ISREG(st.st_mode));
684 }
685
686
687 static struct dt_fun dt_types[] = {
688 { "or", dt_type_or },
689 { "and", dt_type_and },
690 { "not", dt_type_not },
691 { "neg", dt_type_neg },
692 { "list", dt_type_list },
693 { "min", dt_type_min },
694 { "max", dt_type_max },
695 { "range", dt_type_range },
696 { "minlength", dt_type_minlen },
697 { "maxlength", dt_type_maxlen },
698 { "rangelength", dt_type_rangelen },
699 { "integer", dt_type_int },
700 { "uinteger", dt_type_uint },
701 { "float", dt_type_float },
702 { "ufloat", dt_type_ufloat },
703 { "bool", dt_type_bool },
704 { "string", dt_type_string },
705 { "ip4addr", dt_type_ip4addr },
706 { "ip6addr", dt_type_ip6addr },
707 { "ipaddr", dt_type_ipaddr },
708 { "cidr4", dt_type_cidr4 },
709 { "cidr6", dt_type_cidr6 },
710 { "cidr", dt_type_cidr },
711 { "netmask4", dt_type_netmask4 },
712 { "netmask6", dt_type_netmask6 },
713 { "ipmask4", dt_type_ipmask4 },
714 { "ipmask6", dt_type_ipmask6 },
715 { "ipmask", dt_type_ipmask },
716 { "port", dt_type_port },
717 { "portrange", dt_type_portrange },
718 { "macaddr", dt_type_macaddr },
719 { "uciname", dt_type_uciname },
720 { "wpakey", dt_type_wpakey },
721 { "wepkey", dt_type_wepkey },
722 { "hostname", dt_type_hostname },
723 { "host", dt_type_host },
724 { "network", dt_type_network },
725 { "phonedigit", dt_type_phonedigit },
726 { "directory", dt_type_directory },
727 { "device", dt_type_device },
728 { "file", dt_type_file },
729
730 { }
731 };
732
733 static struct dt_fun *
734 dt_lookup_function(const char *s, const char *e)
735 {
736 struct dt_fun *fun = dt_types;
737
738 while (fun->name)
739 {
740 if (!strncmp(fun->name, s, e - s) && *(fun->name + (e - s)) == '\0')
741 return fun;
742
743 fun++;
744 }
745
746 return NULL;
747 }
748
749 static bool
750 dt_parse_atom(struct dt_state *s, const char *label, const char *end)
751 {
752 char q, *e;
753 const char *p;
754 bool esc;
755 double dval;
756 struct dt_fun *func;
757 struct dt_op *op = &s->stack[s->depth];
758
759 if ((s->depth + 1) >= (sizeof(s->stack) / sizeof(s->stack[0])))
760 {
761 printf("Syntax error, expression too long\n");
762 return false;
763 }
764
765 while (isspace(*label))
766 label++;
767
768 /* test whether label is a float */
769 dval = strtod(label, &e);
770
771 if (e > label)
772 {
773 op->next = e;
774 op->type = OP_NUMBER;
775 op->value.number = dval;
776 op->nextop = ++s->depth;
777
778 return true;
779 }
780 else if ((*label == '"') || (*label == '\''))
781 {
782 for (p = label + 1, q = *label, esc = false; p <= end; p++)
783 {
784 if (esc)
785 {
786 esc = false;
787 continue;
788 }
789 else if (*p == '\\')
790 {
791 esc = true;
792 continue;
793 }
794 else if (*p == q)
795 {
796 op->next = p + 1;
797 op->type = OP_STRING;
798 op->length = (p - label) - 2;
799 op->value.string = label + 1;
800 op->nextop = ++s->depth;
801
802 return true;
803 }
804 }
805
806 printf("Syntax error, unterminated string\n");
807 return false;
808 }
809 else if (*label)
810 {
811 for (p = label;
812 p <= end && ((*p >= 'A' && *p <= 'Z') ||
813 (*p >= 'a' && *p <= 'z') ||
814 (*p >= '0' && *p <= '9') ||
815 (*p == '_'));
816 p++);
817
818 func = dt_lookup_function(label, p);
819
820 if (!func)
821 {
822 printf("Syntax error, unrecognized function\n");
823 return false;
824 }
825
826 op->next = p;
827 op->type = OP_FUNCTION;
828 op->value.function = func;
829 op->nextop = ++s->depth;
830
831 return true;
832 }
833
834 printf("Syntax error, unexpected EOF\n");
835 return false;
836 }
837
838 static bool
839 dt_parse_list(struct dt_state *s, const char *code, const char *end);
840
841 static bool
842 dt_parse_expr(const char *code, const char *end, struct dt_state *s)
843 {
844 struct dt_op *tok;
845
846 if (!dt_parse_atom(s, code, end))
847 return false;
848
849 tok = &s->stack[s->depth - 1];
850
851 while (isspace(*tok->next))
852 tok->next++;
853
854 if (tok->type == OP_FUNCTION)
855 {
856 if (*tok->next == '(')
857 {
858 end--;
859
860 while (isspace(*end) && end > tok->next + 1)
861 end--;
862
863 return dt_parse_list(s, tok->next + 1, end);
864 }
865 else if (tok->next == end)
866 {
867 return dt_parse_list(s, tok->next, tok->next);
868 }
869
870 printf("Syntax error, expected '(' or EOF after function label\n");
871 return false;
872 }
873 else if (tok->next == end)
874 {
875 return true;
876 }
877
878 printf("Syntax error, expected ',' after literal\n");
879 return false;
880 }
881
882 static bool
883 dt_parse_list(struct dt_state *s, const char *code, const char *end)
884 {
885 char c;
886 bool esc;
887 int nest;
888 const char *p, *last;
889 struct dt_op *fptr;
890
891 if (!code)
892 return false;
893
894 fptr = &s->stack[s->depth - 1];
895
896 for (nest = 0, p = last = code, esc = false, c = *p;
897 p <= end;
898 p++, c = (p < end) ? *p : '\0')
899 {
900 if (esc)
901 {
902 esc = false;
903 continue;
904 }
905
906 switch (c)
907 {
908 case '\\':
909 esc = true;
910 break;
911
912 case '(':
913 nest++;
914 break;
915
916 case ')':
917 nest--;
918 break;
919
920 case ',':
921 case '\0':
922 if (nest <= 0)
923 {
924 if (p > last)
925 {
926 if (!dt_parse_expr(last, p, s))
927 return false;
928
929 fptr->length++;
930 }
931
932 last = p + 1;
933 }
934
935 break;
936 }
937 }
938
939 fptr->nextop = s->depth;
940 return true;
941 }
942
943 static bool
944 dt_step(struct dt_state *s)
945 {
946 bool rv;
947 struct dt_op *op = &s->stack[s->pos];
948
949 switch (op->type)
950 {
951 case OP_BOOL:
952 rv = op->value.boolean;
953 break;
954
955 case OP_NUMBER:
956 rv = dt_test_number(op->value.number, s->value);
957 break;
958
959 case OP_STRING:
960 rv = dt_test_string(op->value.string, op->value.string + op->length, s->value);
961 break;
962
963 case OP_FUNCTION:
964 rv = dt_call(s);
965 break;
966
967 default:
968 rv = false;
969 break;
970 }
971
972 s->pos = op->nextop;
973 return rv;
974 }
975
976 static bool
977 dt_call(struct dt_state *s)
978 {
979 bool rv;
980 struct dt_op *fptr = &s->stack[s->pos];
981 struct dt_fun *func = fptr->value.function;
982
983 s->pos++;
984
985 rv = func->call(s, fptr->length);
986
987 s->pos = fptr->nextop;
988
989 return rv;
990 }
991
992 bool
993 dt_parse(const char *code, const char *value)
994 {
995 struct dt_state s = {
996 .depth = 1,
997 .stack = {
998 {
999 .type = OP_FUNCTION,
1000 .value.function = &dt_types[0],
1001 .next = code
1002 }
1003 }
1004 };
1005
1006 if (!value || !*value)
1007 return false;
1008
1009 if (!dt_parse_list(&s, code, code + strlen(code)))
1010 return false;
1011
1012 s.value = value;
1013
1014 return dt_call(&s);
1015 }