/* * examples_code_check.c -- Validate the code in EXAMPLES of the man pages * * Exits with a non-zero value if there is a coding error. * * Copyright (C) 2020 Jon Shallow * * SPDX-License-Identifier: BSD-2-Clause * * This file is part of the CoAP library libcoap. Please see README for terms * of use. */ #include #include #include #include #include #include #include #include #ifdef _WIN32 #define GCC_OPTIONS "-I../include" #else /* ! _WIN32 */ #define GCC_OPTIONS "\ -I../include \ -std=c99 \ -g \ -O2 \ -pedantic \ -Wall \ -Wcast-qual \ -Wextra \ -Wformat-security \ -Winline \ -Wmissing-declarations \ -Wmissing-prototypes \ -Wnested-externs \ -Wpointer-arith \ -Wshadow \ -Wstrict-prototypes \ -Wswitch-default \ -Wswitch-enum \ -Wunused \ -Wwrite-strings \ -Wno-unused-function \ -Wno-unused-but-set-variable \ -Werror \ " #endif /* ! _WIN32 */ const char *inline_list[] = { "coap_malloc(", "coap_free(", }; const char *define_list[] = { "coap_string_equal(", "coap_binary_equal(", "coap_log(", }; const char *struct_list[] = { "coap_fixed_point_t ", "coap_string_t ", "coap_str_const_t ", "coap_binary_t ", "coap_bin_const_t ", "coap_opt_iterator_t ", "coap_opt_t ", }; const char *number_list[] = { "coap_log_t ", "coap_pdu_type_t ", "coap_mid_t ", "coap_pdu_code_t ", "coap_proto_t ", "coap_session_state_t ", "coap_session_type_t ", "int ", "uint32_t ", "uint64_t ", "unsigned int ", "size_t ", "const uint8_t ", }; int exit_code = 0; static void check_synopsis(const char* file) { char buffer[1024]; char file_name[300]; FILE *fpcode; int status; snprintf(file_name, sizeof (file_name), "tmp/%s-header.c", file); fpcode = fopen(file_name, "w"); if (!fpcode) { fprintf(stderr, "fopen: %s: %s (%d)\n", file_name, strerror(errno), errno); exit_code = 1; return; } fprintf(fpcode, "#include \n"); fprintf(fpcode, "#ifdef __GNUC__\n"); fprintf(fpcode, "#define U __attribute__ ((unused))\n"); fprintf(fpcode, "#else /* not a GCC */\n"); fprintf(fpcode, "#define U\n"); fprintf(fpcode, "#endif /* GCC */\n"); fprintf(fpcode, "\n"); fprintf(fpcode, "#include \"%s.h\"\n", file); fclose(fpcode); file_name[strlen(file_name)-1] = '\000'; snprintf (buffer, sizeof (buffer), "gcc " GCC_OPTIONS " -c %sc -o %so", file_name, file_name); status = system(buffer); if (WEXITSTATUS(status)) { exit_code = WEXITSTATUS(status); } return; } int main(int argc, char* argv[]) { DIR *pdir; struct dirent *pdir_ent; char buffer[1024]; int status; if (argc != 2) { fprintf(stderr, "usage: %s man_directory\n", argv[0]); exit (1); } pdir = opendir(argv[1]); if (pdir == NULL) { fprintf(stderr, "opendir: %s: %s (%d)\n", argv[1], strerror(errno), errno); exit(1); } if (chdir(argv[1]) == -1) { fprintf(stderr, "chdir: %s: %s (%d)\n", argv[1], strerror(errno), errno); exit(1); } if (mkdir("tmp", 0777) == -1 && errno != EEXIST) { fprintf(stderr, "mkdir: %s: %s (%d)\n", "tmp", strerror(errno), errno); exit(1); } while ((pdir_ent = readdir (pdir)) != NULL) { if (!strncmp(pdir_ent->d_name, "coap_", sizeof ("coap_")-1) && strstr (pdir_ent->d_name, ".txt.in")) { FILE* fp; int skip = 1; int in_examples = 0; int in_synopsis = 0; int count = 1; char keep_line[1024] = {0}; FILE* fpcode = NULL; FILE* fpheader = NULL; char file_name[300]; int is_void_func = 0; int is_number_func = 0; int is_inline_func = 0; int is_struct_func = 0; char *func_start = NULL; int is_struct = 0; unsigned int i; fprintf(stderr, "Processing: %s\n", pdir_ent->d_name); snprintf(buffer, sizeof (buffer), "%s", pdir_ent->d_name); fp = fopen(buffer, "r"); if (fp == NULL) { fprintf(stderr, "fopen: %s: %s (%d)\n", buffer, strerror(errno), errno); continue; } while (fgets(buffer, sizeof (buffer), fp) != NULL) { if (strncmp(buffer, "SYNOPSIS", sizeof("SYNOPSIS")-1) == 0) { in_synopsis = 1; snprintf(file_name, sizeof (file_name), "tmp/%s.h", pdir_ent->d_name); fpheader = fopen(file_name, "w"); if (!fpheader) { fprintf(stderr, "fopen: %s: %s (%d)\n", file_name, strerror(errno), errno); goto bad; } continue; } if (strncmp(buffer, "DESCRIPTION", sizeof("DESCRIPTION")-1) == 0) { in_synopsis = 0; if (fpheader) fclose(fpheader); fpheader = NULL; check_synopsis(pdir_ent->d_name); continue; } if (strncmp(buffer, "EXAMPLES", sizeof("EXAMPLES")-1) == 0) { in_synopsis = 0; in_examples = 1; continue; } if (strncmp(buffer, "SEE ALSO", sizeof("SEE ALSO")-1) == 0) { break; } if (in_synopsis) { /* Working in SYNOPSIS section */ size_t len; char outbuf[1024]; char *cp; char *ecp; if (buffer[0] == '\n') continue; if (buffer[0] == '-') continue; if (buffer[0] == 'F') { /* For specific ..... is the end */ in_synopsis = 0; if (fpheader) fclose(fpheader); fpheader = NULL; check_synopsis(pdir_ent->d_name); continue; } if (buffer[0] == '*' && buffer[1] == '#') continue; if (buffer[0] == '*') { /* start of a new function */ is_void_func = 0; is_number_func = 0; is_struct_func = 0; is_inline_func = 0; is_struct = 0; func_start = NULL; if (strncmp(buffer, "*void ", sizeof("*void ")-1) == 0) { if (strncmp(buffer, "*void *", sizeof("*void *")-1) == 0) { func_start = &buffer[sizeof("*void *")-1]; } else { is_void_func = 1; func_start = &buffer[sizeof("*void ")-1]; } } for (i = 0; i < sizeof(number_list)/sizeof(number_list[0]); i++) { if (strncmp(&buffer[1], number_list[i], strlen(number_list[i])) == 0) { if (buffer[1 + strlen(number_list[i])] == '*') { func_start = &buffer[2 + strlen(number_list[i])]; } else { is_number_func = 1; func_start = &buffer[1 + strlen(number_list[i])]; } break; } } for (i = 0; i < sizeof(struct_list)/sizeof(struct_list[0]); i++) { if (strncmp(&buffer[1], struct_list[i], strlen(struct_list[i])) == 0) { if (buffer[1 + strlen(struct_list[i])] == '*') { func_start = &buffer[2 + strlen(struct_list[i])]; } else { is_struct_func = i + 1; func_start = &buffer[1 + strlen(struct_list[i])]; } break; } } if (strncmp(buffer, "*struct ", sizeof("*struct ")-1) == 0) { is_struct = 1; } } if (func_start) { /* see if COAP_STATIC_INLINE function */ for (i = 0; i < sizeof(inline_list)/sizeof(inline_list[0]); i++) { if (strncmp(func_start, inline_list[i], strlen(inline_list[i])) == 0) { is_inline_func = 1; break; } } /* see if #define function */ for (i = 0; i < sizeof(define_list)/sizeof(define_list[0]); i++) { if (strncmp(func_start, define_list[i], strlen(define_list[i])) == 0) { break; } } if (i != sizeof(define_list)/sizeof(define_list[0])) continue; } /* Need to include use of U for unused parameters just before comma */ cp = buffer; ecp = strchr(cp, ','); if (!ecp) ecp = strchr(cp, ')'); outbuf[0] = '\000'; while (ecp) { len = strlen(outbuf); if (strncmp(cp, "void", ecp-cp) == 0) snprintf(&outbuf[len], sizeof(outbuf)-len, "%*.*s%c", (int)(ecp-cp), (int)(ecp-cp), cp, *ecp); else snprintf(&outbuf[len], sizeof(outbuf)-len, "%*.*s U%c", (int)(ecp-cp), (int)(ecp-cp), cp, *ecp); cp = ecp+1; if(*cp) { ecp = strchr(cp, ','); if (!ecp) ecp = strchr(cp, ')'); } else { ecp = NULL; } } if (*cp) { len = strlen(outbuf); snprintf(&outbuf[len], sizeof(outbuf)-len, "%s", cp); } len = strlen(outbuf); if (len > 3 && ((outbuf[len-3] == ';' && outbuf[len-2] == '*') || (outbuf[len-3] == '*' && outbuf[len-2] == ';'))) { if (is_inline_func) { strcpy(&outbuf[len-3], ";\n"); } /* Replace ;* or ;* with simple function definition */ else if (is_void_func) { strcpy(&outbuf[len-3], "{}\n"); } else if (is_number_func) { strcpy(&outbuf[len-3], "{return 0;}\n"); } else if (is_struct_func) { snprintf(&outbuf[len-3], sizeof(outbuf)-(len-3), "{%s v; memset(&v, 0, sizeof(v)); return v;}\n", struct_list[is_struct_func - 1]); } else if (is_struct) { strcpy(&outbuf[len-3], ";\n"); } else { strcpy(&outbuf[len-3], "{return NULL;}\n"); } } if (outbuf[0] == '*') { fprintf(fpheader, "%s", &outbuf[1]); } else { fprintf(fpheader, "%s", outbuf); } continue; } if (!in_examples) { continue; } /* Working in EXAMPLES section */ if (skip) { if (!strcmp(buffer, "----\n") || !strcmp(buffer, "---\n") || !strcmp(buffer, "--\n") || !strcmp(buffer, "-\n") || !strcmp(buffer, "-----\n")) { /* Found start of code */ if (strcmp(buffer, "----\n")) { fprintf(stderr, "Unexpected start of code '%.*s' - expected ----\n", (int)strlen(buffer)-1, buffer); } snprintf(file_name, sizeof (file_name), "tmp/%s-%d.c", pdir_ent->d_name, count++); fpcode = fopen(file_name, "w"); if (!fpcode) { fprintf(stderr, "fopen: %s: %s (%d)\n", file_name, strerror(errno), errno); goto bad; } else { fprintf(fpcode, "/* %s */\n", keep_line); } skip = 0; fprintf(stderr, "Processing: %s EXAMPLE - '%d'\n", pdir_ent->d_name, count-1); } else if (buffer[0] == '*') { snprintf(keep_line, sizeof (keep_line), "%s", buffer); } continue; } if (!strcmp(buffer, "----\n") || !strcmp(buffer, "---\n") || !strcmp(buffer, "--\n") || !strcmp(buffer, "-\n") || !strcmp(buffer, "-----\n")) { /* Found end of code */ skip = 1; if (fpcode) fclose(fpcode); keep_line[0] = '\000'; file_name[strlen(file_name)-1] = '\000'; snprintf (buffer, sizeof (buffer), "gcc " GCC_OPTIONS " -c %sc -o %so", file_name, file_name); status = system(buffer); if (WEXITSTATUS(status)) { exit_code = WEXITSTATUS(status); } continue; } if (fpcode) { if (strstr (buffer, "LIBCOAP_API_VERSION")) { fprintf(fpcode, "#include \n"); fprintf(fpcode, "#ifdef __GNUC__\n"); fprintf(fpcode, "#define U __attribute__ ((unused))\n"); fprintf(fpcode, "#else /* not a GCC */\n"); fprintf(fpcode, "#define U\n"); fprintf(fpcode, "#endif /* GCC */\n"); fprintf(fpcode, "#include \"%s.h\"\n", pdir_ent->d_name); fprintf(fpcode, "#undef U\n"); continue; } fprintf(fpcode, "%s", buffer); } } bad: fclose(fp); } } closedir (pdir); exit(exit_code); }