]> diplodocus.org Git - nmh/commitdiff
When debugging is enabled, replace the ctype functions with
authorDavid Levine <levinedl@acm.org>
Thu, 7 Feb 2013 02:39:06 +0000 (20:39 -0600)
committerDavid Levine <levinedl@acm.org>
Thu, 7 Feb 2013 02:39:06 +0000 (20:39 -0600)
macros that index into arrays.  This allows the compiler to
catch use of char, instead of unsigned char, arguments to
those functions.  gcc uses -Wchar-subscripts, which is enabled
with the -Wall that we use by default.  Thanks to Tom Lane for
suggesting this approach.

This feature can be enabled by undef'ing NDEBUG in config.h.
configure --enable-debug does that.  We can now use the same
mechanism to enable and disable assert(3), too.

.gitignore
Makefile.am
configure.ac
etc/gen-ctype-checked.c [new file with mode: 0644]
h/nmh.h

index af04fac..bc20fbc 100644 (file)
@@ -41,6 +41,7 @@
 /RPM/
 a.out.dSYM/
 /config/version.c
 /RPM/
 a.out.dSYM/
 /config/version.c
+/etc/gen-ctype-checked
 /etc/mhn.defaults
 /etc/mts.conf
 /man/*.[1578]
 /etc/mhn.defaults
 /etc/mts.conf
 /man/*.[1578]
@@ -48,6 +49,7 @@ a.out.dSYM/
 /man/mh-chart.man
 /mts/libmts.a
 /sbr/*.a
 /man/mh-chart.man
 /mts/libmts.a
 /sbr/*.a
+/sbr/ctype-checked.*
 /sbr/sigmsg.h
 /test/testdir/
 /uip/ali
 /sbr/sigmsg.h
 /test/testdir/
 /uip/ali
index d07b2e4..f4dfc9c 100644 (file)
@@ -93,6 +93,7 @@ DISTCHECK_CONFIGURE_FLAGS = DISABLE_SETGID_MAIL=1
 ## Stuff that should be cleaned via "make clean"
 ##
 CLEANFILES = config/version.c sbr/sigmsg.h etc/mts.conf \
 ## Stuff that should be cleaned via "make clean"
 ##
 CLEANFILES = config/version.c sbr/sigmsg.h etc/mts.conf \
+            etc/gen-ctype-checked sbr/ctype-checked.h sbr/ctype-checked.c \
             etc/mhn.defaults man/man.sed man/mh-chart.man $(man_MANS)
 clean-local:
        @rm -rf RPM a.out.dSYM uip/a.out.dSYM
             etc/mhn.defaults man/man.sed man/mh-chart.man $(man_MANS)
 clean-local:
        @rm -rf RPM a.out.dSYM uip/a.out.dSYM
@@ -116,7 +117,7 @@ superclean: maintainer-clean
 ##
 ## Files that need to be built before everything else
 ##
 ##
 ## Files that need to be built before everything else
 ##
-BUILT_SOURCES = sbr/sigmsg.h
+BUILT_SOURCES = sbr/sigmsg.h sbr/ctype-checked.c
 
 ##
 ## This is a list of all programs that get installed in the "bin" directory
 
 ##
 ## This is a list of all programs that get installed in the "bin" directory
@@ -157,7 +158,7 @@ noinst_HEADERS = h/addrsbr.h h/aliasbr.h h/crawl_folders.h h/dropsbr.h \
                 h/mh.h h/mhcachesbr.h h/mhparse.h h/mime.h h/msh.h \
                 h/mts.h h/nmh.h h/picksbr.h h/popsbr.h h/prototypes.h \
                 h/rcvmail.h h/scansbr.h h/signals.h h/tws.h h/utils.h \
                 h/mh.h h/mhcachesbr.h h/mhparse.h h/mime.h h/msh.h \
                 h/mts.h h/nmh.h h/picksbr.h h/popsbr.h h/prototypes.h \
                 h/rcvmail.h h/scansbr.h h/signals.h h/tws.h h/utils.h \
-                h/vmhsbr.h mts/smtp/smtp.h
+                h/vmhsbr.h mts/smtp/smtp.h sbr/ctype-checked.h
 
 ##
 ## Extra files we need to install in various places
 
 ##
 ## Extra files we need to install in various places
@@ -240,6 +241,7 @@ man_SRCS = man/ali.man man/anno.man man/ap.man man/burst.man man/comp.man \
 ## Automake using the automatic rules
 ##
 EXTRA_DIST = config/version.sh sbr/sigmsg.awk etc/mts.conf.in \
 ## Automake using the automatic rules
 ##
 EXTRA_DIST = config/version.sh sbr/sigmsg.awk etc/mts.conf.in \
+            etc/gen-ctype-checked.c \
             etc/mhn.defaults.sh etc/sendfiles $(MHNSEARCHPROG) DATE MACHINES \
             docs/ChangeLog_MH-3_to_MH-6.6 \
             docs/ChangeLog_MH-6.7.0_to_MH-6.8.4.html \
             etc/mhn.defaults.sh etc/sendfiles $(MHNSEARCHPROG) DATE MACHINES \
             docs/ChangeLog_MH-3_to_MH-6.6 \
             docs/ChangeLog_MH-6.7.0_to_MH-6.8.4.html \
@@ -422,6 +424,13 @@ config/version.c: Makefile
 sbr/sigmsg.h: $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H)
        $(AWK) -f $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H) > $@
 
 sbr/sigmsg.h: $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H)
        $(AWK) -f $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H) > $@
 
+sbr/ctype-checked.h: etc/gen-ctype-checked
+       etc/gen-ctype-checked
+
+sbr/ctype-checked.c: etc/gen-ctype-checked sbr/ctype-checked.h
+
+etc/gen-type-checked: etc/gen-type-checked.c
+
 etc/mts.conf: $(srcdir)/etc/mts.conf.in Makefile
        @rm -f $@
        $(SED) -e 's,%mts%,$(MTS),' \
 etc/mts.conf: $(srcdir)/etc/mts.conf.in Makefile
        @rm -f $@
        $(SED) -e 's,%mts%,$(MTS),' \
@@ -513,7 +522,7 @@ sbr_libmh_a_SOURCES = sbr/addrsbr.c sbr/ambigsw.c sbr/atooi.c sbr/arglist.c \
                      sbr/showfile.c sbr/signals.c sbr/smatch.c \
                      sbr/snprintb.c sbr/ssequal.c sbr/strcasecmp.c \
                      sbr/strindex.c sbr/trimcpy.c sbr/uprf.c sbr/vfgets.c \
                      sbr/showfile.c sbr/signals.c sbr/smatch.c \
                      sbr/snprintb.c sbr/ssequal.c sbr/strcasecmp.c \
                      sbr/strindex.c sbr/trimcpy.c sbr/uprf.c sbr/vfgets.c \
-                     sbr/fmt_def.c sbr/mf.c sbr/utils.c \
+                     sbr/fmt_def.c sbr/mf.c sbr/utils.c sbr/ctype-checked.c \
                      sbr/m_mktemp.c sbr/getansreadline.c config/config.c \
                      config/version.c
 
                      sbr/m_mktemp.c sbr/getansreadline.c config/config.c \
                      config/version.c
 
index 819ddc2..238e17a 100644 (file)
@@ -211,6 +211,8 @@ if test -n "$auto_cflags"; then
     else
       test -z "$CFLAGS" && CFLAGS=-O  || CFLAGS="$CFLAGS -O"
     fi
     else
       test -z "$CFLAGS" && CFLAGS=-O  || CFLAGS="$CFLAGS -O"
     fi
+    AC_DEFINE([NDEBUG], [1],
+              [Define to disable run-time debugging and asserts.])
   fi
 fi
 
   fi
 fi
 
diff --git a/etc/gen-ctype-checked.c b/etc/gen-ctype-checked.c
new file mode 100644 (file)
index 0000000..6019167
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * gen-ctype-checked.c:  generates ctype-checked.h on stdout
+ *
+ * This code is Copyright (c) 2013, by the authors of nmh.
+ * See the COPYRIGHT file in the root directory of the nmh
+ * distribution for complete copyright information.
+ */
+
+/*
+ * This is intended to catch any calls using a char argument to any of
+ * the is*(int) functions in <ctype.h>.  It does that by defining a
+ * macro that uses the argument as an array index, along with a
+ * compiler warning such as gcc -Wchar-subscripts (include with
+ * -Wall).  This program creates sbr/ctype-checked.h and
+ * sbr/ctype-checked.c.  The header file is put into sbr/ because h/
+ * is unwritable during make distcheck.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+
+#define CTYPE_FUNCTIONS \
+    X(isalnum) \
+    X(isalpha) \
+    X(iscntrl) \
+    X(isdigit) \
+    X(isgraph) \
+    X(islower) \
+    X(isprint) \
+    X(ispunct) \
+    X(isspace) \
+    X(isupper) \
+    X(isxdigit) \
+
+#if (defined (_BSD_SOURCE) && _BSD_SOURCE) || \
+    (defined (_SVID_SOURCE) && _SVID_SOURCE) || \
+    (defined (_XOPEN_SOURCE) && _XOPEN_SOURCE)
+# define CTYPE_FUNCTION_ISASCII X(isascii)
+#else
+# define CTYPE_FUNCTION_ISASCII
+#endif
+
+#if (defined (_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \
+    (defined (_ISOC99_SOURCE) && _ISOC99_SOURCE) || \
+    (defined (_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+# define CTYPE_FUNCTIONS_C99 X(isblank)
+#else
+# define CTYPE_FUNCTIONS_C99
+#endif
+
+struct ctype_func { const char *name; int (*function) (int); };
+
+#define X(function) { #function, function },
+struct ctype_func functions[] =
+    { CTYPE_FUNCTIONS CTYPE_FUNCTION_ISASCII CTYPE_FUNCTIONS_C99 { NULL, 0} };
+#undef X
+
+
+int
+main () {
+    const char *copyright="\
+/*\n\
+ * %s:  checks type of arguments and argument\n\
+ *     values for each of the ctype functions\n\
+ *\n\
+ * Generated by etc/gen-ctype-checked.\n\
+ *\n\
+ * This code is Copyright (c) 2013, by the authors of nmh.\n\
+ * See the COPYRIGHT file in the root directory of the nmh\n\
+ * distribution for complete copyright information.\n\
+ */\n\n";
+    char headername[] = "sbr/ctype-checked.h";
+    const char cfilename[] =  "sbr/ctype-checked.c";
+    FILE *header, *cfile;
+
+    if ((header = fopen (headername, "w")) != 0  &&
+        (cfile = fopen (cfilename, "w")) != 0) {
+        struct ctype_func *f;
+
+        fprintf (header, copyright, headername);
+        fputs ("#ifndef CTYPE_CHECKED_H\n\
+#define CTYPE_CHECKED_H\n\
+#include <ctype.h>\n\
+\n", header);
+
+        fprintf (cfile, copyright, cfilename);
+        fputs ("#include <config.h>\n\n", cfile);
+        fputs ("#ifndef NDEBUG\n", cfile);
+        fputs ("#include <sbr/ctype-checked.h>\n\n", cfile);
+
+        for (f = functions; f->function; ++f) {
+            const char *cp = f->name;
+            unsigned int i;
+
+            fprintf (header, "extern int %s_type_checked[];\n", cp);
+            fprintf (header, "#ifdef %s\n#undef %s\n#endif\n", cp, cp);
+            fprintf (header, "#define %s(c) %s_type_checked[(c)]\n\n", cp, cp);
+
+            fprintf (cfile, "int %s_type_checked[] = {\n    ", cp);
+            for (i = 0; i < 256; ++i) {
+                fprintf (cfile, "%d", (*f->function) (i));
+                if ((i+1) % 8) {
+                    fputs (", ", cfile);
+                } else {
+                    if (i == 255) {
+                        fputs (" };\n\n", cfile);
+                    } else {
+                        fputs (",\n    ", cfile);
+                    }
+                }
+            }
+        }
+
+        /* Need to put a symbol in the .c file with NDEBUG so the
+           compilation unit isn't empty. */
+        fputs ("#else  /* NDEBUG */\n\nint ctype_checked;\n\n", cfile);
+        fputs ("#endif /* NDEBUG */\n", cfile);
+        fprintf (header, "#endif /* CTYPE_CHECKED_H */\n");
+    } else {
+        perror ("fopen in sbr/");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/h/nmh.h b/h/nmh.h
index 3b91e2c..b2bac8f 100644 (file)
--- a/h/nmh.h
+++ b/h/nmh.h
 #include <unistd.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <ctype.h>
+#ifndef NDEBUG
+  /* See etc/gen-ctype-checked.c. */
+# include <sbr/ctype-checked.h>
+#endif
 #include <sys/stat.h>
 
 # include <dirent.h>
 #include <sys/stat.h>
 
 # include <dirent.h>