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/"
17 #define IDENT_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
18 "abcdefghijklmnopqrstuvwxyz" \
21 static bool verbose = false;
22 static int indent = 0;
23 #define verbose(args...) \
26 for (_i = 0; _i < indent; _i++) printf(" "); \
30 #define verbose_indent() (indent += 2)
31 #define verbose_unindent() (indent -= 2)
33 #define streq(a,b) (strcmp((a),(b)) == 0)
35 #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0)
37 static inline bool strends(const char *str, const char *postfix)
39 if (strlen(str) < strlen(postfix))
42 return streq(str + strlen(str) - strlen(postfix), postfix);
45 static int close_no_errno(int fd)
47 int ret = 0, serrno = errno;
54 static int unlink_no_errno(const char *filename)
56 int ret = 0, serrno = errno;
57 if (unlink(filename) < 0)
63 static void *grab_fd(const void *ctx, int fd)
66 unsigned int max = 16384, size = 0;
69 buffer = talloc_array(ctx, char, max+1);
70 while ((ret = read(fd, buffer + size, max - size)) > 0) {
73 buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1);
84 /* This version adds one byte (for nul term) */
85 static void *grab_file(const void *ctx, const char *filename)
90 if (streq(filename, "-"))
91 fd = dup(STDIN_FILENO);
93 fd = open(filename, O_RDONLY, 0);
98 buffer = grab_fd(ctx, fd);
103 /* This is a dumb one which copies. We could mangle instead. */
104 static char **split(const void *ctx, const char *text, const char *delims,
108 unsigned int max = 64, num = 0;
110 lines = talloc_array(ctx, char *, max+1);
112 while (*text != '\0') {
113 unsigned int len = strcspn(text, delims);
114 lines[num] = talloc_array(lines, char, len + 1);
115 memcpy(lines[num], text, len);
116 lines[num][len] = '\0';
118 text += strspn(text, delims);
120 lines = talloc_realloc(ctx, lines, char *, max*=2 + 1);
128 static char **get_dir(const char *dir)
133 unsigned int size = 0;
139 while ((ent = readdir(d)) != NULL) {
140 names = talloc_realloc(dir, names, char *, size + 2);
142 = talloc_asprintf(names, "%s/%s", dir, ent->d_name);
144 names[size++] = NULL;
149 static char ** __attribute__((format(printf, 2, 3)))
150 lines_from_cmd(const void *ctx, char *format, ...)
156 va_start(ap, format);
157 cmd = talloc_vasprintf(ctx, format, ap);
162 err(1, "Executing '%s'", cmd);
164 buffer = grab_fd(ctx, fileno(p));
166 err(1, "Reading from '%s'", cmd);
169 return split(ctx, buffer, "\n", NULL);
174 struct replace *next;
178 static void __attribute__((noreturn)) usage(void)
181 "namespacize [--verbose] <dir>\n"
182 "namespacize [--verbose] --adjust <dir>...\n"
183 "The first form converts dir/ to insert 'ccan_' prefixes, and\n"
184 "then adjusts any other ccan directories at the same level which\n"
186 "--adjust does an adjustment for each directory, in case a\n"
187 "dependency has been namespacized\n");
190 static void add_replace(struct replace **repl, const char *str)
192 struct replace *new, *i;
194 /* Avoid duplicates. */
195 for (i = *repl; i; i = i->next)
196 if (streq(i->string, str))
199 new = talloc(*repl, struct replace);
201 new->string = talloc_strdup(new, str);
205 static void add_replace_tok(struct replace **repl, const char *s)
208 unsigned int len = strspn(s, IDENT_CHARS);
210 new = talloc(*repl, struct replace);
212 new->string = talloc_strndup(new, s, len);
216 static char *basename(const void *ctx, const char *dir)
218 char *p = strrchr(dir, '/');
222 return talloc_strdup(ctx, p+1);
225 static void look_for_macros(char *contents, struct replace **repl)
228 enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART;
230 /* Look for lines of form #define X */
231 for (p = contents; *p; p++) {
234 else if (!isspace(*p)) {
235 if (state == LINESTART && *p == '#')
237 else if (state==HASH && !strncmp(p, "define", 6)) {
240 } else if (state == DEFINE) {
243 len = strspn(p, IDENT_CHARS);
246 s = talloc_strndup(contents, p, len);
247 /* Don't wrap idempotent wrappers */
248 if (!strstarts(s, "CCAN_")) {
249 verbose("Found %s\n", s);
250 add_replace(repl, s);
260 /* Blank out preprocessor lines, and eliminate \ */
261 static void preprocess(char *p)
265 /* We assume backslashes are only used for macros. */
266 while ((s = strstr(p, "\\\n")) != NULL)
269 /* Now eliminate # lines. */
272 for (i = 0; p[i] != '\n'; i++)
275 while ((s = strstr(p, "\n#")) != NULL) {
277 for (i = 1; s[i] != '\n'; i++)
282 static char *get_statement(const void *ctx, char **p)
284 unsigned brackets = 0;
285 bool seen_brackets = false;
286 char *answer = talloc_strdup(ctx, "");
289 if ((*p)[0] == '/' && (*p)[1] == '/')
290 *p += strcspn(*p, "\n");
291 else if ((*p)[0] == '/' && (*p)[1] == '*')
292 *p = strstr(*p, "*/") + 1;
295 if (c == ';' && !brackets) {
299 /* Compress whitespace into a single ' ' */
302 while (isspace((*p)[1]))
304 } else if (c == '{' || c == '(' || c == '[') {
306 seen_brackets = true;
308 } else if (c == '}' || c == ')' || c == ']')
311 if (answer[0] != '\0' || c != ' ') {
312 answer = talloc_realloc(NULL, answer, char,
314 answer[strlen(answer)+1] = '\0';
315 answer[strlen(answer)] = c;
317 if (c == '}' && seen_brackets && brackets == 0) {
328 /* This hack should handle well-formatted code. */
329 static void look_for_definitions(char *contents, struct replace **repl)
331 char *stmt, *p = contents;
333 preprocess(contents);
335 while ((stmt = get_statement(contents, &p)) != NULL) {
338 /* Definition of struct/union? */
339 if ((strncmp(stmt, "struct", 5) == 0
340 || strncmp(stmt, "union", 5) == 0)
341 && strchr(stmt, '{') && stmt[7] != '{')
342 add_replace_tok(repl, stmt+7);
344 /* Definition of var or typedef? */
345 for (i = strlen(stmt)-1; i >= 0; i--)
346 if (strspn(stmt+i, IDENT_CHARS) == 0)
349 if (i != strlen(stmt)-1) {
350 add_replace_tok(repl, stmt+i+1);
354 /* function or array declaration? */
355 len = strspn(stmt, IDENT_CHARS "* ");
356 if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) {
357 if (strspn(stmt + len + 1, IDENT_CHARS) != 0) {
358 for (i = len-1; i >= 0; i--)
359 if (strspn(stmt+i, IDENT_CHARS) == 0)
362 add_replace_tok(repl, stmt+i+1);
366 /* Pointer to function? */
368 len += strspn(stmt + len, " *");
369 i = strspn(stmt + len, IDENT_CHARS);
370 if (i > 0 && stmt[len + i] == ')')
371 add_replace_tok(repl, stmt+len);
377 /* FIXME: Only does main header, should chase local includes. */
378 static void analyze_headers(const char *dir, struct replace **repl)
380 char *hdr, *contents;
382 /* Get hold of header, assume that's it. */
383 hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
384 contents = grab_file(dir, hdr);
386 err(1, "Reading %s", hdr);
388 verbose("Looking in %s for macros\n", hdr);
390 look_for_macros(contents, repl);
393 verbose("Looking in %s for symbols\n", hdr);
395 look_for_definitions(contents, repl);
399 static void write_replacement_file(const char *dir, struct replace **repl)
401 char *replname = talloc_asprintf(dir, "%s/.namespacize", dir);
405 fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644);
408 errx(1, "%s already exists: can't namespacize twice",
410 err(1, "Opening %s", replname);
413 for (r = *repl; r; r = r->next) {
414 if (write(fd,r->string,strlen(r->string)) != strlen(r->string)
415 || write(fd, "\n", 1) != 1) {
416 unlink_no_errno(replname);
418 errx(1, "Short write to %s: disk full?",
420 errx(1, "Writing to %s", replname);
427 static int unlink_destroy(char *name)
433 static char *find_word(char *f, const char *str)
437 while ((p = strstr(p, str)) != NULL) {
438 /* Check it's not in the middle of a word. */
439 if (p > f && (isalnum(p[-1]) || p[-1] == '_')) {
443 if (isalnum(p[strlen(str)]) || p[strlen(str)] == '_') {
452 /* This is horribly inefficient but simple. */
453 static const char *rewrite_file(const char *filename,
454 const struct replace *repl)
456 char *newname, *file;
459 verbose("Rewriting %s\n", filename);
460 file = grab_file(filename, filename);
462 err(1, "Reading file %s", filename);
464 for (; repl; repl = repl->next) {
467 while ((p = find_word(file, repl->string)) != NULL) {
469 char *new = talloc_array(file, char, strlen(file)+6);
472 memcpy(new, file, off);
473 if (isupper(repl->string[0]))
474 memcpy(new + off, "CCAN_", 5);
476 memcpy(new + off, "ccan_", 5);
477 strcpy(new + off + 5, file + off);
482 /* If we exit for some reason, we want this erased. */
483 newname = talloc_asprintf(talloc_autofree_context(), "%s.tmp",
485 fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644);
487 err(1, "Creating %s", newname);
489 talloc_set_destructor(newname, unlink_destroy);
490 if (write(fd, file, strlen(file)) != strlen(file)) {
492 errx(1, "Short write to %s: disk full?", newname);
493 errx(1, "Writing to %s", newname);
501 struct adjusted *next;
506 static void setup_adjust_files(const char *dir,
507 const struct replace *repl,
508 struct adjusted **adj)
512 for (files = get_dir(dir); *files; files++) {
513 if (strends(*files, "/test"))
514 setup_adjust_files(*files, repl, adj);
515 else if (strends(*files, ".c") || strends(*files, ".h")) {
516 struct adjusted *a = talloc(dir, struct adjusted);
519 a->tmpfile = rewrite_file(a->file, repl);
525 /* This is the "commit" stage, so we hope it won't fail. */
526 static void rename_files(const struct adjusted *adj)
529 if (rename(adj->tmpfile, adj->file) != 0)
530 warn("Could not rename over '%s', we're in trouble",
536 static void convert_dir(const char *dir)
539 struct replace *replace = NULL;
540 struct adjusted *adj = NULL;
542 /* Remove any ugly trailing slashes. */
543 name = talloc_strdup(NULL, dir);
544 while (strends(name, "/"))
545 name[strlen(name)-1] = '\0';
547 analyze_headers(name, &replace);
548 write_replacement_file(name, &replace);
549 setup_adjust_files(name, replace, &adj);
552 talloc_free(replace);
555 static struct replace *read_replacement_file(const char *depdir)
557 struct replace *repl = NULL;
558 char *replname = talloc_asprintf(depdir, "%s/.namespacize", depdir);
561 file = grab_file(replname, replname);
564 err(1, "Opening %s", replname);
568 for (line = split(file, file, "\n", NULL); *line; line++)
569 add_replace(&repl, *line);
573 static char *build_info(const void *ctx, const char *dir)
575 char *file, *cfile, *cmd;
577 cfile = talloc_asprintf(ctx, "%s/%s", dir, "_info.c");
578 file = talloc_asprintf(cfile, "%s/%s", dir, "_info");
579 cmd = talloc_asprintf(file, "gcc " CFLAGS " -o %s %s", file, cfile);
580 if (system(cmd) != 0)
581 errx(1, "Failed to compile %s", file);
586 static char **get_deps(const void *ctx, const char *dir)
590 cmd = talloc_asprintf(ctx, "%s depends", build_info(ctx, dir));
591 deps = lines_from_cmd(cmd, cmd);
593 err(1, "Could not run '%s'", cmd);
597 static char *parent_dir(const void *ctx, const char *dir)
599 char *parent, *slash;
601 parent = talloc_strdup(ctx, dir);
602 slash = strrchr(parent, '/');
606 parent = talloc_strdup(ctx, ".");
610 static void adjust_dir(const char *dir)
612 char *parent = parent_dir(NULL, dir);
615 verbose("Adjusting %s\n", dir);
617 for (deps = get_deps(parent, dir); *deps; deps++) {
619 struct adjusted *adj = NULL;
620 struct replace *repl;
622 depdir = talloc_asprintf(parent, "%s/%s", parent, *deps);
623 repl = read_replacement_file(depdir);
625 verbose("%s has been namespacized\n", depdir);
626 setup_adjust_files(parent, repl, &adj);
629 verbose("%s has not been namespacized\n", depdir);
635 static void adjust_dependents(const char *dir)
637 char *parent = parent_dir(NULL, dir);
638 char *base = basename(parent, dir);
641 verbose("Looking for dependents in %s\n", parent);
643 for (file = get_dir(parent); *file; file++) {
647 if (basename(*file, *file)[0] == '.')
650 infoc = talloc_asprintf(*file, "%s/_info.c", *file);
651 if (access(infoc, R_OK) != 0)
654 for (deps = get_deps(*file, *file); *deps; deps++) {
655 if (streq(*deps, base))
661 verbose("%s is not dependent\n", *file);
666 int main(int argc, char *argv[])
668 if (argv[1] && streq(argv[1], "--verbose")) {
675 verbose("Namespacizing %s\n", argv[1]);
677 convert_dir(argv[1]);
678 adjust_dependents(argv[1]);
683 if (argc > 2 && streq(argv[1], "--adjust")) {
686 for (i = 2; i < argc; i++)