namespacize.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /* Code to move a ccan module into the ccan_ namespace. */
  2. #include <err.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <stdbool.h>
  6. #include <ctype.h>
  7. #include <sys/types.h>
  8. #include <dirent.h>
  9. #include <unistd.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <fcntl.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"
  17. #include "tools.h"
  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. static int unlink_no_errno(const char *filename)
  30. {
  31. int ret = 0, serrno = errno;
  32. if (unlink(filename) < 0)
  33. ret = errno;
  34. errno = serrno;
  35. return ret;
  36. }
  37. static char **get_dir(const char *dir)
  38. {
  39. DIR *d;
  40. struct dirent *ent;
  41. char **names = NULL;
  42. unsigned int size = 0;
  43. d = opendir(dir);
  44. if (!d)
  45. return NULL;
  46. while ((ent = readdir(d)) != NULL) {
  47. names = talloc_realloc(dir, names, char *, size + 2);
  48. names[size++]
  49. = talloc_asprintf(names, "%s/%s", dir, ent->d_name);
  50. }
  51. /* FIXME: if the loop doesn't run at least once, we'll segfault here */
  52. names[size] = NULL;
  53. closedir(d);
  54. return names;
  55. }
  56. struct replace
  57. {
  58. struct replace *next;
  59. char *string;
  60. };
  61. static void __attribute__((noreturn)) usage(void)
  62. {
  63. errx(1, "Usage:\n"
  64. "namespacize [--verbose] <dir>\n"
  65. "namespacize [--verbose] --adjust <dir>...\n"
  66. "The first form converts dir/ to insert 'ccan_' prefixes, and\n"
  67. "then adjusts any other ccan directories at the same level which\n"
  68. "are effected.\n"
  69. "--adjust does an adjustment for each directory, in case a\n"
  70. "dependency has been namespacized\n");
  71. }
  72. static void add_replace(struct replace **repl, const char *str)
  73. {
  74. struct replace *new, *i;
  75. /* Avoid duplicates. */
  76. for (i = *repl; i; i = i->next)
  77. if (streq(i->string, str))
  78. return;
  79. new = talloc(*repl, struct replace);
  80. new->next = *repl;
  81. new->string = talloc_strdup(new, str);
  82. *repl = new;
  83. }
  84. static void add_replace_tok(struct replace **repl, const char *s)
  85. {
  86. struct replace *new;
  87. unsigned int len = strspn(s, IDENT_CHARS);
  88. new = talloc(*repl, struct replace);
  89. new->next = *repl;
  90. new->string = talloc_strndup(new, s, len);
  91. *repl = new;
  92. }
  93. static void look_for_macros(char *contents, struct replace **repl)
  94. {
  95. char *p;
  96. enum { LINESTART, HASH, DEFINE, NONE } state = LINESTART;
  97. /* Look for lines of form #define X */
  98. for (p = contents; *p; p++) {
  99. if (*p == '\n')
  100. state = LINESTART;
  101. else if (!cisspace(*p)) {
  102. if (state == LINESTART && *p == '#')
  103. state = HASH;
  104. else if (state==HASH && !strncmp(p, "define", 6)) {
  105. state = DEFINE;
  106. p += 5;
  107. } else if (state == DEFINE) {
  108. unsigned int len;
  109. len = strspn(p, IDENT_CHARS);
  110. if (len) {
  111. char *s;
  112. s = talloc_strndup(contents, p, len);
  113. /* Don't wrap idempotent wrappers */
  114. if (!strstarts(s, "CCAN_")) {
  115. verbose("Found %s\n", s);
  116. add_replace(repl, s);
  117. }
  118. }
  119. state = NONE;
  120. } else
  121. state = NONE;
  122. }
  123. }
  124. }
  125. /* Blank out preprocessor lines, and eliminate \ */
  126. static void preprocess(char *p)
  127. {
  128. char *s;
  129. /* We assume backslashes are only used for macros. */
  130. while ((s = strstr(p, "\\\n")) != NULL)
  131. s[0] = s[1] = ' ';
  132. /* Now eliminate # lines. */
  133. if (p[0] == '#') {
  134. unsigned int i;
  135. for (i = 0; p[i] != '\n'; i++)
  136. p[i] = ' ';
  137. }
  138. while ((s = strstr(p, "\n#")) != NULL) {
  139. unsigned int i;
  140. for (i = 1; s[i] != '\n'; i++)
  141. s[i] = ' ';
  142. }
  143. }
  144. static char *get_statement(const void *ctx, char **p)
  145. {
  146. unsigned brackets = 0;
  147. bool seen_brackets = false;
  148. char *answer = talloc_strdup(ctx, "");
  149. for (;;) {
  150. if ((*p)[0] == '/' && (*p)[1] == '/')
  151. *p += strcspn(*p, "\n");
  152. else if ((*p)[0] == '/' && (*p)[1] == '*')
  153. *p = strstr(*p, "*/") + 1;
  154. else {
  155. char c = **p;
  156. if (c == ';' && !brackets) {
  157. (*p)++;
  158. return answer;
  159. }
  160. /* Compress whitespace into a single ' ' */
  161. if (cisspace(c)) {
  162. c = ' ';
  163. while (cisspace((*p)[1]))
  164. (*p)++;
  165. } else if (c == '{' || c == '(' || c == '[') {
  166. if (c == '(')
  167. seen_brackets = true;
  168. brackets++;
  169. } else if (c == '}' || c == ')' || c == ']')
  170. brackets--;
  171. if (answer[0] != '\0' || c != ' ') {
  172. answer = talloc_realloc(NULL, answer, char,
  173. strlen(answer) + 2);
  174. answer[strlen(answer)+1] = '\0';
  175. answer[strlen(answer)] = c;
  176. }
  177. if (c == '}' && seen_brackets && brackets == 0) {
  178. (*p)++;
  179. return answer;
  180. }
  181. }
  182. (*p)++;
  183. if (**p == '\0')
  184. return NULL;
  185. }
  186. }
  187. /* This hack should handle well-formatted code. */
  188. static void look_for_definitions(char *contents, struct replace **repl)
  189. {
  190. char *stmt, *p = contents;
  191. preprocess(contents);
  192. while ((stmt = get_statement(contents, &p)) != NULL) {
  193. int i, len;
  194. /* Definition of struct/union? */
  195. if ((strncmp(stmt, "struct", 5) == 0
  196. || strncmp(stmt, "union", 5) == 0)
  197. && strchr(stmt, '{') && stmt[7] != '{')
  198. add_replace_tok(repl, stmt+7);
  199. /* Definition of var or typedef? */
  200. for (i = strlen(stmt)-1; i >= 0; i--)
  201. if (strspn(stmt+i, IDENT_CHARS) == 0)
  202. break;
  203. if (i != strlen(stmt)-1) {
  204. add_replace_tok(repl, stmt+i+1);
  205. continue;
  206. }
  207. /* function or array declaration? */
  208. len = strspn(stmt, IDENT_CHARS "* ");
  209. if (len > 0 && (stmt[len] == '(' || stmt[len] == '[')) {
  210. if (strspn(stmt + len + 1, IDENT_CHARS) != 0) {
  211. for (i = len-1; i >= 0; i--)
  212. if (strspn(stmt+i, IDENT_CHARS) == 0)
  213. break;
  214. if (i != len-1) {
  215. add_replace_tok(repl, stmt+i+1);
  216. continue;
  217. }
  218. } else {
  219. /* Pointer to function? */
  220. len++;
  221. len += strspn(stmt + len, " *");
  222. i = strspn(stmt + len, IDENT_CHARS);
  223. if (i > 0 && stmt[len + i] == ')')
  224. add_replace_tok(repl, stmt+len);
  225. }
  226. }
  227. }
  228. }
  229. /* FIXME: Only does main header, should chase local includes. */
  230. static void analyze_headers(const char *dir, struct replace **repl)
  231. {
  232. char *hdr, *contents;
  233. /* Get hold of header, assume that's it. */
  234. hdr = talloc_asprintf(dir, "%s/%s.h", dir, talloc_basename(dir, dir));
  235. contents = grab_file(dir, hdr, NULL);
  236. if (!contents)
  237. err(1, "Reading %s", hdr);
  238. verbose("Looking in %s for macros\n", hdr);
  239. verbose_indent();
  240. look_for_macros(contents, repl);
  241. verbose_unindent();
  242. verbose("Looking in %s for symbols\n", hdr);
  243. verbose_indent();
  244. look_for_definitions(contents, repl);
  245. verbose_unindent();
  246. }
  247. static void write_replacement_file(const char *dir, struct replace **repl)
  248. {
  249. char *replname = talloc_asprintf(dir, "%s/.namespacize", dir);
  250. int fd;
  251. struct replace *r;
  252. fd = open(replname, O_WRONLY|O_CREAT|O_EXCL, 0644);
  253. if (fd < 0) {
  254. if (errno == EEXIST)
  255. errx(1, "%s already exists: can't namespacize twice",
  256. replname);
  257. err(1, "Opening %s", replname);
  258. }
  259. for (r = *repl; r; r = r->next) {
  260. if (write(fd,r->string,strlen(r->string)) != strlen(r->string)
  261. || write(fd, "\n", 1) != 1) {
  262. unlink_no_errno(replname);
  263. if (errno == 0)
  264. errx(1, "Short write to %s: disk full?",
  265. replname);
  266. errx(1, "Writing to %s", replname);
  267. }
  268. }
  269. close(fd);
  270. }
  271. static int unlink_destroy(char *name)
  272. {
  273. unlink(name);
  274. return 0;
  275. }
  276. static char *find_word(char *f, const char *str)
  277. {
  278. char *p = f;
  279. while ((p = strstr(p, str)) != NULL) {
  280. /* Check it's not in the middle of a word. */
  281. if (p > f && (cisalnum(p[-1]) || p[-1] == '_')) {
  282. p++;
  283. continue;
  284. }
  285. if (cisalnum(p[strlen(str)]) || p[strlen(str)] == '_') {
  286. p++;
  287. continue;
  288. }
  289. return p;
  290. }
  291. return NULL;
  292. }
  293. /* This is horribly inefficient but simple. */
  294. static const char *rewrite_file(const char *filename,
  295. const struct replace *repl)
  296. {
  297. char *newname, *file;
  298. int fd;
  299. verbose("Rewriting %s\n", filename);
  300. file = grab_file(filename, filename, NULL);
  301. if (!file)
  302. err(1, "Reading file %s", filename);
  303. for (; repl; repl = repl->next) {
  304. char *p;
  305. while ((p = find_word(file, repl->string)) != NULL) {
  306. unsigned int off;
  307. char *new = talloc_array(file, char, strlen(file)+6);
  308. off = p - file;
  309. memcpy(new, file, off);
  310. if (cisupper(repl->string[0]))
  311. memcpy(new + off, "CCAN_", 5);
  312. else
  313. memcpy(new + off, "ccan_", 5);
  314. strcpy(new + off + 5, file + off);
  315. file = new;
  316. }
  317. }
  318. /* If we exit for some reason, we want this erased. */
  319. newname = talloc_asprintf(talloc_autofree_context(), "%s.tmp",
  320. filename);
  321. fd = open(newname, O_WRONLY|O_CREAT|O_EXCL, 0644);
  322. if (fd < 0)
  323. err(1, "Creating %s", newname);
  324. talloc_set_destructor(newname, unlink_destroy);
  325. if (write(fd, file, strlen(file)) != strlen(file)) {
  326. if (errno == 0)
  327. errx(1, "Short write to %s: disk full?", newname);
  328. errx(1, "Writing to %s", newname);
  329. }
  330. close(fd);
  331. return newname;
  332. }
  333. struct adjusted
  334. {
  335. struct adjusted *next;
  336. const char *file;
  337. const char *tmpfile;
  338. };
  339. static void setup_adjust_files(const char *dir,
  340. const struct replace *repl,
  341. struct adjusted **adj)
  342. {
  343. char **files;
  344. for (files = get_dir(dir); *files; files++) {
  345. if (strends(*files, "/test"))
  346. setup_adjust_files(*files, repl, adj);
  347. else if (strends(*files, ".c") || strends(*files, ".h")) {
  348. struct adjusted *a = talloc(dir, struct adjusted);
  349. a->next = *adj;
  350. a->file = *files;
  351. a->tmpfile = rewrite_file(a->file, repl);
  352. *adj = a;
  353. }
  354. }
  355. }
  356. /* This is the "commit" stage, so we hope it won't fail. */
  357. static void rename_files(const struct adjusted *adj)
  358. {
  359. while (adj) {
  360. if (!move_file(adj->tmpfile, adj->file))
  361. warn("Could not rename over '%s', we're in trouble",
  362. adj->file);
  363. adj = adj->next;
  364. }
  365. }
  366. static void convert_dir(const char *dir)
  367. {
  368. char *name;
  369. struct replace *replace = NULL;
  370. struct adjusted *adj = NULL;
  371. /* Remove any ugly trailing slashes. */
  372. name = talloc_strdup(NULL, dir);
  373. while (strends(name, "/"))
  374. name[strlen(name)-1] = '\0';
  375. analyze_headers(name, &replace);
  376. write_replacement_file(name, &replace);
  377. setup_adjust_files(name, replace, &adj);
  378. rename_files(adj);
  379. talloc_free(name);
  380. talloc_free(replace);
  381. }
  382. static struct replace *read_replacement_file(const char *depdir)
  383. {
  384. struct replace *repl = NULL;
  385. char *replname = talloc_asprintf(depdir, "%s/.namespacize", depdir);
  386. char *file, **line;
  387. file = grab_file(replname, replname, NULL);
  388. if (!file) {
  389. if (errno != ENOENT)
  390. err(1, "Opening %s", replname);
  391. return NULL;
  392. }
  393. for (line = strsplit(file, file, "\n"); *line; line++)
  394. add_replace(&repl, *line);
  395. return repl;
  396. }
  397. static void adjust_dir(const char *dir)
  398. {
  399. char *parent = talloc_dirname(talloc_autofree_context(), dir);
  400. char **deps;
  401. verbose("Adjusting %s\n", dir);
  402. verbose_indent();
  403. for (deps = get_deps(parent, dir, false, NULL); *deps; deps++) {
  404. char *depdir;
  405. struct adjusted *adj = NULL;
  406. struct replace *repl;
  407. depdir = talloc_asprintf(parent, "%s/%s", parent, *deps);
  408. repl = read_replacement_file(depdir);
  409. if (repl) {
  410. verbose("%s has been namespacized\n", depdir);
  411. setup_adjust_files(parent, repl, &adj);
  412. rename_files(adj);
  413. } else
  414. verbose("%s has not been namespacized\n", depdir);
  415. talloc_free(depdir);
  416. }
  417. verbose_unindent();
  418. talloc_free(parent);
  419. }
  420. static void adjust_dependents(const char *dir)
  421. {
  422. char *parent = talloc_dirname(NULL, dir);
  423. char *base = talloc_basename(parent, dir);
  424. char **file;
  425. verbose("Looking for dependents in %s\n", parent);
  426. verbose_indent();
  427. for (file = get_dir(parent); *file; file++) {
  428. char *info, **deps;
  429. bool isdep = false;
  430. if (talloc_basename(*file, *file)[0] == '.')
  431. continue;
  432. info = talloc_asprintf(*file, "%s/_info", *file);
  433. if (access(info, R_OK) != 0)
  434. continue;
  435. for (deps = get_deps(*file, *file, false, NULL);
  436. *deps; deps++) {
  437. if (!strstarts(*deps, "ccan/"))
  438. continue;
  439. if (streq(*deps + strlen("ccan/"), base))
  440. isdep = true;
  441. }
  442. if (isdep)
  443. adjust_dir(*file);
  444. else
  445. verbose("%s is not dependent\n", *file);
  446. }
  447. verbose_unindent();
  448. }
  449. int main(int argc, char *argv[])
  450. {
  451. if (argv[1] && streq(argv[1], "--verbose")) {
  452. verbose = true;
  453. argv++;
  454. argc--;
  455. }
  456. if (argc == 2) {
  457. verbose("Namespacizing %s\n", argv[1]);
  458. verbose_indent();
  459. convert_dir(argv[1]);
  460. adjust_dependents(argv[1]);
  461. verbose_unindent();
  462. return 0;
  463. }
  464. if (argc > 2 && streq(argv[1], "--adjust")) {
  465. unsigned int i;
  466. for (i = 2; i < argc; i++)
  467. adjust_dir(argv[i]);
  468. return 0;
  469. }
  470. usage();
  471. }