ccanlint: list dependencies by key
[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                                 ret[num-1] = talloc_append_string(ret[num-1],
31                                                                   "\n");
32                         }
33                 } else if (streq(lines[i], " */")) 
34                         printing = false;
35                 else if (printing) {
36                         if (strstarts(lines[i], " * "))
37                                 ret[num++] = talloc_strdup(ret, lines[i]+3);
38                         else if (strstarts(lines[i], " *"))
39                                 ret[num++] = talloc_strdup(ret, lines[i]+2);
40                         else
41                                 errx(1, "Malformed line %u", i);
42                 }
43         }
44         ret[num] = NULL;
45         return ret;
46 }
47
48 static bool is_blank(const char *line)
49 {
50         return line && line[strspn(line, " \t\n")] == '\0';
51 }
52
53 static bool is_section(const char *line, bool one_liner)
54 {
55         unsigned int len = 0;
56
57         /* Any number of upper case words separated by spaces, ending in : */
58         for (;;) {
59                 if (!isupper(line[len]))
60                         return false;
61                 len += strspn(line+len, IDENT_CHARS);
62                 if (line[len] == ':')
63                         break;
64
65                 if (line[len] != ' ')
66                         return false;
67                 len++;
68         }
69
70         /* If it can be a one-liner, a space is sufficient.*/
71         if (one_liner)
72                 return (line[len+1] == ' ' || line[len+1] == '\t');
73
74         return line[len] == ':' && is_blank(line+len+1);
75 }
76
77 /* Summary line is form '<identifier> - ' (spaces for 'struct foo -') */
78 static unsigned int is_summary_line(const char *line)
79 {
80         unsigned int id_len;
81
82         id_len = strspn(line, IDENT_CHARS" ");
83         if (id_len == 0)
84                 return 0;
85         if (strspn(line, " ") == id_len)
86                 return 0;
87         if (!strstarts(line + id_len-1, " - "))
88                 return 0;
89         return id_len - 1;
90 }
91
92 static bool empty_section(struct doc_section *d)
93 {
94         unsigned int i;
95
96         for (i = 0; i < d->num_lines; i++)
97                 if (!is_blank(d->lines[i]))
98                         return false;
99         return true;
100 }
101
102 static struct doc_section *new_section(struct list_head *list,
103                                        const char *function,
104                                        const char *type)
105 {
106         struct doc_section *d;
107         char *lowertype;
108         unsigned int i;
109
110         /* If previous section was empty, delete it. */
111         d = list_tail(list, struct doc_section, list);
112         if (d && empty_section(d)) {
113                 list_del(&d->list);
114                 talloc_free(d);
115         }
116
117         d = talloc(list, struct doc_section);
118         d->function = function;
119         lowertype = talloc_size(d, strlen(type) + 1);
120         /* Canonicalize type to lower case. */
121         for (i = 0; i < strlen(type)+1; i++)
122                 lowertype[i] = tolower(type[i]);
123         d->type = lowertype;
124         d->lines = NULL;
125         d->num_lines = 0;
126
127         list_add_tail(list, &d->list);
128         return d;
129 }
130
131 static void add_line(struct doc_section *curr, const char *line)
132 {
133         curr->lines = talloc_realloc(curr, curr->lines, char *,
134                                      curr->num_lines+1);
135         curr->lines[curr->num_lines++] = talloc_strdup(curr->lines, line);
136 }
137
138 struct list_head *extract_doc_sections(char **rawlines, unsigned int num)
139 {
140         char **lines = grab_doc(rawlines, num);
141         const char *function = NULL;
142         struct doc_section *curr = NULL;
143         unsigned int i;
144         struct list_head *list;
145
146         list = talloc(NULL, struct list_head);
147         list_head_init(list);
148
149         for (i = 0; lines[i]; i++) {
150                 unsigned funclen;
151
152                 funclen = is_summary_line(lines[i]);
153                 if (funclen) {
154                         function = talloc_strndup(list, lines[i], funclen);
155                         curr = new_section(list, function, "summary");
156                         add_line(curr, lines[i] + funclen + 3);
157                         curr = new_section(list, function, "description");
158                 } else if (is_section(lines[i], false)) {
159                         char *type = talloc_strndup(curr, lines[i],
160                                                     strcspn(lines[i], ":"));
161                         curr = new_section(list, function, type);
162                 } else if (is_section(lines[i], true)) {
163                         unsigned int sectlen = strcspn(lines[i], ":");
164                         char *type = talloc_strndup(curr, lines[i], sectlen);
165                         curr = new_section(list, function, type);
166                         add_line(curr, lines[i] + sectlen + 1
167                                  + strspn(lines[i] + sectlen + 1, " \t"));
168                 } else {
169                         if (!curr)
170                                 continue;
171                         add_line(curr, lines[i]);
172                 }
173         }
174         talloc_free(lines);
175         return list;
176 }