Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / hexdump / odsyntax.c
1 /*-
2  * Copyright (c) 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)odsyntax.c  8.2 (Berkeley) 5/4/95";
37 #endif
38 #endif /* not lint */
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD: src/usr.bin/hexdump/odsyntax.c,v 1.8.2.1 2002/07/23 14:27:06 tjr Exp $");
41
42 #include <sys/types.h>
43
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <float.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52
53 #include "hexdump.h"
54
55 #define PADDING "         "
56
57 int odmode;
58
59 static void odadd(const char *);
60 static void odformat(const char *);
61 static const char *odformatfp(char, const char *);
62 static const char *odformatint(char, const char *);
63 static void odoffset(int, char ***);
64 static void odusage(void);
65
66 void
67 oldsyntax(argc, argvp)
68         int argc;
69         char ***argvp;
70 {
71         static char empty[] = "", padding[] = PADDING;
72         int ch;
73         char **argv, *end;
74
75         /* Add initial (default) address format. -A may change it later. */
76 #define TYPE_OFFSET     7
77         add("\"%07.7_Ao\n\"");
78         add("\"%07.7_ao  \"");
79
80         odmode = 1;
81         argv = *argvp;
82         while ((ch = getopt(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx")) != -1)
83                 switch (ch) {
84                 case 'A':
85                         switch (*optarg) {
86                         case 'd': case 'o': case 'x':
87                                 fshead->nextfu->fmt[TYPE_OFFSET] = *optarg;
88                                 fshead->nextfs->nextfu->fmt[TYPE_OFFSET] =
89                                     *optarg;
90                                 break;
91                         case 'n':
92                                 fshead->nextfu->fmt = empty;
93                                 fshead->nextfs->nextfu->fmt = padding;
94                                 break;
95                         default:
96                                 errx(1, "%s: invalid address base", optarg);
97                         }
98                         break;
99                 case 'a':
100                         odformat("a");
101                         break;
102                 case 'B':
103                 case 'o':
104                         odformat("o2");
105                         break;
106                 case 'b':
107                         odformat("o1");
108                         break;
109                 case 'c':
110                         odformat("c");
111                         break;
112                 case 'd':
113                         odformat("u2");
114                         break;
115                 case 'D':
116                         odformat("u4");
117                         break;
118                 case 'e':               /* undocumented in od */
119                 case 'F':
120                         odformat("fD");
121                         break;
122                 case 'f':
123                         odformat("fF");
124                         break;
125                 case 'H':
126                 case 'X':
127                         odformat("x4");
128                         break;
129                 case 'h':
130                 case 'x':
131                         odformat("x2");
132                         break;
133                 case 'I':
134                 case 'L':
135                 case 'l':
136                         odformat("dL");
137                         break;
138                 case 'i':
139                         odformat("dI");
140                         break;
141                 case 'j':
142                         errno = 0;
143                         skip = strtoll(optarg, &end, 0);
144                         if (*end == 'b')
145                                 skip *= 512;
146                         else if (*end == 'k')
147                                 skip *= 1024;
148                         else if (*end == 'm')
149                                 skip *= 1048576L;
150                         if (errno != 0 || skip < 0 || strlen(end) > 1)
151                                 errx(1, "%s: invalid skip amount", optarg);
152                         break;
153                 case 'N':
154                         if ((length = atoi(optarg)) <= 0)
155                                 errx(1, "%s: invalid length", optarg);
156                         break;
157                 case 'O':
158                         odformat("o4");
159                         break;
160                 case 's':
161                         odformat("d2");
162                         break;
163                 case 't':
164                         odformat(optarg);
165                         break;
166                 case 'v':
167                         vflag = ALL;
168                         break;
169                 case '?':
170                 default:
171                         odusage();
172                 }
173
174         if (fshead->nextfs->nextfs == NULL)
175                 odformat("oS");
176
177         argc -= optind;
178         *argvp += optind;
179
180         if (argc)
181                 odoffset(argc, argvp);
182 }
183
184 static void
185 odusage(void)
186 {
187
188         fprintf(stderr,
189 "usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n");
190         fprintf(stderr,
191 "          [[+]offset[.][Bb]] [file ...]\n");
192         exit(1);
193 }
194
195 static void
196 odoffset(argc, argvp)
197         int argc;
198         char ***argvp;
199 {
200         unsigned char *p, *num, *end;
201         int base;
202
203         /*
204          * The offset syntax of od(1) was genuinely bizarre.  First, if
205          * it started with a plus it had to be an offset.  Otherwise, if
206          * there were at least two arguments, a number or lower-case 'x'
207          * followed by a number makes it an offset.  By default it was
208          * octal; if it started with 'x' or '0x' it was hex.  If it ended
209          * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
210          * multiplied the number by 512 or 1024 byte units.  There was
211          * no way to assign a block count to a hex offset.
212          *
213          * We assume it's a file if the offset is bad.
214          */
215         p = argc == 1 ? (*argvp)[0] : (*argvp)[1];
216
217         if (*p != '+' && (argc < 2 ||
218             (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1])))))
219                 return;
220
221         base = 0;
222         /*
223          * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
224          * set base.
225          */
226         if (p[0] == '+')
227                 ++p;
228         if (p[0] == 'x' && isxdigit(p[1])) {
229                 ++p;
230                 base = 16;
231         } else if (p[0] == '0' && p[1] == 'x') {
232                 p += 2;
233                 base = 16;
234         }
235
236         /* skip over the number */
237         if (base == 16)
238                 for (num = p; isxdigit(*p); ++p);
239         else
240                 for (num = p; isdigit(*p); ++p);
241
242         /* check for no number */
243         if (num == p)
244                 return;
245
246         /* if terminates with a '.', base is decimal */
247         if (*p == '.') {
248                 if (base)
249                         return;
250                 base = 10;
251         }
252
253         skip = strtoll(num, (char **)&end, base ? base : 8);
254
255         /* if end isn't the same as p, we got a non-octal digit */
256         if (end != p) {
257                 skip = 0;
258                 return;
259         }
260
261         if (*p) {
262                 if (*p == 'B') {
263                         skip *= 1024;
264                         ++p;
265                 } else if (*p == 'b') {
266                         skip *= 512;
267                         ++p;
268                 }
269         }
270
271         if (*p) {
272                 skip = 0;
273                 return;
274         }
275
276         /*
277          * If the offset uses a non-octal base, the base of the offset
278          * is changed as well.  This isn't pretty, but it's easy.
279          */
280         if (base == 16) {
281                 fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
282                 fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
283         } else if (base == 10) {
284                 fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
285                 fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
286         }
287
288         /* Terminate file list. */
289         (*argvp)[1] = NULL;
290 }
291
292 static void
293 odformat(const char *fmt)
294 {
295         char fchar;
296
297         while (*fmt != '\0') {
298                 switch ((fchar = *fmt++)) {
299                 case 'a':
300                         odadd("16/1 \"%3_u \" \"\\n\"");
301                         break;
302                 case 'c':
303                         odadd("16/1 \"%3_c \" \"\\n\"");
304                         break;
305                 case 'o': case 'u': case 'd': case 'x':
306                         fmt = odformatint(fchar, fmt);
307                         break;
308                 case 'f':
309                         fmt = odformatfp(fchar, fmt);
310                         break;
311                 default:
312                         errx(1, "%c: unrecognised format character", fchar);
313                 }
314         }
315 }
316
317 static const char *
318 odformatfp(char fchar __unused, const char *fmt)
319 {
320         size_t isize;
321         int digits;
322         char *end, *hdfmt;
323
324         isize = sizeof(double);
325         switch (*fmt) {
326         case 'F':
327                 isize = sizeof(float);
328                 fmt++;
329                 break;
330         case 'D':
331                 isize = sizeof(double);
332                 fmt++;
333                 break;
334         case 'L':
335                 isize = sizeof(long double);
336                 fmt++;
337                 break;
338         default:
339                 if (isdigit((unsigned char)*fmt)) {
340                         errno = 0;
341                         isize = (size_t)strtoul(fmt, &end, 10);
342                         if (errno != 0 || isize == 0)
343                                 errx(1, "%s: invalid size", fmt);
344                         fmt = (const char *)end;
345                 }
346         }
347         switch (isize) {
348         case sizeof(float):
349                 digits = FLT_DIG;
350                 break;
351         case sizeof(double):
352                 digits = DBL_DIG;
353                 break;
354         default:
355                 if (isize == sizeof(long double))
356                         digits = LDBL_DIG;
357                 else
358                         errx(1, "unsupported floating point size %lu",
359                             (u_long)isize);
360         }
361
362         asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"",
363             16UL / (u_long)isize, (u_long)isize, digits + 8, digits);
364         if (hdfmt == NULL)
365                 err(1, NULL);
366         odadd(hdfmt);
367         free(hdfmt);
368
369         return (fmt);
370 }
371
372 static const char *
373 odformatint(char fchar, const char *fmt)
374 {
375         unsigned long long n;
376         size_t isize;
377         int digits;
378         char *end, *hdfmt;
379
380         isize = sizeof(int);
381         switch (*fmt) {
382         case 'C':
383                 isize = sizeof(char);
384                 fmt++;
385                 break;
386         case 'I':
387                 isize = sizeof(int);
388                 fmt++;
389                 break;
390         case 'L':
391                 isize = sizeof(long);
392                 fmt++;
393                 break;
394         case 'S':
395                 isize = sizeof(short);
396                 fmt++;
397                 break;
398         default:
399                 if (isdigit((unsigned char)*fmt)) {
400                         errno = 0;
401                         isize = (size_t)strtoul(fmt, &end, 10);
402                         if (errno != 0 || isize == 0)
403                                 errx(1, "%s: invalid size", fmt);
404                         if (isize != sizeof(char) && isize != sizeof(short) &&
405                             isize != sizeof(int) && isize != sizeof(long))
406                                 errx(1, "unsupported int size %lu",
407                                     (u_long)isize);
408                         fmt = (const char *)end;
409                 }
410         }
411
412         /*
413          * Calculate the maximum number of digits we need to
414          * fit the number. Overestimate for decimal with log
415          * base 8. We need one extra space for signed numbers
416          * to store the sign.
417          */
418         n = (1ULL << (8 * isize)) - 1;
419         digits = 0;
420         while (n != 0) {
421                 digits++;
422                 n >>= (fchar == 'x') ? 4 : 3;
423         }
424         if (fchar == 'd')
425                 digits++;
426         asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"",
427             16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits),
428             "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar);
429         if (hdfmt == NULL)
430                 err(1, NULL);
431         odadd(hdfmt);
432         free(hdfmt);
433
434         return (fmt);
435 }
436
437 static void
438 odadd(const char *fmt)
439 {
440         static int needpad;
441
442         if (needpad)
443                 add("\""PADDING"\"");
444         add(fmt);
445         needpad = 1;
446 }