[LTP] [PATCH v2 1/7] docparse: Implement #define and #include

Richard Palethorpe rpalethorpe@suse.de
Tue Nov 2 11:05:51 CET 2021


Cyril Hrubis <chrubis@suse.cz> writes:

> We ignore most of the include statements and we attempt to parse only
> header files that reside in the same directory as the test source code,
> that is since we are not interested in any system or library headers as
> we are only looking for constants used in the tst_test structure that
> are always either directly in the test source or in header in the same
> directory.
>
> The macro support is very simple as well, it's a single pass as we are
> not interested in intricate macros. We just need values for constants
> that are used in the tst_test structure intializations.
>
> + Also add -v verbose mode that prints included files and defined macros
>
> Signed-off-by: Cyril Hrubis <chrubis@suse.cz>

I don't see any issues that are likely to cause trouble
immediately. However please check the comments below to ensure they are
out-of-scope.

Reviewed-by: rpalethorpe@suse.com

> ---
>  docparse/docparse.c | 234 ++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 224 insertions(+), 10 deletions(-)
>
> diff --git a/docparse/docparse.c b/docparse/docparse.c
> index 8cd0d0eef..64f9d08d9 100644
> --- a/docparse/docparse.c
> +++ b/docparse/docparse.c
> @@ -1,9 +1,12 @@
>  // SPDX-License-Identifier: GPL-2.0-or-later
>  /*
> - * Copyright (c) 2019 Cyril Hrubis <chrubis@suse.cz>
> + * Copyright (c) 2019-2021 Cyril Hrubis <chrubis@suse.cz>
>   * Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
>   */
>  
> +#define _GNU_SOURCE
> +
> +#include <search.h>
>  #include <stdio.h>
>  #include <string.h>
>  #include <libgen.h>
> @@ -12,6 +15,9 @@
>  
>  #include "data_storage.h"
>  
> +static int verbose;
> +static char *includepath;
> +
>  #define WARN(str) fprintf(stderr, "WARNING: " str "\n")
>  
>  static void oneline_comment(FILE *f)
> @@ -126,7 +132,7 @@ static void maybe_comment(FILE *f, struct data_node *doc)
>  	}
>  }
>  
> -const char *next_token(FILE *f, struct data_node *doc)
> +static char *next_token(FILE *f, struct data_node *doc)
>  {
>  	size_t i = 0;
>  	static char buf[4096];
> @@ -159,6 +165,7 @@ const char *next_token(FILE *f, struct data_node *doc)
>  		case ',':
>  		case '[':
>  		case ']':
> +		case '#':
>  			if (i) {
>  				ungetc(c, f);
>  				goto exit;
> @@ -197,6 +204,46 @@ exit:
>  	return buf;
>  }
>  
> +static FILE *open_include(const char *includepath, FILE *f)
> +{
> +	char buf[256];
> +	char *path;
> +	FILE *inc;
> +
> +	if (!fscanf(f, "%s\n", buf))
> +		return NULL;
> +
> +	if (buf[0] != '"')
> +		return NULL;
> +
> +	char *filename = buf + 1;
> +
> +	if (!buf[0])
> +		return NULL;
> +
> +	filename[strlen(filename)-1] = 0;
> +
> +	if (asprintf(&path, "%s/%s", includepath, filename) < 0)
> +		return NULL;
> +
> +	inc = fopen(path, "r");
> +
> +	if (inc && verbose)
> +		fprintf(stderr, "INCLUDE %s\n", path);
> +
> +	free(path);
> +
> +	return inc;
> +}
> +
> +static void close_include(FILE *inc)
> +{
> +	if (verbose)
> +		fprintf(stderr, "INCLUDE END\n");
> +
> +	fclose(inc);
> +}
> +
>  static int parse_array(FILE *f, struct data_node *node)
>  {
>  	const char *token;
> @@ -234,9 +281,28 @@ static int parse_array(FILE *f, struct data_node *node)
>  	return 0;
>  }
>  
> +static void try_apply_macro(char **res)
> +{
> +	ENTRY macro = {
> +		.key = *res,
> +	};
> +
> +	ENTRY *ret;
> +
> +	ret = hsearch(macro, FIND);
> +
> +	if (!ret)
> +		return;
> +
> +	if (verbose)
> +		fprintf(stderr, "APPLYING MACRO %s=%s\n", ret->key, (char*)ret->data);
> +
> +	*res = ret->data;
> +}
> +
>  static int parse_test_struct(FILE *f, struct data_node *doc, struct data_node *node)
>  {
> -	const char *token;
> +	char *token;
>  	char *id = NULL;
>  	int state = 0;
>  	struct data_node *ret;
> @@ -280,6 +346,7 @@ static int parse_test_struct(FILE *f, struct data_node *doc, struct data_node *n
>  			ret = data_node_array();
>  			parse_array(f, ret);
>  		} else {
> +			try_apply_macro(&token);
>  			ret = data_node_string(token);
>  		}
>  
> @@ -302,6 +369,114 @@ static const char *tokens[] = {
>  	"{",
>  };
>  
> +static void macro_get_string(FILE *f, char *buf, char *buf_end)
> +{
> +	int c;
> +
> +	for (;;) {
> +		c = fgetc(f);
> +
> +		switch (c) {
> +		case '"':

Luckily there are no instances of '#define MACRO "...\"...\"..."' in LTP
AFAICT. Also there don't appear to be any '#define MACRO "..." \\n' that
we would care about.

> +		case EOF:
> +			*buf = 0;
> +			return;
> +		default:
> +			if (buf < buf_end)
> +				*(buf++) = c;
> +		}
> +	}
> +}
> +
> +static void macro_get_val(FILE *f, char *buf, size_t buf_len)
> +{
> +	int c, prev = 0;
> +	char *buf_end = buf + buf_len - 1;
> +
> +	c = fgetc(f);
> +	if (c == '"') {

I guess this could be whitespace unless scanf slurps any trailing
whitespace?

Again no actual instances of this AFAICT.

> +		macro_get_string(f, buf, buf_end);
> +		return;
> +	}
> +
> +	for (;;) {
> +		switch (c) {
> +		case '\n':
> +			if (prev == '\\') {
> +				buf--;
> +			} else {
> +				*buf = 0;
> +				return;
> +			}
> +		break;
> +		case EOF:
> +			*buf = 0;
> +			return;
> +		case ' ':
> +		case '\t':
> +		break;
> +		default:
> +			if (buf < buf_end)
> +				*(buf++) = c;
> +		}
> +
> +		prev = c;
> +		c = fgetc(f);
> +	}
> +}
> +
> +static void parse_macro(FILE *f)
> +{
> +	char name[128];
> +	char val[256];
> +
> +	if (!fscanf(f, "%s[^\n]", name))
> +		return;
> +
> +	if (fgetc(f) == '\n')
> +		return;
> +
> +	macro_get_val(f, val, sizeof(val));
> +
> +	ENTRY e = {
> +		.key = strdup(name),
> +		.data = strdup(val),
> +	};
> +
> +	if (verbose)
> +		fprintf(stderr, " MACRO %s=%s\n", e.key, (char*)e.data);
> +
> +	hsearch(e, ENTER);
> +}
> +
> +static void parse_include_macros(FILE *f)
> +{
> +	FILE *inc;
> +	const char *token;
> +	int hash = 0;
> +
> +	inc = open_include(includepath, f);
> +	if (!inc)
> +		return;
> +
> +	while ((token = next_token(inc, NULL))) {
> +		if (token[0] == '#') {
> +			hash = 1;
> +			continue;
> +		}
> +
> +		if (!hash)
> +			continue;
> +
> +		if (!strcmp(token, "define"))
> +			parse_macro(inc);
> +
> +		hash = 0;
> +	}
> +
> +	close_include(inc);
> +}
> +
>  static struct data_node *parse_file(const char *fname)
>  {
>  	int state = 0, found = 0;
> @@ -314,14 +489,28 @@ static struct data_node *parse_file(const char *fname)
>  
>  	FILE *f = fopen(fname, "r");
>  
> +	includepath = dirname(strdup(fname));
> +
>  	struct data_node *res = data_node_hash();
>  	struct data_node *doc = data_node_array();
>  
>  	while ((token = next_token(f, doc))) {
> -		if (state < 6 && !strcmp(tokens[state], token))
> +		if (state < 6 && !strcmp(tokens[state], token)) {
>  			state++;
> -		else
> +		} else {
> +			if (token[0] == '#') {
> +				token = next_token(f, doc);
> +				if (token) {
> +					if (!strcmp(token, "define"))
> +						parse_macro(f);
> +
> +					if (!strcmp(token, "include"))
> +						parse_include_macros(f);
> +				}
> +			}
> +
>  			state = 0;
> +		}
>  
>  		if (state < 6)
>  			continue;
> @@ -386,17 +575,42 @@ const char *strip_name(char *path)
>  	return name;
>  }
>  
> +static void print_help(const char *prgname)
> +{
> +	printf("usage: %s [-vh] input.c\n\n", prgname);
> +	printf("-v sets verbose mode\n");
> +	printf("-h prints this help\n\n");
> +	exit(0);
> +}
> +
>  int main(int argc, char *argv[])
>  {
>  	unsigned int i, j;
>  	struct data_node *res;
> +	int opt;
> +
> +	while ((opt = getopt(argc, argv, "hv")) != -1) {
> +		switch (opt) {
> +		case 'h':
> +			print_help(argv[0]);
> +		break;
> +		case 'v':
> +			verbose = 1;
> +		break;
> +		}
> +	}
> +
> +	if (optind >= argc) {
> +		fprintf(stderr, "No input filename.c\n");
> +		return 1;
> +	}
>  
> -	if (argc != 2) {
> -		fprintf(stderr, "Usage: docparse filename.c\n");
> +	if (!hcreate(128)) {
> +		fprintf(stderr, "Failed to initialize hash table\n");
>  		return 1;
>  	}
>  
> -	res = parse_file(argv[1]);
> +	res = parse_file(argv[optind]);
>  	if (!res)
>  		return 0;
>  
> @@ -425,8 +639,8 @@ int main(int argc, char *argv[])
>  		}
>  	}
>  
> -	data_node_hash_add(res, "fname", data_node_string(argv[1]));
> -	printf("  \"%s\": ", strip_name(argv[1]));
> +	data_node_hash_add(res, "fname", data_node_string(argv[optind]));
> +	printf("  \"%s\": ", strip_name(argv[optind]));
>  	data_to_json(res, stdout, 2);
>  	data_node_free(res);
>  
> -- 
> 2.32.0


-- 
Thank you,
Richard.


More information about the ltp mailing list