1 /* Convert files for Emacs Hexl mode.
2    Copyright (C) 1989, 2001-2021 Free Software Foundation, Inc.
3 
4 Author: Keith Gabryelski (according to authors.el)
5 
6 This file is not considered part of GNU Emacs.
7 
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or (at
11 your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
20 
21 
22 #include <config.h>
23 
24 #include <inttypes.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <binary-io.h>
29 #include <unlocked-io.h>
30 
31 static char *progname;
32 
33 static _Noreturn void
output_error(void)34 output_error (void)
35 {
36   fprintf (stderr, "%s: write error\n", progname);
37   exit (EXIT_FAILURE);
38 }
39 
40 static int
hexchar(int c)41 hexchar (int c)
42 {
43   return c - ('0' <= c && c <= '9' ? '0' : 'a' - 10);
44 }
45 
46 int
main(int argc,char ** argv)47 main (int argc, char **argv)
48 {
49   int status = EXIT_SUCCESS;
50   int DEFAULT_GROUPING = 0x01;
51   int group_by = DEFAULT_GROUPING;
52   bool un_flag = false, iso_flag = false;
53   progname = *argv++;
54 
55   /*
56    ** -hex		hex dump
57    ** -group-by-8-bits
58    ** -group-by-16-bits
59    ** -group-by-32-bits
60    ** -group-by-64-bits
61    ** -iso		iso character set.
62    ** -un || -de	from hexl format to binary.
63    ** --		End switch list.
64    ** <filename>	dump filename
65    ** -		(as filename == stdin)
66    */
67 
68   for (; *argv && *argv[0] == '-' && (*argv)[1]; argv++)
69     {
70       /* A switch! */
71       if (!strcmp (*argv, "--"))
72 	{
73 	  argv++;
74 	  break;
75 	}
76       else if (!strcmp (*argv, "-un") || !strcmp (*argv, "-de"))
77 	{
78 	  un_flag = true;
79 	  set_binary_mode (fileno (stdout), O_BINARY);
80 	}
81       else if (!strcmp (*argv, "-hex"))
82 	/* Hex is the default and is only base supported.  */;
83       else if (!strcmp (*argv, "-iso"))
84 	iso_flag = true;
85       else if (!strcmp (*argv, "-group-by-8-bits"))
86 	group_by = 0x00;
87       else if (!strcmp (*argv, "-group-by-16-bits"))
88 	group_by = 0x01;
89       else if (!strcmp (*argv, "-group-by-32-bits"))
90 	group_by = 0x03;
91       else if (!strcmp (*argv, "-group-by-64-bits"))
92 	group_by = 0x07;
93       else
94 	{
95 	  fprintf (stderr, "%s: invalid switch: \"%s\".\n", progname,
96 		   *argv);
97 	  fprintf (stderr, "usage: %s [-de] [-iso]\n", progname);
98 	  return EXIT_FAILURE;
99 	}
100     }
101 
102   char const *filename = *argv ? *argv++ : "-";
103 
104   do
105     {
106       FILE *fp;
107 
108       if (!strcmp (filename, "-"))
109 	{
110 	  fp = stdin;
111 	  if (!un_flag)
112 	    set_binary_mode (fileno (stdin), O_BINARY);
113 	}
114       else
115 	{
116 	  fp = fopen (filename, un_flag ? "r" : "rb");
117 	  if (!fp)
118 	    {
119 	      perror (filename);
120 	      status = EXIT_FAILURE;
121 	      continue;
122 	    }
123 	}
124 
125       if (un_flag)
126 	{
127 	  for (int c; 0 <= (c = getc (fp)); )
128 	    {
129 	      /* Skip address at start of line.  */
130 	      if (c != ' ')
131 		continue;
132 
133 	      for (int i = 0; i < 16; i++)
134 		{
135 		  c = getc (fp);
136 		  if (c < 0 || c == ' ')
137 		    break;
138 
139 		  int hc = hexchar (c);
140 		  c = getc (fp);
141 		  if (c < 0)
142 		    break;
143 		  putchar (hc * 0x10 + hexchar (c));
144 
145 		  if ((i & group_by) == group_by)
146 		    {
147 		      c = getc (fp);
148 		      if (c < 0)
149 			break;
150 		    }
151 		}
152 
153 	      while (0 <= c && c != '\n')
154 		c = getc (fp);
155 	      if (c < 0)
156 		break;
157 	      if (ferror (stdout))
158 		output_error ();
159 	    }
160 	}
161       else
162 	{
163 	  int c = 0;
164 	  char string[18];
165 	  string[0] = ' ';
166 	  string[17] = '\0';
167 	  for (uintmax_t address = 0; 0 <= c; address += 0x10)
168 	    {
169 	      int i;
170 	      for (i = 0; i < 16; i++)
171 		{
172 		  if (0 <= c)
173 		    c = getc (fp);
174 		  if (c < 0)
175 		    {
176 		      if (!i)
177 			break;
178 
179 		      fputs ("  ", stdout);
180 		      string[i + 1] = '\0';
181 		    }
182 		  else
183 		    {
184 		      if (!i)
185 			printf ("%08"PRIxMAX": ", address);
186 
187 		      string[i + 1]
188 			= (c < 0x20 || (0x7F <= c && (!iso_flag || c < 0xa0))
189 			   ? '.' : c);
190 
191 		      printf ("%02x", c + 0u);
192 		    }
193 
194 		  if ((i & group_by) == group_by)
195 		    putchar (' ');
196 		}
197 
198 	      if (i)
199 		puts (string);
200 
201 	      if (ferror (stdout))
202 		output_error ();
203 	    }
204 	}
205 
206       bool trouble = ferror (fp) != 0;
207       trouble |= fp != stdin && fclose (fp) != 0;
208       if (trouble)
209 	{
210 	  fprintf (stderr, "%s: read error\n", progname);
211 	  status = EXIT_FAILURE;
212 	}
213 
214       filename = *argv++;
215     }
216   while (filename);
217 
218   if (ferror (stdout) || fclose (stdout) != 0)
219     output_error ();
220   return status;
221 }
222