From 82f1f1db8de7b833068a3ef0dc7d10759bb94941 Mon Sep 17 00:00:00 2001 From: David Levine Date: Wed, 6 Feb 2013 20:39:06 -0600 Subject: [PATCH] When debugging is enabled, replace the ctype functions with 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 | 2 + Makefile.am | 15 ++++- configure.ac | 2 + etc/gen-ctype-checked.c | 125 ++++++++++++++++++++++++++++++++++++++++ h/nmh.h | 4 ++ 5 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 etc/gen-ctype-checked.c diff --git a/.gitignore b/.gitignore index af04fac2..bc20fbc4 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ /RPM/ a.out.dSYM/ /config/version.c +/etc/gen-ctype-checked /etc/mhn.defaults /etc/mts.conf /man/*.[1578] @@ -48,6 +49,7 @@ a.out.dSYM/ /man/mh-chart.man /mts/libmts.a /sbr/*.a +/sbr/ctype-checked.* /sbr/sigmsg.h /test/testdir/ /uip/ali diff --git a/Makefile.am b/Makefile.am index d07b2e44..f4dfc9ca 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ + 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 @@ -116,7 +117,7 @@ superclean: maintainer-clean ## ## 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 @@ -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/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 @@ -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 \ + 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 \ @@ -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/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),' \ @@ -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/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 diff --git a/configure.ac b/configure.ac index 819ddc24..238e17a4 100644 --- a/configure.ac +++ b/configure.ac @@ -211,6 +211,8 @@ if test -n "$auto_cflags"; then else test -z "$CFLAGS" && CFLAGS=-O || CFLAGS="$CFLAGS -O" fi + AC_DEFINE([NDEBUG], [1], + [Define to disable run-time debugging and asserts.]) fi fi diff --git a/etc/gen-ctype-checked.c b/etc/gen-ctype-checked.c new file mode 100644 index 00000000..60191673 --- /dev/null +++ b/etc/gen-ctype-checked.c @@ -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 . 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 +#include + +#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 \n\ +\n", header); + + fprintf (cfile, copyright, cfilename); + fputs ("#include \n\n", cfile); + fputs ("#ifndef NDEBUG\n", cfile); + fputs ("#include \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 3b91e2c3..b2bac8f0 100644 --- a/h/nmh.h +++ b/h/nmh.h @@ -10,6 +10,10 @@ #include #include #include +#ifndef NDEBUG + /* See etc/gen-ctype-checked.c. */ +# include +#endif #include # include -- 2.56.0