1 /* Code to move a ccan module into the ccan_ namespace. */
11 #include <sys/types.h>
13 #include "talloc/talloc.h"
15 #define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. -Iccan_tools/libtap/src/"
16 #define CFLAGS_HDR "-Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Werror -I."
18 static bool verbose = false;
19 static int indent = 0;
20 #define verbose(args...) \
23 for (_i = 0; _i < indent; _i++) printf(" "); \
27 #define verbose_indent() (indent += 2)
28 #define verbose_unindent() (indent -= 2)
30 #define streq(a,b) (strcmp((a),(b)) == 0)
32 #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0)
34 static inline bool strends(const char *str, const char *postfix)
36 if (strlen(str) < strlen(postfix))
39 return streq(str + strlen(str) - strlen(postfix), postfix);
42 static int close_no_errno(int fd)
44 int ret = 0, serrno = errno;
51 static int unlink_no_errno(const char *filename)
53 int ret = 0, serrno = errno;
54 if (unlink(filename) < 0)
60 static void *grab_fd(const void *ctx, int fd)
63 unsigned int max = 16384, size = 0;
66 buffer = talloc_array(ctx, char, max+1);
67 while ((ret = read(fd, buffer + size, max - size)) > 0) {
70 buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1);
81 /* This version adds one byte (for nul term) */
82 static void *grab_file(const void *ctx, const char *filename)
87 if (streq(filename, "-"))
88 fd = dup(STDIN_FILENO);
90 fd = open(filename, O_RDONLY, 0);
95 buffer = grab_fd(ctx, fd);
100 /* This is a dumb one which copies. We could mangle instead. */
101 static char **split(const void *ctx, const char *text, const char *delims,
105 unsigned int max = 64, num = 0;
107 lines = talloc_array(ctx, char *, max+1);
109 while (*text != '\0') {
110 unsigned int len = strcspn(text, delims);
111 lines[num] = talloc_array(lines, char, len + 1);
112 memcpy(lines[num], text, len);
113 lines[num][len] = '\0';
115 text += strspn(text, delims);
117 lines = talloc_realloc(ctx, lines, char *, max*=2 + 1);
125 static char **get_dir(const char *dir)
130 unsigned int size = 0;
136 while ((ent = readdir(d)) != NULL) {
137 names = talloc_realloc(dir, names, char *, size + 2);
139 = talloc_asprintf(names, "%s/%s", dir, ent->d_name);
141 names[size++] = NULL;
146 static char ** __attribute__((format(printf, 2, 3)))
147 lines_from_cmd(const void *ctx, char *format, ...)
153 va_start(ap, format);
154 cmd = talloc_vasprintf(ctx, format, ap);
159 err(1, "Executing '%s'", cmd);
161 buffer = grab_fd(ctx, fileno(p));
163 err(1, "Reading from '%s'", cmd);
166 return split(ctx, buffer, "\n", NULL);
169 static char *build_obj(const char *cfile)
172 char *ofile = talloc_strdup(cfile, cfile);
174 ofile[strlen(ofile)-1] = 'c';
176 cmd = talloc_asprintf(ofile, "gcc " CFLAGS " -o %s -c %s",
178 if (system(cmd) != 0)
179 errx(1, "Failed to compile %s", cfile);
185 struct replace *next;
189 static void __attribute__((noreturn)) usage(void)
192 "namespacize [--verbose] <dir>\n"
193 "namespacize [--verbose] --adjust <dir>...\n"
194 "The first form converts dir/ to insert 'ccan_' prefixes, and\n"
195 "then adjusts any other ccan directories at the same level which\n"
197 "--adjust does an adjustment for each directory, in case a\n"
198 "dependency has been namespacized\n");
201 static void add_replace(struct replace **repl, const char *str)
205 /* Don't replace things already CCAN-ized (eg. idempotent wrappers) */
206 if (strstarts(str, "CCAN_") || strstarts(str, "ccan_"))
209 new = talloc(*repl, struct replace);
211 new->string = talloc_strdup(new, str);
215 static char *basename(const void *ctx, const char *dir)
217 char *p = strrchr(dir, '/');
221 return talloc_strdup(ctx, p+1);
224 /* FIXME: Only does main header, should chase local includes. */
225 static void analyze_headers(const char *dir, struct replace **repl)
227 char *hdr, *contents, *p;
228 enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART;
230 /* Get hold of header, assume that's it. */
231 hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
232 contents = grab_file(dir, hdr);
234 err(1, "Reading %s", hdr);
236 verbose("Looking in %s\n", hdr);
238 /* Look for lines of form #define X */
239 for (p = contents; *p; p++) {
242 else if (!isspace(*p)) {
243 if (state == LINESTART && *p == '#')
245 else if (state==HASH && !strncmp(p, "define", 6)) {
248 } else if (state == DEFINE) {
251 len = strspn(p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
252 "abcdefghijklmnopqrstuvwxyz"
256 s = talloc_strndup(contents, p, len);
257 verbose("Found %s\n", s);
258 add_replace(repl, s);
268 static void add_extern_symbols(const char *ofile, struct replace **repl)
270 /* Should actually read the elf: this is a hack. */
273 line = lines_from_cmd(ofile, "nm --defined-only --extern %s", ofile);
275 /* nm output is of form [hexaddr] [char] [name]\n */
276 for (; *line; line++) {
278 char **names = split(ofile, *line, " \t", &cols);
280 errx(1, "Unexpected nm line '%s' (%i cols)", *line, cols);
282 verbose("Found %s\n", names[2]);
283 add_replace(repl, names[2]);
287 static void get_header_symbols(const char *dir, struct replace **repl)
290 char *hfile = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
291 char *ofile = talloc_asprintf(dir, "%s.o", hfile);
293 /* Horrible hack to get static inlines. */
294 cmd = talloc_asprintf(dir, "gcc " CFLAGS_HDR
295 " -Dstatic= -include %s -o %s -c -x c /dev/null",
297 if (system(cmd) != 0)
298 errx(1, "Failed to compile %s", hfile);
300 add_extern_symbols(ofile, repl);
303 /* FIXME: Better to analyse headers in more depth, rather than recompile. */
304 static void get_exposed_symbols(const char *dir, struct replace **repl)
309 files = get_dir(dir);
310 for (i = 0; files[i]; i++) {
312 if (!strends(files[i], ".c") || strends(files[i], "/_info.c"))
315 /* This produces file.c -> file.o */
316 ofile = build_obj(files[i]);
317 verbose("Looking in %s\n", ofile);
319 add_extern_symbols(ofile, repl);
323 get_header_symbols(dir, repl);
326 static void write_replacement_file(const char *dir, struct replace **repl)
328 char *replname = talloc_asprintf(dir, "%s/.namespacize", dir);
332 fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644);
335 errx(1, "%s already exists: can't namespacize twice",
337 err(1, "Opening %s", replname);
340 for (r = *repl; r; r = r->next) {
341 if (write(fd,r->string,strlen(r->string)) != strlen(r->string)
342 || write(fd, "\n", 1) != 1) {
343 unlink_no_errno(replname);
345 errx(1, "Short write to %s: disk full?",
347 errx(1, "Writing to %s", replname);
354 static int unlink_destroy(char *name)
360 static char *find_word(char *f, const char *str)
364 while ((p = strstr(p, str)) != NULL) {
365 /* Check it's not in the middle of a word. */
366 if (p > f && (isalnum(p[-1]) || p[-1] == '_')) {
370 if (isalnum(p[strlen(str)]) || p[strlen(str)] == '_') {
379 /* This is horribly inefficient but simple. */
380 static const char *rewrite_file(const char *filename,
381 const struct replace *repl)
383 char *newname, *file;
386 verbose("Rewriting %s\n", filename);
387 file = grab_file(filename, filename);
389 err(1, "Reading file %s", filename);
391 for (; repl; repl = repl->next) {
394 while ((p = find_word(file, repl->string)) != NULL) {
396 char *new = talloc_array(file, char, strlen(file)+6);
399 memcpy(new, file, off);
400 if (isupper(repl->string[0]))
401 memcpy(new + off, "CCAN_", 5);
403 memcpy(new + off, "ccan_", 5);
404 strcpy(new + off + 5, file + off);
409 /* If we exit for some reason, we want this erased. */
410 newname = talloc_asprintf(talloc_autofree_context(), "%s.tmp",
412 fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644);
414 err(1, "Creating %s", newname);
416 talloc_set_destructor(newname, unlink_destroy);
417 if (write(fd, file, strlen(file)) != strlen(file)) {
419 errx(1, "Short write to %s: disk full?", newname);
420 errx(1, "Writing to %s", newname);
428 struct adjusted *next;
433 static void setup_adjust_files(const char *dir,
434 const struct replace *repl,
435 struct adjusted **adj)
439 for (files = get_dir(dir); *files; files++) {
440 if (strends(*files, "/test"))
441 setup_adjust_files(*files, repl, adj);
442 else if (strends(*files, ".c") || strends(*files, ".h")) {
443 struct adjusted *a = talloc(dir, struct adjusted);
446 a->tmpfile = rewrite_file(a->file, repl);
452 /* This is the "commit" stage, so we hope it won't fail. */
453 static void rename_files(const struct adjusted *adj)
456 if (rename(adj->tmpfile, adj->file) != 0)
457 warn("Could not rename over '%s', we're in trouble",
463 static void convert_dir(const char *dir)
466 struct replace *replace = NULL;
467 struct adjusted *adj = NULL;
469 /* Remove any ugly trailing slashes. */
470 name = talloc_strdup(NULL, dir);
471 while (strends(name, "/"))
472 name[strlen(name)-1] = '\0';
474 analyze_headers(name, &replace);
475 get_exposed_symbols(name, &replace);
476 write_replacement_file(name, &replace);
477 setup_adjust_files(name, replace, &adj);
480 talloc_free(replace);
483 static struct replace *read_replacement_file(const char *depdir)
485 struct replace *repl = NULL;
486 char *replname = talloc_asprintf(depdir, "%s/.namespacize", depdir);
489 file = grab_file(replname, replname);
492 err(1, "Opening %s", replname);
496 for (line = split(file, file, "\n", NULL); *line; line++)
497 add_replace(&repl, *line);
501 static char *build_info(const void *ctx, const char *dir)
503 char *file, *cfile, *cmd;
505 cfile = talloc_asprintf(ctx, "%s/%s", dir, "_info.c");
506 file = talloc_asprintf(cfile, "%s/%s", dir, "_info");
507 cmd = talloc_asprintf(file, "gcc " CFLAGS " -o %s %s", file, cfile);
508 if (system(cmd) != 0)
509 errx(1, "Failed to compile %s", file);
514 static char **get_deps(const void *ctx, const char *dir)
518 cmd = talloc_asprintf(ctx, "%s depends", build_info(ctx, dir));
519 deps = lines_from_cmd(cmd, cmd);
521 err(1, "Could not run '%s'", cmd);
525 static char *parent_dir(const void *ctx, const char *dir)
527 char *parent, *slash;
529 parent = talloc_strdup(ctx, dir);
530 slash = strrchr(parent, '/');
534 parent = talloc_strdup(ctx, ".");
538 static void adjust_dir(const char *dir)
540 char *parent = parent_dir(NULL, dir);
543 verbose("Adjusting %s\n", dir);
545 for (deps = get_deps(parent, dir); *deps; deps++) {
547 struct adjusted *adj = NULL;
548 struct replace *repl;
550 depdir = talloc_asprintf(parent, "%s/%s", parent, *deps);
551 repl = read_replacement_file(depdir);
553 verbose("%s has been namespacized\n", depdir);
554 setup_adjust_files(parent, repl, &adj);
557 verbose("%s has not been namespacized\n", depdir);
563 static void adjust_dependents(const char *dir)
565 char *parent = parent_dir(NULL, dir);
566 char *base = basename(parent, dir);
569 verbose("Looking for dependents in %s\n", parent);
571 for (file = get_dir(parent); *file; file++) {
575 if (basename(*file, *file)[0] == '.')
578 infoc = talloc_asprintf(*file, "%s/_info.c", *file);
579 if (access(infoc, R_OK) != 0)
582 for (deps = get_deps(*file, *file); *deps; deps++) {
583 if (streq(*deps, base))
589 verbose("%s is not dependent\n", *file);
594 int main(int argc, char *argv[])
596 if (argv[1] && streq(argv[1], "--verbose")) {
603 verbose("Namespacizing %s\n", argv[1]);
605 convert_dir(argv[1]);
606 adjust_dependents(argv[1]);
611 if (argc > 2 && streq(argv[1], "--adjust")) {
614 for (i = 2; i < argc; i++)