namespacize.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. /* Code to move a ccan module into the ccan_ namespace. */
  2. #include <err.h>
  3. #include <errno.h>
  4. #include <unistd.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <stdbool.h>
  10. #include <ctype.h>
  11. #include <sys/types.h>
  12. #include <dirent.h>
  13. #include "talloc/talloc.h"
  14. #define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. -Iccan_tools/libtap/src/"
  15. #define IDENT_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
  16. "abcdefghijklmnopqrstuvwxyz" \
  17. "01234567889_"
  18. static bool verbose = false;
  19. static int indent = 0;
  20. #define verbose(args...) \
  21. do { if (verbose) { \
  22. unsigned int _i; \
  23. for (_i = 0; _i < indent; _i++) printf(" "); \
  24. printf(args); \
  25. } \
  26. } while(0)
  27. #define verbose_indent() (indent += 2)
  28. #define verbose_unindent() (indent -= 2)
  29. #define streq(a,b) (strcmp((a),(b)) == 0)
  30. #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0)
  31. static inline bool strends(const char *str, const char *postfix)
  32. {
  33. if (strlen(str) < strlen(postfix))
  34. return false;
  35. return streq(str + strlen(str) - strlen(postfix), postfix);
  36. }
  37. static int close_no_errno(int fd)
  38. {
  39. int ret = 0, serrno = errno;
  40. if (close(fd) < 0)
  41. ret = errno;
  42. errno = serrno;
  43. return ret;
  44. }
  45. static int unlink_no_errno(const char *filename)
  46. {
  47. int ret = 0, serrno = errno;
  48. if (unlink(filename) < 0)
  49. ret = errno;
  50. errno = serrno;
  51. return ret;
  52. }
  53. static void *grab_fd(const void *ctx, int fd)
  54. {
  55. int ret;
  56. unsigned int max = 16384, size = 0;
  57. char *buffer;
  58. buffer = talloc_array(ctx, char, max+1);
  59. while ((ret = read(fd, buffer + size, max - size)) > 0) {
  60. size += ret;
  61. if (size == max)
  62. buffer = talloc_realloc(ctx, buffer, char, max*=2 + 1);
  63. }
  64. if (ret < 0) {
  65. talloc_free(buffer);
  66. buffer = NULL;
  67. } else
  68. buffer[size] = '\0';
  69. return buffer;
  70. }
  71. /* This version adds one byte (for nul term) */
  72. static void *grab_file(const void *ctx, const char *filename)
  73. {
  74. int fd;
  75. char *buffer;
  76. if (streq(filename, "-"))
  77. fd = dup(STDIN_FILENO);
  78. else
  79. fd = open(filename, O_RDONLY, 0);
  80. if (fd < 0)
  81. return NULL;
  82. buffer = grab_fd(ctx, fd);
  83. close_no_errno(fd);
  84. return buffer;
  85. }
  86. /* This is a dumb one which copies. We could mangle instead. */
  87. static char **split(const void *ctx, const char *text, const char *delims,
  88. unsigned int *nump)
  89. {
  90. char **lines = NULL;
  91. unsigned int max = 64, num = 0;
  92. lines = talloc_array(ctx, char *, max+1);
  93. while (*text != '\0') {
  94. unsigned int len = strcspn(text, delims);
  95. lines[num] = talloc_array(lines, char, len + 1);
  96. memcpy(lines[num], text, len);
  97. lines[num][len] = '\0';
  98. text += len;
  99. text += strspn(text, delims);
  100. if (++num == max)
  101. lines = talloc_realloc(ctx, lines, char *, max*=2 + 1);
  102. }
  103. lines[num] = NULL;
  104. if (nump)
  105. *nump = num;
  106. return lines;
  107. }
  108. static char **get_dir(const char *dir)
  109. {
  110. DIR *d;
  111. struct dirent *ent;
  112. char **names = NULL;
  113. unsigned int size = 0;
  114. d = opendir(dir);
  115. if (!d)
  116. return NULL;
  117. while ((ent = readdir(d)) != NULL) {
  118. names = talloc_realloc(dir, names, char *, size + 2);
  119. names[size++]
  120. = talloc_asprintf(names, "%s/%s", dir, ent->d_name);
  121. }
  122. names[size++] = NULL;
  123. closedir(d);
  124. return names;
  125. }
  126. static char ** __attribute__((format(printf, 2, 3)))
  127. lines_from_cmd(const void *ctx, char *format, ...)
  128. {
  129. va_list ap;
  130. char *cmd, *buffer;
  131. FILE *p;
  132. va_start(ap, format);
  133. cmd = talloc_vasprintf(ctx, format, ap);
  134. va_end(ap);
  135. p = popen(cmd, "r");
  136. if (!p)
  137. err(1, "Executing '%s'", cmd);
  138. buffer = grab_fd(ctx, fileno(p));
  139. if (!buffer)
  140. err(1, "Reading from '%s'", cmd);
  141. pclose(p);
  142. return split(ctx, buffer, "\n", NULL);
  143. }
  144. struct replace
  145. {
  146. struct replace *next;
  147. char *string;
  148. };
  149. static void __attribute__((noreturn)) usage(void)
  150. {
  151. errx(1, "Usage:\n"
  152. "namespacize [--verbose] <dir>\n"
  153. "namespacize [--verbose] --adjust <dir>...\n"
  154. "The first form converts dir/ to insert 'ccan_' prefixes, and\n"
  155. "then adjusts any other ccan directories at the same level which\n"
  156. "are effected.\n"
  157. "--adjust does an adjustment for each directory, in case a\n"
  158. "dependency has been namespacized\n");
  159. }
  160. static void add_replace(struct replace **repl, const char *str)
  161. {
  162. struct replace *new, *i;
  163. /* Avoid duplicates. */
  164. for (i = *repl; i; i = i->next)
  165. if (streq(i->string, str))
  166. return;
  167. new = talloc(*repl, struct replace);
  168. new->next = *repl;
  169. new->string = talloc_strdup(new, str);
  170. *repl = new;
  171. }
  172. static void add_replace_tok(struct replace **repl, const char *s)
  173. {
  174. struct replace *new;
  175. unsigned int len = strspn(s, IDENT_CHARS);
  176. new = talloc(*repl, struct replace);
  177. new->next = *repl;
  178. new->string = talloc_strndup(new, s, len);
  179. *repl = new;
  180. }
  181. static char *basename(const void *ctx, const char *dir)
  182. {
  183. char *p = strrchr(dir, '/');
  184. if (!p)
  185. return (char *)dir;
  186. return talloc_strdup(ctx, p+1);
  187. }
  188. static void look_for_macros(char *contents, struct replace **repl)
  189. {
  190. char *p;
  191. enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART;
  192. /* Look for lines of form #define X */
  193. for (p = contents; *p; p++) {
  194. if (*p == '\n')
  195. state = LINESTART;
  196. else if (!isspace(*p)) {
  197. if (state == LINESTART && *p == '#')
  198. state = HASH;
  199. else if (state==HASH && !strncmp(p, "define", 6)) {
  200. state = DEFINE;
  201. p += 5;
  202. } else if (state == DEFINE) {
  203. unsigned int len;
  204. len = strspn(p, IDENT_CHARS);
  205. if (len) {
  206. char *s;
  207. s = talloc_strndup(contents, p, len);
  208. /* Don't wrap idempotent wrappers */
  209. if (!strstarts(s, "CCAN_")) {
  210. verbose("Found %s\n", s);
  211. add_replace(repl, s);
  212. }
  213. }
  214. state = NONE;
  215. } else
  216. state = NONE;
  217. }
  218. }
  219. }
  220. /* Blank out preprocessor lines, and eliminate \ */
  221. static void preprocess(char *p)
  222. {
  223. char *s;
  224. /* We assume backslashes are only used for macros. */
  225. while ((s = strstr(p, "\\\n")) != NULL)
  226. s[0] = s[1] = ' ';
  227. /* Now eliminate # lines. */
  228. if (p[0] == '#') {
  229. unsigned int i;
  230. for (i = 0; p[i] != '\n'; i++)
  231. p[i] = ' ';
  232. }
  233. while ((s = strstr(p, "\n#")) != NULL) {
  234. unsigned int i;
  235. for (i = 1; s[i] != '\n'; i++)
  236. s[i] = ' ';
  237. }
  238. }
  239. static char *get_statement(const void *ctx, char **p)
  240. {
  241. unsigned brackets = 0;
  242. bool seen_brackets = false;
  243. char *answer = talloc_strdup(ctx, "");
  244. for (;;) {
  245. if ((*p)[0] == '/' && (*p)[1] == '/')
  246. *p += strcspn(*p, "\n");
  247. else if ((*p)[0] == '/' && (*p)[1] == '*')
  248. *p = strstr(*p, "*/") + 1;
  249. else {
  250. char c = **p;
  251. if (c == ';' && !brackets) {
  252. (*p)++;
  253. return answer;
  254. }
  255. /* Compress whitespace into a single ' ' */
  256. if (isspace(c)) {
  257. c = ' ';
  258. while (isspace((*p)[1]))
  259. (*p)++;
  260. } else if (c == '{' || c == '(' || c == '[') {
  261. if (c == '(')
  262. seen_brackets = true;
  263. brackets++;
  264. } else if (c == '}' || c == ')' || c == ']')
  265. brackets--;
  266. if (answer[0] != '\0' || c != ' ') {
  267. answer = talloc_realloc(NULL, answer, char,
  268. strlen(answer) + 2);
  269. answer[strlen(answer)+1] = '\0';
  270. answer[strlen(answer)] = c;
  271. }
  272. if (c == '}' && seen_brackets && brackets == 0) {
  273. (*p)++;
  274. return answer;
  275. }
  276. }
  277. (*p)++;
  278. if (**p == '\0')
  279. return NULL;
  280. }
  281. }
  282. /* This hack should handle well-formatted code. */
  283. static void look_for_definitions(char *contents, struct replace **repl)
  284. {
  285. char *stmt, *p = contents;
  286. preprocess(contents);
  287. while ((stmt = get_statement(contents, &p)) != NULL) {
  288. int i, len;
  289. /* Definition of struct/union? */
  290. if ((strncmp(stmt, "struct", 5) == 0
  291. || strncmp(stmt, "union", 5) == 0)
  292. && strchr(stmt, '{') && stmt[7] != '{')
  293. add_replace_tok(repl, stmt+7);
  294. /* Definition of var or typedef? */
  295. for (i = strlen(stmt)-1; i >= 0; i--)
  296. if (strspn(stmt+i, IDENT_CHARS) == 0)
  297. break;
  298. if (i != strlen(stmt)-1) {
  299. add_replace_tok(repl, stmt+i+1);
  300. continue;
  301. }
  302. /* function or array declaration? */
  303. len = strspn(stmt, IDENT_CHARS "* ");
  304. if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) {
  305. if (strspn(stmt + len + 1, IDENT_CHARS) != 0) {
  306. for (i = len-1; i >= 0; i--)
  307. if (strspn(stmt+i, IDENT_CHARS) == 0)
  308. break;
  309. if (i != len-1) {
  310. add_replace_tok(repl, stmt+i+1);
  311. continue;
  312. }
  313. } else {
  314. /* Pointer to function? */
  315. len++;
  316. len += strspn(stmt + len, " *");
  317. i = strspn(stmt + len, IDENT_CHARS);
  318. if (i > 0 && stmt[len + i] == ')')
  319. add_replace_tok(repl, stmt+len);
  320. }
  321. }
  322. }
  323. }
  324. /* FIXME: Only does main header, should chase local includes. */
  325. static void analyze_headers(const char *dir, struct replace **repl)
  326. {
  327. char *hdr, *contents;
  328. /* Get hold of header, assume that's it. */
  329. hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
  330. contents = grab_file(dir, hdr);
  331. if (!contents)
  332. err(1, "Reading %s", hdr);
  333. verbose("Looking in %s for macros\n", hdr);
  334. verbose_indent();
  335. look_for_macros(contents, repl);
  336. verbose_unindent();
  337. verbose("Looking in %s for symbols\n", hdr);
  338. verbose_indent();
  339. look_for_definitions(contents, repl);
  340. verbose_unindent();
  341. }
  342. static void write_replacement_file(const char *dir, struct replace **repl)
  343. {
  344. char *replname = talloc_asprintf(dir, "%s/.namespacize", dir);
  345. int fd;
  346. struct replace *r;
  347. fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644);
  348. if (fd < 0) {
  349. if (errno == EEXIST)
  350. errx(1, "%s already exists: can't namespacize twice",
  351. replname);
  352. err(1, "Opening %s", replname);
  353. }
  354. for (r = *repl; r; r = r->next) {
  355. if (write(fd,r->string,strlen(r->string)) != strlen(r->string)
  356. || write(fd, "\n", 1) != 1) {
  357. unlink_no_errno(replname);
  358. if (errno == 0)
  359. errx(1, "Short write to %s: disk full?",
  360. replname);
  361. errx(1, "Writing to %s", replname);
  362. }
  363. }
  364. close(fd);
  365. }
  366. static int unlink_destroy(char *name)
  367. {
  368. unlink(name);
  369. return 0;
  370. }
  371. static char *find_word(char *f, const char *str)
  372. {
  373. char *p = f;
  374. while ((p = strstr(p, str)) != NULL) {
  375. /* Check it's not in the middle of a word. */
  376. if (p > f && (isalnum(p[-1]) || p[-1] == '_')) {
  377. p++;
  378. continue;
  379. }
  380. if (isalnum(p[strlen(str)]) || p[strlen(str)] == '_') {
  381. p++;
  382. continue;
  383. }
  384. return p;
  385. }
  386. return NULL;
  387. }
  388. /* This is horribly inefficient but simple. */
  389. static const char *rewrite_file(const char *filename,
  390. const struct replace *repl)
  391. {
  392. char *newname, *file;
  393. int fd;
  394. verbose("Rewriting %s\n", filename);
  395. file = grab_file(filename, filename);
  396. if (!file)
  397. err(1, "Reading file %s", filename);
  398. for (; repl; repl = repl->next) {
  399. char *p;
  400. while ((p = find_word(file, repl->string)) != NULL) {
  401. unsigned int off;
  402. char *new = talloc_array(file, char, strlen(file)+6);
  403. off = p - file;
  404. memcpy(new, file, off);
  405. if (isupper(repl->string[0]))
  406. memcpy(new + off, "CCAN_", 5);
  407. else
  408. memcpy(new + off, "ccan_", 5);
  409. strcpy(new + off + 5, file + off);
  410. file = new;
  411. }
  412. }
  413. /* If we exit for some reason, we want this erased. */
  414. newname = talloc_asprintf(talloc_autofree_context(), "%s.tmp",
  415. filename);
  416. fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644);
  417. if (fd < 0)
  418. err(1, "Creating %s", newname);
  419. talloc_set_destructor(newname, unlink_destroy);
  420. if (write(fd, file, strlen(file)) != strlen(file)) {
  421. if (errno == 0)
  422. errx(1, "Short write to %s: disk full?", newname);
  423. errx(1, "Writing to %s", newname);
  424. }
  425. close(fd);
  426. return newname;
  427. }
  428. struct adjusted
  429. {
  430. struct adjusted *next;
  431. const char *file;
  432. const char *tmpfile;
  433. };
  434. static void setup_adjust_files(const char *dir,
  435. const struct replace *repl,
  436. struct adjusted **adj)
  437. {
  438. char **files;
  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);
  444. a->next = *adj;
  445. a->file = *files;
  446. a->tmpfile = rewrite_file(a->file, repl);
  447. *adj = a;
  448. }
  449. }
  450. }
  451. /* This is the "commit" stage, so we hope it won't fail. */
  452. static void rename_files(const struct adjusted *adj)
  453. {
  454. while (adj) {
  455. if (rename(adj->tmpfile, adj->file) != 0)
  456. warn("Could not rename over '%s', we're in trouble",
  457. adj->file);
  458. adj = adj->next;
  459. }
  460. }
  461. static void convert_dir(const char *dir)
  462. {
  463. char *name;
  464. struct replace *replace = NULL;
  465. struct adjusted *adj = NULL;
  466. /* Remove any ugly trailing slashes. */
  467. name = talloc_strdup(NULL, dir);
  468. while (strends(name, "/"))
  469. name[strlen(name)-1] = '\0';
  470. analyze_headers(name, &replace);
  471. write_replacement_file(name, &replace);
  472. setup_adjust_files(name, replace, &adj);
  473. rename_files(adj);
  474. talloc_free(name);
  475. talloc_free(replace);
  476. }
  477. static struct replace *read_replacement_file(const char *depdir)
  478. {
  479. struct replace *repl = NULL;
  480. char *replname = talloc_asprintf(depdir, "%s/.namespacize", depdir);
  481. char *file, **line;
  482. file = grab_file(replname, replname);
  483. if (!file) {
  484. if (errno != ENOENT)
  485. err(1, "Opening %s", replname);
  486. return NULL;
  487. }
  488. for (line = split(file, file, "\n", NULL); *line; line++)
  489. add_replace(&repl, *line);
  490. return repl;
  491. }
  492. static char *build_info(const void *ctx, const char *dir)
  493. {
  494. char *file, *cfile, *cmd;
  495. cfile = talloc_asprintf(ctx, "%s/%s", dir, "_info.c");
  496. file = talloc_asprintf(cfile, "%s/%s", dir, "_info");
  497. cmd = talloc_asprintf(file, "gcc " CFLAGS " -o %s %s", file, cfile);
  498. if (system(cmd) != 0)
  499. errx(1, "Failed to compile %s", file);
  500. return file;
  501. }
  502. static char **get_deps(const void *ctx, const char *dir)
  503. {
  504. char **deps, *cmd;
  505. cmd = talloc_asprintf(ctx, "%s depends", build_info(ctx, dir));
  506. deps = lines_from_cmd(cmd, cmd);
  507. if (!deps)
  508. err(1, "Could not run '%s'", cmd);
  509. return deps;
  510. }
  511. static char *parent_dir(const void *ctx, const char *dir)
  512. {
  513. char *parent, *slash;
  514. parent = talloc_strdup(ctx, dir);
  515. slash = strrchr(parent, '/');
  516. if (slash)
  517. *slash = '\0';
  518. else
  519. parent = talloc_strdup(ctx, ".");
  520. return parent;
  521. }
  522. static void adjust_dir(const char *dir)
  523. {
  524. char *parent = parent_dir(NULL, dir);
  525. char **deps;
  526. verbose("Adjusting %s\n", dir);
  527. verbose_indent();
  528. for (deps = get_deps(parent, dir); *deps; deps++) {
  529. char *depdir;
  530. struct adjusted *adj = NULL;
  531. struct replace *repl;
  532. depdir = talloc_asprintf(parent, "%s/%s", parent, *deps);
  533. repl = read_replacement_file(depdir);
  534. if (repl) {
  535. verbose("%s has been namespacized\n", depdir);
  536. setup_adjust_files(parent, repl, &adj);
  537. rename_files(adj);
  538. } else
  539. verbose("%s has not been namespacized\n", depdir);
  540. talloc_free(depdir);
  541. }
  542. verbose_unindent();
  543. }
  544. static void adjust_dependents(const char *dir)
  545. {
  546. char *parent = parent_dir(NULL, dir);
  547. char *base = basename(parent, dir);
  548. char **file;
  549. verbose("Looking for dependents in %s\n", parent);
  550. verbose_indent();
  551. for (file = get_dir(parent); *file; file++) {
  552. char *infoc, **deps;
  553. bool isdep = false;
  554. if (basename(*file, *file)[0] == '.')
  555. continue;
  556. infoc = talloc_asprintf(*file, "%s/_info.c", *file);
  557. if (access(infoc, R_OK) != 0)
  558. continue;
  559. for (deps = get_deps(*file, *file); *deps; deps++) {
  560. if (streq(*deps, base))
  561. isdep = true;
  562. }
  563. if (isdep)
  564. adjust_dir(*file);
  565. else
  566. verbose("%s is not dependent\n", *file);
  567. }
  568. verbose_unindent();
  569. }
  570. int main(int argc, char *argv[])
  571. {
  572. if (argv[1] && streq(argv[1], "--verbose")) {
  573. verbose = true;
  574. argv++;
  575. argc--;
  576. }
  577. if (argc == 2) {
  578. verbose("Namespacizing %s\n", argv[1]);
  579. verbose_indent();
  580. convert_dir(argv[1]);
  581. adjust_dependents(argv[1]);
  582. verbose_unindent();
  583. return 0;
  584. }
  585. if (argc > 2 && streq(argv[1], "--adjust")) {
  586. unsigned int i;
  587. for (i = 2; i < argc; i++)
  588. adjust_dir(argv[i]);
  589. return 0;
  590. }
  591. usage();
  592. }