namespacize.c 14 KB

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