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-2023 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 #ifndef WEXITSTATUS
26 #define WEXITSTATUS(v) (v & 0xff)
27 #endif /* WEXITSTATUS */
28 #else /* ! _WIN32 */
29 #define GCC_OPTIONS "\
30   -I../include \
31   -std=c99 \
32   -g \
33   -O2 \
34   -pedantic \
35   -Wall \
36   -Wcast-qual \
37   -Wextra \
38   -Wformat-security \
39   -Winline \
40   -Wmissing-declarations \
41   -Wmissing-prototypes \
42   -Wnested-externs \
43   -Wpointer-arith \
44   -Wshadow \
45   -Wstrict-prototypes \
46   -Wswitch-default \
47   -Wswitch-enum \
48   -Wunused \
49   -Wwrite-strings \
50   -Wno-unused-function \
51   -Wno-unused-but-set-variable \
52   -Werror \
53 "
54 #endif /* ! _WIN32 */
55 
56 const char *inline_list[] = {
57   "coap_free(",
58   "coap_malloc(",
59   "coap_encode_var_bytes(",
60   "coap_option_clrb(",
61   "coap_option_getb(",
62   "coap_option_setb(",
63   "coap_read(",
64   "coap_run_once(",
65   "coap_write(",
66 };
67 
68 const char *define_list[] = {
69   "coap_string_equal(",
70   "coap_binary_equal(",
71   "coap_log(",
72   "coap_log_emerg(",
73   "coap_log_alert(",
74   "coap_log_crit(",
75   "coap_log_err(",
76   "coap_log_warn(",
77   "coap_log_info(",
78   "coap_log_notice(",
79   "coap_log_debug(",
80   "coap_log_oscore(",
81   "coap_dtls_log(",
82 };
83 
84 /* xxx *function */
85 const char *pointer_list[] = {
86   "char ",
87   "coap_addr_info_t ",
88   "coap_async_t ",
89   "coap_attr_t ",
90   "coap_bin_const_t ",
91   "coap_binary_t ",
92   "coap_cache_entry_t ",
93   "coap_cache_key_t ",
94   "coap_endpoint_t ",
95   "coap_context_t ",
96   "coap_fixed_point_t ",
97   "coap_oscore_conf_t ",
98   "coap_optlist_t ",
99   "coap_session_t ",
100   "coap_string_t ",
101   "coap_str_const_t ",
102   "coap_opt_iterator_t ",
103   "coap_opt_t ",
104   "coap_pdu_t ",
105   "coap_resource_t ",
106   "coap_subscription_t ",
107   "coap_tls_version_t ",
108   "coap_uri_t ",
109   "const char ",
110   "const coap_address_t ",
111   "const coap_bin_const_t ",
112   "const coap_pdu_t ",
113 };
114 
115 /* xxx function */
116 const char *number_list[] = {
117   "coap_log_t ",
118   "coap_pdu_type_t ",
119   "coap_mid_t ",
120   "coap_pdu_code_t ",
121   "coap_proto_t ",
122   "coap_session_state_t ",
123   "coap_session_type_t ",
124   "int ",
125   "uint16_t ",
126   "uint32_t ",
127   "uint64_t ",
128   "unsigned int ",
129   "size_t ",
130   "const uint8_t ",
131 };
132 
133 const char *return_void_list[] = {
134   "coap_option_filter_set",
135   "coap_resource_set_userdata",
136   "coap_cache_set_app_data",
137 };
138 
139 int exit_code = 0;
140 
141 static char name_list[100][100];
142 static unsigned int name_cnt = 0;
143 static char return_list[100][100];
144 static unsigned int return_cnt = 0;
145 static char man_list[400][100];
146 static unsigned int man_cnt = 0;
147 
148 static void
check_synopsis(const char *file)149 check_synopsis(const char *file) {
150   char buffer[1024];
151   char file_name[300];
152   FILE *fpcode;
153   int  status;
154 
155   snprintf(file_name, sizeof(file_name), "tmp/%s-header.c", file);
156   fpcode = fopen(file_name, "w");
157   if (!fpcode) {
158     fprintf(stderr, "fopen: %s: %s (%d)\n", file_name, strerror(errno), errno);
159     exit_code = 1;
160     return;
161   }
162   fprintf(fpcode, "#include <coap3/coap.h>\n");
163   fprintf(fpcode, "#ifdef __GNUC__\n");
164   fprintf(fpcode, "#define U __attribute__ ((unused))\n");
165   fprintf(fpcode, "#else /* not a GCC */\n");
166   fprintf(fpcode, "#define U\n");
167   fprintf(fpcode, "#endif /* GCC */\n");
168   fprintf(fpcode, "\n");
169   fprintf(fpcode, "#include \"%s.h\"\n", file);
170   fclose(fpcode);
171   file_name[strlen(file_name)-1] = '\000';
172   snprintf(buffer, sizeof(buffer),
173            "gcc " GCC_OPTIONS " -c %sc -o %so",
174            file_name, file_name);
175   status = system(buffer);
176   if (WEXITSTATUS(status)) {
177     exit_code = WEXITSTATUS(status);
178   }
179   return;
180 }
181 
182 static void
dump_name_synopsis_mismatch(void)183 dump_name_synopsis_mismatch(void) {
184   unsigned int i;
185 
186   for (i = 0; i < name_cnt; i++) {
187     if (name_list[i][0] != '\000') {
188       fprintf(stderr, "NAME: %s not in SYNOPSIS or pointer_list[]\n", name_list[i]);
189       exit_code = 1;
190     }
191   }
192   name_cnt = 0;
193 }
194 
195 static void
dump_return_synopsis_mismatch(void)196 dump_return_synopsis_mismatch(void) {
197   unsigned int i;
198 
199   for (i = 0; i < return_cnt; i++) {
200     if (return_list[i][0] != '\000') {
201       fprintf(stderr, "SYNOPSIS: %s not in RETURN VALUES\n", return_list[i]);
202       exit_code = 1;
203     }
204   }
205   return_cnt = 0;
206 }
207 
208 /*
209  * It is assumed for a definition that a leading * and trailing * have been
210  * added to buffer.
211  */
212 static void
decode_synopsis_definition(FILE *fpheader, const char *buffer, int in_synopsis)213 decode_synopsis_definition(FILE *fpheader, const char *buffer, int in_synopsis) {
214   size_t len;
215   char outbuf[1024];
216   const char *cp;
217   const char *ecp;
218   int is_void_func = 0;
219   int is_number_func = 0;
220   int is_inline_func = 0;
221   int is_struct_func = 0;
222   const char *func_start = NULL;
223   int is_struct = 0;
224   unsigned int i;
225 
226   if (strncmp(buffer, "*void ", sizeof("*void ")-1) == 0) {
227     if (strncmp(buffer, "*void *", sizeof("*void *")-1) == 0) {
228       func_start = &buffer[sizeof("*void *")-1];
229     } else {
230       is_void_func = 1;
231       func_start = &buffer[sizeof("*void ")-1];
232     }
233   }
234 
235   for (i = 0; i < sizeof(number_list)/sizeof(number_list[0]); i++) {
236     if (strncmp(&buffer[1], number_list[i],
237                 strlen(number_list[i])) == 0) {
238       if (buffer[1 + strlen(number_list[i])] == '*') {
239         func_start = &buffer[2 + strlen(number_list[i])];
240       } else {
241         is_number_func = 1;
242         func_start = &buffer[1 + strlen(number_list[i])];
243       }
244       break;
245     }
246   }
247 
248   for (i = 0; i < sizeof(pointer_list)/sizeof(pointer_list[0]); i++) {
249     if (strncmp(&buffer[1], pointer_list[i],
250                 strlen(pointer_list[i])) == 0) {
251       if (buffer[1 + strlen(pointer_list[i])] == '*') {
252         func_start = &buffer[2 + strlen(pointer_list[i])];
253       } else {
254         is_struct_func = i + 1;
255         func_start = &buffer[1 + strlen(pointer_list[i])];
256       }
257       break;
258     }
259   }
260 
261   if (strncmp(buffer, "*struct ", sizeof("*struct ")-1) == 0) {
262     is_struct = 1;
263     func_start = &buffer[sizeof("*struct ")-1];
264   }
265 
266   if (func_start) {
267     /* see if COAP_STATIC_INLINE function */
268     for (i = 0; i < sizeof(inline_list)/sizeof(inline_list[0]); i++) {
269       if (strncmp(func_start, inline_list[i],
270                   strlen(inline_list[i])) == 0) {
271         is_inline_func = 1;
272         break;
273       }
274     }
275     /* see if #define function */
276     for (i = 0; i < sizeof(define_list)/sizeof(define_list[0]); i++) {
277       if (strncmp(func_start, define_list[i], strlen(define_list[i])) == 0) {
278         break;
279       }
280     }
281     if (i != sizeof(define_list)/sizeof(define_list[0]))
282       goto cleanup;
283   }
284 
285   /* Need to include use of U for unused parameters just before comma */
286   cp = buffer;
287   ecp = strchr(cp, ',');
288   if (!ecp)
289     ecp = strchr(cp, ')');
290   outbuf[0] = '\000';
291   while (ecp) {
292     len = strlen(outbuf);
293     if (strncmp(cp, "void", ecp-cp) == 0)
294       snprintf(&outbuf[len], sizeof(outbuf)-len, "%*.*s%c",
295                (int)(ecp-cp), (int)(ecp-cp), cp, *ecp);
296     else
297       snprintf(&outbuf[len], sizeof(outbuf)-len, "%*.*s U%c",
298                (int)(ecp-cp), (int)(ecp-cp), cp, *ecp);
299     cp = ecp+1;
300     if (*cp) {
301       ecp = strchr(cp, ',');
302       if (!ecp)
303         ecp = strchr(cp, ')');
304     } else {
305       ecp = NULL;
306     }
307   }
308   if (*cp) {
309     len = strlen(outbuf);
310     snprintf(&outbuf[len], sizeof(outbuf)-len, "%s", cp);
311   }
312 
313   len = strlen(outbuf);
314   if (len > 3 && ((outbuf[len-3] == ';' && outbuf[len-2] == '*') ||
315                   (outbuf[len-3] == '*' && outbuf[len-2] == ';'))) {
316     if (is_inline_func) {
317       strcpy(&outbuf[len-3], ";\n");
318     }
319     /* Replace ;* or *; with simple function definition */
320     else if (is_void_func) {
321       strcpy(&outbuf[len-3], "{}\n");
322     } else if (is_number_func) {
323       strcpy(&outbuf[len-3], "{return 0;}\n");
324     } else if (is_struct_func) {
325       snprintf(&outbuf[len-3], sizeof(outbuf)-(len-3),
326                "{%s v; memset(&v, 0, sizeof(v)); return v;}\n",
327                pointer_list[is_struct_func - 1]);
328     } else if (is_struct) {
329       strcpy(&outbuf[len-3], ";\n");
330     } else {
331       strcpy(&outbuf[len-3], "{return NULL;}\n");
332     }
333   }
334   if (outbuf[0] == '*') {
335     fprintf(fpheader, "%s", &outbuf[1]);
336   } else {
337     fprintf(fpheader, "%s", outbuf);
338   }
339 cleanup:
340   if (in_synopsis && func_start) {
341     char *wcp = strchr(func_start, '(');
342 
343     if (!wcp && is_struct)
344       wcp = strchr(func_start, ';');
345     if (!wcp) {
346       wcp = strchr(func_start, '\n');
347       if (wcp)
348         *wcp = '\000';
349       fprintf(stderr, "SYNOPSIS: function %s issue\n", func_start);
350       return;
351     }
352     *wcp = '\000';
353     for (i = 0; i < name_cnt; i++) {
354       if (strcmp(name_list[i], func_start) == 0) {
355         name_list[i][0] = '\000';
356         break;
357       }
358     }
359     if (i == name_cnt) {
360       fprintf(stderr, "SYNOPSIS: %s not in NAME\n", func_start);
361       exit_code = 1;
362     }
363     if (!is_void_func && !is_struct) {
364       for (i = 0; i < return_cnt; i++) {
365         if (strcmp(func_start, return_list[i]) == 0) {
366           fprintf(stderr, "SYNOPSIS: %s duplicated\n", func_start);
367           break;
368         }
369       }
370       if (i != return_cnt)
371         return;
372       if (i >= (int)(sizeof(return_list)/sizeof(return_list[0]))) {
373         fprintf(stderr, "SYNOPSIS: %s insufficient space (%u >= %u)\n", func_start,
374                 i, (int)(sizeof(return_list)/sizeof(return_list[0])));
375         return;
376       }
377       strncpy(return_list[i], func_start, sizeof(return_list[i])-1);
378       return_cnt++;
379     }
380   }
381   return;
382 }
383 
384 int
main(int argc, char *argv[])385 main(int argc, char *argv[]) {
386   DIR *pdir;
387   struct dirent *pdir_ent;
388   char buffer[1024];
389   char man_name[1024];
390   int  status;
391   size_t i;
392   int man_missing_first = 1;
393 
394   if (argc != 2 && argc != 3) {
395     fprintf(stderr, "usage: %s man_directory [libcoap-3.sym_file]\n", argv[0]);
396     exit(1);
397   }
398 
399   pdir = opendir(argv[1]);
400   if (pdir == NULL) {
401     fprintf(stderr, "opendir: %s: %s (%d)\n", argv[1], strerror(errno), errno);
402     exit(1);
403   }
404   if (argc == 3) {
405     FILE *fp = fopen(argv[2], "r");
406     char tmp_name[100];
407 
408     if (fp == NULL) {
409       fprintf(stderr, "fopen: %s: %s (%d)\n", argv[2], strerror(errno), errno);
410       exit(1);
411     }
412     while (fgets(tmp_name, sizeof(tmp_name), fp) != NULL) {
413       char *cp = strchr(tmp_name, '\n');
414 
415       if (cp)
416         *cp = '\000';
417       if (tmp_name[0]) {
418         strncpy(man_list[man_cnt], tmp_name, sizeof(man_list[man_cnt]));
419         man_cnt++;
420       }
421       if (man_cnt == sizeof(man_list) / sizeof(name_list[0])) {
422         fprintf(stderr, "man_list[] too small (%zd) for entries from %s\n",
423                 sizeof(man_list) / sizeof(name_list[0]), argv[2]);
424         exit(1);
425       }
426     }
427     fclose(fp);
428   }
429   if (chdir(argv[1]) == -1) {
430     fprintf(stderr, "chdir: %s: %s (%d)\n", argv[1], strerror(errno), errno);
431     exit(1);
432   }
433 #if defined(WIN32) || defined(__MINGW32__)
434   if (mkdir("tmp") == -1 && errno != EEXIST) {
435     fprintf(stderr, "mkdir: %s: %s (%d)\n", "tmp", strerror(errno), errno);
436     exit(1);
437   }
438 #else /* ! WIN32 && ! __MINGW32__ */
439   if (mkdir("tmp", 0777) == -1 && errno != EEXIST) {
440     fprintf(stderr, "mkdir: %s: %s (%d)\n", "tmp", strerror(errno), errno);
441     exit(1);
442   }
443 #endif /* ! WIN32 && ! __MINGW32__ */
444 
445   while ((pdir_ent = readdir(pdir)) != NULL) {
446     if (!strncmp(pdir_ent->d_name, "coap_", sizeof("coap_")-1) &&
447         strstr(pdir_ent->d_name, ".txt.in")) {
448       FILE  *fp;
449       int skip = 1;
450       int in_examples = 0;
451       int in_synopsis = 0;
452       int in_functions = 0;
453       int in_name = 0;
454       int in_return = 0;
455       int count = 1;
456       char keep_line[1024] = {0};
457       FILE *fpcode = NULL;
458       FILE *fpheader = NULL;
459       char file_name[300];
460       char *cp;
461 
462       fprintf(stderr, "Processing: %s\n", pdir_ent->d_name);
463 
464       snprintf(man_name, sizeof(man_name), "%s", pdir_ent->d_name);
465       fp = fopen(man_name, "r");
466       if (fp == NULL) {
467         fprintf(stderr, "fopen: %s: %s (%d)\n", man_name, strerror(errno), errno);
468         continue;
469       }
470       cp = strstr(man_name, ".txt.in");
471       if (cp)
472         *cp = '\000';
473       name_cnt = 0;
474       while (fgets(buffer, sizeof(buffer), fp) != NULL) {
475         if (strncmp(buffer, "NAME", sizeof("NAME")-1) == 0) {
476           in_name = 1;
477           continue;
478         }
479         if (strncmp(buffer, "SYNOPSIS", sizeof("SYNOPSIS")-1) == 0) {
480           in_name = 0;
481           in_synopsis = 1;
482           snprintf(file_name, sizeof(file_name), "tmp/%s.h",
483                    pdir_ent->d_name);
484           fpheader = fopen(file_name, "w");
485           if (!fpheader) {
486             fprintf(stderr, "fopen: %s: %s (%d)\n", file_name,
487                     strerror(errno), errno);
488             goto bad;
489           }
490           continue;
491         }
492         if (strncmp(buffer, "FUNCTIONS", sizeof("FUNCTIONS")-1) == 0) {
493           in_synopsis = 0;
494           dump_name_synopsis_mismatch();
495           in_functions = 1;
496           snprintf(file_name, sizeof(file_name), "tmp/%s.h",
497                    pdir_ent->d_name);
498           fpheader = fopen(file_name, "w");
499           if (!fpheader) {
500             fprintf(stderr, "fopen: %s: %s (%d)\n", file_name,
501                     strerror(errno), errno);
502             goto bad;
503           }
504           continue;
505         }
506         if (strncmp(buffer, "DESCRIPTION", sizeof("DESCRIPTION")-1) == 0) {
507           in_synopsis = 0;
508           dump_name_synopsis_mismatch();
509           if (fpheader)
510             fclose(fpheader);
511           fpheader = NULL;
512           check_synopsis(pdir_ent->d_name);
513           continue;
514         }
515         if (strncmp(buffer, "RETURN VALUES", sizeof("RETURN VALUES")-1) == 0 ||
516             strncmp(buffer, "RETURN VALUE", sizeof("RETURN VALUE")-1) == 0) {
517           if (in_functions) {
518             if (fpheader)
519               fclose(fpheader);
520             fpheader = NULL;
521             check_synopsis(pdir_ent->d_name);
522           }
523           in_name = 0;
524           in_synopsis = 0;
525           dump_name_synopsis_mismatch();
526           in_functions = 0;
527           in_return = 1;
528           continue;
529         }
530         if (strncmp(buffer, "TESTING", sizeof("TESTING")-1) == 0) {
531           if (in_functions) {
532             if (fpheader)
533               fclose(fpheader);
534             fpheader = NULL;
535             check_synopsis(pdir_ent->d_name);
536           }
537           in_name = 0;
538           in_synopsis = 0;
539           in_functions = 0;
540           in_return = 0;
541           continue;
542         }
543         if (strncmp(buffer, "EXAMPLES", sizeof("EXAMPLES")-1) == 0 ||
544             strncmp(buffer, "EXAMPLE", sizeof("EXAMPLE")-1) == 0) {
545           if (in_functions) {
546             if (fpheader)
547               fclose(fpheader);
548             fpheader = NULL;
549             check_synopsis(pdir_ent->d_name);
550           }
551           in_name = 0;
552           in_synopsis = 0;
553           in_return = 0;
554           dump_name_synopsis_mismatch();
555           dump_return_synopsis_mismatch();
556           in_functions = 0;
557           in_examples = 1;
558           continue;
559         }
560         if (strncmp(buffer, "SEE ALSO", sizeof("SEE ALSO")-1) == 0) {
561           if (in_functions) {
562             if (fpheader)
563               fclose(fpheader);
564             fpheader = NULL;
565             check_synopsis(pdir_ent->d_name);
566           }
567           in_name = 0;
568           in_synopsis = 0;
569           in_return = 0;
570           dump_name_synopsis_mismatch();
571           dump_return_synopsis_mismatch();
572           in_functions = 0;
573           in_examples = 1;
574           break;
575         }
576 
577         if (in_name) {
578           /* Working in NAME section */
579           if (buffer[0] == '\n')
580             continue;
581           if (buffer[0] == '-')
582             continue;
583           cp = strchr(buffer, '\n');
584           if (cp)
585             *cp = '\000';
586           cp = strchr(buffer, ',');
587           if (cp)
588             *cp = '\000';
589           if (strcmp(man_name, buffer) == 0)
590             continue;
591           if (strlen(buffer) >= sizeof(name_list[0])) {
592             fprintf(stderr, "NAME: %s is too long (%u >= %u)\n", buffer,
593                     (int)strlen(buffer), (int)sizeof(name_list[0]));
594             continue;
595           }
596           for (i = 0; i < name_cnt; i++) {
597             if (strncmp(buffer, name_list[i], sizeof(name_list[i])) == 0) {
598               fprintf(stderr, "NAME: %s duplicated\n", buffer);
599               break;
600             }
601           }
602           if (i != name_cnt)
603             continue;
604           if (i >= (int)(sizeof(name_list)/sizeof(name_list[0]))) {
605             fprintf(stderr, "NAME: %s insufficient space (%zu >= %u)\n", buffer,
606                     i, (int)(sizeof(name_list)/sizeof(name_list[0])));
607             continue;
608           }
609           memcpy(name_list[i], buffer, sizeof(name_list[i])-1);
610           name_list[i][sizeof(name_list[i])-1] = '\000';
611           name_cnt++;
612           for (i = 0; i < man_cnt; i++) {
613             if (strncmp(man_list[i], buffer, sizeof(man_list[i])) == 0) {
614               man_list[i][0] = '\000';
615               break;
616             }
617           }
618         }
619 
620         if (in_synopsis) {
621           /* Working in SYNOPSIS section */
622           size_t len;
623           char outbuf[1024];
624 
625           if (buffer[0] == '*' && buffer[1] != '#') {
626             /* Start of a new entry */
627             snprintf(outbuf, sizeof(outbuf), "%s", buffer);
628             while (fgets(buffer, sizeof(buffer), fp) != NULL) {
629               if (buffer[0] == '\n') {
630                 break;
631               }
632               len = strlen(outbuf);
633               outbuf[len-1] = ' ';
634               snprintf(&outbuf[len], sizeof(outbuf) - len, "%s", buffer);
635             }
636             decode_synopsis_definition(fpheader, outbuf, 1);
637             continue;
638           }
639           if (buffer[0] == '\n')
640             continue;
641           if (buffer[0] == '-')
642             continue;
643           if (buffer[0] == 'F') {
644             /* For specific ..... is the end */
645             in_synopsis = 0;
646             if (fpheader)
647               fclose(fpheader);
648             fpheader = NULL;
649             check_synopsis(pdir_ent->d_name);
650             continue;
651           }
652         }
653 
654         if (in_functions) {
655           /* Working in FUNCTIONS section */
656           size_t len;
657           char outbuf[1024];
658 
659           if (strncmp(buffer, "Prototype:\n", sizeof("Prototype:\n")-1)== 0) {
660             /* Start of a new entry */
661             outbuf[0] = '*';
662             outbuf[1] = '\000';
663             while (fgets(buffer, sizeof(buffer), fp) != NULL) {
664               if (buffer[0] == '\n') {
665                 break;
666               }
667               len = strlen(outbuf);
668               if (outbuf[len-1] == '\n')
669                 outbuf[len-1] = ' ';
670               snprintf(&outbuf[len], sizeof(outbuf) - len, "%s", buffer);
671             }
672             len = strlen(outbuf);
673             len--;
674             snprintf(&outbuf[len], sizeof(outbuf) - len, "*\n");
675             decode_synopsis_definition(fpheader, outbuf, 0);
676             continue;
677           }
678         }
679 
680         if (in_return) {
681           cp = strstr(buffer, "*coap_");
682           while (cp) {
683             char *ecp = strchr(cp+1, '*');
684 
685             if (!ecp) {
686               fprintf(stderr, "RETURN VALUES: %s undecipherable\n", cp + 1);
687               exit_code = 1;
688               break;
689             }
690             *ecp = '\000';
691             for (i = 0; i < return_cnt; i++) {
692               if (strcmp(cp+1, return_list[i]) == 0) {
693                 return_list[i][0] = '\000';
694                 break;
695               }
696             }
697             if (i == return_cnt) {
698               for (i = 0;
699                    i < sizeof(return_void_list)/sizeof(return_void_list[0]);
700                    i++) {
701                 if (strcmp(cp+1, return_void_list[i]) == 0)
702                   break;
703               }
704               if (i == sizeof(return_void_list)/sizeof(return_void_list[0])) {
705                 fprintf(stderr, "RETURN VALUES: %s not defined in SYNOPSIS\n", cp + 1);
706                 exit_code = 1;
707               }
708             }
709             cp = strstr(ecp+1, "*coap_");
710           }
711         }
712 
713         if (!in_examples) {
714           continue;
715         }
716         /* Working in EXAMPLES section */
717         if (skip) {
718           if (!strcmp(buffer, "----\n") || !strcmp(buffer, "---\n") ||
719               !strcmp(buffer, "--\n") || !strcmp(buffer, "-\n") ||
720               !strcmp(buffer, "-----\n")) {
721             /* Found start of code */
722             if (strcmp(buffer, "----\n")) {
723               fprintf(stderr,
724                       "Unexpected start of code '%.*s' - expected ----\n",
725                       (int)strlen(buffer)-1, buffer);
726             }
727             snprintf(file_name, sizeof(file_name), "tmp/%s-%d.c",
728                      pdir_ent->d_name, count++);
729             fpcode = fopen(file_name, "w");
730             if (!fpcode) {
731               fprintf(stderr, "fopen: %s: %s (%d)\n", file_name,
732                       strerror(errno), errno);
733               goto bad;
734             } else {
735               fprintf(fpcode, "/* %s */\n", keep_line);
736             }
737             skip = 0;
738             fprintf(stderr, "Processing: %s EXAMPLE - '%d'\n",
739                     pdir_ent->d_name,
740                     count-1);
741           } else if (buffer[0] == '*') {
742             snprintf(keep_line, sizeof(keep_line), "%s", buffer);
743           }
744           continue;
745         }
746         if (!strcmp(buffer, "----\n") || !strcmp(buffer, "---\n") ||
747             !strcmp(buffer, "--\n") || !strcmp(buffer, "-\n") ||
748             !strcmp(buffer, "-----\n")) {
749           /* Found end of code */
750 
751           skip = 1;
752           if (fpcode)
753             fclose(fpcode);
754           keep_line[0] = '\000';
755           file_name[strlen(file_name)-1] = '\000';
756           snprintf(buffer, sizeof(buffer),
757                    "gcc " GCC_OPTIONS " -c %sc -o %so",
758                    file_name, file_name);
759           status = system(buffer);
760           if (WEXITSTATUS(status)) {
761             exit_code = WEXITSTATUS(status);
762           }
763           continue;
764         }
765         if (fpcode) {
766           if (strstr(buffer, "LIBCOAP_API_VERSION")) {
767             fprintf(fpcode, "#include <coap3/coap.h>\n");
768             fprintf(fpcode, "#ifdef __GNUC__\n");
769             fprintf(fpcode, "#define U __attribute__ ((unused))\n");
770             fprintf(fpcode, "#else /* not a GCC */\n");
771             fprintf(fpcode, "#define U\n");
772             fprintf(fpcode, "#endif /* GCC */\n");
773             fprintf(fpcode, "#include \"%s.h\"\n", pdir_ent->d_name);
774             fprintf(fpcode, "#undef U\n");
775             continue;
776           }
777           fprintf(fpcode, "%s", buffer);
778         }
779       }
780 bad:
781       fclose(fp);
782     }
783   }
784   closedir(pdir);
785   for (i = 0; i < man_cnt; i++) {
786     if (man_list[i][0]) {
787       if (man_missing_first) {
788         man_missing_first = 0;
789         fprintf(stderr, "\nMissing man pages (for reference only)\n");
790       }
791       fprintf(stderr, "%s\n", man_list[i]);
792     }
793   }
794   exit(exit_code);
795 }
796