Browse Source

Avoid hacky recompilation of files to namespacize, use hacky parser
instead!

Rusty Russell 18 years ago
parent
commit
f7315b84a7
1 changed files with 150 additions and 78 deletions
  1. 150 78
      ccan_tools/namespacize.c

+ 150 - 78
ccan_tools/namespacize.c

@@ -13,7 +13,10 @@
 #include "talloc/talloc.h"
 
 #define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. -Iccan_tools/libtap/src/"
-#define CFLAGS_HDR "-Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Werror -I."
+
+#define IDENT_CHARS	"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+			"abcdefghijklmnopqrstuvwxyz" \
+			"01234567889_"
 
 static bool verbose = false;
 static int indent = 0;
@@ -166,20 +169,6 @@ lines_from_cmd(const void *ctx, char *format, ...)
 	return split(ctx, buffer, "\n", NULL);
 }
 
-static char *build_obj(const char *cfile)
-{
-	char *cmd;
-	char *ofile = talloc_strdup(cfile, cfile);
-
-	ofile[strlen(ofile)-1] = 'c';
-
-	cmd = talloc_asprintf(ofile, "gcc " CFLAGS " -o %s -c %s",
-			      ofile, cfile);
-	if (system(cmd) != 0)
-		errx(1, "Failed to compile %s", cfile);
-	return ofile;
-}
-
 struct replace
 {
 	struct replace *next;
@@ -200,11 +189,12 @@ static void __attribute__((noreturn)) usage(void)
 
 static void add_replace(struct replace **repl, const char *str)
 {
-	struct replace *new;
+	struct replace *new, *i;
 
-	/* Don't replace things already CCAN-ized (eg. idempotent wrappers) */
-	if (strstarts(str, "CCAN_") || strstarts(str, "ccan_"))
-		return;
+	/* Avoid duplicates. */
+	for (i = *repl; i; i = i->next)
+		if (streq(i->string, str))
+			return;
 
 	new = talloc(*repl, struct replace);
 	new->next = *repl;
@@ -212,6 +202,17 @@ static void add_replace(struct replace **repl, const char *str)
 	*repl = new;
 }
 
+static void add_replace_tok(struct replace **repl, const char *s)
+{
+	struct replace *new;
+	unsigned int len = strspn(s, IDENT_CHARS);
+
+	new = talloc(*repl, struct replace);
+	new->next = *repl;
+	new->string = talloc_strndup(new, s, len);
+	*repl = new;
+}
+
 static char *basename(const void *ctx, const char *dir)
 {
 	char *p = strrchr(dir, '/');
@@ -221,20 +222,11 @@ static char *basename(const void *ctx, const char *dir)
 	return talloc_strdup(ctx, p+1);
 }
 
-/* FIXME: Only does main header, should chase local includes. */ 
-static void analyze_headers(const char *dir, struct replace **repl)
+static void look_for_macros(char *contents, struct replace **repl)
 {
-	char *hdr, *contents, *p;
+	char *p;
 	enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART;
 
-	/* Get hold of header, assume that's it. */
-	hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
-	contents = grab_file(dir, hdr);
-	if (!contents)
-		err(1, "Reading %s", hdr);
-
-	verbose("Looking in %s\n", hdr);
-	verbose_indent();
 	/* Look for lines of form #define X */
 	for (p = contents; *p; p++) {
 		if (*p == '\n')
@@ -248,79 +240,160 @@ static void analyze_headers(const char *dir, struct replace **repl)
 			} else if (state == DEFINE) {
 				unsigned int len;
 
-				len = strspn(p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-					     "abcdefghijklmnopqrstuvwxyz"
-					     "01234567889_");
+				len = strspn(p, IDENT_CHARS);
 				if (len) {
 					char *s;
 					s = talloc_strndup(contents, p, len);
-					verbose("Found %s\n", s);
-					add_replace(repl, s);
+					/* Don't wrap idempotent wrappers */
+					if (!strstarts(s, "CCAN_")) {
+						verbose("Found %s\n", s);
+						add_replace(repl, s);
+					}
 				}
 				state = NONE;
 			} else
 				state = NONE;
 		}
 	}
-	verbose_unindent();
 }
 
