diff options
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | doc/coreutils.texi | 10 | ||||
-rw-r--r-- | src/od.c | 59 | ||||
-rw-r--r-- | tests/local.mk | 1 | ||||
-rwxr-xr-x | tests/misc/od-endian.sh | 46 |
5 files changed, 118 insertions, 3 deletions
@@ -18,6 +18,11 @@ GNU coreutils NEWS -*- outline -*- it would display an error, requiring --no-dereference to avoid the issue. [bug introduced in coreutils-5.3.0] +** New features + + od accepts a new option: --endian=TYPE to handle inputs with different byte + orders, or to provide consistent output on systems with disparate endianness. + ** Improvements stat and tail work better with HFS+ and HFSX. stat -f --format=%T now reports diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 35e7bd9fd..326929125 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -1868,6 +1868,16 @@ none (do not print offsets). The default is octal. +@item --endian=@var{order} +@opindex --endian +@cindex byte-swapping +@cindex endianness +Reorder input bytes, to handle inputs with differing byte orders, +or to provide consistent output independent of the endian convention +of the current system. Swapping is performed according to the +specified @option{--type} size and endian @var{order}, which can be +@samp{little} or @samp{big}. + @item -j @var{bytes} @itemx --skip-bytes=@var{bytes} @opindex -j @@ -23,6 +23,7 @@ #include <getopt.h> #include <sys/types.h> #include "system.h" +#include "argmatch.h" #include "error.h" #include "ftoastr.h" #include "quote.h" @@ -259,13 +260,37 @@ static enum size_spec integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1]; #define MAX_FP_TYPE_SIZE sizeof (long double) static enum size_spec fp_type_size[MAX_FP_TYPE_SIZE + 1]; +#ifndef WORDS_BIGENDIAN +# define WORDS_BIGENDIAN 0 +#endif + +/* Use native endianess by default. */ +static bool input_swap; + static char const short_options[] = "A:aBbcDdeFfHhIij:LlN:OoS:st:vw::Xx"; /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum { - TRADITIONAL_OPTION = CHAR_MAX + 1 + TRADITIONAL_OPTION = CHAR_MAX + 1, + ENDIAN_OPTION, +}; + +enum endian_type +{ + endian_little, + endian_big +}; + +static char const *const endian_args[] = +{ + "little", "big", NULL +}; + +static enum endian_type const endian_types[] = +{ + endian_little, endian_big }; static struct option const long_options[] = @@ -278,6 +303,7 @@ static struct option const long_options[] = {"strings", optional_argument, NULL, 'S'}, {"traditional", no_argument, NULL, TRADITIONAL_OPTION}, {"width", optional_argument, NULL, 'w'}, + {"endian", required_argument, NULL, ENDIAN_OPTION }, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, @@ -318,6 +344,7 @@ suffixes may be . for octal and b for multiply by 512.\n\ fputs (_("\ -A, --address-radix=RADIX output format for file offsets; RADIX is one\n\ of [doxn], for Decimal, Octal, Hex or None\n\ + --endian={big|little} swap input bytes according the specified order\n\ -j, --skip-bytes=BYTES skip BYTES input bytes first\n\ "), stdout); fputs (_("\ @@ -400,13 +427,27 @@ N (size_t fields, size_t blank, void const *block, \ char const *FMT_STRING, int width, int pad) \ { \ T const *p = block; \ - uintmax_t i; \ + uintmax_t i; \ int pad_remaining = pad; \ for (i = fields; blank < i; i--) \ { \ int next_pad = pad * (i - 1) / fields; \ int adjusted_width = pad_remaining - next_pad + width; \ - T x = *p++; \ + T x; \ + if (input_swap && sizeof (T) > 1) \ + { \ + size_t j; \ + union { \ + T x; \ + char b[sizeof (T)]; \ + } u; \ + for (j = 0; j < sizeof (T); j++) \ + u.b[j] = ((const char *) p)[sizeof (T) - 1 - j]; \ + x = u.x; \ + } \ + else \ + x = *p; \ + p++; \ ACTION; \ pad_remaining = next_pad; \ } \ @@ -1664,6 +1705,18 @@ main (int argc, char **argv) traditional = true; break; + case ENDIAN_OPTION: + switch (XARGMATCH ("--endian", optarg, endian_args, endian_types)) + { + case endian_big: + input_swap = ! WORDS_BIGENDIAN; + break; + case endian_little: + input_swap = WORDS_BIGENDIAN; + break; + } + break; + /* The next several cases map the traditional format specification options to the corresponding modern format specs. GNU od accepts any combination of old- and diff --git a/tests/local.mk b/tests/local.mk index 9d556f6db..815dc6fdd 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -239,6 +239,7 @@ all_tests = \ tests/misc/xstrtol.pl \ tests/tail-2/pid.sh \ tests/misc/od.pl \ + tests/misc/od-endian.sh \ tests/misc/od-float.sh \ tests/misc/mktemp.pl \ tests/misc/arch.sh \ diff --git a/tests/misc/od-endian.sh b/tests/misc/od-endian.sh new file mode 100755 index 000000000..5ee17e617 --- /dev/null +++ b/tests/misc/od-endian.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# verify that od --endian works properly + +# Copyright (C) 2014 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ od + +in='0123456789abcdef' + +# rev(1) is not generally available, so here's a simplistic +# implementation sufficient for our purposes. +rev() { + while read line; do + printf '%s' "$line" | sed 's/./&\n/g' | tac | paste -s -d '' + done +} + +in_swapped() { printf '%s' "$in" | sed "s/.\{$1\}/&\\n/g" | rev | tr -d '\n'; } + +for e in little big; do + test $e = little && eo=big || eo=little + for s in 1 2 4 8 16; do + for t in x f; do + od -t $t$s --endian=$e /dev/null > /dev/null 2>&1 || continue + printf '%s' "$in" | od -An -t $t$s --endian=$e > out1 + in_swapped "$s" | od -An -t $t$s --endian=$eo > out2 + compare out1 out2 || fail=1 + done + done +done + +Exit $fail |