namespacize.c 12 KB

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