From 23e64facf9f74133c6bacedeec56ad782ae69b65 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 23 Feb 2017 08:58:39 -0800
Subject: [PATCH] hexl: handle large files and I/O errors
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit

* lib-src/hexl.c: Include inttypes.h, for PRIxMAX etc.
Do not include ctype.h, as the code no longer uses isdigit.
(DEFAULT_GROUPING, un_flag, iso_flag, group_by): Now local to ‘main’.
(DEFAULT_BASE, endian): Remove; was not really used.
(usage): Remove; now done by ‘main’, as that’s simpler.
(progname): Now static.
(output_error, hexchar): New functions.
(main): Use them.  Simplify.  Remove "-oct", "-big-endian", and
"-little-endian" options, as they did not work and were not used.
Use SET_BINARY only on stdin, and fopen with "rb" otherwise.
Use SET_BINARY only once on stdout.
Do not assume file offsets fit in ‘long’.
If an I/O error occurs, report it and exit with nonzero status.
---
 lib-src/hexl.c | 230 ++++++++++++++++++++-----------------------------
 1 file changed, 95 insertions(+), 135 deletions(-)

diff --git a/lib-src/hexl.c b/lib-src/hexl.c
index 2eef7b3a63d..2c7e8c44161 100644
--- a/lib-src/hexl.c
+++ b/lib-src/hexl.c
@@ -21,241 +21,201 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 
 #include <binary-io.h>
 
-#define DEFAULT_GROUPING	0x01
-#define DEFAULT_BASE		16
+static char *progname;
 
-int base = DEFAULT_BASE;
-bool un_flag = false, iso_flag = false, endian = true;
-int group_by = DEFAULT_GROUPING;
-char *progname;
+static _Noreturn void
+output_error (void)
+{
+  fprintf (stderr, "%s: write error\n", progname);
+  exit (EXIT_FAILURE);
+}
 
