Add -e and -E flags to od(1) and properly handle endianness

The -e and -E flags allow the user to override the host endianness
and force od(1) to handle input according to a little (-e) or big (-E)
endian environment.
The previous handling was broken as bitshifts alone are already
endian-independent.
This commit is contained in:
FRIGN 2015-10-25 23:26:49 +01:00 committed by sin
parent c619e168c9
commit d03baf1697
2 changed files with 34 additions and 14 deletions

13
od.1
View File

@ -1,4 +1,4 @@
.Dd 2015-10-09 .Dd 2015-10-25
.Dt OD 1 .Dt OD 1
.Os sbase .Os sbase
.Sh NAME .Sh NAME
@ -7,6 +7,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl A Ar addrformat .Op Fl A Ar addrformat
.Op Fl E | e
.Op Fl t Ar outputformat... .Op Fl t Ar outputformat...
.Op Fl v .Op Fl v
.Op Ar file ... .Op Ar file ...
@ -26,6 +27,12 @@ reads from stdin.
is one of d|o|x|n and sets the address to be is one of d|o|x|n and sets the address to be
either in \fId\fRecimal, \fIo\fRctal, he\fIx\fRadecimal or \fIn\fRot either in \fId\fRecimal, \fIo\fRctal, he\fIx\fRadecimal or \fIn\fRot
printed at all. The default is octal. printed at all. The default is octal.
.It Fl E | e
Force Little Endian
.Fl ( e )
or Big Endian
.Fl ( E )
system-independently.
.It Fl t Ar outputformat .It Fl t Ar outputformat
.Ar outputformat .Ar outputformat
is a list of a|c|d|o|u|x followed by a digit or C|S|I|L and sets is a list of a|c|d|o|u|x followed by a digit or C|S|I|L and sets
@ -49,3 +56,7 @@ The
flag is enabled by default and the 'd' parameter for the flag is enabled by default and the 'd' parameter for the
.Op Fl t .Op Fl t
flag is interpreted as 'u'. flag is interpreted as 'u'.
.Pp
The
.Op Ee
flags are an extension to that specification.

35
od.c
View File

@ -1,5 +1,4 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <endian.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -8,6 +7,8 @@
#include "queue.h" #include "queue.h"
#include "util.h" #include "util.h"
#define HOST_BIG_ENDIAN (*(uint16_t *)"\0\xff" == 0xff)
struct type { struct type {
unsigned char format; unsigned char format;
unsigned int len; unsigned int len;
@ -19,6 +20,7 @@ static unsigned char addr_format = 'o';
static off_t skip = 0; static off_t skip = 0;
static off_t max = -1; static off_t max = -1;
static size_t linelen = 1; static size_t linelen = 1;
static int big_endian;
static void static void
printaddress(off_t addr) printaddress(off_t addr)
@ -37,7 +39,7 @@ static void
printchunk(unsigned char *s, unsigned char format, size_t len) { printchunk(unsigned char *s, unsigned char format, size_t len) {
long long res, basefac; long long res, basefac;
size_t i; size_t i;
char fmt[] = " %0*ll#"; char fmt[] = " %#*ll#";
const char *namedict[] = { const char *namedict[] = {
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "nul", "soh", "stx", "etx", "eot", "enq", "ack",
@ -70,17 +72,18 @@ printchunk(unsigned char *s, unsigned char format, size_t len) {
} }
break; break;
default: default:
res = 0; if (big_endian == HOST_BIG_ENDIAN) {
basefac = 1; for (res = 0, basefac = 1, i = 0; i < len; i++) {
#if __BYTE_ORDER == __BIG_ENDIAN res += s[i] * basefac;
for (i = len; i; i--) { basefac <<= 8;
res += s[i - 1] * basefac; }
#else } else {
for (i = 0; i < len; i++) { for (res = 0, basefac = 1, i = len; i; i--) {
res += s[i] * basefac; res += s[i - 1] * basefac;
#endif basefac <<= 8;
basefac <<= 8; }
} }
fmt[2] = big_endian ? '-' : '+';
fmt[6] = format; fmt[6] = format;
printf(fmt, (int)(3 * len + len - 1), res); printf(fmt, (int)(3 * len + len - 1), res);
} }
@ -165,7 +168,7 @@ lcm(unsigned int a, unsigned int b)
static void static void
usage(void) usage(void)
{ {
eprintf("usage: %s [-A addressformat] [-t outputformat] " eprintf("usage: %s [-A addressformat] [-E | -e] [-t outputformat] "
"[-v] [file ...]\n", argv0); "[-v] [file ...]\n", argv0);
} }
@ -177,6 +180,8 @@ main(int argc, char *argv[])
int ret = 0; int ret = 0;
char *s; char *s;
big_endian = HOST_BIG_ENDIAN;
ARGBEGIN { ARGBEGIN {
case 'A': case 'A':
s = EARGF(usage()); s = EARGF(usage());
@ -184,6 +189,10 @@ main(int argc, char *argv[])
usage(); usage();
addr_format = s[0]; addr_format = s[0];
break; break;
case 'E':
case 'e':
big_endian = (ARGC() == 'E');
break;
case 'j': case 'j':
if ((skip = parseoffset(EARGF(usage()))) < 0) if ((skip = parseoffset(EARGF(usage()))) < 0)
usage(); usage();