Another Joey fix:
[ccan] / tools / ccanlint / idempotent.c
1 #include "ccanlint.h"
2 #include <ccan/talloc/talloc.h>
3 #include <ccan/str/str.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <limits.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <err.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include "../tools.h"
16
17 static const char explain[] 
18 = "Headers usually start with the C preprocessor lines to prevent multiple\n"
19   "inclusions.  These look like the following:\n"
20   "#ifndef MY_HEADER_H\n"
21   "#define MY_HEADER_H\n"
22   "...\n"
23   "#endif /* MY_HEADER_H */\n";
24
25 static char *get_ifndef_sym(char *line)
26 {
27         line += strspn(line, SPACE_CHARS);
28         if (line[0] == '#')
29         {
30                 line++;
31                 line += strspn(line, SPACE_CHARS);
32                 if (strstarts(line, "ifndef") && isspace(line[6]))
33                         return line+6+strspn(line+6, SPACE_CHARS);
34                 else if (strstarts(line, "if"))
35                 {
36                         line += 2;
37                         line += strspn(line, SPACE_CHARS);
38                         if (line[0] == '!')
39                         {
40                                 line++;
41                                 line += strspn(line, SPACE_CHARS);
42                                 if (strstarts(line, "defined"))
43                                 {
44                                         line += 7;
45                                         line += strspn(line, SPACE_CHARS);
46                                         if (line[0] == '(')
47                                         {
48                                                 line++;
49                                                 line += strspn(line,
50                                                         SPACE_CHARS);
51                                         }
52                                         return line;
53                                 }
54                         }
55                 }
56         }
57         return NULL;
58 }
59
60 static int is_define(char *line, char *id, size_t id_len)
61 {
62         line += strspn(line, SPACE_CHARS);
63         if (line[0] == '#')
64         {
65                 line++;
66                 line += strspn(line, SPACE_CHARS);
67                 if (strstarts(line, "define") && isspace(line[6]))
68                 {
69                         line += 6;
70                         line += strspn(line, SPACE_CHARS);
71                         if (strspn(line, IDENT_CHARS) == id_len &&
72                             memcmp(id, line, id_len) == 0)
73                                 return 1;
74                 }
75         }
76         return 0;
77 }
78
79 static char *report_idem(struct ccan_file *f, char *sofar)
80 {
81         char **lines;
82         char *id;
83         size_t id_len;
84
85         lines = get_ccan_file_lines(f);
86         if (f->num_lines < 3)
87                 /* FIXME: We assume small headers probably uninteresting. */
88                 return NULL;
89
90         id = get_ifndef_sym(lines[0]);
91         if (!id)
92                 return talloc_asprintf_append(sofar,
93                         "%s:1:expect first line to be #ifndef.\n", f->name);
94         id_len = strspn(id, IDENT_CHARS);
95
96         if (!is_define(lines[1], id, id_len))
97                 return talloc_asprintf_append(sofar,
98                         "%s:2:expect second line to be '#define %.*s'.\n",
99                         f->name, (int)id_len, id);
100
101         return sofar;
102 }
103
104 static void *check_idempotent(struct manifest *m)
105 {
106         struct ccan_file *f;
107         char *report = NULL;
108
109         list_for_each(&m->h_files, f, list)
110                 report = report_idem(f, report);
111
112         return report;
113 }
114
115 static const char *describe_idempotent(struct manifest *m, void *check_result)
116 {
117         return talloc_asprintf(check_result, 
118                                "Some headers not idempotent:\n"
119                                "%s\n%s", (char *)check_result,
120                                explain);
121 }
122
123 struct ccanlint idempotent = {
124         .name = "Headers are #ifndef/#define idempotent wrapped",
125         .total_score = 1,
126         .check = check_idempotent,
127         .describe = describe_idempotent,
128 };