1 /* Code to move a ccan module into the ccan_ namespace. */
12 #include "ccan/str/str.h"
13 #include "ccan/take/take.h"
14 #include "ccan/rbuf/rbuf.h"
15 #include "ccan/tal/path/path.h"
16 #include "ccan/tal/grab_file/grab_file.h"
17 #include "ccan/err/err.h"
18 #include "ccan/noerr/noerr.h"
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 static char **get_dir(const char *dir)
37 char **names = tal_arr(NULL, char *, 0), *n;
43 while ((ent = readdir(d)) != NULL) {
44 n = tal_fmt(names, "%s/%s", dir, ent->d_name);
45 tal_expand(&names, &n, 1);
48 tal_expand(&names, &n, 1);
59 static void __attribute__((noreturn)) usage(void)
62 "namespacize [--verbose] <dir>\n"
63 "namespacize [--verbose] --adjust <dir>...\n"
64 "The first form converts dir/ to insert 'ccan_' prefixes, and\n"
65 "then adjusts any other ccan directories at the same level which\n"
67 "--adjust does an adjustment for each directory, in case a\n"
68 "dependency has been namespacized\n");
71 static void add_replace(struct replace **repl, const char *str)
73 struct replace *new, *i;
75 /* Avoid duplicates. */
76 for (i = *repl; i; i = i->next)
77 if (streq(i->string, str))
80 new = tal(*repl, struct replace);
82 new->string = tal_strdup(new, str);
86 static void add_replace_tok(struct replace **repl, const char *s)
89 unsigned int len = strspn(s, IDENT_CHARS);
91 new = tal(*repl, struct replace);
93 new->string = tal_strndup(new, s, len);
97 static void look_for_macros(char *contents, struct replace **repl)
100 enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART;
102 /* Look for lines of form #define X */
103 for (p = contents; *p; p++) {
106 else if (!cisspace(*p)) {
107 if (state == LINESTART && *p == '#')
109 else if (state==HASH && !strncmp(p, "define", 6)) {
112 } else if (state == DEFINE) {
115 len = strspn(p, IDENT_CHARS);
118 s = tal_strndup(contents, p, len);
119 /* Don't wrap idempotent wrappers */
120 if (!strstarts(s, "CCAN_")) {
121 verbose("Found %s\n", s);
122 add_replace(repl, s);
132 /* Blank out preprocessor lines, and eliminate \ */
133 static void preprocess(char *p)
137 /* We assume backslashes are only used for macros. */
138 while ((s = strstr(p, "\\\n")) != NULL)
141 /* Now eliminate # lines. */
144 for (i = 0; p[i] != '\n'; i++)
147 while ((s = strstr(p, "\n#")) != NULL) {
149 for (i = 1; s[i] != '\n'; i++)
154 static char *get_statement(const void *ctx, char **p)
156 unsigned brackets = 0;
157 bool seen_brackets = false;
158 char *answer = tal_strdup(ctx, "");
161 if ((*p)[0] == '/' && (*p)[1] == '/')
162 *p += strcspn(*p, "\n");
163 else if ((*p)[0] == '/' && (*p)[1] == '*')
164 *p = strstr(*p, "*/") + 1;
167 if (c == ';' && !brackets) {
171 /* Compress whitespace into a single ' ' */
174 while (cisspace((*p)[1]))
176 } else if (c == '{' || c == '(' || c == '[') {
178 seen_brackets = true;
180 } else if (c == '}' || c == ')' || c == ']')
183 if (answer[0] != '\0' || c != ' ') {
184 tal_append_fmt(&answer, "%c", c);
186 if (c == '}' && seen_brackets && brackets == 0) {
197 /* This hack should handle well-formatted code. */
198 static void look_for_definitions(char *contents, struct replace **repl)
200 char *stmt, *p = contents;
202 preprocess(contents);
204 while ((stmt = get_statement(contents, &p)) != NULL) {
207 /* Definition of struct/union? */
208 if ((strncmp(stmt, "struct", 5) == 0
209 || strncmp(stmt, "union", 5) == 0)
210 && strchr(stmt, '{') && stmt[7] != '{')
211 add_replace_tok(repl, stmt+7);
213 /* Definition of var or typedef? */
214 for (i = strlen(stmt)-1; i >= 0; i--)
215 if (strspn(stmt+i, IDENT_CHARS) == 0)
218 if (i != strlen(stmt)-1) {
219 add_replace_tok(repl, stmt+i+1);
223 /* function or array declaration? */
224 len = strspn(stmt, IDENT_CHARS "* ");
225 if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) {
226 if (strspn(stmt + len + 1, IDENT_CHARS) != 0) {
227 for (i = len-1; i >= 0; i--)
228 if (strspn(stmt+i, IDENT_CHARS) == 0)
231 add_replace_tok(repl, stmt+i+1);
235 /* Pointer to function? */
237 len += strspn(stmt + len, " *");
238 i = strspn(stmt + len, IDENT_CHARS);
239 if (i > 0 && stmt[len + i] == ')')
240 add_replace_tok(repl, stmt+len);
246 /* FIXME: Only does main header, should chase local includes. */
247 static void analyze_headers(const char *dir, struct replace **repl)
249 char *hdr, *contents;
251 /* Get hold of header, assume that's it. */
252 hdr = tal_fmt(dir, "%s.h",
253 path_join(NULL, dir, take(path_basename(NULL, dir))));
255 contents = grab_file(dir, hdr);
257 err(1, "Reading %s", hdr);
259 verbose("Looking in %s for macros\n", hdr);
261 look_for_macros(contents, repl);
264 verbose("Looking in %s for symbols\n", hdr);
266 look_for_definitions(contents, repl);
270 static void write_replacement_file(const char *dir, struct replace **repl)
272 char *replname = path_join(dir, dir, ".namespacize");
276 fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644);
279 errx(1, "%s already exists: can't namespacize twice",
281 err(1, "Opening %s", replname);
284 for (r = *repl; r; r = r->next) {
285 if (write(fd,r->string,strlen(r->string)) != strlen(r->string)
286 || write(fd, "\n", 1) != 1) {
287 unlink_noerr(replname);
289 errx(1, "Short write to %s: disk full?",
291 errx(1, "Writing to %s", replname);
298 static void unlink_destroy(char *name)
303 static char *find_word(char *f, const char *str)
307 while ((p = strstr(p, str)) != NULL) {
308 /* Check it's not in the middle of a word. */
309 if (p > f && (cisalnum(p[-1]) || p[-1] == '_')) {
313 if (cisalnum(p[strlen(str)]) || p[strlen(str)] == '_') {
322 /* This is horribly inefficient but simple. */
323 static const char *rewrite_file(const char *filename,
324 const struct replace *repl)
326 char *newname, *file;
329 verbose("Rewriting %s\n", filename);
330 file = grab_file(filename, filename);
332 err(1, "Reading file %s", filename);
334 for (; repl; repl = repl->next) {
337 while ((p = find_word(file, repl->string)) != NULL) {
339 char *new = tal_arr(file, char, strlen(file)+6);
342 memcpy(new, file, off);
343 if (cisupper(repl->string[0]))
344 memcpy(new + off, "CCAN_", 5);
346 memcpy(new + off, "ccan_", 5);
347 strcpy(new + off + 5, file + off);
352 /* If we exit for some reason, we want this erased. */
353 newname = tal_fmt(autofree(), "%s.tmp", filename);
354 fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644);
356 err(1, "Creating %s", newname);
358 tal_add_destructor(newname, unlink_destroy);
359 if (write(fd, file, strlen(file)) != strlen(file)) {
361 errx(1, "Short write to %s: disk full?", newname);
362 errx(1, "Writing to %s", newname);
370 struct adjusted *next;
375 static void setup_adjust_files(const char *dir,
376 const struct replace *repl,
377 struct adjusted **adj)
381 for (files = get_dir(dir); *files; files++) {
382 if (strends(*files, "/test"))
383 setup_adjust_files(*files, repl, adj);
384 else if (strends(*files, ".c") || strends(*files, ".h")) {
385 struct adjusted *a = tal(dir, struct adjusted);
388 a->tmpfile = rewrite_file(a->file, repl);
394 /* This is the "commit" stage, so we hope it won't fail. */
395 static void rename_files(const struct adjusted *adj)
398 if (!move_file(adj->tmpfile, adj->file))
399 warn("Could not rename over '%s', we're in trouble",
405 static void convert_dir(const char *dir)
408 struct replace *replace = NULL;
409 struct adjusted *adj = NULL;
411 /* Remove any ugly trailing slashes. */
412 name = path_canon(NULL, dir);
413 analyze_headers(name, &replace);
414 write_replacement_file(name, &replace);
415 setup_adjust_files(name, replace, &adj);
421 static struct replace *read_replacement_file(const char *depdir)
423 struct replace *repl = NULL;
424 char *replname = path_join(depdir, depdir, ".namespacize");
427 file = grab_file(replname, replname);
430 err(1, "Opening %s", replname);
434 for (line = tal_strsplit(file, file, "\n", STR_EMPTY_OK); *line; line++)
435 add_replace(&repl, *line);
439 static void adjust_dir(const char *dir)
441 char *parent = path_dirname(autofree(), dir);
444 verbose("Adjusting %s\n", dir);
446 for (deps = get_deps(parent, dir, "depends", false, compile_info);
450 struct adjusted *adj = NULL;
451 struct replace *repl;
453 depdir = path_join(parent, parent, *deps);
454 repl = read_replacement_file(depdir);
456 verbose("%s has been namespacized\n", depdir);
457 setup_adjust_files(parent, repl, &adj);
460 verbose("%s has not been namespacized\n", depdir);
467 static void adjust_dependents(const char *dir)
469 char *parent = path_dirname(NULL, dir);
470 char *base = path_basename(parent, dir);
473 verbose("Looking for dependents in %s\n", parent);
475 for (file = get_dir(parent); *file; file++) {
479 if (path_basename(*file, *file)[0] == '.')
482 info = path_join(*file, *file, "_info");
483 if (access(info, R_OK) != 0)
486 for (deps = get_deps(*file, *file, "depends", false,
489 if (!strstarts(*deps, "ccan/"))
491 if (streq(*deps + strlen("ccan/"), base))
497 verbose("%s is not dependent\n", *file);
502 int main(int argc, char *argv[])
504 if (argv[1] && streq(argv[1], "--verbose")) {
511 verbose("Namespacizing %s\n", argv[1]);
513 convert_dir(argv[1]);
514 adjust_dependents(argv[1]);
519 if (argc > 2 && streq(argv[1], "--adjust")) {
522 for (i = 2; i < argc; i++)