ccanlint: objects_build_with_stringchecks
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 17 Mar 2011 11:42:22 +0000 (22:12 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 17 Mar 2011 11:42:22 +0000 (22:12 +1030)
If we detect any mention of a problematic string function, try compiling
the entire module with string debugging enabled.

tools/ccanlint/tests/objects_build_with_stringchecks.c [new file with mode: 0644]

diff --git a/tools/ccanlint/tests/objects_build_with_stringchecks.c b/tools/ccanlint/tests/objects_build_with_stringchecks.c
new file mode 100644 (file)
index 0000000..32fa22e
--- /dev/null
@@ -0,0 +1,145 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <ccan/str_talloc/str_talloc.h>
+#include <ccan/foreach/foreach.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+#include "reduce_features.h"
+
+static const char *uses_stringfuncs(struct manifest *m)
+{
+       struct list_head *list;
+
+       foreach_ptr(list, &m->c_files, &m->h_files) {
+               struct ccan_file *i;
+               char *match;
+
+               list_for_each(list, i, list) {
+                       if (strreg(m, get_ccan_file_contents(i),
+                                  "(isalnum|isalpha|isascii|isblank|iscntrl"
+                                  "|isdigit|isgraph|islower|isprint|ispunct"
+                                  "|isspace|isupper|isxdigit"
+                                  "|strstr|strchr|strrchr)", &match)) {
+                               if (verbose > 2)
+                                       printf("Matched '%s' in %s\n",
+                                              match, i->fullname);
+                               return NULL;
+                       }
+               }
+       }
+       return "No ctype.h or string functions found";
+}
+
+static void write_str(int fd, const char *str)
+{
+       if (write(fd, str, strlen(str)) != strlen(str))
+               err(1, "Writing to temporary file");
+}
+
+static int start_file(const char *filename)
+{
+       int fd;
+       fd = open(filename, O_WRONLY|O_CREAT, 0600);
+       write_str(fd, "#define CCAN_STR_DEBUG 1\n#include <ccan/str/str.h>\n");
+       return fd;
+}
+
+static void test_compile(struct score *score,
+                        struct ccan_file *file,
+                        const char *filename,
+                        bool keep,
+                        const char *flags,
+                        bool *errors,
+                        bool *warnings)
+{
+       char *output, *compiled;
+
+       compiled = maybe_temp_file(score, "", keep, filename);
+       if (!compile_object(score, filename, ccan_dir, compiler, flags,
+                           compiled, &output)) {
+               score_file_error(score, file, 0,
+                                "Compiling object files:\n%s",
+                                output);
+               *errors = true;
+       } else if (!streq(output, "")) {
+               score_file_error(score, file, 0,
+                                "Compiling object files gave warnings:\n%s",
+                                output);
+               *warnings = true;
+       }
+}
+
+static struct ccan_file *get_main_header(struct manifest *m)
+{
+       struct ccan_file *f;
+
+       list_for_each(&m->h_files, f, list) {
+               if (strstarts(f->name, m->basename)
+                   && strlen(f->name) == strlen(m->basename) + 2) {
+                       return f;
+               }
+       }
+       /* Should not happen, since we passed main_header_exists! */
+       errx(1, "No main header?");
+}
+
+static void build_objects_with_stringchecks(struct manifest *m,
+                                           bool keep, unsigned int *timeleft,
+                                           struct score *score)
+{
+       struct ccan_file *i;
+       bool errors = false, warnings = false;
+       char *tmp, *flags;
+       int tmpfd;
+
+       /* FIXME:: We need -I so local #includes work outside normal dir. */
+       flags = talloc_asprintf(score, "-I%s %s", m->dir, cflags);
+
+       /* Won't work into macros, but will get inline functions. */
+       if (list_empty(&m->c_files)) {
+               char *line;
+               i = get_main_header(m);
+               tmp = maybe_temp_file(score, ".c", keep, i->fullname);
+               tmpfd = start_file(tmp);
+               line = talloc_asprintf(score, "#include <ccan/%s/%s.h>\n",
+                                      m->basename, m->basename);
+               write_str(tmpfd, line);
+               close(tmpfd);
+               test_compile(score, i, tmp, keep, flags, &errors, &warnings);
+       } else {
+               list_for_each(&m->c_files, i, list) {
+                       tmp = maybe_temp_file(score, ".c", keep, i->fullname);
+                       tmpfd = start_file(tmp);
+                       write_str(tmpfd, get_ccan_file_contents(i));
+                       close(tmpfd);
+                       test_compile(score, i, tmp, keep, flags,
+                                    &errors, &warnings);
+               }
+       }
+
+       score->total = 1;
+       if (!errors) {
+               score->pass = true;
+               score->score = !warnings;
+       }
+}
+
+struct ccanlint objects_build_with_stringchecks = {
+       .key = "objects_build_with_stringchecks",
+       .name = "Module compiles with extra ctype.h and str function checks",
+       .check = build_objects_with_stringchecks,
+       .can_run = uses_stringfuncs,
+       .needs = "objects_build main_header_exists"
+};
+REGISTER_TEST(objects_build_with_stringchecks);