• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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