1 /* Code to move a ccan module into the ccan_ namespace. */
10 #include <sys/types.h>
13 #include "ccan/str/str.h"
14 #include "ccan/str_talloc/str_talloc.h"
15 #include "ccan/grab_file/grab_file.h"
16 #include "ccan/talloc/talloc.h"
19 #define IDENT_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
20 "abcdefghijklmnopqrstuvwxyz" \
23 static bool verbose = false;
24 static int indent = 0;
25 #define verbose(args...) \
28 for (_i = 0; _i < indent; _i++) printf(" "); \
32 #define verbose_indent() (indent += 2)
33 #define verbose_unindent() (indent -= 2)
35 static int unlink_no_errno(const char *filename)
37 int ret = 0, serrno = errno;
38 if (unlink(filename) < 0)
44 static char **get_dir(const char *dir)
49 unsigned int size = 0;
55 while ((ent = readdir(d)) != NULL) {
56 names = talloc_realloc(dir, names, char *, size + 2);
58 = talloc_asprintf(names, "%s/%s", dir, ent->d_name);
71 static void __attribute__((noreturn)) usage(void)
74 "namespacize [--verbose] <dir>\n"
75 "namespacize [--verbose] --adjust <dir>...\n"
76 "The first form converts dir/ to insert 'ccan_' prefixes, and\n"
77 "then adjusts any other ccan directories at the same level which\n"
79 "--adjust does an adjustment for each directory, in case a\n"
80 "dependency has been namespacized\n");
83 static void add_replace(struct replace **repl, const char *str)
85 struct replace *new, *i;
87 /* Avoid duplicates. */
88 for (i = *repl; i; i = i->next)
89 if (streq(i->string, str))
92 new = talloc(*repl, struct replace);
94 new->string = talloc_strdup(new, str);
98 static void add_replace_tok(struct replace **repl, const char *s)
101 unsigned int len = strspn(s, IDENT_CHARS);
103 new = talloc(*repl, struct replace);
105 new->string = talloc_strndup(new, s, len);
109 static char *basename(const void *ctx, const char *dir)
111 char *p = strrchr(dir, '/');
115 return talloc_strdup(ctx, p+1);
118 static void look_for_macros(char *contents, struct replace **repl)
121 enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART;
123 /* Look for lines of form #define X */
124 for (p = contents; *p; p++) {
127 else if (!isspace(*p)) {
128 if (state == LINESTART && *p == '#')
130 else if (state==HASH && !strncmp(p, "define", 6)) {
133 } else if (state == DEFINE) {
136 len = strspn(p, IDENT_CHARS);
139 s = talloc_strndup(contents, p, len);
140 /* Don't wrap idempotent wrappers */
141 if (!strstarts(s, "CCAN_")) {
142 verbose("Found %s\n", s);
143 add_replace(repl, s);
153 /* Blank out preprocessor lines, and eliminate \ */
154 static void preprocess(char *p)
158 /* We assume backslashes are only used for macros. */
159 while ((s = strstr(p, "\\\n")) != NULL)
162 /* Now eliminate # lines. */
165 for (i = 0; p[i] != '\n'; i++)
168 while ((s = strstr(p, "\n#")) != NULL) {
170 for (i = 1; s[i] != '\n'; i++)
175 static char *get_statement(const void *ctx, char **p)
177 unsigned brackets = 0;
178 bool seen_brackets = false;
179 char *answer = talloc_strdup(ctx, "");
182 if ((*p)[0] == '/' && (*p)[1] == '/')
183 *p += strcspn(*p, "\n");
184 else if ((*p)[0] == '/' && (*p)[1] == '*')
185 *p = strstr(*p, "*/") + 1;
188 if (c == ';' && !brackets) {
192 /* Compress whitespace into a single ' ' */
195 while (isspace((*p)[1]))
197 } else if (c == '{' || c == '(' || c == '[') {
199 seen_brackets = true;
201 } else if (c == '}' || c == ')' || c == ']')
204 if (answer[0] != '\0' || c != ' ') {
205 answer = talloc_realloc(NULL, answer, char,
207 answer[strlen(answer)+1] = '\0';
208 answer[strlen(answer)] = c;
210 if (c == '}' && seen_brackets && brackets == 0) {
221 /* This hack should handle well-formatted code. */
222 static void look_for_definitions(char *contents, struct replace **repl)
224 char *stmt, *p = contents;
226 preprocess(contents);
228 while ((stmt = get_statement(contents, &p)) != NULL) {
231 /* Definition of struct/union? */
232 if ((strncmp(stmt, "struct", 5) == 0
233 || strncmp(stmt, "union", 5) == 0)
234 && strchr(stmt, '{') && stmt[7] != '{')
235 add_replace_tok(repl, stmt+7);
237 /* Definition of var or typedef? */
238 for (i = strlen(stmt)-1; i >= 0; i--)
239 if (strspn(stmt+i, IDENT_CHARS) == 0)
242 if (i != strlen(stmt)-1) {
243 add_replace_tok(repl, stmt+i+1);
247 /* function or array declaration? */
248 len = strspn(stmt, IDENT_CHARS "* ");
249 if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) {
250 if (strspn(stmt + len + 1, IDENT_CHARS) != 0) {
251 for (i = len-1; i >= 0; i--)
252 if (strspn(stmt+i, IDENT_CHARS) == 0)
255 add_replace_tok(repl, stmt+i+1);
259 /* Pointer to function? */
261 len += strspn(stmt + len, " *");
262 i = strspn(stmt + len, IDENT_CHARS);
263 if (i > 0 && stmt[len + i] == ')')
264 add_replace_tok(repl, stmt+len);
270 /* FIXME: Only does main header, should chase local includes. */
271 static void analyze_headers(const char *dir, struct replace **repl)
273 char *hdr, *contents;
275 /* Get hold of header, assume that's it. */
276 hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
277 contents = grab_file(dir, hdr, NULL);
279 err(1, "Reading %s", hdr);
281 verbose("Looking in %s for macros\n", hdr);
283 look_for_macros(contents, repl);
286 verbose("Looking in %s for symbols\n", hdr);
288 look_for_definitions(contents, repl);
292 static void write_replacement_file(const char *dir, struct replace **repl)
294 char *replname = talloc_asprintf(dir, "%s/.namespacize", dir);
298 fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644);
301 errx(1, "%s already exists: can't namespacize twice",
303 err(1, "Opening %s", replname);
306 for (r = *repl; r; r = r->next) {
307 if (write(fd,r->string,strlen(r->string)) != strlen(r->string)
308 || write(fd, "\n", 1) != 1) {
309 unlink_no_errno(replname);
311 errx(1, "Short write to %s: disk full?",
313 errx(1, "Writing to %s", replname);
320 static int unlink_destroy(char *name)
326 static char *find_word(char *f, const char *str)
330 while ((p = strstr(p, str)) != NULL) {
331 /* Check it's not in the middle of a word. */
332 if (p > f && (isalnum(p[-1]) || p[-1] == '_')) {
336 if (isalnum(p[strlen(str)]) || p[strlen(str)] == '_') {
345 /* This is horribly inefficient but simple. */
346 static const char *rewrite_file(const char *filename,
347 const struct replace *repl)
349 char *newname, *file;
352 verbose("Rewriting %s\n", filename);
353 file = grab_file(filename, filename, NULL);
355 err(1, "Reading file %s", filename);
357 for (; repl; repl = repl->next) {
360 while ((p = find_word(file, repl->string)) != NULL) {
362 char *new = talloc_array(file, char, strlen(file)+6);
365 memcpy(new, file, off);
366 if (isupper(repl->string[0]))
367 memcpy(new + off, "CCAN_", 5);
369 memcpy(new + off, "ccan_", 5);
370 strcpy(new + off + 5, file + off);
375 /* If we exit for some reason, we want this erased. */
376 newname = talloc_asprintf(talloc_autofree_context(), "%s.tmp",
378 fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644);
380 err(1, "Creating %s", newname);
382 talloc_set_destructor(newname, unlink_destroy);
383 if (write(fd, file, strlen(file)) != strlen(file)) {
385 errx(1, "Short write to %s: disk full?", newname);
386 errx(1, "Writing to %s", newname);
394 struct adjusted *next;
399 static void setup_adjust_files(const char *dir,
400 const struct replace *repl,
401 struct adjusted **adj)
405 for (files = get_dir(dir); *files; files++) {
406 if (strends(*files, "/test"))
407 setup_adjust_files(*files, repl, adj);
408 else if (strends(*files, ".c") || strends(*files, ".h")) {
409 struct adjusted *a = talloc(dir, struct adjusted);
412 a->tmpfile = rewrite_file(a->file, repl);
418 /* This is the "commit" stage, so we hope it won't fail. */
419 static void rename_files(const struct adjusted *adj)
422 if (rename(adj->tmpfile, adj->file) != 0)
423 warn("Could not rename over '%s', we're in trouble",
429 static void convert_dir(const char *dir)
432 struct replace *replace = NULL;
433 struct adjusted *adj = NULL;
435 /* Remove any ugly trailing slashes. */
436 name = talloc_strdup(NULL, dir);
437 while (strends(name, "/"))
438 name[strlen(name)-1] = '\0';
440 analyze_headers(name, &replace);
441 write_replacement_file(name, &replace);
442 setup_adjust_files(name, replace, &adj);
445 talloc_free(replace);
448 static struct replace *read_replacement_file(const char *depdir)
450 struct replace *repl = NULL;
451 char *replname = talloc_asprintf(depdir, "%s/.namespacize", depdir);
454 file = grab_file(replname, replname, NULL);
457 err(1, "Opening %s", replname);
461 for (line = strsplit(file, file, "\n", NULL); *line; line++)
462 add_replace(&repl, *line);
466 static char *parent_dir(const void *ctx, const char *dir)
468 char *parent, *slash;
470 parent = talloc_strdup(ctx, dir);
471 slash = strrchr(parent, '/');
475 parent = talloc_strdup(ctx, ".");
479 static void adjust_dir(const char *dir)
481 char *parent = parent_dir(talloc_autofree_context(), dir);
484 verbose("Adjusting %s\n", dir);
486 for (deps = get_deps(parent, dir); *deps; deps++) {
488 struct adjusted *adj = NULL;
489 struct replace *repl;
491 depdir = talloc_asprintf(parent, "%s/%s", parent, *deps);
492 repl = read_replacement_file(depdir);
494 verbose("%s has been namespacized\n", depdir);
495 setup_adjust_files(parent, repl, &adj);
498 verbose("%s has not been namespacized\n", depdir);
505 static void adjust_dependents(const char *dir)
507 char *parent = parent_dir(NULL, dir);
508 char *base = basename(parent, dir);
511 verbose("Looking for dependents in %s\n", parent);
513 for (file = get_dir(parent); *file; file++) {
517 if (basename(*file, *file)[0] == '.')
520 infoc = talloc_asprintf(*file, "%s/_info.c", *file);
521 if (access(infoc, R_OK) != 0)
524 for (deps = get_deps(*file, *file); *deps; deps++) {
525 if (streq(*deps, base))
531 verbose("%s is not dependent\n", *file);
536 int main(int argc, char *argv[])
538 if (argv[1] && streq(argv[1], "--verbose")) {
545 verbose("Namespacizing %s\n", argv[1]);
547 convert_dir(argv[1]);
548 adjust_dependents(argv[1]);
553 if (argc > 2 && streq(argv[1], "--adjust")) {
556 for (i = 2; i < argc; i++)