1 /* This merely extracts, doesn't do XML or anything. */
2 #include <ccan/take/take.h>
3 #include <ccan/str/str.h>
14 #include "doc_extract.h"
17 static char **grab_doc(char **lines, unsigned int **linemap,
22 bool printing = false;
24 ret = tal_arr(NULL, char *, tal_count(lines));
25 *linemap = tal_arr(ret, unsigned int, tal_count(lines));
28 for (i = 0; lines[i]; i++) {
29 if (streq(lines[i], "/**")) {
32 ret[num-1] = tal_strcat(NULL,
33 take(ret[num-1]), "\n");
35 } else if (streq(lines[i], " */"))
38 if (strstarts(lines[i], " * "))
39 ret[num++] = tal_strdup(ret, lines[i]+3);
40 else if (strstarts(lines[i], " *"))
41 ret[num++] = tal_strdup(ret, lines[i]+2);
43 /* Weird, malformed? */
47 " Expected ' *' in comment.",
51 ret[num++] = tal_strdup(ret, lines[i]);
52 if (strstr(lines[i], "*/"))
55 (*linemap)[num-1] = i;
62 static bool is_blank(const char *line)
64 return line && line[strspn(line, " \t\n")] == '\0';
67 static char *is_section(const void *ctx, const char *line, char **value)
71 /* Any number of upper case words separated by spaces, ending in : */
72 if (!tal_strreg(ctx, line,
73 "^([A-Z][a-zA-Z0-9_]*( [A-Z][a-zA-Z0-9_]*)*):[ \t\n]*(.*)",
74 &secname, NULL, value))
80 /* Summary line is form '<identifier> - ' (spaces for 'struct foo -') */
81 static unsigned int is_summary_line(const char *line)
85 /* We allow /, because it can be in (nested) module names. */
86 id_len = strspn(line, IDENT_CHARS" /");
89 if (strspn(line, " ") == id_len)
91 if (!strstarts(line + id_len-1, " - "))
96 static bool empty_section(struct doc_section *d)
100 for (i = 0; i < d->num_lines; i++)
101 if (!is_blank(d->lines[i]))
106 static struct doc_section *new_section(struct list_head *list,
107 const char *function,
109 unsigned int srcline)
111 struct doc_section *d;
115 /* If previous section was empty, delete it. */
116 d = list_tail(list, struct doc_section, list);
117 if (d && empty_section(d)) {
122 d = tal(list, struct doc_section);
123 d->function = function;
124 lowertype = tal_arr(d, char, strlen(type) + 1);
125 /* Canonicalize type to lower case. */
126 for (i = 0; i < strlen(type)+1; i++)
127 lowertype[i] = tolower(type[i]);
129 d->lines = tal_arr(d, char *, 0);
131 d->srcline = srcline;
133 list_add_tail(list, &d->list);
137 static void add_line(struct doc_section *curr, const char *line)
139 char *myline = tal_strdup(curr->lines, line);
140 tal_expand(&curr->lines, &myline, 1);
144 /* We convert tabs to spaces here. */
145 static void add_detabbed_line(struct doc_section *curr, const char *rawline)
147 unsigned int i, eff_i, len, off = 0;
150 /* Worst-case alloc: 8 spaces per tab. */
151 line = tal_arr(curr, char, strlen(rawline) +
152 strcount(rawline, "\t") * 7 + 1);
155 /* We keep track of the *effective* offset of i. */
156 for (i = eff_i = 0; i < strlen(rawline); i++) {
157 if (rawline[i] == '\t') {
161 } while (eff_i % 8 != 0);
163 line[len++] = rawline[i];
164 if (off == 0 && rawline[i] == '*')
171 add_line(curr, line + off);
175 /* Not very efficient: we could track prefix length while doing
176 * add_detabbed_line */
177 static void trim_lines(struct doc_section *curr)
179 unsigned int i, trim = -1;
180 int last_non_empty = -1;
182 /* Get minimum whitespace prefix. */
183 for (i = 0; i < curr->num_lines; i++) {
184 unsigned int prefix = strspn(curr->lines[i], " ");
185 /* Ignore blank lines */
186 if (curr->lines[i][prefix] == '\0')
193 for (i = 0; i < curr->num_lines; i++) {
194 unsigned int prefix = strspn(curr->lines[i], " ");
196 curr->lines[i] += prefix;
198 curr->lines[i] += trim;
200 /* All blank? Potential to trim. */
201 if (curr->lines[i][strspn(curr->lines[i], " \t")] != '\0')
205 /* Remove trailing blank lines. */
206 curr->num_lines = last_non_empty + 1;
209 struct list_head *extract_doc_sections(char **rawlines, const char *file)
211 unsigned int *linemap;
212 char **lines = grab_doc(rawlines, &linemap, file);
213 const char *function = NULL;
214 struct doc_section *curr = NULL;
216 struct list_head *list;
218 list = tal(NULL, struct list_head);
219 list_head_init(list);
221 for (i = 0; lines[i]; i++) {
225 funclen = is_summary_line(lines[i]);
227 function = tal_strndup(list, lines[i], funclen);
228 curr = new_section(list, function, "summary",
230 add_line(curr, lines[i] + funclen + 3);
231 curr = new_section(list, function, "description",
233 } else if ((type = is_section(list, lines[i], &extra)) != NULL){
234 curr = new_section(list, function, type, linemap[i]);
235 if (!streq(extra, "")) {
236 add_line(curr, extra);
241 add_detabbed_line(curr, rawlines[linemap[i]]);
245 list_for_each(list, curr, list)