• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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