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
56const 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
68const 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 */
85const 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 */
116const 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
133const char *return_void_list[] = {
134  "coap_option_filter_set",
135  "coap_resource_set_userdata",
136  "coap_cache_set_app_data",
137};
138
139int exit_code = 0;
140
141static char name_list[100][100];
142static unsigned int name_cnt = 0;
143static char return_list[100][100];
144static unsigned int return_cnt = 0;
145static char man_list[400][100];
146static unsigned int man_cnt = 0;
147
148static void
149check_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
182static void
183dump_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
195static void
196dump_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 */
212static void
213decode_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  }
339cleanup:
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
384int
385main(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      }
780bad:
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