docproc.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. /*
  2. * docproc is a simple preprocessor for the template files
  3. * used as placeholders for the kernel internal documentation.
  4. * docproc is used for documentation-frontend and
  5. * dependency-generator.
  6. * The two usages have in common that they require
  7. * some knowledge of the .tmpl syntax, therefore they
  8. * are kept together.
  9. *
  10. * documentation-frontend
  11. * Scans the template file and call kernel-doc for
  12. * all occurrences of ![EIF]file
  13. * Beforehand each referenced file is scanned for
  14. * any symbols that are exported via these macros:
  15. * EXPORT_SYMBOL(), EXPORT_SYMBOL_GPL(), &
  16. * EXPORT_SYMBOL_GPL_FUTURE()
  17. * This is used to create proper -function and
  18. * -nofunction arguments in calls to kernel-doc.
  19. * Usage: docproc doc file.tmpl
  20. *
  21. * dependency-generator:
  22. * Scans the template file and list all files
  23. * referenced in a format recognized by make.
  24. * Usage: docproc depend file.tmpl
  25. * Writes dependency information to stdout
  26. * in the following format:
  27. * file.tmpl src.c src2.c
  28. * The filenames are obtained from the following constructs:
  29. * !Efilename
  30. * !Ifilename
  31. * !Dfilename
  32. * !Ffilename
  33. * !Pfilename
  34. *
  35. */
  36. #define _GNU_SOURCE
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <ctype.h>
  41. #include <unistd.h>
  42. #include <limits.h>
  43. #include <errno.h>
  44. #include <getopt.h>
  45. #include <sys/types.h>
  46. #include <sys/wait.h>
  47. #include <time.h>
  48. /* exitstatus is used to keep track of any failing calls to kernel-doc,
  49. * but execution continues. */
  50. int exitstatus = 0;
  51. typedef void DFL(char *);
  52. DFL *defaultline;
  53. typedef void FILEONLY(char * file);
  54. FILEONLY *internalfunctions;
  55. FILEONLY *externalfunctions;
  56. FILEONLY *symbolsonly;
  57. FILEONLY *findall;
  58. typedef void FILELINE(char * file, char * line);
  59. FILELINE * singlefunctions;
  60. FILELINE * entity_system;
  61. FILELINE * docsection;
  62. #define MAXLINESZ 2048
  63. #define MAXFILES 250
  64. #define KERNELDOCPATH "scripts/"
  65. #define KERNELDOC "kernel-doc"
  66. #define DOCBOOK "-docbook"
  67. #define RST "-rst"
  68. #define LIST "-list"
  69. #define FUNCTION "-function"
  70. #define NOFUNCTION "-nofunction"
  71. #define NODOCSECTIONS "-no-doc-sections"
  72. #define SHOWNOTFOUND "-show-not-found"
  73. enum file_format {
  74. FORMAT_AUTO,
  75. FORMAT_DOCBOOK,
  76. FORMAT_RST,
  77. };
  78. static enum file_format file_format = FORMAT_AUTO;
  79. #define KERNELDOC_FORMAT (file_format == FORMAT_RST ? RST : DOCBOOK)
  80. static char *srctree, *kernsrctree;
  81. static char **all_list = NULL;
  82. static int all_list_len = 0;
  83. static void consume_symbol(const char *sym)
  84. {
  85. int i;
  86. for (i = 0; i < all_list_len; i++) {
  87. if (!all_list[i])
  88. continue;
  89. if (strcmp(sym, all_list[i]))
  90. continue;
  91. all_list[i] = NULL;
  92. break;
  93. }
  94. }
  95. static void usage (void)
  96. {
  97. fprintf(stderr, "Usage: docproc [{--docbook|--rst}] {doc|depend} file\n");
  98. fprintf(stderr, "Input is read from file.tmpl. Output is sent to stdout\n");
  99. fprintf(stderr, "doc: frontend when generating kernel documentation\n");
  100. fprintf(stderr, "depend: generate list of files referenced within file\n");
  101. fprintf(stderr, "Environment variable SRCTREE: absolute path to sources.\n");
  102. fprintf(stderr, " KBUILD_SRC: absolute path to kernel source tree.\n");
  103. }
  104. /*
  105. * Execute kernel-doc with parameters given in svec
  106. */
  107. static void exec_kernel_doc(char **svec)
  108. {
  109. pid_t pid;
  110. int ret;
  111. char real_filename[PATH_MAX + 1];
  112. /* Make sure output generated so far are flushed */
  113. fflush(stdout);
  114. switch (pid=fork()) {
  115. case -1:
  116. perror("fork");
  117. exit(1);
  118. case 0:
  119. memset(real_filename, 0, sizeof(real_filename));
  120. strncat(real_filename, kernsrctree, PATH_MAX);
  121. strncat(real_filename, "/" KERNELDOCPATH KERNELDOC,
  122. PATH_MAX - strlen(real_filename));
  123. execvp(real_filename, svec);
  124. fprintf(stderr, "exec ");
  125. perror(real_filename);
  126. exit(1);
  127. default:
  128. waitpid(pid, &ret ,0);
  129. }
  130. if (WIFEXITED(ret))
  131. exitstatus |= WEXITSTATUS(ret);
  132. else
  133. exitstatus = 0xff;
  134. }
  135. /* Types used to create list of all exported symbols in a number of files */
  136. struct symbols
  137. {
  138. char *name;
  139. };
  140. struct symfile
  141. {
  142. char *filename;
  143. struct symbols *symbollist;
  144. int symbolcnt;
  145. };
  146. struct symfile symfilelist[MAXFILES];
  147. int symfilecnt = 0;
  148. static void add_new_symbol(struct symfile *sym, char * symname)
  149. {
  150. sym->symbollist =
  151. realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
  152. sym->symbollist[sym->symbolcnt++].name = strdup(symname);
  153. }
  154. /* Add a filename to the list */
  155. static struct symfile * add_new_file(char * filename)
  156. {
  157. symfilelist[symfilecnt++].filename = strdup(filename);
  158. return &symfilelist[symfilecnt - 1];
  159. }
  160. /* Check if file already are present in the list */
  161. static struct symfile * filename_exist(char * filename)
  162. {
  163. int i;
  164. for (i=0; i < symfilecnt; i++)
  165. if (strcmp(symfilelist[i].filename, filename) == 0)
  166. return &symfilelist[i];
  167. return NULL;
  168. }
  169. /*
  170. * List all files referenced within the template file.
  171. * Files are separated by tabs.
  172. */
  173. static void adddep(char * file) { printf("\t%s", file); }
  174. static void adddep2(char * file, char * line) { line = line; adddep(file); }
  175. static void noaction(char * line) { line = line; }
  176. static void noaction2(char * file, char * line) { file = file; line = line; }
  177. /* Echo the line without further action */
  178. static void printline(char * line) { printf("%s", line); }
  179. /*
  180. * Find all symbols in filename that are exported with EXPORT_SYMBOL &
  181. * EXPORT_SYMBOL_GPL (& EXPORT_SYMBOL_GPL_FUTURE implicitly).
  182. * All symbols located are stored in symfilelist.
  183. */
  184. static void find_export_symbols(char * filename)
  185. {
  186. FILE * fp;
  187. struct symfile *sym;
  188. char line[MAXLINESZ];
  189. if (filename_exist(filename) == NULL) {
  190. char real_filename[PATH_MAX + 1];
  191. memset(real_filename, 0, sizeof(real_filename));
  192. strncat(real_filename, srctree, PATH_MAX);
  193. strncat(real_filename, "/", PATH_MAX - strlen(real_filename));
  194. strncat(real_filename, filename,
  195. PATH_MAX - strlen(real_filename));
  196. sym = add_new_file(filename);
  197. fp = fopen(real_filename, "r");
  198. if (fp == NULL) {
  199. fprintf(stderr, "docproc: ");
  200. perror(real_filename);
  201. exit(1);
  202. }
  203. while (fgets(line, MAXLINESZ, fp)) {
  204. char *p;
  205. char *e;
  206. if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != NULL) ||
  207. ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
  208. /* Skip EXPORT_SYMBOL{_GPL} */
  209. while (isalnum(*p) || *p == '_')
  210. p++;
  211. /* Remove parentheses & additional whitespace */
  212. while (isspace(*p))
  213. p++;
  214. if (*p != '(')
  215. continue; /* Syntax error? */
  216. else
  217. p++;
  218. while (isspace(*p))
  219. p++;
  220. e = p;
  221. while (isalnum(*e) || *e == '_')
  222. e++;
  223. *e = '\0';
  224. add_new_symbol(sym, p);
  225. }
  226. }
  227. fclose(fp);
  228. }
  229. }
  230. /*
  231. * Document all external or internal functions in a file.
  232. * Call kernel-doc with following parameters:
  233. * kernel-doc [-docbook|-rst] -nofunction function_name1 filename
  234. * Function names are obtained from all the src files
  235. * by find_export_symbols.
  236. * intfunc uses -nofunction
  237. * extfunc uses -function
  238. */
  239. static void docfunctions(char * filename, char * type)
  240. {
  241. int i,j;
  242. int symcnt = 0;
  243. int idx = 0;
  244. char **vec;
  245. for (i=0; i <= symfilecnt; i++)
  246. symcnt += symfilelist[i].symbolcnt;
  247. vec = malloc((2 + 2 * symcnt + 3) * sizeof(char *));
  248. if (vec == NULL) {
  249. perror("docproc: ");
  250. exit(1);
  251. }
  252. vec[idx++] = KERNELDOC;
  253. vec[idx++] = KERNELDOC_FORMAT;
  254. vec[idx++] = NODOCSECTIONS;
  255. for (i=0; i < symfilecnt; i++) {
  256. struct symfile * sym = &symfilelist[i];
  257. for (j=0; j < sym->symbolcnt; j++) {
  258. vec[idx++] = type;
  259. consume_symbol(sym->symbollist[j].name);
  260. vec[idx++] = sym->symbollist[j].name;
  261. }
  262. }
  263. vec[idx++] = filename;
  264. vec[idx] = NULL;
  265. if (file_format == FORMAT_RST)
  266. printf(".. %s\n", filename);
  267. else
  268. printf("<!-- %s -->\n", filename);
  269. exec_kernel_doc(vec);
  270. fflush(stdout);
  271. free(vec);
  272. }
  273. static void intfunc(char * filename) { docfunctions(filename, NOFUNCTION); }
  274. static void extfunc(char * filename) { docfunctions(filename, FUNCTION); }
  275. /*
  276. * Document specific function(s) in a file.
  277. * Call kernel-doc with the following parameters:
  278. * kernel-doc -docbook -function function1 [-function function2]
  279. */
  280. static void singfunc(char * filename, char * line)
  281. {
  282. char *vec[200]; /* Enough for specific functions */
  283. int i, idx = 0;
  284. int startofsym = 1;
  285. vec[idx++] = KERNELDOC;
  286. vec[idx++] = KERNELDOC_FORMAT;
  287. vec[idx++] = SHOWNOTFOUND;
  288. /* Split line up in individual parameters preceded by FUNCTION */
  289. for (i=0; line[i]; i++) {
  290. if (isspace(line[i])) {
  291. line[i] = '\0';
  292. startofsym = 1;
  293. continue;
  294. }
  295. if (startofsym) {
  296. startofsym = 0;
  297. vec[idx++] = FUNCTION;
  298. vec[idx++] = &line[i];
  299. }
  300. }
  301. for (i = 0; i < idx; i++) {
  302. if (strcmp(vec[i], FUNCTION))
  303. continue;
  304. consume_symbol(vec[i + 1]);
  305. }
  306. vec[idx++] = filename;
  307. vec[idx] = NULL;
  308. exec_kernel_doc(vec);
  309. }
  310. /*
  311. * Insert specific documentation section from a file.
  312. * Call kernel-doc with the following parameters:
  313. * kernel-doc -docbook -function "doc section" filename
  314. */
  315. static void docsect(char *filename, char *line)
  316. {
  317. /* kerneldoc -docbook -show-not-found -function "section" file NULL */
  318. char *vec[7];
  319. char *s;
  320. for (s = line; *s; s++)
  321. if (*s == '\n')
  322. *s = '\0';
  323. if (asprintf(&s, "DOC: %s", line) < 0) {
  324. perror("asprintf");
  325. exit(1);
  326. }
  327. consume_symbol(s);
  328. free(s);
  329. vec[0] = KERNELDOC;
  330. vec[1] = KERNELDOC_FORMAT;
  331. vec[2] = SHOWNOTFOUND;
  332. vec[3] = FUNCTION;
  333. vec[4] = line;
  334. vec[5] = filename;
  335. vec[6] = NULL;
  336. exec_kernel_doc(vec);
  337. }
  338. static void find_all_symbols(char *filename)
  339. {
  340. char *vec[4]; /* kerneldoc -list file NULL */
  341. pid_t pid;
  342. int ret, i, count, start;
  343. char real_filename[PATH_MAX + 1];
  344. int pipefd[2];
  345. char *data, *str;
  346. size_t data_len = 0;
  347. vec[0] = KERNELDOC;
  348. vec[1] = LIST;
  349. vec[2] = filename;
  350. vec[3] = NULL;
  351. if (pipe(pipefd)) {
  352. perror("pipe");
  353. exit(1);
  354. }
  355. switch (pid=fork()) {
  356. case -1:
  357. perror("fork");
  358. exit(1);
  359. case 0:
  360. close(pipefd[0]);
  361. dup2(pipefd[1], 1);
  362. memset(real_filename, 0, sizeof(real_filename));
  363. strncat(real_filename, kernsrctree, PATH_MAX);
  364. strncat(real_filename, "/" KERNELDOCPATH KERNELDOC,
  365. PATH_MAX - strlen(real_filename));
  366. execvp(real_filename, vec);
  367. fprintf(stderr, "exec ");
  368. perror(real_filename);
  369. exit(1);
  370. default:
  371. close(pipefd[1]);
  372. data = malloc(4096);
  373. do {
  374. while ((ret = read(pipefd[0],
  375. data + data_len,
  376. 4096)) > 0) {
  377. data_len += ret;
  378. data = realloc(data, data_len + 4096);
  379. }
  380. } while (ret == -EAGAIN);
  381. if (ret != 0) {
  382. perror("read");
  383. exit(1);
  384. }
  385. waitpid(pid, &ret ,0);
  386. }
  387. if (WIFEXITED(ret))
  388. exitstatus |= WEXITSTATUS(ret);
  389. else
  390. exitstatus = 0xff;
  391. count = 0;
  392. /* poor man's strtok, but with counting */
  393. for (i = 0; i < data_len; i++) {
  394. if (data[i] == '\n') {
  395. count++;
  396. data[i] = '\0';
  397. }
  398. }
  399. start = all_list_len;
  400. all_list_len += count;
  401. all_list = realloc(all_list, sizeof(char *) * all_list_len);
  402. str = data;
  403. for (i = 0; i < data_len && start != all_list_len; i++) {
  404. if (data[i] == '\0') {
  405. all_list[start] = str;
  406. str = data + i + 1;
  407. start++;
  408. }
  409. }
  410. }
  411. /*
  412. * Terminate s at first space, if any. If there was a space, return pointer to
  413. * the character after that. Otherwise, return pointer to the terminating NUL.
  414. */
  415. static char *chomp(char *s)
  416. {
  417. while (*s && !isspace(*s))
  418. s++;
  419. if (*s)
  420. *s++ = '\0';
  421. return s;
  422. }
  423. /* Return pointer to directive content, or NULL if not a directive. */
  424. static char *is_directive(char *line)
  425. {
  426. if (file_format == FORMAT_DOCBOOK && line[0] == '!')
  427. return line + 1;
  428. else if (file_format == FORMAT_RST && !strncmp(line, ".. !", 4))
  429. return line + 4;
  430. return NULL;
  431. }
  432. /*
  433. * Parse file, calling action specific functions for:
  434. * 1) Lines containing !E
  435. * 2) Lines containing !I
  436. * 3) Lines containing !D
  437. * 4) Lines containing !F
  438. * 5) Lines containing !P
  439. * 6) Lines containing !C
  440. * 7) Default lines - lines not matching the above
  441. */
  442. static void parse_file(FILE *infile)
  443. {
  444. char line[MAXLINESZ];
  445. char *p, *s;
  446. while (fgets(line, MAXLINESZ, infile)) {
  447. p = is_directive(line);
  448. if (!p) {
  449. defaultline(line);
  450. continue;
  451. }
  452. switch (*p++) {
  453. case 'E':
  454. chomp(p);
  455. externalfunctions(p);
  456. break;
  457. case 'I':
  458. chomp(p);
  459. internalfunctions(p);
  460. break;
  461. case 'D':
  462. chomp(p);
  463. symbolsonly(p);
  464. break;
  465. case 'F':
  466. /* filename */
  467. s = chomp(p);
  468. /* function names */
  469. while (isspace(*s))
  470. s++;
  471. singlefunctions(p, s);
  472. break;
  473. case 'P':
  474. /* filename */
  475. s = chomp(p);
  476. /* DOC: section name */
  477. while (isspace(*s))
  478. s++;
  479. docsection(p, s);
  480. break;
  481. case 'C':
  482. chomp(p);
  483. if (findall)
  484. findall(p);
  485. break;
  486. default:
  487. defaultline(line);
  488. }
  489. }
  490. fflush(stdout);
  491. }
  492. /*
  493. * Is this a RestructuredText template? Answer the question by seeing if its
  494. * name ends in ".rst".
  495. */
  496. static int is_rst(const char *file)
  497. {
  498. char *dot = strrchr(file, '.');
  499. return dot && !strcmp(dot + 1, "rst");
  500. }
  501. enum opts {
  502. OPT_DOCBOOK,
  503. OPT_RST,
  504. OPT_HELP,
  505. };
  506. int main(int argc, char *argv[])
  507. {
  508. const char *subcommand, *filename;
  509. FILE * infile;
  510. int i;
  511. srctree = getenv("SRCTREE");
  512. if (!srctree)
  513. srctree = getcwd(NULL, 0);
  514. kernsrctree = getenv("KBUILD_SRC");
  515. if (!kernsrctree || !*kernsrctree)
  516. kernsrctree = srctree;
  517. for (;;) {
  518. int c;
  519. struct option opts[] = {
  520. { "docbook", no_argument, NULL, OPT_DOCBOOK },
  521. { "rst", no_argument, NULL, OPT_RST },
  522. { "help", no_argument, NULL, OPT_HELP },
  523. {}
  524. };
  525. c = getopt_long_only(argc, argv, "", opts, NULL);
  526. if (c == -1)
  527. break;
  528. switch (c) {
  529. case OPT_DOCBOOK:
  530. file_format = FORMAT_DOCBOOK;
  531. break;
  532. case OPT_RST:
  533. file_format = FORMAT_RST;
  534. break;
  535. case OPT_HELP:
  536. usage();
  537. return 0;
  538. default:
  539. case '?':
  540. usage();
  541. return 1;
  542. }
  543. }
  544. argc -= optind;
  545. argv += optind;
  546. if (argc != 2) {
  547. usage();
  548. exit(1);
  549. }
  550. subcommand = argv[0];
  551. filename = argv[1];
  552. if (file_format == FORMAT_AUTO)
  553. file_format = is_rst(filename) ? FORMAT_RST : FORMAT_DOCBOOK;
  554. /* Open file, exit on error */
  555. infile = fopen(filename, "r");
  556. if (infile == NULL) {
  557. fprintf(stderr, "docproc: ");
  558. perror(filename);
  559. exit(2);
  560. }
  561. if (strcmp("doc", subcommand) == 0) {
  562. if (file_format == FORMAT_RST) {
  563. time_t t = time(NULL);
  564. printf(".. generated from %s by docproc %s\n",
  565. filename, ctime(&t));
  566. }
  567. /* Need to do this in two passes.
  568. * First pass is used to collect all symbols exported
  569. * in the various files;
  570. * Second pass generate the documentation.
  571. * This is required because some functions are declared
  572. * and exported in different files :-((
  573. */
  574. /* Collect symbols */
  575. defaultline = noaction;
  576. internalfunctions = find_export_symbols;
  577. externalfunctions = find_export_symbols;
  578. symbolsonly = find_export_symbols;
  579. singlefunctions = noaction2;
  580. docsection = noaction2;
  581. findall = find_all_symbols;
  582. parse_file(infile);
  583. /* Rewind to start from beginning of file again */
  584. fseek(infile, 0, SEEK_SET);
  585. defaultline = printline;
  586. internalfunctions = intfunc;
  587. externalfunctions = extfunc;
  588. symbolsonly = printline;
  589. singlefunctions = singfunc;
  590. docsection = docsect;
  591. findall = NULL;
  592. parse_file(infile);
  593. for (i = 0; i < all_list_len; i++) {
  594. if (!all_list[i])
  595. continue;
  596. fprintf(stderr, "Warning: didn't use docs for %s\n",
  597. all_list[i]);
  598. }
  599. } else if (strcmp("depend", subcommand) == 0) {
  600. /* Create first part of dependency chain
  601. * file.tmpl */
  602. printf("%s\t", filename);
  603. defaultline = noaction;
  604. internalfunctions = adddep;
  605. externalfunctions = adddep;
  606. symbolsonly = adddep;
  607. singlefunctions = adddep2;
  608. docsection = adddep2;
  609. findall = adddep;
  610. parse_file(infile);
  611. printf("\n");
  612. } else {
  613. fprintf(stderr, "Unknown option: %s\n", subcommand);
  614. exit(1);
  615. }
  616. fclose(infile);
  617. fflush(stdout);
  618. return exitstatus;
  619. }