1 /*
2 * examples_code_check.c -- Validate the code in EXAMPLES of the man pages
3 *
4 * Exits with a non-zero value if there is a coding error.
5 *
6 * Copyright (C) 2020 Jon Shallow <supjps-libcoap@jpshallow.com>
7 *
8 * SPDX-License-Identifier: BSD-2-Clause
9 *
10 * This file is part of the CoAP library libcoap. Please see README for terms
11 * of use.
12 */
13
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <dirent.h>
20 #include <errno.h>
21 #include <string.h>
22
23 #ifdef _WIN32
24 #define GCC_OPTIONS "-I../include"
25 #else /* ! _WIN32 */
26 #define GCC_OPTIONS "\
27 -I../include \
28 -std=c99 \
29 -g \
30 -O2 \
31 -pedantic \
32 -Wall \
33 -Wcast-qual \
34 -Wextra \
35 -Wformat-security \
36 -Winline \
37 -Wmissing-declarations \
38 -Wmissing-prototypes \
39 -Wnested-externs \
40 -Wpointer-arith \
41 -Wshadow \
42 -Wstrict-prototypes \
43 -Wswitch-default \
44 -Wswitch-enum \
45 -Wunused \
46 -Wwrite-strings \
47 -Wno-unused-function \
48 -Wno-unused-but-set-variable \
49 -Werror \
50 "
51 #endif /* ! _WIN32 */
52
53 const char *inline_list[] = {
54 "coap_malloc(",
55 "coap_free(",
56 };
57
58 const char *define_list[] = {
59 "coap_string_equal(",
60 "coap_binary_equal(",
61 "coap_log(",
62 };
63
64 const char *struct_list[] = {
65 "coap_fixed_point_t ",
66 "coap_string_t ",
67 "coap_str_const_t ",
68 "coap_binary_t ",
69 "coap_bin_const_t ",
70 "coap_opt_iterator_t ",
71 "coap_opt_t ",
72 };
73
74 const char *number_list[] = {
75 "coap_log_t ",
76 "coap_pdu_type_t ",
77 "coap_mid_t ",
78 "coap_pdu_code_t ",
79 "coap_proto_t ",
80 "coap_session_state_t ",
81 "coap_session_type_t ",
82 "int ",
83 "uint32_t ",
84 "uint64_t ",
85 "unsigned int ",
86 "size_t ",
87 "const uint8_t ",
88 };
89
90 int exit_code = 0;
91
check_synopsis(const char * file)92 static void check_synopsis(const char* file) {
93 char buffer[1024];
94 char file_name[300];
95 FILE *fpcode;
96 int status;
97
98 snprintf(file_name, sizeof (file_name), "tmp/%s-header.c", file);
99 fpcode = fopen(file_name, "w");
100 if (!fpcode) {
101 fprintf(stderr, "fopen: %s: %s (%d)\n", file_name, strerror(errno), errno);
102 exit_code = 1;
103 return;
104 }
105 fprintf(fpcode, "#include <coap3/coap.h>\n");
106 fprintf(fpcode, "#ifdef __GNUC__\n");
107 fprintf(fpcode, "#define U __attribute__ ((unused))\n");
108 fprintf(fpcode, "#else /* not a GCC */\n");
109 fprintf(fpcode, "#define U\n");
110 fprintf(fpcode, "#endif /* GCC */\n");
111 fprintf(fpcode, "\n");
112 fprintf(fpcode, "#include \"%s.h\"\n", file);
113 fclose(fpcode);
114 file_name[strlen(file_name)-1] = '\000';
115 snprintf (buffer, sizeof (buffer),
116 "gcc " GCC_OPTIONS " -c %sc -o %so",
117 file_name, file_name);
118 status = system(buffer);
119 if (WEXITSTATUS(status)) {
120 exit_code = WEXITSTATUS(status);
121 }
122 return;
123 }
124
main(int argc,char * argv[])125 int main(int argc, char* argv[])
126 {
127 DIR *pdir;
128 struct dirent *pdir_ent;
129 char buffer[1024];
130 int status;
131
132 if (argc != 2) {
133 fprintf(stderr, "usage: %s man_directory\n", argv[0]);
134 exit (1);
135 }
136
137 pdir = opendir(argv[1]);
138 if (pdir == NULL) {
139 fprintf(stderr, "opendir: %s: %s (%d)\n", argv[1], strerror(errno), errno);
140 exit(1);
141 }
142 if (chdir(argv[1]) == -1) {
143 fprintf(stderr, "chdir: %s: %s (%d)\n", argv[1], strerror(errno), errno);
144 exit(1);
145 }
146 if (mkdir("tmp", 0777) == -1 && errno != EEXIST) {
147 fprintf(stderr, "mkdir: %s: %s (%d)\n", "tmp", strerror(errno), errno);
148 exit(1);
149 }
150
151 while ((pdir_ent = readdir (pdir)) != NULL) {
152 if (!strncmp(pdir_ent->d_name, "coap_", sizeof ("coap_")-1) &&
153 strstr (pdir_ent->d_name, ".txt.in")) {
154 FILE* fp;
155 int skip = 1;
156 int in_examples = 0;
157 int in_synopsis = 0;
158 int count = 1;
159 char keep_line[1024] = {0};
160 FILE* fpcode = NULL;
161 FILE* fpheader = NULL;
162 char file_name[300];
163 int is_void_func = 0;
164 int is_number_func = 0;
165 int is_inline_func = 0;
166 int is_struct_func = 0;
167 char *func_start = NULL;
168 int is_struct = 0;
169 unsigned int i;
170
171 fprintf(stderr, "Processing: %s\n", pdir_ent->d_name);
172
173 snprintf(buffer, sizeof (buffer), "%s", pdir_ent->d_name);
174 fp = fopen(buffer, "r");
175 if (fp == NULL) {
176 fprintf(stderr, "fopen: %s: %s (%d)\n", buffer, strerror(errno), errno);
177 continue;
178 }
179 while (fgets(buffer, sizeof (buffer), fp) != NULL) {
180 if (strncmp(buffer, "SYNOPSIS", sizeof("SYNOPSIS")-1) == 0) {
181 in_synopsis = 1;
182 snprintf(file_name, sizeof (file_name), "tmp/%s.h",
183 pdir_ent->d_name);
184 fpheader = fopen(file_name, "w");
185 if (!fpheader) {
186 fprintf(stderr, "fopen: %s: %s (%d)\n", file_name,
187 strerror(errno), errno);
188 goto bad;
189 }
190 continue;
191 }
192 if (strncmp(buffer, "DESCRIPTION", sizeof("DESCRIPTION")-1) == 0) {
193 in_synopsis = 0;
194 if (fpheader)
195 fclose(fpheader);
196 fpheader = NULL;
197 check_synopsis(pdir_ent->d_name);
198 continue;
199 }
200 if (strncmp(buffer, "EXAMPLES", sizeof("EXAMPLES")-1) == 0) {
201 in_synopsis = 0;
202 in_examples = 1;
203 continue;
204 }
205 if (strncmp(buffer, "SEE ALSO", sizeof("SEE ALSO")-1) == 0) {
206 break;
207 }
208 if (in_synopsis) {
209 /* Working in SYNOPSIS section */
210 size_t len;
211 char outbuf[1024];
212 char *cp;
213 char *ecp;
214
215 if (buffer[0] == '\n')
216 continue;
217 if (buffer[0] == '-')
218 continue;
219 if (buffer[0] == 'F') {
220 /* For specific ..... is the end */
221 in_synopsis = 0;
222 if (fpheader)
223 fclose(fpheader);
224 fpheader = NULL;
225 check_synopsis(pdir_ent->d_name);
226 continue;
227 }
228 if (buffer[0] == '*' && buffer[1] == '#')
229 continue;
230 if (buffer[0] == '*') {
231 /* start of a new function */
232 is_void_func = 0;
233 is_number_func = 0;
234 is_struct_func = 0;
235 is_inline_func = 0;
236 is_struct = 0;
237 func_start = NULL;
238
239 if (strncmp(buffer, "*void ", sizeof("*void ")-1) == 0) {
240 if (strncmp(buffer, "*void *", sizeof("*void *")-1) == 0) {
241 func_start = &buffer[sizeof("*void *")-1];
242 }
243 else {
244 is_void_func = 1;
245 func_start = &buffer[sizeof("*void ")-1];
246 }
247 }
248
249 for (i = 0; i < sizeof(number_list)/sizeof(number_list[0]); i++) {
250 if (strncmp(&buffer[1], number_list[i],
251 strlen(number_list[i])) == 0) {
252 if (buffer[1 + strlen(number_list[i])] == '*') {
253 func_start = &buffer[2 + strlen(number_list[i])];
254 }
255 else {
256 is_number_func = 1;
257 func_start = &buffer[1 + strlen(number_list[i])];
258 }
259 break;
260 }
261 }
262
263 for (i = 0; i < sizeof(struct_list)/sizeof(struct_list[0]); i++) {
264 if (strncmp(&buffer[1], struct_list[i],
265 strlen(struct_list[i])) == 0) {
266 if (buffer[1 + strlen(struct_list[i])] == '*') {
267 func_start = &buffer[2 + strlen(struct_list[i])];
268 }
269 else {
270 is_struct_func = i + 1;
271 func_start = &buffer[1 + strlen(struct_list[i])];
272 }
273 break;
274 }
275 }
276
277 if (strncmp(buffer, "*struct ", sizeof("*struct ")-1) == 0) {
278 is_struct = 1;
279 }
280 }
281
282 if (func_start) {
283 /* see if COAP_STATIC_INLINE function */
284 for (i = 0; i < sizeof(inline_list)/sizeof(inline_list[0]); i++) {
285 if (strncmp(func_start, inline_list[i],
286 strlen(inline_list[i])) == 0) {
287 is_inline_func = 1;
288 break;
289 }
290 }
291 /* see if #define function */
292 for (i = 0; i < sizeof(define_list)/sizeof(define_list[0]); i++) {
293 if (strncmp(func_start, define_list[i], strlen(define_list[i])) == 0) {
294 break;
295 }
296 }
297 if (i != sizeof(define_list)/sizeof(define_list[0]))
298 continue;
299 }
300
301 /* Need to include use of U for unused parameters just before comma */
302 cp = buffer;
303 ecp = strchr(cp, ',');
304 if (!ecp) ecp = strchr(cp, ')');
305 outbuf[0] = '\000';
306 while (ecp) {
307 len = strlen(outbuf);
308 if (strncmp(cp, "void", ecp-cp) == 0)
309 snprintf(&outbuf[len], sizeof(outbuf)-len, "%*.*s%c",
310 (int)(ecp-cp), (int)(ecp-cp), cp, *ecp);
311 else
312 snprintf(&outbuf[len], sizeof(outbuf)-len, "%*.*s U%c",
313 (int)(ecp-cp), (int)(ecp-cp), cp, *ecp);
314 cp = ecp+1;
315 if(*cp) {
316 ecp = strchr(cp, ',');
317 if (!ecp) ecp = strchr(cp, ')');
318 }
319 else {
320 ecp = NULL;
321 }
322 }
323 if (*cp) {
324 len = strlen(outbuf);
325 snprintf(&outbuf[len], sizeof(outbuf)-len, "%s", cp);
326 }
327
328 len = strlen(outbuf);
329 if (len > 3 && ((outbuf[len-3] == ';' && outbuf[len-2] == '*') ||
330 (outbuf[len-3] == '*' && outbuf[len-2] == ';'))) {
331 if (is_inline_func) {
332 strcpy(&outbuf[len-3], ";\n");
333 }
334 /* Replace ;* or ;* with simple function definition */
335 else if (is_void_func) {
336 strcpy(&outbuf[len-3], "{}\n");
337 }
338 else if (is_number_func) {
339 strcpy(&outbuf[len-3], "{return 0;}\n");
340 }
341 else if (is_struct_func) {
342 snprintf(&outbuf[len-3], sizeof(outbuf)-(len-3),
343 "{%s v; memset(&v, 0, sizeof(v)); return v;}\n",
344 struct_list[is_struct_func - 1]);
345 }
346 else if (is_struct) {
347 strcpy(&outbuf[len-3], ";\n");
348 }
349 else {
350 strcpy(&outbuf[len-3], "{return NULL;}\n");
351 }
352 }
353 if (outbuf[0] == '*') {
354 fprintf(fpheader, "%s", &outbuf[1]);
355 }
356 else {
357 fprintf(fpheader, "%s", outbuf);
358 }
359 continue;
360 }
361
362 if (!in_examples) {
363 continue;
364 }
365 /* Working in EXAMPLES section */
366 if (skip) {
367 if (!strcmp(buffer, "----\n") || !strcmp(buffer, "---\n") ||
368 !strcmp(buffer, "--\n") || !strcmp(buffer, "-\n") ||
369 !strcmp(buffer, "-----\n")) {
370 /* Found start of code */
371 if (strcmp(buffer, "----\n")) {
372 fprintf(stderr,
373 "Unexpected start of code '%.*s' - expected ----\n",
374 (int)strlen(buffer)-1, buffer);
375 }
376 snprintf(file_name, sizeof (file_name), "tmp/%s-%d.c",
377 pdir_ent->d_name, count++);
378 fpcode = fopen(file_name, "w");
379 if (!fpcode) {
380 fprintf(stderr, "fopen: %s: %s (%d)\n", file_name,
381 strerror(errno), errno);
382 goto bad;
383 }
384 else {
385 fprintf(fpcode, "/* %s */\n", keep_line);
386 }
387 skip = 0;
388 fprintf(stderr, "Processing: %s EXAMPLE - '%d'\n",
389 pdir_ent->d_name,
390 count-1);
391 }
392 else if (buffer[0] == '*') {
393 snprintf(keep_line, sizeof (keep_line), "%s", buffer);
394 }
395 continue;
396 }
397 if (!strcmp(buffer, "----\n") || !strcmp(buffer, "---\n") ||
398 !strcmp(buffer, "--\n") || !strcmp(buffer, "-\n") ||
399 !strcmp(buffer, "-----\n")) {
400 /* Found end of code */
401
402 skip = 1;
403 if (fpcode) fclose(fpcode);
404 keep_line[0] = '\000';
405 file_name[strlen(file_name)-1] = '\000';
406 snprintf (buffer, sizeof (buffer),
407 "gcc " GCC_OPTIONS " -c %sc -o %so",
408 file_name, file_name);
409 status = system(buffer);
410 if (WEXITSTATUS(status)) {
411 exit_code = WEXITSTATUS(status);
412 }
413 continue;
414 }
415 if (fpcode) {
416 if (strstr (buffer, "LIBCOAP_API_VERSION")) {
417 fprintf(fpcode, "#include <coap3/coap.h>\n");
418 fprintf(fpcode, "#ifdef __GNUC__\n");
419 fprintf(fpcode, "#define U __attribute__ ((unused))\n");
420 fprintf(fpcode, "#else /* not a GCC */\n");
421 fprintf(fpcode, "#define U\n");
422 fprintf(fpcode, "#endif /* GCC */\n");
423 fprintf(fpcode, "#include \"%s.h\"\n", pdir_ent->d_name);
424 fprintf(fpcode, "#undef U\n");
425 continue;
426 }
427 fprintf(fpcode, "%s", buffer);
428 }
429 }
430 bad:
431 fclose(fp);
432 }
433 }
434 closedir (pdir);
435 exit(exit_code);
436 }
437