-_Noreturn void usage (void);
+static int
+hexchar (int c)
+{
+  return c - ('0' <= c && c <= '9' ? '0' : 'a' - 10);
+}
 
 int
 main (int argc, char **argv)
 {
-  register long address;
-  char string[18];
-  FILE *fp;
-
-  progname = *argv++; --argc;
+  int status = EXIT_SUCCESS;
+  int DEFAULT_GROUPING = 0x01;
+  int group_by = DEFAULT_GROUPING;
+  bool un_flag = false, iso_flag = false;
+  progname = *argv++;
 
   /*
    ** -hex		hex dump
-   ** -oct		Octal dump
    ** -group-by-8-bits
    ** -group-by-16-bits
    ** -group-by-32-bits
    ** -group-by-64-bits
    ** -iso		iso character set.
-   ** -big-endian	Big Endian
-   ** -little-endian	Little Endian
    ** -un || -de	from hexl format to binary.
    ** --		End switch list.
    ** <filename>	dump filename
    ** -		(as filename == stdin)
    */
 
-  while (*argv && *argv[0] == '-' && (*argv)[1])
+  for (; *argv && *argv[0] == '-' && (*argv)[1]; argv++)
     {
       /* A switch! */
       if (!strcmp (*argv, "--"))
 	{
-	  --argc; argv++;
+	  argv++;
 	  break;
 	}
       else if (!strcmp (*argv, "-un") || !strcmp (*argv, "-de"))
 	{
 	  un_flag = true;
-	  --argc; argv++;
+	  SET_BINARY (fileno (stdout));
 	}
       else if (!strcmp (*argv, "-hex"))
-	{
-	  base = 16;
-	  --argc; argv++;
-	}
+	/* Hex is the default and is only base supported.  */;
       else if (!strcmp (*argv, "-iso"))
-	{
-	  iso_flag = true;
-	  --argc; argv++;
-	}
-      else if (!strcmp (*argv, "-oct"))
-	{
-	  base = 8;
-	  --argc; argv++;
-	}
-      else if (!strcmp (*argv, "-big-endian"))
-	{
-	  endian = true;
-	  --argc; argv++;
-	}
-      else if (!strcmp (*argv, "-little-endian"))
-	{
-	  endian = false;
-	  --argc; argv++;
-	}
+	iso_flag = true;
       else if (!strcmp (*argv, "-group-by-8-bits"))
-	{
-	  group_by = 0x00;
-	  --argc; argv++;
-	}
+	group_by = 0x00;
       else if (!strcmp (*argv, "-group-by-16-bits"))
-	{
-	  group_by = 0x01;
-	  --argc; argv++;
-	}
+	group_by = 0x01;
       else if (!strcmp (*argv, "-group-by-32-bits"))
-	{
-	  group_by = 0x03;
-	  --argc; argv++;
-	}
+	group_by = 0x03;
       else if (!strcmp (*argv, "-group-by-64-bits"))
-	{
-	  group_by = 0x07;
-	  endian = false;
-	  --argc; argv++;
-	}
+	group_by = 0x07;
       else
 	{
 	  fprintf (stderr, "%s: invalid switch: \"%s\".\n", progname,
 		   *argv);
-	  usage ();
+	  fprintf (stderr, "usage: %s [-de] [-iso]\n", progname);
+	  return EXIT_FAILURE;
 	}
     }
 
+  char const *filename = *argv ? *argv++ : "-";
+
   do
     {
-      if (*argv == NULL)
-	fp = stdin;
+      FILE *fp;
+
+      if (!strcmp (filename, "-"))
+	{
+	  fp = stdin;
+	  if (!un_flag)
+	    SET_BINARY (fileno (stdin));
+	}
       else
 	{
-	  char *filename = *argv++;
-
-	  if (!strcmp (filename, "-"))
-	    fp = stdin;
-	  else if ((fp = fopen (filename, "r")) == NULL)
+	  fp = fopen (filename, un_flag ? "r" : "rb");
+	  if (!fp)
 	    {
 	      perror (filename);
+	      status = EXIT_FAILURE;
 	      continue;
 	    }
 	}
 
       if (un_flag)
 	{
-	  SET_BINARY (fileno (stdout));
-
-	  for (;;)
+	  for (int c; 0 <= (c = getc (fp)); )
 	    {
-	      int i, c = 0, d;
-	      char buf[18];
-
-#define hexchar(x) (isdigit (x) ? x - '0' : x - 'a' + 10)
-
-	      /* Skip 10 bytes.  */
-	      if (fread (buf, 1, 10, fp) != 10)
-		break;
+	      /* Skip address at start of line.  */
+	      if (c != ' ')
+		continue;
 
-	      for (i=0; i < 16; ++i)
+	      for (int i = 0; i < 16; i++)
 		{
-		  if ((c = getc (fp)) == ' ' || c == EOF)
+		  c = getc (fp);
+		  if (c < 0 || c == ' ')
 		    break;
 
-		  d = getc (fp);
-		  c = hexchar (c) * 0x10 + hexchar (d);
-		  putchar (c);
-
-		  if ((i&group_by) == group_by)
-		    getc (fp);
-		}
-
-	      if (c == ' ')
-		{
-		  while ((c = getc (fp)) != '\n' && c != EOF)
-		    ;
-
-		  if (c == EOF)
-		    break;
-		}
-	      else
-		{
-		  if (i < 16)
+		  int hc = hexchar (c);
+		  c = getc (fp);
+		  if (c < 0)
 		    break;
+		  putchar (hc * 0x10 + hexchar (c));
 
-		  /* Skip 18 bytes.  */
-		  if (fread (buf, 1, 18, fp) != 18)
-		    break;
+		  if ((i & group_by) == group_by)
+		    {
+		      c = getc (fp);
+		      if (c < 0)
+			break;
+		    }
 		}
+
+	      while (0 <= c && c != '\n')
+		c = getc (fp);
+	      if (c < 0)
+		break;
+	      if (ferror (stdout))
+		output_error ();
 	    }
 	}
       else
 	{
-	  SET_BINARY (fileno (fp));
-	  address = 0;
+	  int c = 0;
+	  char string[18];
 	  string[0] = ' ';
 	  string[17] = '\0';
-	  for (;;)
+	  for (uintmax_t address = 0; 0 <= c; address += 0x10)
 	    {
-	      register int i, c = 0;
-
-	      for (i=0; i < 16; ++i)
+	      int i;
+	      for (i = 0; i < 16; i++)
 		{
-		  if ((c = getc (fp)) == EOF)
+		  if (0 <= c)
+		    c = getc (fp);
+		  if (c < 0)
 		    {
 		      if (!i)
 			break;
 
 		      fputs ("  ", stdout);
-		      string[i+1] = '\0';
+		      string[i + 1] = '\0';
 		    }
 		  else
 		    {
 		      if (!i)
-			printf ("%08lx: ", address + 0ul);
+			printf ("%08"PRIxMAX": ", address);
 
-		      if (iso_flag)
-			string[i+1] =
-			  (c < 0x20 || (c >= 0x7F && c < 0xa0)) ? '.' :c;
-		      else
-			string[i+1] = (c < 0x20 || c >= 0x7F) ? '.' : c;
+		      string[i + 1]
+			= (c < 0x20 || (0x7F <= c && (!iso_flag || c < 0xa0))
+			   ? '.' : c);
 
 		      printf ("%02x", c + 0u);
 		    }
 
-		  if ((i&group_by) == group_by)
+		  if ((i & group_by) == group_by)
 		    putchar (' ');
 		}
 
 	      if (i)
 		puts (string);
 
-	      if (c == EOF)
-		break;
-
-	      address += 0x10;
-
+	      if (ferror (stdout))
+		output_error ();
 	    }
 	}
 
-      if (fp != stdin)
-	fclose (fp);
+      bool trouble = ferror (fp) != 0;
+      trouble |= fp != stdin && fclose (fp) != 0;
+      if (trouble)
+	{
+	  fprintf (stderr, "%s: read error\n", progname);
+	  status = EXIT_FAILURE;
+	}
 
-    } while (*argv != NULL);
-  return EXIT_SUCCESS;
-}
+      filename = *argv++;
+    }
+  while (filename);
 
-void
-usage (void)
-{
-  fprintf (stderr, "usage: %s [-de] [-iso]\n", progname);
-  exit (EXIT_FAILURE);
+  if (ferror (stdout) || fclose (stdout) != 0)
+    output_error ();
+  return status;
 }
-
-
-/* hexl.c ends here */
-- 
2.39.5