Broaden use of doc_extract code, put in ccanlint, and fix ccanlint
[ccan] / tools / doc_extract-core.c
1 /* This merely extracts, doesn't do XML or anything. */
2 #include <err.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <stdbool.h>
11 #include <ctype.h>
12 #include <ccan/talloc/talloc.h>
13 #include <ccan/str/str.h>
14 #include "doc_extract.h"
15 #include "tools.h"
16
17 static char **grab_doc(char **lines, unsigned int num)
18 {
19         char **ret;
20         unsigned int i;
21         bool printing = false;
22
23         ret = talloc_array(NULL, char *, num+1);
24
25         num = 0;
26         for (i = 0; lines[i]; i++) {
27                 if (streq(lines[i], "/**")) {
28                         printing = true;
29                         if (num != 0)
30                                 talloc_append_string(ret[num-1], "\n");
31                 } else if (streq(lines[i], " */")) 
32                         printing = false;
33                 else if (printing) {
34                         if (strstarts(lines[i], " * "))
35                                 ret[num++] = talloc_strdup(ret, lines[i]+3);
36                         else if (strstarts(lines[i], " *"))
37                                 ret[num++] = talloc_strdup(ret, lines[i]+2);
38                         else
39                                 errx(1, "Malformed line %u", i);
40                 }
41         }
42         ret[num] = NULL;
43         return ret;
44 }
45
46 static bool is_blank(const char *line)
47 {
48         return line && line[strspn(line, " \t\n")] == '\0';
49 }
50
51 static bool is_section(const char *line, bool one_liner)
52 {
53         unsigned int len;
54
55         if (!isupper(line[0]))
56                 return false;
57         len = strspn(line, IDENT_CHARS);
58         if (line[len] != ':')
59                 return false;
60
61         /* If it can be a one-liner, a space is sufficient.*/
62         if (one_liner)
63                 return (line[len+1] == ' ' || line[len+1] == '\t');
64
65         return line[len] == ':' && is_blank(line+len+1);
66 }
67
68 /* Summary line is form '<identifier> - ' */
69 static bool is_summary_line(const char *line)
70 {
71         unsigned int id_len;
72
73         id_len = strspn(line, IDENT_CHARS);
74         if (id_len == 0)
75                 return false;
76         if (!strstarts(line + id_len, " - "))
77                 return false;
78
79         return true;
80 }
81
82 static struct doc_section *new_section(struct list_head *list,
83                                        const char *function,
84                                        const char *type)
85 {
86         struct doc_section *d = talloc(list, struct doc_section);
87         d->function = function;
88         d->type = type;
89         d->lines = NULL;
90         d->num_lines = 0;
91         list_add_tail(list, &d->list);
92         return d;
93 }
94
95 static void add_line(struct doc_section *curr, const char *line)
96 {
97         curr->lines = talloc_realloc(curr, curr->lines, char *,
98                                      curr->num_lines+1);
99         curr->lines[curr->num_lines++] = talloc_strdup(curr->lines, line);
100 }
101
102 struct list_head *extract_doc_sections(char **rawlines, unsigned int num)
103 {
104         char **lines = grab_doc(rawlines, num);
105         const char *function = NULL;
106         struct doc_section *curr = NULL;
107         unsigned int i;
108         struct list_head *list;
109
110         list = talloc(NULL, struct list_head);
111         list_head_init(list);
112
113         for (i = 0; lines[i]; i++) {
114                 if (is_summary_line(lines[i])) {
115                         function = talloc_strndup(list, lines[i],
116                                                   strcspn(lines[i], " "));
117                         curr = new_section(list, function, "summary");
118                         add_line(curr, strstr(lines[i], " - ") + 3);
119                         curr = new_section(list, function, "description");
120                 } else if (is_section(lines[i], false)) {
121                         char *type = talloc_strndup(curr, lines[i],
122                                                     strcspn(lines[i], ":"));
123                         curr = new_section(list, function, type);
124                 } else if (is_section(lines[i], true)) {
125                         unsigned int sectlen = strcspn(lines[i], ":");
126                         char *type = talloc_strndup(curr, lines[i], sectlen);
127                         curr = new_section(list, function, type);
128                         add_line(curr, lines[i] + sectlen + 1
129                                  + strspn(lines[i] + sectlen + 1, " \t"));
130                 } else {
131                         if (!curr)
132                                 continue;
133                         add_line(curr, lines[i]);
134                 }
135         }
136         talloc_free(lines);
137         return list;
138 }