Extraction of parts of documentation.
[ccan] / tools / doc_extract.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 "talloc/talloc.h"
12 #include "string/string.h"
13
14 static char **grab_doc(const char *fname)
15 {
16         char *file;
17         char **lines, **ret;
18         unsigned int i, num;
19         bool printing = false, printed = false;
20
21         file = grab_file(NULL, fname, NULL);
22         if (!file)
23                 err(1, "Reading file %s", fname);
24         lines = strsplit(file, file, "\n", &num);
25         ret = talloc_array(NULL, char *, num+1);
26
27         num = 0;
28         for (i = 0; lines[i]; i++) {
29                 if (streq(lines[i], "/**")) {
30                         printing = true;
31                         if (printed++)
32                                 talloc_append_string(ret[num], "\n");
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 %s:%u", fname, i);
42                 }
43         }
44         ret[num] = NULL;
45         talloc_free(file);
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 bool is_section(const char *line)
55 {
56         unsigned int len;
57
58         len = strcspn(line, " \t\n:");
59         if (len == 0)
60                 return false;
61
62         return line[len] == ':' && is_blank(line+len+1);
63 }
64
65
66 static bool end_section(const char *line)
67 {
68         return !line || is_section(line);
69 }
70
71 static unsigned int find_section(char **lines, const char *name)
72 {
73         unsigned int i;
74
75         for (i = 0; lines[i]; i++) {
76                 if (!is_section(lines[i]))
77                         continue;
78                 if (strncasecmp(lines[i], name, strlen(name)) != 0)
79                         continue;
80                 if (lines[i][strlen(name)] == ':')
81                         break;
82         }
83         return i;
84 }
85
86 int main(int argc, char *argv[])
87 {
88         unsigned int i;
89         const char *type;
90
91         if (argc < 3)
92                 errx(1, "Usage: doc_extract TYPE <file>...\n"
93                      "Where TYPE is author|licence|maintainer|summary|description|example|all");
94
95         type = argv[1];
96         for (i = 2; i < argc; i++) {
97                 unsigned int line;
98                 char **lines = grab_doc(argv[i]);
99
100                 if (!lines[0])
101                         errx(1, "No documentation in file");
102
103                 /* Simple one-line fields. */
104                 if (streq(type, "author")
105                     || streq(type, "maintainer")
106                     || streq(type, "licence")) {
107                         line = find_section(lines, type);
108                         if (lines[line]) {
109                                 if (!lines[line+1])
110                                         errx(1, "Malformed %s, end of file",
111                                              type);
112                                 puts(lines[line+1]);
113                         }
114                 } else if (streq(type, "summary")) {
115                         /* Summary comes after - on first line. */
116                         char *dash;
117
118                         dash = strchr(lines[0], '-');
119                         if (!dash)
120                                 errx(1, "Malformed first line: no -");
121                         dash += strspn(dash, "- ");
122                         puts(dash);
123                 } else if (streq(type, "description")) {
124                         line = 1;
125                         while (is_blank(lines[line]))
126                                 line++;
127
128                         while (!end_section(lines[line]))
129                                 puts(lines[line++]);
130                 } else if (streq(type, "example")) {
131                         line = find_section(lines, type);
132                         if (lines[line]) {
133                                 unsigned int strip;
134                                 line++;
135
136                                 while (is_blank(lines[line]))
137                                         line++;
138
139                                 /* Examples can be indented.  Take cue
140                                  * from first non-blank line. */
141                                 if (lines[line])
142                                         strip = strspn(lines[line], " \t");
143
144                                 while (!end_section(lines[line])) {
145                                         if (strspn(lines[line], " \t") >= strip)
146                                                 puts(lines[line] + strip);
147                                         else
148                                                 puts(lines[line]);
149                                         line++;
150                                 }
151                         }
152                 } else if (streq(type, "all")) {
153                         for (line = 0; lines[line]; line++)
154                                 puts(lines[line]);
155                 } else
156                         errx(1, "Unknown type '%s'", type);
157                         
158                 talloc_free(lines);
159         }
160         return 0;
161 }
162
163                 
164