/* radare - LGPL - Copyright 2013-2017 - pancake */

#include "r_util.h"
#include "r_types.h"
#include "r_parse.h"
#include "libr_tcc.h"

/* parse C code and return it in key-value form */

static void appendstring(const char *msg, char **s) {
	if (!s) {
		printf ("%s\n", msg);
	} else if (*s) {
		char *p = malloc (strlen (msg) + strlen (*s) + 1);
		if (!p) return;
		strcpy (p, *s);
		free (*s);
		*s = p;
		strcpy (p + strlen (p), msg);
	} else {
		*s = strdup (msg);
	}
}

static int typeload(void *p, const char *k, const char *v) {
	if (!p) {
		return -1;
	}
	int btype = 0;
	RAnal *anal = (RAnal*)p;
	//r_cons_printf ("tk %s=%s\n", k, v);
	// TODO: Add unions support
	if (!strncmp (v, "struct", 6) && strncmp(k, "struct.", 7)) {
		// structure
		btype = VT_STRUCT;
		const char *typename = k;
		int typesize = 0;
		// TODO: Add typesize here
		char* query = sdb_fmt (-1, "struct.%s", k);
		char *members = sdb_get (anal->sdb_types, query, 0);
		char *next, *ptr = members;
		if (members) {
			do {
				char *name = sdb_anext (ptr, &next);
				if (!name) {
					break;
				}
				query = sdb_fmt (-1, "struct.%s.%s", k, name);
				char *subtype = sdb_get (anal->sdb_types, query, 0);
				if (!subtype) {
					break;
				}
				char *tmp = strchr (subtype, ',');
				if (tmp) {
					*tmp++ = 0;
					tmp = strchr (tmp, ',');
					if (tmp) {
						*tmp++ = 0;
					}
					char *subname = tmp;
					// TODO: Go recurse here
					query = sdb_fmt (-1, "struct.%s.%s.meta", subtype, subname);
					btype = sdb_num_get (anal->sdb_types, query, 0);
					tcc_sym_push (subtype, 0, btype);
				}
				free (subtype);
				ptr = next;
			} while (next);
			free (members);
		}
		tcc_sym_push ((char *)typename, typesize, btype);
	}
	return 0;
}

R_API char *r_parse_c_file(RAnal *anal, const char *path) {
	char *str = NULL;
	TCCState *T = tcc_new (anal->cpu, anal->bits, anal->os);
	if (!T) return NULL;
	tcc_set_callback (T, &appendstring, &str);
	sdb_foreach (anal->sdb_types, typeload, anal);
	if (tcc_add_file (T, path) == -1) {
		free (str);
		str = NULL;
	}
	tcc_delete (T);
	return str;
}

R_API char *r_parse_c_string(RAnal *anal, const char *code) {
	char *str = NULL;
	TCCState *T = tcc_new (anal->cpu, anal->bits, anal->os);
	if (!T) return NULL;
	tcc_set_callback (T, &appendstring, &str);
	sdb_foreach (anal->sdb_types, typeload, NULL);
	tcc_compile_string (T, code);
	tcc_delete (T);
	return str;
}

R_API int r_parse_is_c_file (const char *file) {
	const char *ext = r_str_lchr (file, '.');
	if (ext) {
		ext = ext + 1;
		if (!strcmp (ext, "cparse")
		||  !strcmp (ext, "c")
		||  !strcmp (ext, "h")) {
			return true;
		}
	}
	return false;
}
