1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019-2021 Cyril Hrubis <chrubis@suse.cz>
4 * Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
5 */
6
7 #define _GNU_SOURCE
8
9 #include <search.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <libgen.h>
13 #include <ctype.h>
14 #include <unistd.h>
15 #include <errno.h>
16
17 #include "data_storage.h"
18
19 #define INCLUDE_PATH_MAX 5
20
21 static int verbose;
22 static char *cmdline_includepath[INCLUDE_PATH_MAX];
23 static unsigned int cmdline_includepaths;
24 static char *includepath;
25
26 #define WARN(str) fprintf(stderr, "WARNING: " str "\n")
27
oneline_comment(FILE * f)28 static void oneline_comment(FILE *f)
29 {
30 int c;
31
32 do {
33 c = getc(f);
34 } while (c != '\n');
35 }
36
eat_asterisk_space(const char * c)37 static const char *eat_asterisk_space(const char *c)
38 {
39 unsigned int i = 0;
40
41 while (isspace(c[i]))
42 i++;
43
44 if (c[i] == '*') {
45 if (isspace(c[i+1]))
46 i++;
47 return &c[i+1];
48 }
49
50 return c;
51 }
52
multiline_comment(FILE * f,struct data_node * doc)53 static void multiline_comment(FILE *f, struct data_node *doc)
54 {
55 int c;
56 int state = 0;
57 char buf[4096];
58 unsigned int bufp = 0;
59
60 for (;;) {
61 c = getc(f);
62
63 if (doc) {
64 if (c == '\n') {
65 struct data_node *line;
66 buf[bufp] = 0;
67 line = data_node_string(eat_asterisk_space(buf));
68 if (data_node_array_add(doc, line))
69 WARN("doc string comment truncated");
70 bufp = 0;
71 continue;
72 }
73
74 if (bufp + 1 >= sizeof(buf))
75 continue;
76
77 buf[bufp++] = c;
78 }
79
80 switch (state) {
81 case 0:
82 if (c == '*')
83 state = 1;
84 break;
85 case 1:
86 switch (c) {
87 case '/':
88 return;
89 case '*':
90 continue;
91 default:
92 state = 0;
93 break;
94 }
95 break;
96 }
97 }
98
99 }
100
101 static const char doc_prefix[] = "\\\n";
102
maybe_doc_comment(FILE * f,struct data_node * doc)103 static void maybe_doc_comment(FILE *f, struct data_node *doc)
104 {
105 int c, i;
106
107 for (i = 0; doc_prefix[i]; i++) {
108 c = getc(f);
109
110 if (c == doc_prefix[i])
111 continue;
112
113 if (c == '*')
114 ungetc(c, f);
115
116 multiline_comment(f, NULL);
117 return;
118 }
119
120 multiline_comment(f, doc);
121 }
122
maybe_comment(FILE * f,struct data_node * doc)123 static void maybe_comment(FILE *f, struct data_node *doc)
124 {
125 int c = getc(f);
126
127 switch (c) {
128 case '/':
129 oneline_comment(f);
130 break;
131 case '*':
132 maybe_doc_comment(f, doc);
133 break;
134 default:
135 ungetc(c, f);
136 break;
137 }
138 }
139
next_token2(FILE * f,char * buf,size_t buf_len,struct data_node * doc)140 static char *next_token2(FILE *f, char *buf, size_t buf_len, struct data_node *doc)
141 {
142 size_t i = 0;
143 int c;
144 int in_str = 0;
145
146 buf_len--;
147
148 for (;;) {
149 c = fgetc(f);
150
151 if (c == EOF)
152 goto exit;
153
154 if (in_str) {
155 if (c == '"') {
156 if (i == 0 || buf[i-1] != '\\')
157 goto exit;
158 }
159
160 if (i < buf_len)
161 buf[i++] = c;
162 continue;
163 }
164
165 switch (c) {
166 case '{':
167 case '}':
168 case ';':
169 case '(':
170 case ')':
171 case '=':
172 case ',':
173 case '[':
174 case ']':
175 case '#':
176 case '|':
177 case '+':
178 case '*':
179 case '%':
180 if (i) {
181 ungetc(c, f);
182 goto exit;
183 }
184
185 if (i < buf_len)
186 buf[i++] = c;
187 goto exit;
188 case '0' ... '9':
189 case 'a' ... 'z':
190 case 'A' ... 'Z':
191 case '.':
192 case '_':
193 case '-':
194 buf[i++] = c;
195 break;
196 case '/':
197 maybe_comment(f, doc);
198 break;
199 case '"':
200 in_str = 1;
201 break;
202 case ' ':
203 case '\n':
204 case '\t':
205 if (i)
206 goto exit;
207 break;
208 }
209 }
210
211 exit:
212 if (i == 0 && !in_str)
213 return NULL;
214
215 buf[i] = 0;
216 return buf;
217 }
218
next_token(FILE * f,struct data_node * doc)219 static char *next_token(FILE *f, struct data_node *doc)
220 {
221 static char buf[4096];
222
223 return next_token2(f, buf, sizeof(buf), doc);
224 }
225
open_file(const char * dir,const char * fname)226 static FILE *open_file(const char *dir, const char *fname)
227 {
228 FILE *f;
229 char *path;
230
231 if (asprintf(&path, "%s/%s", dir, fname) < 0)
232 return NULL;
233
234 f = fopen(path, "r");
235
236 free(path);
237
238 return f;
239 }
240
241 /**
242 * List of includes to be skipped.
243 *
244 * These define many macros or include many include files that are mostly
245 * useless to values expanded in tst_test structure. Or macros that shouldn't
246 * be expanded at all.
247 */
248 static const char *skip_includes[] = {
249 "\"tst_test.h\"",
250 "\"config.h\"",
251 "\"tst_taint.h\"",
252 NULL
253 };
254
open_include(FILE * f)255 static FILE *open_include(FILE *f)
256 {
257 char buf[256], *fname;
258 FILE *inc;
259 unsigned int i;
260
261 if (!fscanf(f, "%s\n", buf))
262 return NULL;
263
264 if (buf[0] != '"')
265 return NULL;
266
267 for (i = 0; skip_includes[i]; i++) {
268 if (!strcmp(skip_includes[i], buf)) {
269 if (verbose)
270 fprintf(stderr, "INCLUDE SKIP %s\n", buf);
271 return NULL;
272 }
273 }
274
275 if (!strncmp(buf, "\"lapi/", 6)) {
276 if (verbose)
277 fprintf(stderr, "INCLUDE SKIP %s\n", buf);
278 return NULL;
279 }
280
281 fname = buf + 1;
282
283 if (!buf[0])
284 return NULL;
285
286 fname[strlen(fname)-1] = 0;
287
288 inc = open_file(includepath, fname);
289 if (inc) {
290 if (verbose)
291 fprintf(stderr, "INCLUDE %s/%s\n", includepath, fname);
292
293 return inc;
294 }
295
296 for (i = 0; i < cmdline_includepaths; i++) {
297 inc = open_file(cmdline_includepath[i], fname);
298
299 if (!inc)
300 continue;
301
302 if (verbose) {
303 fprintf(stderr, "INCLUDE %s/%s\n",
304 cmdline_includepath[i], fname);
305 }
306
307 return inc;
308 }
309
310 return NULL;
311 }
312
close_include(FILE * inc)313 static void close_include(FILE *inc)
314 {
315 if (verbose)
316 fprintf(stderr, "INCLUDE END\n");
317
318 fclose(inc);
319 }
320
try_apply_macro(char ** res)321 static void try_apply_macro(char **res)
322 {
323 ENTRY macro = {
324 .key = *res,
325 };
326
327 ENTRY *ret;
328
329 ret = hsearch(macro, FIND);
330
331 if (!ret)
332 return;
333
334 if (verbose)
335 fprintf(stderr, "APPLYING MACRO %s=%s\n", ret->key, (char*)ret->data);
336
337 *res = ret->data;
338 }
339
finalize_array_entry(char ** entry,struct data_node * node)340 static void finalize_array_entry(char **entry, struct data_node *node)
341 {
342 if (!*entry)
343 return;
344
345 data_node_array_add(node, data_node_string(*entry));
346
347 free(*entry);
348 *entry = NULL;
349 }
350
str_append(char ** res,char * append)351 static void str_append(char **res, char *append)
352 {
353 char *cur_str = *res;
354
355 if (!cur_str) {
356 *res = strdup(append);
357 if (!*res)
358 goto err;
359 return;
360 }
361
362 if (asprintf(res, "%s%s", cur_str, append) < 0)
363 goto err;
364
365 free(cur_str);
366 return;
367 err:
368 fprintf(stderr, "Allocation failed :(\n");
369 exit(1);
370 }
371
parse_array(FILE * f,struct data_node * node)372 static int parse_array(FILE *f, struct data_node *node)
373 {
374 char *token;
375 char *entry = NULL;
376 int parent_cnt = 0;
377
378 for (;;) {
379 if (!(token = next_token(f, NULL)))
380 return 1;
381
382 if (!strcmp(token, "{")) {
383 struct data_node *ret = data_node_array();
384 parse_array(f, ret);
385
386 if (data_node_array_len(ret))
387 data_node_array_add(node, ret);
388 else
389 data_node_free(ret);
390
391 continue;
392 }
393
394 if (!strcmp(token, "}")) {
395 struct data_node *arr_last;
396
397 finalize_array_entry(&entry, node);
398
399 /* Remove NULL terminating entry, if present. */
400 arr_last = data_node_array_last(node);
401 if (arr_last && arr_last->type == DATA_NULL)
402 data_node_array_last_rem(node);
403
404 return 0;
405 }
406
407 if (!strcmp(token, ",") && parent_cnt <= 0) {
408 finalize_array_entry(&entry, node);
409 continue;
410 }
411
412 if (!strcmp(token, "NULL")) {
413 data_node_array_add(node, data_node_null());
414 continue;
415 }
416
417 if (!strcmp(token, "("))
418 parent_cnt++;
419
420 if (!strcmp(token, ")"))
421 parent_cnt--;
422
423 try_apply_macro(&token);
424 str_append(&entry, token);
425 }
426
427 return 0;
428 }
429
parse_get_array_len(FILE * f)430 static int parse_get_array_len(FILE *f)
431 {
432 const char *token;
433 int cnt = 0, depth = 0, prev_comma = 0;
434
435 if (!(token = next_token(f, NULL)))
436 return 0;
437
438 if (strcmp(token, "{"))
439 return 0;
440
441 for (;;) {
442 if (!(token = next_token(f, NULL)))
443 return 0;
444
445 if (!strcmp(token, "{"))
446 depth++;
447
448 if (!strcmp(token, "}"))
449 depth--;
450 else
451 prev_comma = 0;
452
453 if (!strcmp(token, ",") && !depth) {
454 prev_comma = 1;
455 cnt++;
456 }
457
458 if (depth < 0)
459 return cnt + !prev_comma;
460 }
461 }
462
look_for_array_size(FILE * f,const char * arr_id,struct data_node ** res)463 static void look_for_array_size(FILE *f, const char *arr_id, struct data_node **res)
464 {
465 const char *token;
466 char buf[2][2048] = {};
467 int cur_buf = 0;
468 int prev_buf = 1;
469
470 for (;;) {
471 if (!(token = next_token2(f, buf[cur_buf], sizeof(buf[cur_buf]), NULL)))
472 break;
473
474 if (!strcmp(token, "=") && !strcmp(buf[prev_buf], arr_id)) {
475 int arr_len = parse_get_array_len(f);
476
477 if (verbose)
478 fprintf(stderr, "ARRAY %s LENGTH = %i\n", arr_id, arr_len);
479
480 *res = data_node_int(arr_len);
481
482 break;
483 }
484
485 if (strcmp(buf[cur_buf], "]") && strcmp(buf[cur_buf], "[")) {
486 cur_buf = !cur_buf;
487 prev_buf = !prev_buf;
488 }
489 }
490 }
491
parse_array_size(FILE * f,struct data_node ** res)492 static int parse_array_size(FILE *f, struct data_node **res)
493 {
494 const char *token;
495 char *arr_id;
496 long pos;
497 int hash = 0;
498
499 *res = NULL;
500
501 if (!(token = next_token(f, NULL)))
502 return 1;
503
504 if (strcmp(token, "("))
505 return 1;
506
507 if (!(token = next_token(f, NULL)))
508 return 1;
509
510 arr_id = strdup(token);
511
512 if (verbose)
513 fprintf(stderr, "COMPUTING ARRAY '%s' LENGHT\n", arr_id);
514
515 pos = ftell(f);
516
517 rewind(f);
518
519 look_for_array_size(f, arr_id, res);
520
521 if (!*res) {
522 FILE *inc;
523
524 rewind(f);
525
526 for (;;) {
527 if (!(token = next_token(f, NULL)))
528 break;
529
530 if (token[0] == '#') {
531 hash = 1;
532 continue;
533 }
534
535 if (!hash)
536 continue;
537
538 if (!strcmp(token, "include")) {
539 inc = open_include(f);
540
541 if (inc) {
542 look_for_array_size(inc, arr_id, res);
543 close_include(inc);
544 }
545 }
546
547 if (*res)
548 break;
549 }
550 }
551
552 free(arr_id);
553
554 if (fseek(f, pos, SEEK_SET))
555 return 1;
556
557 return 0;
558 }
559
parse_test_struct(FILE * f,struct data_node * doc,struct data_node * node)560 static int parse_test_struct(FILE *f, struct data_node *doc, struct data_node *node)
561 {
562 char *token;
563 char *id = NULL;
564 int state = 0;
565 struct data_node *ret;
566
567 for (;;) {
568 if (!(token = next_token(f, doc)))
569 return 1;
570
571 if (!strcmp(token, "}"))
572 return 0;
573
574 switch (state) {
575 case 0:
576 id = strdup(token);
577 state = 1;
578 continue;
579 case 1:
580 if (!strcmp(token, "="))
581 state = 2;
582 else
583 WARN("Expected '='");
584 continue;
585 case 2:
586 if (!strcmp(token, "(")) {
587 state = 3;
588 continue;
589 }
590 break;
591 case 3:
592 if (!strcmp(token, ")"))
593 state = 2;
594 continue;
595
596 case 4:
597 if (!strcmp(token, ","))
598 state = 0;
599 continue;
600 }
601
602 if (!strcmp(token, "{")) {
603 ret = data_node_array();
604 parse_array(f, ret);
605 } else if (!strcmp(token, "ARRAY_SIZE")) {
606 if (parse_array_size(f, &ret))
607 return 1;
608 } else {
609 try_apply_macro(&token);
610 ret = data_node_string(token);
611 }
612
613 if (!ret)
614 continue;
615
616 const char *key = id;
617 if (key[0] == '.')
618 key++;
619
620 data_node_hash_add(node, key, ret);
621 free(id);
622 state = 4;
623 }
624 }
625
626 static const char *tokens[] = {
627 "static",
628 "struct",
629 "tst_test",
630 "test",
631 "=",
632 "{",
633 };
634
macro_get_string(FILE * f,char * buf,char * buf_end)635 static void macro_get_string(FILE *f, char *buf, char *buf_end)
636 {
637 int c;
638 char *buf_start = buf;
639
640 for (;;) {
641 c = fgetc(f);
642
643 switch (c) {
644 case EOF:
645 *buf = 0;
646 return;
647 case '"':
648 if (buf == buf_start || buf[-1] != '\\') {
649 *buf = 0;
650 return;
651 }
652 buf[-1] = '"';
653 break;
654 default:
655 if (buf < buf_end)
656 *(buf++) = c;
657 }
658 }
659 }
660
macro_get_val(FILE * f,char * buf,size_t buf_len)661 static void macro_get_val(FILE *f, char *buf, size_t buf_len)
662 {
663 int c, prev = 0;
664 char *buf_end = buf + buf_len - 1;
665
666 while (isspace(c = fgetc(f)));
667
668 if (c == '"') {
669 macro_get_string(f, buf, buf_end);
670 return;
671 }
672
673 for (;;) {
674 switch (c) {
675 case '\n':
676 if (prev == '\\') {
677 buf--;
678 } else {
679 *buf = 0;
680 return;
681 }
682 break;
683 case EOF:
684 *buf = 0;
685 return;
686 case ' ':
687 case '\t':
688 break;
689 default:
690 if (buf < buf_end)
691 *(buf++) = c;
692 }
693
694 prev = c;
695 c = fgetc(f);
696 }
697 }
698
parse_macro(FILE * f)699 static void parse_macro(FILE *f)
700 {
701 char name[128];
702 char val[256];
703
704 if (!fscanf(f, "%s[^\n]", name))
705 return;
706
707 if (fgetc(f) == '\n')
708 return;
709
710 macro_get_val(f, val, sizeof(val));
711
712 if (name[0] == '_')
713 return;
714
715 ENTRY e = {
716 .key = strdup(name),
717 .data = strdup(val),
718 };
719
720 if (verbose)
721 fprintf(stderr, " MACRO %s=%s\n", e.key, (char*)e.data);
722
723 hsearch(e, ENTER);
724 }
725
parse_include_macros(FILE * f,int level)726 static void parse_include_macros(FILE *f, int level)
727 {
728 FILE *inc;
729 const char *token;
730 int hash = 0;
731
732 /**
733 * Allow only three levels of include indirection.
734 *
735 * Should be more than enough (TM).
736 */
737 if (level >= 3)
738 return;
739
740 inc = open_include(f);
741 if (!inc)
742 return;
743
744 while ((token = next_token(inc, NULL))) {
745 if (token[0] == '#') {
746 hash = 1;
747 continue;
748 }
749
750 if (!hash)
751 continue;
752
753 if (!strcmp(token, "define"))
754 parse_macro(inc);
755 else if (!strcmp(token, "include"))
756 parse_include_macros(inc, level+1);
757
758 hash = 0;
759 }
760
761 close_include(inc);
762 }
763
764 /* pre-defined macros that makes the output cleaner. */
765 static const struct macro {
766 char *from;
767 char *to;
768 } internal_macros[] = {
769 {"TST_CG_V2", "2"},
770 {"TST_CG_V1", "1"},
771 {"TST_KB", "1024"},
772 {"TST_MB", "1048576"},
773 {"TST_GB", "1073741824"},
774 {"TST_SR_TBROK", "TBROK"},
775 {"TST_SR_TCONF", "TCONF"},
776 {"TST_SR_SKIP", "SKIP"},
777 {"TST_SR_TBROK_MISSING", "TBROK_MISSING"},
778 {"TST_SR_TCONF_MISSING", "TCONF_MISSING"},
779 {"TST_SR_SKIP_MISSING", "SKIP_MISSING"},
780 {"TST_SR_TBROK_RO", "TBROK_RO"},
781 {"TST_SR_TCONF_RO", "TCONF_RO"},
782 {"TST_SR_SKIP_RO", "SKIP_RO"},
783 {}
784 };
785
load_internal_macros(void)786 static void load_internal_macros(void)
787 {
788 unsigned int i;
789
790 if (verbose)
791 fprintf(stderr, "PREDEFINED MACROS\n");
792
793 for (i = 0; internal_macros[i].from; i++) {
794 ENTRY e = {
795 .key = internal_macros[i].from,
796 .data = internal_macros[i].to,
797 };
798
799 if (verbose)
800 fprintf(stderr, " MACRO %s=%s\n", e.key, (char*)e.data);
801
802 hsearch(e, ENTER);
803 }
804
805 if (verbose)
806 fprintf(stderr, "END PREDEFINED MACROS\n");
807 }
808
parse_file(const char * fname)809 static struct data_node *parse_file(const char *fname)
810 {
811 int state = 0, found = 0;
812 const char *token;
813
814 if (access(fname, F_OK)) {
815 fprintf(stderr, "file %s does not exist\n", fname);
816 return NULL;
817 }
818
819 FILE *f = fopen(fname, "r");
820
821 includepath = dirname(strdup(fname));
822
823 struct data_node *res = data_node_hash();
824 struct data_node *doc = data_node_array();
825
826 load_internal_macros();
827
828 while ((token = next_token(f, doc))) {
829 if (state < 6 && !strcmp(tokens[state], token)) {
830 state++;
831 } else {
832 if (token[0] == '#') {
833 token = next_token(f, doc);
834 if (token) {
835 if (!strcmp(token, "define"))
836 parse_macro(f);
837
838 if (!strcmp(token, "include"))
839 parse_include_macros(f, 0);
840 }
841 }
842
843 state = 0;
844 }
845
846 if (state < 6)
847 continue;
848
849 found = 1;
850 parse_test_struct(f, doc, res);
851 }
852
853 if (data_node_array_len(doc)) {
854 data_node_hash_add(res, "doc", doc);
855 found = 1;
856 } else {
857 data_node_free(doc);
858 }
859
860 fclose(f);
861
862 if (!found) {
863 data_node_free(res);
864 return NULL;
865 }
866
867 return res;
868 }
869
870 static struct typemap {
871 const char *id;
872 enum data_type type;
873 } tst_test_typemap[] = {
874 {.id = "test_variants", .type = DATA_INT},
875 {}
876 };
877
convert_str2int(struct data_node * res,const char * id,const char * str_val)878 static void convert_str2int(struct data_node *res, const char *id, const char *str_val)
879 {
880 long val;
881 char *endptr;
882
883 errno = 0;
884 val = strtol(str_val, &endptr, 10);
885
886 if (errno || *endptr) {
887 fprintf(stderr, "Cannot convert %s value %s to int!\n", id, str_val);
888 exit(1);
889 }
890
891 if (verbose)
892 fprintf(stderr, "NORMALIZING %s TO INT %li\n", id, val);
893
894 data_node_hash_del(res, id);
895 data_node_hash_add(res, id, data_node_int(val));
896 }
897
check_normalize_types(struct data_node * res)898 static void check_normalize_types(struct data_node *res)
899 {
900 unsigned int i;
901
902 for (i = 0; tst_test_typemap[i].id; i++) {
903 struct data_node *n;
904 struct typemap *typemap = &tst_test_typemap[i];
905
906 n = data_node_hash_get(res, typemap->id);
907 if (!n)
908 continue;
909
910 if (n->type == typemap->type)
911 continue;
912
913 if (n->type == DATA_STRING && typemap->type == DATA_INT) {
914 convert_str2int(res, typemap->id, n->string.val);
915 continue;
916 }
917
918 fprintf(stderr, "Cannot convert %s from %s to %s!\n",
919 typemap->id, data_type_name(n->type),
920 data_type_name(typemap->type));
921 exit(1);
922 }
923 }
924
925 static const char *filter_out[] = {
926 "bufs",
927 "cleanup",
928 "mntpoint",
929 "setup",
930 "tcnt",
931 "test",
932 "test_all",
933 NULL
934 };
935
936 static struct implies {
937 const char *flag;
938 const char **implies;
939 } implies[] = {
940 {"mount_device", (const char *[]) {"format_device", "needs_device",
941 "needs_tmpdir", NULL}},
942 {"format_device", (const char *[]) {"needs_device", "needs_tmpdir",
943 NULL}},
944 {"all_filesystems", (const char *[]) {"needs_device", "needs_tmpdir",
945 NULL}},
946 {"needs_device", (const char *[]) {"needs_tmpdir", NULL}},
947 {"needs_checkpoints", (const char *[]) {"needs_tmpdir", NULL}},
948 {"resource_files", (const char *[]) {"needs_tmpdir", NULL}},
949 {NULL, (const char *[]) {NULL}}
950 };
951
strip_name(char * path)952 const char *strip_name(char *path)
953 {
954 char *name = basename(path);
955 size_t len = strlen(name);
956
957 if (len > 2 && name[len-1] == 'c' && name[len-2] == '.')
958 name[len-2] = '\0';
959
960 return name;
961 }
962
print_help(const char * prgname)963 static void print_help(const char *prgname)
964 {
965 printf("usage: %s [-vh] input.c\n\n", prgname);
966 printf("-v sets verbose mode\n");
967 printf("-I add include path\n");
968 printf("-h prints this help\n\n");
969 exit(0);
970 }
971
main(int argc,char * argv[])972 int main(int argc, char *argv[])
973 {
974 unsigned int i, j;
975 struct data_node *res;
976 int opt;
977
978 while ((opt = getopt(argc, argv, "hI:v")) != -1) {
979 switch (opt) {
980 case 'h':
981 print_help(argv[0]);
982 break;
983 case 'I':
984 if (cmdline_includepaths >= INCLUDE_PATH_MAX) {
985 fprintf(stderr, "Too much include paths!");
986 exit(1);
987 }
988
989 cmdline_includepath[cmdline_includepaths++] = optarg;
990 break;
991 case 'v':
992 verbose = 1;
993 break;
994 }
995 }
996
997 if (optind >= argc) {
998 fprintf(stderr, "No input filename.c\n");
999 return 1;
1000 }
1001
1002 if (!hcreate(128)) {
1003 fprintf(stderr, "Failed to initialize hash table\n");
1004 return 1;
1005 }
1006
1007 res = parse_file(argv[optind]);
1008 if (!res)
1009 return 0;
1010
1011 /* Filter out useless data */
1012 for (i = 0; filter_out[i]; i++)
1013 data_node_hash_del(res, filter_out[i]);
1014
1015 /* Normalize the result */
1016 for (i = 0; implies[i].flag; i++) {
1017 if (data_node_hash_get(res, implies[i].flag)) {
1018 for (j = 0; implies[i].implies[j]; j++) {
1019 if (data_node_hash_get(res, implies[i].implies[j]))
1020 fprintf(stderr, "%s: useless tag: %s\n",
1021 argv[optind], implies[i].implies[j]);
1022 }
1023 }
1024 }
1025
1026 /* Normalize types */
1027 check_normalize_types(res);
1028
1029 for (i = 0; implies[i].flag; i++) {
1030 if (data_node_hash_get(res, implies[i].flag)) {
1031 for (j = 0; implies[i].implies[j]; j++) {
1032 if (!data_node_hash_get(res, implies[i].implies[j]))
1033 data_node_hash_add(res, implies[i].implies[j],
1034 data_node_string("1"));
1035 }
1036 }
1037 }
1038
1039 data_node_hash_add(res, "fname", data_node_string(argv[optind]));
1040 printf(" \"%s\": ", strip_name(argv[optind]));
1041 data_to_json(res, stdout, 2);
1042 data_node_free(res);
1043
1044 return 0;
1045 }
1046