ccanlint: use strreg for section extraction.
[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 <ccan/str_talloc/str_talloc.h>
15 #include "doc_extract.h"
16 #include "tools.h"
17
18 static char **grab_doc(char **lines, unsigned int num)
19 {
20         char **ret;
21         unsigned int i;
22         bool printing = false;
23
24         ret = talloc_array(NULL, char *, num+1);
25
26         num = 0;
27         for (i = 0; lines[i]; i++) {
28                 if (streq(lines[i], "/**")) {
29                         printing = true;
30                         if (num != 0) {
31                                 ret[num-1] = talloc_append_string(ret[num-1],
32                                                                   "\n");
33                         }
34                 } else if (streq(lines[i], " */")) 
35                         printing = false;
36                 else if (printing) {
37                         if (strstarts(lines[i], " * "))
38                                 ret[num++] = talloc_strdup(ret, lines[i]+3);
39                         else if (strstarts(lines[i], " *"))
40                                 ret[num++] = talloc_strdup(ret, lines[i]+2);
41                         else
42                                 errx(1, "Malformed line %u", i);
43                 }
44         }
45         ret[num] = NULL;
46         return ret;
47 }
48
49 static bool is_blank(const char *line)
50 {
51         return line && line[strspn(line, " \t\n")] == '\0';
52 }
53
54 static char *is_section(const void *ctx, const char *line, char **value)
55 {
56         char *secname;
57
58         /* Any number of upper case words separated by spaces, ending in : */
59         if (!strreg(ctx, line,
60                     "^([A-Z][a-zA-Z0-9_]*( [A-Z][a-zA-Z0-9_]*)*):[ \t\n]*(.*)",
61                     &secname, NULL, value))
62                 return NULL;
63
64         return secname;
65 }
66
67 /* Summary line is form '<identifier> - ' (spaces for 'struct foo -') */
68 static unsigned int is_summary_line(const char *line)
69 {
70         unsigned int id_len;
71
72         id_len = strspn(line, IDENT_CHARS" ");
73         if (id_len == 0)
74                 return 0;
75         if (strspn(line, " ") == id_len)
76                 return 0;
77         if (!strstarts(line + id_len-1, " - "))
78                 return 0;
79         return id_len - 1;
80 }
81
82 static bool empty_section(struct doc_section *d)
83 {
84         unsigned int i;
85
86         for (i = 0; i < d->num_lines; i++)
87                 if (!is_blank(d->lines[i]))
88                         return false;
89         return true;
90 }
91
92 static struct doc_section *new_section(struct list_head *list,
93                                        const char *function,
94                                        const char *type)
95 {
96         struct doc_section *d;
97         char *lowertype;
98         unsigned int i;
99
100         /* If previous section was empty, delete it. */
101         d = list_tail(list, struct doc_section, list);
102         if (d && empty_section(d)) {
103                 list_del(&d->list);
104                 talloc_free(d);
105         }
106
107         d = talloc(list, struct doc_section);
108         d->function = function;
109         lowertype = talloc_size(d, strlen(type) + 1);
110         /* Canonicalize type to lower case. */
111         for (i = 0; i < strlen(type)+1; i++)
112                 lowertype[i] = tolower(type[i]);
113         d->type = lowertype;
114         d->lines = NULL;
115         d->num_lines = 0;
116
117         list_add_tail(list, &d->list);
118         return d;
119 }
120
121 static void add_line(struct doc_section *curr, const char *line)
122 {
123         curr->lines = talloc_realloc(curr, curr->lines, char *,
124                                      curr->num_lines+1);
125         curr->lines[curr->num_lines++] = talloc_strdup(curr->lines, line);
126 }
127
128 struct list_head *extract_doc_sections(char **rawlines, unsigned int num)
129 {
130         char **lines = grab_doc(rawlines, num);
131         const char *function = NULL;
132         struct doc_section *curr = NULL;
133         unsigned int i;
134         struct list_head *list;
135
136         list = talloc(NULL, struct list_head);
137         list_head_init(list);
138
139         for (i = 0; lines[i]; i++) {
140                 unsigned funclen;
141                 char *type, *extra;
142
143                 funclen = is_summary_line(lines[i]);
144                 if (funclen) {
145                         function = talloc_strndup(list, lines[i], funclen);
146                         curr = new_section(list, function, "summary");
147                         add_line(curr, lines[i] + funclen + 3);
148                         curr = new_section(list, function, "description");
149                 } else if ((type = is_section(list, lines[i], &extra)) != NULL){
150                         curr = new_section(list, function, type);
151                         if (!streq(extra, "")) {
152                                 add_line(curr, extra);
153                                 curr = NULL;
154                         }
155                 } else {
156                         if (curr)
157                                 add_line(curr, lines[i]);
158                 }
159         }
160         talloc_free(lines);
161         return list;
162 }