-static void add_extern_symbols(const char *ofile, struct replace **repl)
+/* Blank out preprocessor lines, and eliminate \ */
+static void preprocess(char *p)
 {
-	/* Should actually read the elf: this is a hack. */
-	char **line;
+	char *s;
 
-	line = lines_from_cmd(ofile, "nm --defined-only --extern %s", ofile);
+	/* We assume backslashes are only used for macros. */
+	while ((s = strstr(p, "\\\n")) != NULL)
+		s[0] = s[1] = ' ';
 
-	/* nm output is of form [hexaddr] [char] [name]\n */
-	for (; *line; line++) {
-		unsigned int cols;
-		char **names = split(ofile, *line, " \t", &cols);
-		if (cols != 3)
-			errx(1, "Unexpected nm line '%s' (%i cols)", *line, cols);
+	/* Now eliminate # lines. */
+	if (p[0] == '#') {
+		unsigned int i;
+		for (i = 0; p[i] != '\n'; i++)
+			p[i] = ' ';
+	}
+	while ((s = strstr(p, "\n#")) != NULL) {
+		unsigned int i;
+		for (i = 1; s[i] != '\n'; i++)
+			s[i] = ' ';
+	}
+}
 
-		verbose("Found %s\n", names[2]);
-		add_replace(repl, names[2]);
+static char *get_statement(const void *ctx, char **p)
+{
+	unsigned brackets = 0;
+	bool seen_brackets = false;
+	char *answer = talloc_strdup(ctx, "");
+
+	for (;;) {
+		if ((*p)[0] == '/' && (*p)[1] == '/')
+			*p += strcspn(*p, "\n");
+		else if ((*p)[0] == '/' && (*p)[1] == '*')
+			*p = strstr(*p, "*/") + 1;
+		else {
+			char c = **p;
+			if (c == ';' && !brackets) {
+				(*p)++;
+				return answer;
+			}
+			/* Compress whitespace into a single ' ' */
+			if (isspace(c)) {
+				c = ' ';
+				while (isspace((*p)[1]))
+					(*p)++;
+			} else if (c == '{' || c == '(' || c == '[') {
+				if (c == '(')
+					seen_brackets = true;
+				brackets++;
+			} else if (c == '}' || c == ')' || c == ']')
+				brackets--;
+
+			if (answer[0] != '\0' || c != ' ') {
+				answer = talloc_realloc(NULL, answer, char,
+							strlen(answer) + 2);
+				answer[strlen(answer)+1] = '\0';
+				answer[strlen(answer)] = c;
+			}
+			if (c == '}' && seen_brackets && brackets == 0) {
+				(*p)++;
+				return answer;
+			}
+		}
+		(*p)++;
+		if (**p == '\0')
+			return NULL;
 	}
 }
 
-static void get_header_symbols(const char *dir, struct replace **repl)
+/* This hack should handle well-formatted code. */
+static void look_for_definitions(char *contents, struct replace **repl)
 {
-	char *cmd;
-	char *hfile = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
-	char *ofile = talloc_asprintf(dir, "%s.o", hfile);
+	char *stmt, *p = contents;
 
-	/* Horrible hack to get static inlines. */
-	cmd = talloc_asprintf(dir, "gcc " CFLAGS_HDR
-			      " -Dstatic= -include %s -o %s -c -x c /dev/null",
-			      hfile, ofile);
-	if (system(cmd) != 0)
-		errx(1, "Failed to compile %s", hfile);
+	preprocess(contents);
 
-	add_extern_symbols(ofile, repl);
-}
+	while ((stmt = get_statement(contents, &p)) != NULL) {
+		int i, len;
 
-/* FIXME: Better to analyse headers in more depth, rather than recompile. */
-static void get_exposed_symbols(const char *dir, struct replace **repl)
-{
-	char **files;
-	unsigned int i;
+		/* Definition of struct/union? */
+		if ((strncmp(stmt, "struct", 5) == 0
+		     || strncmp(stmt, "union", 5) == 0)
+		    && strchr(stmt, '{') && stmt[7] != '{')
+			add_replace_tok(repl, stmt+7);
 
-	files = get_dir(dir);
-	for (i = 0; files[i]; i++) {
-		char *ofile;
-		if (!strends(files[i], ".c") || strends(files[i], "/_info.c"))
+		/* Definition of var or typedef? */
+		for (i = strlen(stmt)-1; i >= 0; i--)
+			if (strspn(stmt+i, IDENT_CHARS) == 0)
+				break;
+
+		if (i != strlen(stmt)-1) {
+			add_replace_tok(repl, stmt+i+1);
 			continue;
+		}
 
-		/* This produces file.c -> file.o */
-		ofile = build_obj(files[i]);
-		verbose("Looking in %s\n", ofile);
-		verbose_indent();
-		add_extern_symbols(ofile, repl);
-		unlink(ofile);
-		verbose_unindent();
+		/* function or array declaration? */
+		len = strspn(stmt, IDENT_CHARS "* ");
+		if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) {
+			if (strspn(stmt + len + 1, IDENT_CHARS) != 0) {
+				for (i = len-1; i >= 0; i--)
+					if (strspn(stmt+i, IDENT_CHARS) == 0)
+						break;
+				if (i != len-1) {
+					add_replace_tok(repl, stmt+i+1);
+					continue;
+				}
+			} else {
+				/* Pointer to function? */
+				len++;
+				len += strspn(stmt + len, " *");
+				i = strspn(stmt + len, IDENT_CHARS);
+				if (i > 0 && stmt[len + i] == ')')
+					add_replace_tok(repl, stmt+len);
+			}
+		}
 	}
-	get_header_symbols(dir, repl);
+}
+
+/* FIXME: Only does main header, should chase local includes. */ 
+static void analyze_headers(const char *dir, struct replace **repl)
+{
+	char *hdr, *contents;
+
+	/* Get hold of header, assume that's it. */
+	hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
+	contents = grab_file(dir, hdr);
+	if (!contents)
+		err(1, "Reading %s", hdr);
+
+	verbose("Looking in %s for macros\n", hdr);
+	verbose_indent();
+	look_for_macros(contents, repl);
+	verbose_unindent();
+
+	verbose("Looking in %s for symbols\n", hdr);
+	verbose_indent();
+	look_for_definitions(contents, repl);
+	verbose_unindent();
 }
 
 static void write_replacement_file(const char *dir, struct replace **repl)
@@ -472,7 +545,6 @@ static void convert_dir(const char *dir)
 		name[strlen(name)-1] = '\0';
 
 	analyze_headers(name, &replace);
-	get_exposed_symbols(name, &replace);
 	write_replacement_file(name, &replace);
 	setup_adjust_files(name, replace, &adj);
 	rename_files(adj);