[sharutils PATCH] build: avoid range errors in use of ctype functions

Eric Blake <[email protected]> Tue, 6 Jan 2015 17:27:16 -0700
Newsgroups gmane.comp.gnu.utils.bugs
Message-ID <[email protected]>
On platforms where 'char' is signed, using ctype functions on
'char' arguments produces undefined behavior for arguments that
are larger than 127.  In particular, on Cygwin, isspace((char)255)
MUST return the same 0 result as isspace(EOF), while there are some
single-byte locales in which isspace((unsigned char)255) returns 1.

POSIX is explicit that the ctype functions are only well-defined on
the range of EOF plus the values of unsigned char.

There are also a number of ctype abuses in libopts/makeshell.c,
but as that file is generated rather than owned by sharutils, it
will need to be fixed upstream in the autogen package.

* src/shar.c (to_uchar): New helper, borrowed from coreutils.
(ISASCII, IS_GRAPH, process_shar_input, trim): Avoid range errors.

Signed-off-by: Eric Blake <[email protected]>
---
 ChangeLog  |  4 ++++
 src/shar.c | 21 +++++++++++++--------
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4c7fa2a..8e0e590 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2015-01-06  Eric Blake  <[email protected]>

+	build: avoid range errors in use of ctype functions
+	* src/shar.c (to_uchar): New helper, borrowed from coreutils.
+	(ISASCII, IS_GRAPH, process_shar_input, trim): Avoid range errors.
+
 	build: avoid error message failure on 64-bit hosts
 	* src/scribble.c (xscribble_get): Use correct specifier.

diff --git a/src/shar.c b/src/shar.c
index e2b315a..91e7e53 100644
--- a/src/shar.c
+++ b/src/shar.c
@@ -85,14 +85,19 @@ static const char cright_years_z[] =

 /* System related declarations.  */

+/* Convert a possibly-signed character to an unsigned character.  This is
+   a bit safer than casting to unsigned char, since it catches some type
+   errors that the cast doesn't.  */
+static inline unsigned char to_uchar (char ch) { return ch; }
+
 #if STDC_HEADERS
 # define ISASCII(Char) 1
 #else
 # ifdef isascii
-#  define ISASCII(Char) isascii (Char)
+#  define ISASCII(Char) isascii (to_uchar (Char))
 # else
 #  if HAVE_ISASCII
-#   define ISASCII(Char) isascii (Char)
+#   define ISASCII(Char) isascii (to_uchar (Char))
 #  else
 #   define ISASCII(Char) ((Char) & 0x7f == (unsigned char) (Char))
 #  endif
@@ -100,9 +105,9 @@ static const char cright_years_z[] =
 #endif

 #ifdef isgraph
-#define IS_GRAPH(_c) isgraph (_c)
+#define IS_GRAPH(_c) isgraph (to_uchar (_c))
 #else
-#define IS_GRAPH(_c) (isprint (_c) && !isspace (_c))
+#define IS_GRAPH(_c) (isprint (to_uchar (_c)) && !isspace (to_uchar (_c)))
 #endif

 struct tm *localtime ();
@@ -1580,8 +1585,8 @@ process_shar_input (FILE * input, off_t * size_left, int * split_flag,
        * Find the start of the last token
        */
       e = p + strlen(p);
-      while (  isspace (e[-1]) && (e > p))  e--;
-      while (! isspace (e[-1]) && (e > p))  e--;
+      while (  isspace (to_uchar (e[-1])) && (e > p))  e--;
+      while (! isspace (to_uchar (e[-1])) && (e > p))  e--;
       fwrite (p, e - p, 1, output);
       fprintf (output, "_sh%05d/%s\n", (int)sharpid, cmpr_state->cmpr_mode);
     }
@@ -2079,7 +2084,7 @@ static char *
 trim (char * pz)
 {
   char * res;
-  while (isspace (*pz))  pz++;
+  while (isspace (to_uchar (*pz)))  pz++;
   switch (*pz)
     {
     case NUL:
@@ -2088,7 +2093,7 @@ trim (char * pz)
     }
   res = pz;
   pz += strlen (pz);
-  while (isspace (pz[-1]))  pz--;
+  while (isspace (to_uchar (pz[-1])))  pz--;
   *pz = NUL;
   return res;
 }
-- 
2.1.0