Revision: 75109
          http://sourceforge.net/p/brlcad/code/75109
Author:   starseeker
Date:     2020-03-25 22:15:48 +0000 (Wed, 25 Mar 2020)
Log Message:
-----------
Stage hexdump utility in misc/tools

Modified Paths:
--------------
    brlcad/branches/thirdparty_rework/misc/tools/CMakeLists.txt

Added Paths:
-----------
    brlcad/branches/thirdparty_rework/misc/tools/hexdump/
    brlcad/branches/thirdparty_rework/misc/tools/hexdump/CMakeLists.txt
    brlcad/branches/thirdparty_rework/misc/tools/hexdump/README.md
    brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.c
    brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.h
    brlcad/branches/thirdparty_rework/misc/tools/hexdump.dist

Modified: brlcad/branches/thirdparty_rework/misc/tools/CMakeLists.txt
===================================================================
--- brlcad/branches/thirdparty_rework/misc/tools/CMakeLists.txt 2020-03-25 
20:20:15 UTC (rev 75108)
+++ brlcad/branches/thirdparty_rework/misc/tools/CMakeLists.txt 2020-03-25 
22:15:48 UTC (rev 75109)
@@ -194,6 +194,11 @@
 SetTargetFolder(debug2c "Compilation Utilities")
 DISTCLEAN(${CMAKE_CURRENT_BINARY_DIR}/debug2c/Makefile)
 
+# hexdump - used to translate text files into a form suitable for inclusion in 
C code
+THIRD_PARTY_EXECUTABLE(hexdump HEXDUMP hexdump REQUIRED "BRLCAD_LEVEL2" NOSYS)
+DISTCLEAN(${CMAKE_CURRENT_BINARY_DIR}/perplex/Makefile)
+SetTargetFolder(hexdump "Compilation Utilities")
+
 CMAKEFILES(README)
 CMAKEFILES(CMakeLists.txt)
 

Added: brlcad/branches/thirdparty_rework/misc/tools/hexdump/CMakeLists.txt
===================================================================
--- brlcad/branches/thirdparty_rework/misc/tools/hexdump/CMakeLists.txt         
                (rev 0)
+++ brlcad/branches/thirdparty_rework/misc/tools/hexdump/CMakeLists.txt 
2020-03-25 22:15:48 UTC (rev 75109)
@@ -0,0 +1,58 @@
+#                     C M A K E L I S T S . T X T
+# BRL-CAD
+#
+# Copyright (c) 2020 United States Government as represented by
+# the U.S. Army Research Laboratory.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+###
+
+project(HEXDUMP)
+
+cmake_minimum_required(VERSION 3.12)
+
+include_directories(
+  ${CMAKE_CURRENT_BINARY_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  )
+
+if (NOT DEFINED BIN_DIR)
+  set(BIN_DIR bin)
+endif (NOT DEFINED BIN_DIR)
+
+add_executable(hexdump hexdump.c)
+set_property(TARGET hexdump APPEND PROPERTY COMPILE_DEFINITIONS "HEXDUMP_MAIN")
+
+# Local Variables:
+# tab-width: 8
+# mode: cmake
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=2 tabstop=8
+


Property changes on: 
brlcad/branches/thirdparty_rework/misc/tools/hexdump/CMakeLists.txt
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/thirdparty_rework/misc/tools/hexdump/README.md
===================================================================
--- brlcad/branches/thirdparty_rework/misc/tools/hexdump/README.md              
                (rev 0)
+++ brlcad/branches/thirdparty_rework/misc/tools/hexdump/README.md      
2020-03-25 22:15:48 UTC (rev 75109)
@@ -0,0 +1,187 @@
+## DESCRIPTION
+
+`hexdump.c` is a single-file C library implementation of the arcane BSD
+command-line utility, `hexdump(1)`. It allows specifying simple formatting
+programs for display and analysis of binary blobs. For example, the format
+specification
+
+```
+       "%08.8_ax  " 8/1 "%02x " "  " 8/1 "%02x "
+       "  |" 16/1 "%_p" "|\n"
+```
+
+produces the more familiar output
+
+```
+       00000000  54 68 65 20 71 75 69 63  6b 20 62 72 6f 77 6e 20  |The quick 
brown |
+       00000010  66 6f 78 20 6a 75 6d 70  73 20 6f 76 65 72 20 74  |fox jumps 
over t|
+       00000020  68 65 20 6c 61 7a 79 20  64 6f 67                 |he lazy 
dog|
+```
+
+`hexdump.c` can be built as a simple library, a Lua module, or a
+command-line utility; or just dropped into your project without fuss. In
+addition to a few bugs (see below), unlike the traditional utility
+`hexdump.c` allows specifying the byte order of the converted words. It also
+allows processing of odd sized words (e.g. 3 bytes), and in the future will
+allow processing of words larger than 4 bytes.
+
+I wrote `hexdump.c` because I kept rewriting fixed format ouput generators
+over-and-over, in both C and various scripting languages; for simple
+hexadecimal conversion of checksums, analysis of I/O buffers, etc. One bored
+day I asked myself, "why not solve this once and for all." `hexdump(1)` is
+what I use on the command-line, so I decided to copy its semantics.
+
+Instead of refactoring or otherwise copying the BSD implementation, I took
+the opportunity to flex some creative muscle and implement hexdump by
+translating the formatting specification to instructions for a simple
+virtual machine. I mean... why not, right?
+
+`hexdump.c` is fairly conformant to the manual page description of
+`hexdump(1)`. Known bugs include the lack of floating point support (i.e. no
+%E, %e, %f, %G, or %g conversions), the inability to handle %_A address
+conversions, and in a multiline format string no implicit looping of a
+trailing formatting unit to consume the remainder of a block. Because
+`hexdump.c` doesn't generate a parse tree of the formatting string, these
+latter two are more difficult to support and will have to wait until I have
+the patience to add the necessary black magic (i.e. splicing instructions
+into the generated code after analyzing more context).
+
+Note that the original BSD implementation contains a typo, printing the
+ASCII label "dcl" instead of "dc1" for the %_u conversion of octet 021
+(0x11). This typo also manifests in the POSIX `od(1)` utility, which on BSD
+systems is implemented with hexdump. I've filed bug reports with FreeBSD,
+OpenBSD, NetBSD, Debian, and Dragonfly BSD. Apple appears to have quietly
+fixed their copy of BSD hexdump in OS X. (UPDATE: OpenBSD, NetBSD, FreeBSD,
+and Debian have fixed this in their respective trunks.)
+
+I'm unaware of any other independent implementations of hexdump. Some Linux
+distributions repackage BSD hexdump, although not od, which comes from GNU
+core-utils. RedHat's "hexdump" from util-linux appears to be a simple
+wrapper to GNU core-utils od, which cannot handle arbitrary formats, yet
+which is ironically much larger than BSD `hexdump(1)` or `hexdump.c`, which
+belies POSIX's stated reason for excluding hexdump. Solaris doesn't provide
+anything named hexdump; just od.
+
+
+## DEPENDENCIES
+
+None, except that the Lua module requires headers from Lua 5.1 (or LuaJIT),
+Lua 5.2, or Lua 5.3.
+
+The source is mostly C99 compliant. The library optionally uses POSIX
+`_setjmp`/`_longjmp` instead of C99 `setjmp`/`longjmp`, and the command-line
+utility uses POSIX `getopt(3)`. The source should compile cleanly with GCC,
+clang, Sun Studio, and Visual Studio (2013 and later).
+
+The Makefile requires GNU Make, however, so it can introspect the
+compilation environment.
+
+
+## BUILDING
+
+There's little reason not to just plop `hexdump.c` and `hexdump.h` into your
+own project, and that's how I use the library. Neither of the two source
+files will ever be split up. But build rules are included for convenience of
+development.
+
+### Build Targets
+
+#### hexdump
+
+Command-line utility substantially like BSD `hexdump(1)`.
+
+#### libhexdump.so
+
+Dynamic library.
+
+#### libhexdump.dylib
+
+Mach-O dynamic library.
+
+#### 5.1/hexdump.so, 5.2/hexdump.so, 5.3/hexdump.so
+
+Lua 5.1, 5.2, and 5.3 modules, respectively.
+
+#### all
+
+Builds hexdump, dynamically selects libhexdump.so or libhexdump.dylib, and
+builds Lua modules according to `$(LUA_APIS)`, which can contain a
+space-separated list of Lua APIs (e.g. `make LUA_APIS="5.1 5.2 5.3"`). If
+empty then `LUA_APIS` is dynamically set by searching for available Lua
+headers.
+
+
+## USAGE
+
+See `hexdump.h` for some documentation. The most concise example of usage of
+the C API can be found in `hxdL_apply()` in `hexdump.c`. Just ignore the
+parts where the context object is cached. Also see `main()`.
+
+
+## EXTENSIONS
+
+o Explicit byte order support. By default the native byte order is used when
+  loading multibyte words. Currently only big-endian and little-endian is
+  supported, however.
+
+o 3-octet-word conversions. BSD `hexdump(1)` only supports loading of 1-, 2-,
+  and 4-octet words. In the future I'd also like to support up to 16-octet
+  words, which would help debug more complex protocols, like some Microsoft
+  protocols which are fond of such objects as 16-byte, little-endian UUIDs.
+  Maybe binary words, too.
+
+
+## BUGS
+
+* No floating point support. In particular, no %E, %e, %f, %G, or %g
+  conversions.
+
+* No %_A[dox] conversions. %_a[dox] works fine, however.
+
+* No implicit looping of trailing units to consume the remainder of
+  a block. Instead of
+
+  <pre>
+  "%08.8_ax  " 8/1 "%02x " "  " 8/1 "%02x "
+  "  |" "%_p" "|\n"
+  </pre>
+
+  you must write
+
+  <pre>
+  "%08.8_ax  " 8/1 "%02x " "  " 8/1 "%02x "
+  "  |" 16 "%_p" "|\n"
+  </pre>
+
+  Note the additional loop specification, "16", in the second format block.
+
+* Unlike hexdump(1), runs of identical blocks are not skipped by default.
+  There's no configuration option to change this, yet, either.
+
+* No locale support for %_c and %_p, although I consider this a feature.
+  For equivalent hexdump(1) output you need to do `env LANG=C hexdump ...`.
+
+## LICENSE
+
+Copyright (c) 2013, 2016 William Ahern
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
+
+<!-- Markdeep: --><style 
class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script
 src="markdeep.min.js"></script><script 
src="https://casual-effects.com/markdeep/latest/markdeep.min.js";></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>


Property changes on: 
brlcad/branches/thirdparty_rework/misc/tools/hexdump/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.c
===================================================================
--- brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.c              
                (rev 0)
+++ brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.c      
2020-03-25 22:15:48 UTC (rev 75109)
@@ -0,0 +1,2270 @@
+/* ==========================================================================
+ * hexdump.c - hexdump.c
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2013  William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#if __STDC__ && !_XOPEN_SOURCE
+#define _XOPEN_SOURCE 600 /* _setjmp(3), _longjmp(3), getopt(3) */
+#endif
+
+#include <errno.h>  /* ERANGE errno */
+#include <limits.h> /* INT_MAX */
+#include <setjmp.h> /* _setjmp(3) _longjmp(3) */
+#include <stdint.h> /* int64_t */
+#include <stdio.h>  /* FILE fprintf(3) snprintf(3) */
+#include <stdlib.h> /* malloc(3) realloc(3) free(3) abort(3) */
+#include <string.h> /* memset(3) memmove(3) */
+
+#include "hexdump.h"
+
+
+#define SAY_(fmt, ...) fprintf(stderr, fmt "%s", __FILE__, __LINE__, __func__, 
__VA_ARGS__);
+#define SAY(...) SAY_("@@ %s:%d:%s: " __VA_ARGS__, "\n");
+#define HAI SAY("HAI")
+
+#define OOPS(...) do { \
+       SAY(__VA_ARGS__); \
+       abort(); \
+} while (0)
+
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#define countof(a) (sizeof (a) / sizeof *(a))
+
+#ifndef NOTUSED
+#if __GNUC__
+#define NOTUSED __attribute__((unused))
+#else
+#define NOTUSED
+#endif
+#endif
+
+#ifndef NORETURN
+#if __GNUC__
+#define NORETURN __attribute__((noreturn))
+#else
+#define NORETURN
+#endif
+#endif
+
+#if _MSC_VER && _MSC_VER < 1900 && !defined inline
+#define inline __inline
+#endif
+
+#if _MSC_VER
+#define NARG_OUTER(x) x
+#define NARG_INNER(a, b, c, d, e, f, g, h, N,...) N
+#define NARG(...) NARG_OUTER(NARG_INNER(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 
0))
+#else
+#define NARG_(a, b, c, d, e, f, g, h, N,...) N
+#define NARG(...) NARG_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#endif
+
+#define PASTE(x, y) x##y
+#define XPASTE(x, y) PASTE(x, y)
+
+#if _MSC_VER && _MSC_VER < 1900 && !defined snprintf
+#include <stdarg.h> /* va_list va_start va_end */
+
+#define snprintf(...) hxd_snprintf(__VA_ARGS__)
+
+static int (hxd_snprintf)(char *dst, size_t lim, const char *fmt, ...) {
+       va_list ap;
+       int n;
+
+       va_start(ap, fmt);
+       n = _vsnprintf(dst, lim, fmt, ap);
+       va_end(ap);
+
+       if (lim)
+               dst[lim - 1] = '\0';
+
+       return n;
+}
+#endif
+
+
+static unsigned char toprint(unsigned char chr) {
+       return (chr > 0x1f && chr < 0x7f)? chr : '.';
+} /* toprint() */
+
+
+static const char *tooctal(char buf[3], unsigned char chr) {
+       if (chr > 0x1f && chr < 0x7f) {
+               buf[0] = chr;
+               buf[1] = '\0';
+       } else {
+               switch (chr) {
+               case '\0':
+                       buf[0] = '\\';
+                       buf[1] = '0';
+                       buf[2] = '\0';
+
+                       break;
+               case '\a':
+                       buf[0] = '\\';
+                       buf[1] = 'a';
+                       buf[2] = '\0';
+
+                       break;
+               case '\b':
+                       buf[0] = '\\';
+                       buf[1] = 'b';
+                       buf[2] = '\0';
+
+                       break;
+               case '\f':
+                       buf[0] = '\\';
+                       buf[1] = 'f';
+                       buf[2] = '\0';
+
+                       break;
+               case '\n':
+                       buf[0] = '\\';
+                       buf[1] = 'n';
+                       buf[2] = '\0';
+
+                       break;
+               case '\r':
+                       buf[0] = '\\';
+                       buf[1] = 'r';
+                       buf[2] = '\0';
+
+                       break;
+               case '\t':
+                       buf[0] = '\\';
+                       buf[1] = 't';
+                       buf[2] = '\0';
+
+                       break;
+               case '\v':
+                       buf[0] = '\\';
+                       buf[1] = 'v';
+                       buf[2] = '\0';
+
+                       break;
+               default:
+                       buf[0] = "01234567"[0x7 & (chr >> 6)];
+                       buf[1] = "01234567"[0x7 & (chr >> 3)];
+                       buf[2] = "01234567"[0x7 & (chr >> 0)];
+
+                       break;
+               }
+       }
+
+       return buf;
+} /* tooctal() */
+
+
+static const char *toshort(char buf[3], unsigned char chr) {
+       static const char map[][3] = {
+               [0x00] = "nul", [0x01] = "soh", [0x02] = "stx", [0x03] = "etx",
+               [0x04] = "eot", [0x05] = "enq", [0x06] = "ack", [0x07] = "bel",
+               [0x08] = "bs",  [0x09] = "ht",  [0x0a] = "lf",  [0x0b] = "vt",
+               [0x0c] = "ff",  [0x0d] = "cr",  [0x0e] = "so",  [0x0f] = "si",
+               [0x10] = "dle", [0x11] = "dc1", [0x12] = "dc2", [0x13] = "dc3",
+               [0x14] = "dc4", [0x15] = "nak", [0x16] = "syn", [0x17] = "etb",
+               [0x18] = "can", [0x19] = "em",  [0x1a] = "sub", [0x1b] = "esc",
+               [0x1c] = "fs",  [0x1d] = "gs",  [0x1e] = "rs",  [0x1f] = "us",
+               [0x7f] = "del",
+       };
+
+       if (chr <= 0x1f || chr == 0x7f) {
+               memcpy(buf, map[chr], 3);
+       } else if (chr < 0x7f) {
+               buf[0] = chr;
+               buf[1] = '\0';
+       } else {
+               buf[0] = "0123456789abcdef"[0x0f & (chr >> 4)];
+               buf[1] = "0123456789abcdef"[0x0f & chr];
+               buf[2] = '\0';
+       }
+
+       return buf;
+} /* toshort() */
+
+
+static inline _Bool hxd_isspace(unsigned char chr, _Bool nlok) {
+       static const unsigned char space[] = {
+               ['\t'] = 1, ['\n'] = 1, ['\v'] = 1, ['\r'] = 1, ['\f'] = 1, [' 
'] = 1,
+       };
+
+       return (chr < sizeof space && space[chr] && (nlok || chr != '\n'));
+} /* hxd_isspace() */
+
+
+static inline unsigned char skipws(const unsigned char **fmt, _Bool nlok) {
+       while (hxd_isspace(**fmt, nlok))
+               ++*fmt;
+
+       return **fmt;
+} /* skipws() */
+
+
+static inline int getint(const unsigned char **fmt) {
+       static const int limit = ((INT_MAX - (INT_MAX % 10) - 1) / 10);
+       int i = -1;
+
+       if (**fmt >= '0' && **fmt <= '9') {
+               i = 0;
+
+               do {
+                       i *= 10;
+                       i += **fmt - '0';
+                       ++*fmt;
+               } while (**fmt >= '0' && **fmt <= '9' && i <= limit);
+       }
+
+       return i;
+} /* getint() */
+
+
+#define F_HASH  1
+#define F_ZERO  2
+#define F_MINUS 4
+#define F_SPACE 8
+#define F_PLUS 16
+
+#define FC2(x, y) (((0xff & (y)) << 8) | (0xff & (x)))
+#define FC1(x) (0xff & (x))
+#define FC(...) XPASTE(FC, NARG(__VA_ARGS__))(__VA_ARGS__)
+
+static inline int getcnv(int *flags, int *width, int *prec, int *bytes, const 
unsigned char **fmt) {
+       int ch;
+
+       *flags = 0;
+
+       for (; (ch = **fmt); ++*fmt) {
+               switch (ch) {
+               case '#':
+                       *flags |= F_HASH;
+                       break;
+               case '0':
+                       *flags |= F_ZERO;
+                       break;
+               case '-':
+                       *flags |= F_MINUS;
+                       break;
+               case ' ':
+                       *flags |= F_SPACE;
+                       break;
+               case '+':
+                       *flags |= F_PLUS;
+                       break;
+               default:
+                       goto width;
+               } /* switch() */
+       }
+
+width:
+       *width = getint(fmt);
+       *prec = (**fmt == '.')? (++*fmt, getint(fmt)) : -1;
+       *bytes = 0;
+
+       switch ((ch = **fmt)) {
+       case '%':
+               break;
+       case 'c':
+               *bytes = 1;
+               break;
+       case 'd': case 'i': case 'o': case 'u': case 'X': case 'x':
+               *bytes = 4;
+               break;
+       case 's':
+               if (*prec == -1)
+                       return 0;
+               *bytes = *prec;
+               break;
+       case '_':
+               switch (*++*fmt) {
+               case 'a':
+                       switch (*++*fmt) {
+                       case 'd':
+                               ch = FC('_', 'd');
+                               break;
+                       case 'o':
+                               ch = FC('_', 'o');
+                               break;
+                       case 'x':
+                               ch = FC('_', 'x');
+                               break;
+                       default:
+                               return 0;
+                       }
+                       *bytes = 0;
+                       break;
+               case 'A':
+                       switch (*++*fmt) {
+                       case 'd':
+                               ch = FC('_', 'D');
+                               break;
+                       case 'o':
+                               ch = FC('_', 'O');
+                               break;
+                       case 'x':
+                               ch = FC('_', 'X');
+                               break;
+                       default:
+                               return 0;
+                       }
+                       *bytes = 0;
+
+                       /* XXX: Not supported yet. */
+                       return 0;
+
+                       break;
+               case 'c':
+                       ch = FC('_', 'c');
+                       *bytes = 1;
+                       break;
+               case 'p':
+                       ch = FC('_', 'p');
+                       *bytes = 1;
+                       break;
+               case 'u':
+                       ch = FC('_', 'u');
+                       *bytes = 1;
+                       break;
+               default:
+                       return 0;
+               }
+
+               break;
+       } /* switch() */
+
+       ++*fmt;
+
+       return ch;
+} /* getcnv() */
+
+
+enum vm_opcode {
+       OP_HALT,  /* 0/0 */
+       OP_NOOP,  /* 0/0 */
+       OP_TRAP,  /* 0/0 */
+       OP_PC,    /* 0/1 | push program counter */
+       OP_TRUE,  /* 0/1 | push true */
+       OP_FALSE, /* 0/1 | push false */
+       OP_ZERO,  /* 0/1 | push 0 */
+       OP_ONE,   /* 0/1 | push 1 */
+       OP_TWO,   /* 0/1 | push 2 */
+       OP_I8,    /* 0/1 | load 8-bit unsigned int from code */
+       OP_I16,   /* 0/1 | load 16-bit unsigned int from code */
+       OP_I32,   /* 0/1 | load 32-bit unsigned int from code */
+       OP_NEG,   /* 1/1 | arithmetic negative */
+       OP_SUB,   /* 2/1 | S(-2) - S(-1) */
+       OP_ADD,   /* 2/1 | S(-2) + S(-1) */
+       OP_NOT,   /* 1/1 | logical not */
+       OP_OR,    /* 2/1 | logical or */
+       OP_LT,    /* 2/1 | S(-2) < S(-1) */
+       OP_POP,   /* 1/0 | pop top of stack */
+       OP_DUP,   /* 1/2 | dup top of stack */
+       OP_SWAP,  /* 2/2 | swap values at top of stack */
+       OP_READ,  /* 1/1 | read bytes from input buffer */
+       OP_COUNT, /* 0/1 | count of bytes in input buffer */
+       OP_PUTC,  /* 0/0 | copy char directly to output buffer */
+       OP_CONV,  /* 5/0 | write conversion to output buffer */
+       OP_CHOP,  /* 1/0 | chop trailing characters from output buffer */
+       OP_PAD,   /* 1/0 | emit padding space to output buffer */
+       OP_JMP,   /* 2/0 | conditional jump to address */
+       OP_RESET, /* 0/0 | reset input buffer position */
+
+       /*
+        * Optimized conversions with predicates.
+        */
+#define OK_IS0FIXED(F, W, P, N) \
+               (((W) == (N) && ((F) == F_ZERO) && (P) <= 0) || \
+                ((W) == -1 && (P) == (N)) || \
+                ((W) == (P) && (W) == (N)))
+
+#define OK_2XBYTE(F, W, P) OK_IS0FIXED((F), (W), (P), 2)
+       OP_2XBYTE,
+
+#define OK_PBYTE(F, W, P) ((W) <= 1 && (P) <= 1)
+       OP_PBYTE,
+
+#define OK_7XADDR(F, W, P) OK_IS0FIXED((F), (W), (P), 7)
+       OP_7XADDR,
+
+#define OK_8XADDR(F, W, P) OK_IS0FIXED((F), (W), (P), 8)
+       OP_8XADDR,
+}; /* enum vm_opcode */
+
+
+static const char *vm_strop(enum vm_opcode op) {
+       static const char *txt[] = {
+               [OP_HALT]   = "HALT",
+               [OP_NOOP]   = "NOOP",
+               [OP_TRAP]   = "TRAP",
+               [OP_PC]     = "PC",
+               [OP_TRUE]   = "TRUE",
+               [OP_FALSE]  = "FALSE",
+               [OP_ZERO]   = "ZERO",
+               [OP_ONE]    = "ONE",
+               [OP_TWO]    = "TWO",
+               [OP_I8]     = "I8",
+               [OP_I16]    = "I16",
+               [OP_I32]    = "I32",
+               [OP_NEG]    = "NEG",
+               [OP_SUB]    = "SUB",
+               [OP_ADD]    = "ADD",
+               [OP_NOT]    = "NOT",
+               [OP_OR]     = "OR",
+               [OP_LT]     = "LT",
+               [OP_POP]    = "POP",
+               [OP_DUP]    = "DUP",
+               [OP_SWAP]   = "SWAP",
+               [OP_READ]   = "READ",
+               [OP_COUNT]  = "COUNT",
+               [OP_PUTC]   = "PUTC",
+               [OP_CONV]   = "CONV",
+               [OP_CHOP]   = "CHOP",
+               [OP_PAD]    = "PAD",
+               [OP_JMP]    = "JMP",
+               [OP_RESET]  = "RESET",
+               [OP_2XBYTE] = "2XBYTE",
+               [OP_PBYTE]  = "PBYTE",
+               [OP_7XADDR] = "7XADDR",
+               [OP_8XADDR] = "8XADDR",
+       };
+
+       if ((int)op >= 0 && op < (int)countof(txt) && txt[op])
+               return txt[op];
+       else
+               return "-";
+} /* vm_strop() */
+
+
+struct vm_state {
+       jmp_buf trap;
+
+       int flags;
+
+       size_t blocksize;
+
+       int64_t stack[8];
+       int sp;
+
+       unsigned char code[4096];
+       int pc;
+
+       struct {
+               unsigned char *base, *p, *pe;
+               size_t address;
+               _Bool eof;
+       } i;
+
+       struct {
+               unsigned char *base, *p, *pe;
+       } o;
+}; /* struct vm_state */
+
+
+NOTUSED static void op_dump(struct vm_state *M, int *pc, FILE *fp) {
+       enum vm_opcode op = M->code[*pc];
+       unsigned n;
+
+       fprintf(fp, "%d: ", *pc);
+
+       switch (op) {
+       case OP_I8:
+               fprintf(fp, "%s %u\n", vm_strop(op), (unsigned)M->code[++*pc]);
+
+               break;
+       case OP_I16:
+               n = M->code[++*pc] << 8;
+               n |= M->code[++*pc];
+
+               fprintf(fp, "%s %u\n", vm_strop(op), n);
+
+               break;
+       case OP_I32:
+               n = M->code[++*pc] << 24;
+               n |= M->code[++*pc] << 16;
+               n |= M->code[++*pc] << 8;
+               n |= M->code[++*pc] << 0;
+
+               fprintf(fp, "%s %u\n", vm_strop(op), n);
+
+               break;
+       case OP_PUTC: {
+               const char *txt = vm_strop(op);
+               int chr = M->code[++*pc];
+
+               switch (chr) {
+               case '\n':
+                       fprintf(fp, "%s \\n (0x0a)\n", txt);
+
+                       break;
+               case '\r':
+                       fprintf(fp, "%s \\r (0x0d)\n", txt);
+
+                       break;
+               case '\t':
+                       fprintf(fp, "%s \\t (0x09)\n", txt);
+
+                       break;
+               default:
+                       if (chr > 31 && chr < 127)
+                               fprintf(fp, "%s %c (0x%.2x)\n", txt, chr, chr);
+                       else
+                               fprintf(fp, "%s . (0x%.2x)\n", txt, chr);
+
+                       break;
+               } /* switch() */
+
+               break;
+       }
+       default:
+               fprintf(fp, "%s\n", vm_strop(op));
+
+               break;
+       } /* switch() */
+
+       ++*pc;
+} /* op_dump() */
+
+
+NOTUSED static void vm_dump(struct vm_state *M, FILE *fp) {
+       enum vm_opcode op;
+       int pc = 0;
+
+       fprintf(fp, "-- blocksize: %zu\n", M->blocksize);
+
+       do {
+               op = M->code[pc];
+               op_dump(M, &pc, fp);
+       } while (op != OP_HALT);
+} /* vm_dump() */
+
+
+#ifdef _WIN32
+#define vm_enter(M) setjmp((M)->trap)
+#else
+#define vm_enter(M) _setjmp((M)->trap)
+#endif
+
+NORETURN static void vm_throw(struct vm_state *M, int error) {
+#ifdef _WIN32
+       longjmp(M->trap, error);
+#else
+       _longjmp(M->trap, error);
+#endif
+} /* vm_throw() */
+
+
+static inline unsigned char vm_getc(struct vm_state *M) {
+       return (M->i.p < M->i.pe)? *M->i.p++ : 0;
+} /* vm_getc() */
+
+
+static void vm_putc(struct vm_state *M, unsigned char ch) {
+       unsigned char *tmp;
+       size_t size, p;
+
+       if (!(M->o.p < M->o.pe)) {
+               size = MAX(M->o.pe - M->o.base, 64);
+               p = M->o.p - M->o.base;
+
+               if (~size < size)
+                       vm_throw(M, ENOMEM);
+
+               size *= 2;
+
+               if (!(tmp = realloc(M->o.base, size)))
+                       vm_throw(M, errno);
+
+               M->o.base = tmp;
+               M->o.p = &tmp[p];
+               M->o.pe = &tmp[size];
+       }
+
+       *M->o.p++ = ch;
+} /* vm_putc() */
+
+
+static void vm_putx(struct vm_state *M, unsigned char ch) {
+       vm_putc(M, "0123456789abcdef"[0x0f & (ch >> 4)]);
+       vm_putc(M, "0123456789abcdef"[0x0f & (ch >> 0)]);
+} /* vm_putx() */
+
+
+static inline size_t vm_address(struct vm_state *M) {
+       return M->i.address + (M->i.p - M->i.base);
+} /* vm_address() */
+
+
+static void vm_push(struct vm_state *M, int64_t v) {
+       M->stack[M->sp++] = v;
+} /* vm_push() */
+
+
+static int64_t vm_pop(struct vm_state *M) {
+       return M->stack[--M->sp];
+} /* vm_pop() */
+
+
+NOTUSED static int64_t vm_peek(struct vm_state *M, int i) {
+       return (i < 0)? M->stack[M->sp + i] : M->stack[i];
+} /* vm_peek() */
+
+
+static void vm_conv(struct vm_state *M, int flags, int width, int prec, int 
fc, int64_t word) {
+       char fmt[32], *fp, buf[256], label[3];
+       const char *s = NULL;
+       int i, len;
+
+       switch (fc) {
+       case FC('_', 'c'):
+               s = tooctal(label, word);
+               prec = (prec > 0)? MIN(prec, 3) : 3;
+               fc = 's';
+
+               break;
+       case FC('_', 'p'):
+               word = toprint(word);
+               fc = 'c';
+
+               break;
+       case FC('_', 'u'):
+               s = toshort(label, word);
+               prec = (prec > 0)? MIN(prec, 3) : 3;
+               fc = 's';
+
+               break;
+       case FC('_', 'd'):
+               word = M->i.address + (M->i.p - M->i.base);
+               fc = 'd';
+
+               break;
+       case FC('_', 'o'):
+               word = M->i.address + (M->i.p - M->i.base);
+               fc = 'o';
+
+               break;
+       case FC('_', 'x'):
+               word = M->i.address + (M->i.p - M->i.base);
+               fc = 'x';
+
+               break;
+       case FC('_', 'D'):
+               /* FALL THROUGH */
+       case FC('_', 'O'):
+               /* FALL THROUGH */
+       case FC('_', 'X'):
+               fc = 'x';
+
+               vm_throw(M, HXD_ENOTSUPP);
+
+               break;
+       case FC('s'):
+               s = (const char *)M->i.p;
+
+               if (prec <= 0 || prec > M->i.pe - M->i.p)
+                       prec = M->i.pe - M->i.p;
+
+               break;
+       case FC('c'):
+               /* FALL THROUGH */
+       case FC('d'): case FC('i'): case FC('o'):
+       case FC('u'): case FC('X'): case FC('x'):
+               break;
+       default:
+               vm_throw(M, HXD_ENOTSUPP);
+
+               break;
+       } /* switch() */
+
+       fp = fmt;
+
+       *fp++ = '%';
+
+       if (flags & F_HASH)
+               *fp++ = '#';
+       if (flags & F_ZERO)
+               *fp++ = '0';
+       if (flags & F_MINUS)
+               *fp++ = '-';
+       if (flags & F_PLUS)
+               *fp++ = '+';
+
+       *fp++ = '*';
+       *fp++ = '.';
+       *fp++ = '*';
+       *fp++ = fc;
+       *fp = '\0';
+
+       switch (fc) {
+       case 's':
+               len = snprintf(buf, sizeof buf, fmt, MAX(width, 0), MAX(prec, 
0), s);
+
+               break;
+       case 'u':
+               len = snprintf(buf, sizeof buf, fmt, MAX(width, 0), prec, 
(unsigned)word);
+
+               break;
+       default:
+               len = snprintf(buf, sizeof buf, fmt, MAX(width, 0), prec, 
(int)word);
+
+               break;
+       }
+
+       if (-1 == len)
+               vm_throw(M, errno);
+
+       if (len >= (int)sizeof buf)
+               vm_throw(M, ENOMEM);
+
+       for (i = 0; i < len; i++)
+               vm_putc(M, buf[i]);
+} /* vm_conv() */
+
+
+#ifndef VM_FASTER
+#ifdef __GNUC__
+#define VM_FASTER 1
+#else
+#define VM_FASTER 0
+#endif
+#endif
+
+#if VM_FASTER
+#define GNUX(...) (__extension__ ({ __VA_ARGS__; })) /* quiet compiler 
diagnostics */
+#define BEGIN GNUX(goto *jump[M->code[M->pc]])
+#define END (void)0
+#define CASE(op) XPASTE(OP_, op)
+#define NEXT GNUX(goto *jump[M->code[++M->pc]])
+#else
+#define BEGIN exec: switch (M->code[M->pc]) {
+#define END } (void)0
+#define CASE(op) case XPASTE(OP_, op)
+#define NEXT ++M->pc; goto exec
+#endif
+
+static void vm_exec(struct vm_state *M) {
+#if VM_FASTER
+#define L(L) (__extension__ &&XPASTE(OP_, L))
+       static const void *const jump[] = {
+               L(HALT), L(NOOP), L(TRAP), L(PC), L(TRUE), L(FALSE),
+               L(ZERO), L(ONE), L(TWO), L(I8), L(I16), L(I32),
+               L(NEG), L(SUB), L(ADD), L(NOT), L(OR), L(LT),
+               L(POP), L(DUP), L(SWAP), L(READ), L(COUNT), L(PUTC), L(CONV),
+               L(CHOP), L(PAD), L(JMP), L(RESET),
+               L(2XBYTE), L(PBYTE), L(7XADDR), L(8XADDR),
+       };
+#endif
+       int64_t v;
+
+       BEGIN;
+
+       CASE(HALT):
+               return /* void */;
+       CASE(NOOP):
+               NEXT;
+       CASE(TRAP):
+               vm_throw(M, HXD_EOOPS);
+
+               NEXT;
+       CASE(PC):
+               vm_push(M, M->pc);
+
+               NEXT;
+       CASE(TRUE):
+               vm_push(M, 1);
+
+               NEXT;
+       CASE(FALSE):
+               vm_push(M, 0);
+
+               NEXT;
+       CASE(ZERO):
+               vm_push(M, 0);
+
+               NEXT;
+       CASE(ONE):
+               vm_push(M, 1);
+
+               NEXT;
+       CASE(TWO):
+               vm_push(M, 2);
+
+               NEXT;
+       CASE(I8):
+               vm_push(M, M->code[++M->pc]);
+
+               NEXT;
+       CASE(I16):
+               v = M->code[++M->pc] << 8;
+               v |= M->code[++M->pc];
+
+               vm_push(M, v);
+
+               NEXT;
+       CASE(I32):
+               v = M->code[++M->pc] << 24;
+               v |= M->code[++M->pc] << 16;
+               v |= M->code[++M->pc] << 8;
+               v |= M->code[++M->pc];
+
+               vm_push(M, v);
+
+               NEXT;
+       CASE(NEG):
+               vm_push(M, -vm_pop(M));
+
+               NEXT;
+       CASE(SUB): {
+               int64_t b = vm_pop(M);
+               int64_t a = vm_pop(M);
+
+               vm_push(M, a - b);
+
+               NEXT;
+       }
+       CASE(ADD): {
+               int64_t b = vm_pop(M);
+               int64_t a = vm_pop(M);
+
+               vm_push(M, a + b);
+
+               NEXT;
+       }
+       CASE(NOT):
+               vm_push(M, !vm_pop(M));
+
+               NEXT;
+       CASE(OR): {
+               int64_t b = vm_pop(M);
+               int64_t a = vm_pop(M);
+
+               vm_push(M, a || b);
+
+               NEXT;
+       }
+       CASE(LT): {
+               int64_t b = vm_pop(M);
+               int64_t a = vm_pop(M);
+
+               vm_push(M, a < b);
+
+               NEXT;
+       }
+       CASE(POP):
+               vm_pop(M);
+
+               NEXT;
+       CASE(DUP): {
+               int64_t v = vm_pop(M);
+
+               vm_push(M, v);
+               vm_push(M, v);
+
+               NEXT;
+       }
+       CASE(SWAP): {
+               int64_t x = vm_pop(M);
+               int64_t y = vm_pop(M);
+
+               vm_push(M, x);
+               vm_push(M, y);
+
+               NEXT;
+       }
+       CASE(READ): {
+               int64_t i, n, v;
+
+               n = vm_pop(M);
+               v = 0;
+
+               if (M->flags & HXD_BIG_ENDIAN) {
+                       for (i = 0; i < n && M->i.p < M->i.pe; i++) {
+                               v <<= 8;
+                               v |= *M->i.p++;
+                       }
+               } else {
+                       for (i = 0; i < n && M->i.p < M->i.pe; i++) {
+                               v |= *M->i.p++ << (8 * i);
+                       }
+               }
+
+               vm_push(M, v);
+
+               NEXT;
+       }
+       CASE(COUNT):
+               vm_push(M, M->i.pe - M->i.p);
+
+               NEXT;
+       CASE(PUTC): {
+               vm_putc(M, M->code[++M->pc]);
+
+               NEXT;
+       }
+       CASE(CONV): {
+               int fc = vm_pop(M);
+               int prec = vm_pop(M);
+               int width = vm_pop(M);
+               int flags = vm_pop(M);
+               int64_t word = vm_pop(M);
+
+               vm_conv(M, flags, width, prec, fc, word);
+
+               NEXT;
+       }
+       CASE(CHOP):
+               v = vm_pop(M);
+
+               while (v > 0 && M->o.p > M->o.base) {
+                       --M->o.p;
+                       --v;
+               }
+
+               NEXT;
+       CASE(PAD):
+               v = vm_pop(M);
+
+               while (v-- > 0)
+                       vm_putc(M, ' ');
+
+               NEXT;
+       CASE(JMP): {
+               int64_t pc = vm_pop(M);
+
+               if (vm_pop(M)) {
+                       M->pc = pc % countof(M->code);
+#if VM_FASTER
+                       GNUX(goto *jump[M->code[pc]]);
+#else
+                       goto exec;
+#endif
+               }
+
+               NEXT;
+       }
+       CASE(RESET):
+               M->i.p = M->i.base;
+
+               NEXT;
+       CASE(2XBYTE):
+               vm_putx(M, vm_getc(M));
+
+               NEXT;
+       CASE(PBYTE):
+               vm_putc(M, toprint(vm_getc(M)));
+
+               NEXT;
+       CASE(7XADDR): {
+               size_t addr = vm_address(M);
+               vm_putc(M, "0123456789abcdef"[0x0f & (addr >> 24)]);
+               vm_putx(M, (addr >> 16));
+               vm_putx(M, (addr >> 8));
+               vm_putx(M, (addr >> 0));
+
+               NEXT;
+       }
+       CASE(8XADDR): {
+               size_t addr = vm_address(M);
+               vm_putx(M, (addr >> 24));
+               vm_putx(M, (addr >> 16));
+               vm_putx(M, (addr >> 8));
+               vm_putx(M, (addr >> 0));
+
+               NEXT;
+       }
+       END;
+} /* vm_exec() */
+
+
+static void emit_op(struct vm_state *M, unsigned char code) {
+       if (M->pc >= (int)sizeof M->code - 1)
+               vm_throw(M, ENOMEM);
+       M->code[M->pc++] = code;
+} /* emit_op() */
+
+
+static void emit_int(struct vm_state *M, int64_t i) {
+       _Bool isneg;
+
+       if ((isneg = (i < 0)))
+               i *= -1;
+
+       if (i > ((1LL << 32) - 1)) {
+               vm_throw(M, ERANGE);
+       } else if (i > ((1LL << 16) - 1)) {
+               emit_op(M, OP_I32);
+               emit_op(M, 0xff & (i >> 24));
+               emit_op(M, 0xff & (i >> 16));
+               emit_op(M, 0xff & (i >> 8));
+               emit_op(M, 0xff & (i >> 0));
+       } else if (i > ((1LL << 8) - 1)) {
+               emit_op(M, OP_I16);
+               emit_op(M, 0xff & (i >> 8));
+               emit_op(M, 0xff & (i >> 0));
+       } else {
+               switch (i) {
+               case 0:
+                       emit_op(M, OP_ZERO);
+                       break;
+               case 1:
+                       emit_op(M, OP_ONE);
+                       break;
+               case 2:
+                       emit_op(M, OP_TWO);
+                       break;
+               default:
+                       emit_op(M, OP_I8);
+                       emit_op(M, 0xff & i);
+                       break;
+               }
+       }
+
+       if (isneg) {
+               emit_op(M, OP_NEG);
+       }
+} /* emit_int() */
+
+
+static void emit_putc(struct vm_state *M, unsigned char chr) {
+       emit_op(M, OP_PUTC);
+       emit_op(M, chr);
+} /* emit_putc() */
+
+
+static void emit_jmp(struct vm_state *M, int *from) {
+       *from = M->pc;
+       emit_op(M, OP_TRAP);
+       emit_op(M, OP_TRAP);
+       emit_op(M, OP_TRAP);
+       emit_op(M, OP_TRAP);
+       emit_op(M, OP_TRAP);
+       emit_op(M, OP_TRAP);
+} /* emit_jmp() */
+
+
+static void emit_link(struct vm_state *M, int from, int to) {
+       int pc = M->pc;
+
+       M->pc = from;
+
+       emit_op(M, OP_PC);
+
+       if (to < from) {
+               if (from - to > 65535)
+                       vm_throw(M, ERANGE);
+
+               emit_op(M, OP_I16);
+               M->code[M->pc++] = 0xff & ((from - to) >> 8);
+               M->code[M->pc++] = 0xff & ((from - to) >> 0);
+               emit_op(M, OP_SUB);
+       } else {
+               if (to - from > 65535)
+                       vm_throw(M, ERANGE);
+
+               emit_op(M, OP_I16);
+               M->code[M->pc++] = 0xff & ((to - from) >> 8);
+               M->code[M->pc++] = 0xff & ((to - from) >> 0);
+               emit_op(M, OP_ADD);
+       }
+
+       emit_op(M, OP_JMP);
+
+       M->pc = pc;
+} /* emit_link() */
+
+
+static void emit_unit(struct vm_state *M, int loop, int limit, int flags, 
size_t *blocksize, const unsigned char **fmt) {
+       _Bool quoted = 0, escaped = 0;
+       int consumes = 0, chop = 0;
+       int L1, L2, C1 = 0, from, ch;
+
+       loop = (loop < 0)? 1 : loop;
+
+       /* loop counter */
+       emit_int(M, 0);
+
+       /* top of loop */
+       L1 = M->pc;
+       emit_op(M, OP_DUP); /* dup counter */
+       emit_int(M, loop);  /* push loop count */
+       emit_op(M, OP_SWAP);
+       emit_op(M, OP_SUB); /* loop - counter */
+       emit_op(M, OP_NOT);
+       if (flags & HXD_NOPADDING) {
+               emit_op(M, OP_COUNT);
+               C1 = M->pc; /* patch destination for unit size */
+               emit_op(M, OP_TRAP);
+               emit_op(M, OP_TRAP);
+               emit_op(M, OP_LT);
+               emit_op(M, OP_OR);
+       }
+       emit_jmp(M, &L2);
+
+       emit_int(M, 1);
+       emit_op(M, OP_ADD);
+
+       while ((ch = **fmt)) {
+               switch (ch) {
+               case '%': {
+                       int fc, flags, width, prec, bytes;
+
+                       if (escaped)
+                               goto copyout;
+
+                       ++*fmt;
+
+                       if (!(fc = getcnv(&flags, &width, &prec, &bytes, fmt)))
+                               vm_throw(M, HXD_EFORMAT);
+
+                       --*fmt;
+
+                       if (fc == '%') {
+                               ch = '%';
+                               goto copyout;
+                       }
+
+                       if (limit >= 0 && bytes > 0) {
+                               bytes = MIN(limit - consumes, bytes);
+
+                               if (!bytes) /* FIXME: define better error */
+                                       vm_throw(M, HXD_EDRAINED);
+                       }
+
+                       consumes += bytes;
+
+                       {
+                               int J1, J2;
+
+                               if (bytes > 0) {
+                                       if (width > 0) {
+                                               emit_op(M, OP_COUNT);
+                                               emit_jmp(M, &J1);
+                                               emit_int(M, width);
+                                               emit_op(M, OP_PAD);
+                                               emit_op(M, OP_TRUE);
+                                               emit_jmp(M, &J2);
+                                               emit_link(M, J1, M->pc);
+                                       } else {
+                                               emit_op(M, OP_COUNT);
+                                               emit_op(M, OP_NOT);
+                                               emit_jmp(M, &J2);
+                                       }
+                               }
+
+                               if (fc == 'x' && OK_2XBYTE(flags, width, prec)) 
{
+                                       emit_op(M, OP_2XBYTE);
+                               } else if (fc == FC('_', 'p') && 
OK_PBYTE(flags, width, prec)) {
+                                       emit_op(M, OP_PBYTE);
+                               } else if (fc == FC('_', 'x') && 
OK_7XADDR(flags, width, prec)) {
+                                       emit_op(M, OP_7XADDR);
+                               } else if (fc == FC('_', 'x') && 
OK_8XADDR(flags, width, prec)) {
+                                       emit_op(M, OP_8XADDR);
+                               } else {
+                                       emit_int(M, (fc == 's')? 0 : bytes);
+                                       emit_op(M, OP_READ);
+                                       emit_int(M, flags);
+                                       emit_int(M, width);
+                                       emit_int(M, prec);
+                                       emit_int(M, fc);
+                                       emit_op(M, OP_CONV);
+                               }
+
+                               if (bytes > 0)
+                                       emit_link(M, J2, M->pc);
+                       }
+
+                       chop = 0;
+
+                       break;
+               }
+               case ' ': case '\t': case '\n':
+                       if (quoted || escaped)
+                               goto copyout;
+
+                       goto epilog;
+               case '"':
+                       if (escaped)
+                               goto copyout;
+
+                       quoted = !quoted;
+
+                       break;
+               case '\\':
+                       if (escaped)
+                               goto copyout;
+
+                       escaped = 1;
+
+                       break;
+               case '0':
+                       if (escaped)
+                               ch = '\0';
+
+                       goto copyout;
+               case 'a':
+                       if (escaped)
+                               ch = '\a';
+
+                       goto copyout;
+               case 'b':
+                       if (escaped)
+                               ch = '\b';
+
+                       goto copyout;
+               case 'f':
+                       if (escaped)
+                               ch = '\f';
+
+                       goto copyout;
+               case 'n':
+                       if (escaped)
+                               ch = '\n';
+
+                       goto copyout;
+               case 'r':
+                       if (escaped)
+                               ch = '\r';
+
+                       goto copyout;
+               case 't':
+                       if (escaped)
+                               ch = '\t';
+
+                       goto copyout;
+               case 'v':
+                       if (escaped)
+                               ch = '\v';
+
+                       goto copyout;
+               default:
+copyout:
+                       emit_putc(M, ch);
+
+                       escaped = 0;
+
+                       if (hxd_isspace(ch, 0)) {
+                               chop++;
+                       } else {
+                               chop = 0;
+                       }
+               }
+
+               ++*fmt;
+       }
+
+epilog:
+       if (loop > 0 && consumes < limit) {
+               emit_int(M, limit - consumes);
+               emit_op(M, OP_READ);
+               emit_op(M, OP_POP);
+
+               consumes = limit;
+       }
+
+       if (flags & HXD_NOPADDING) {
+               if (consumes > 255)
+                       vm_throw(M, ERANGE);
+
+               /* patch in our unit size */
+               M->code[C1 + 0] = OP_I8;
+               M->code[C1 + 1] = consumes;
+       }
+
+       emit_op(M, OP_TRUE);
+       emit_jmp(M, &from);
+       emit_link(M, from, L1);
+
+       emit_link(M, L2, M->pc);
+       emit_op(M, OP_POP); /* pop loop counter */
+
+       if (loop > 1 && chop > 0) {
+               emit_int(M, chop);
+               emit_op(M, OP_CHOP);
+       }
+
+       *blocksize += (size_t)(consumes * loop);
+
+       return /* void */;
+} /* emit_unit() */
+
+
+struct hexdump {
+       struct vm_state vm;
+
+       char help[64];
+}; /* struct hexdump */
+
+
+static void hxd_init(struct hexdump *X) {
+       memset(X, 0, sizeof *X);
+} /* hxd_init() */
+
+
+struct hexdump *hxd_open(int *error) {
+       struct hexdump *X;
+
+       if (!(X = malloc(sizeof *X)))
+               goto syerr;
+
+       hxd_init(X);
+
+       return X;       
+syerr:
+       *error = errno;
+
+       hxd_close(X);
+
+       return NULL;
+} /* hxd_open() */
+
+
+static void hxd_destroy(struct hexdump *X) {
+       free(X->vm.i.base);
+       free(X->vm.o.base);
+} /* hxd_destroy() */
+
+
+void hxd_close(struct hexdump *X) {
+       if (!X)
+               return /* void */;
+
+       hxd_destroy(X);
+       free(X);
+} /* hxd_close() */
+
+
+void hxd_reset(struct hexdump *X) {
+       X->vm.i.address = 0;
+       X->vm.i.p = X->vm.i.base;
+       X->vm.o.p = X->vm.o.base;
+       X->vm.pc = 0;
+} /* hxd_reset() */
+
+
+int hxd_compile(struct hexdump *X, const char *_fmt, int flags) {
+       const unsigned char *fmt;
+       unsigned char *tmp;
+       int error;
+
+       hxd_reset(X);
+
+       if ((error = vm_enter(&X->vm)))
+               goto error;
+
+       X->vm.flags = flags;
+
+       if (!HXD_BYTEORDER(X->vm.flags)) {
+               union { int i; char c; } u = { 0 };
+
+               u.c = 1;
+               X->vm.flags |= (u.i & 0xff)? HXD_LITTLE_ENDIAN : HXD_BIG_ENDIAN;
+       }
+
+       fmt = (const unsigned char *)_fmt;
+
+       while (skipws(&fmt, 1)) {
+               int lc, loop, limit, flags;
+               size_t blocksize = 0;
+
+               flags = X->vm.flags;
+
+               emit_op(&X->vm, OP_RESET);
+
+               do {
+                       loop = getint(&fmt);
+
+                       if ('/' == skipws(&fmt, 0)) {
+                               fmt++;
+                               limit = getint(&fmt);
+       
+                               if (*fmt == '?') {
+                                       flags |= HXD_NOPADDING;
+                                       fmt++;
+                               }
+                       } else {
+                               limit = -1;
+                       }
+
+                       skipws(&fmt, 0);
+                       emit_unit(&X->vm, loop, limit, flags, &blocksize, &fmt);
+               } while ((lc = skipws(&fmt, 0)) && lc != '\n');
+
+               if (blocksize > X->vm.blocksize)
+                       X->vm.blocksize = blocksize;
+       }
+
+       emit_op(&X->vm, OP_HALT);
+       memset(&X->vm.code[X->vm.pc], OP_TRAP, sizeof X->vm.code - X->vm.pc);
+
+       if (!(tmp = realloc(X->vm.i.base, X->vm.blocksize)))
+               goto syerr;
+
+       X->vm.i.base = tmp;
+       X->vm.i.p = tmp;
+       X->vm.i.pe = &tmp[X->vm.blocksize];
+
+       return 0;
+syerr:
+       error = errno;
+error:
+       hxd_reset(X);
+       memset(X->vm.code, 0, sizeof X->vm.code);
+
+       return error;
+} /* hxd_compile() */
+
+
+size_t hxd_blocksize(struct hexdump *X) {
+       return X->vm.blocksize;
+} /* hxd_blocksize() */
+
+
+const char *hxd_help(struct hexdump *X) {
+       (void)X;
+       return "helps";
+} /* hxd_help() */
+
+
+int hxd_write(struct hexdump *X, const void *src, size_t len) {
+       const unsigned char *p, *pe;
+       size_t n;
+       int error;
+
+       if ((error = vm_enter(&X->vm)))
+               goto error;
+
+       if (X->vm.i.pe == X->vm.i.base)
+               vm_throw(&X->vm, HXD_EOOPS);
+
+       p = src;
+       pe = p + len;
+
+       while (p < pe) {
+               n = MIN(pe - p, X->vm.i.pe - X->vm.i.p);
+               memcpy(X->vm.i.p, p, n);
+               X->vm.i.p += n;
+               p += n;
+
+               if (X->vm.i.p < X->vm.i.pe)
+                       break;
+
+               X->vm.i.p = X->vm.i.base;
+               X->vm.pc = 0;
+               vm_exec(&X->vm);
+               X->vm.i.p = X->vm.i.base;
+               X->vm.i.address += X->vm.blocksize;
+       }
+
+       return 0;
+error:
+       return error;
+} /* hxd_write() */
+
+
+int hxd_flush(struct hexdump *X) {
+       unsigned char *pe;
+       int error;
+
+       if ((error = vm_enter(&X->vm)))
+               goto error;
+
+       if (X->vm.i.p > X->vm.i.base) {
+               pe = X->vm.i.pe;
+               X->vm.i.pe = X->vm.i.p;
+               X->vm.i.p = X->vm.i.base;
+               X->vm.pc = 0;
+               vm_exec(&X->vm);
+               X->vm.i.p = X->vm.i.base;
+               X->vm.i.pe = pe;
+       }
+
+       return 0;
+error:
+       return error;
+} /* hxd_flush() */
+
+
+size_t hxd_read(struct hexdump *X, void *dst, size_t lim) {
+       unsigned char *p, *pe, *op;
+       size_t n;
+
+       p = dst;
+       pe = p + lim;
+       op = X->vm.o.base;
+
+       while (p < pe && op < X->vm.o.p) {
+               n = MIN(pe - p, X->vm.o.p - op);
+               memcpy(p, op, n);
+               p += n;
+               op += n;
+       }
+
+       n = X->vm.o.p - op;
+       memmove(X->vm.o.base, op, n);
+       X->vm.o.p = &X->vm.o.base[n];
+
+       return p - (unsigned char *)dst;
+} /* hxd_read() */
+
+
+const char *hxd_strerror(int error) {
+       static const char *txt[] = {
+               [HXD_EFORMAT - HXD_EBASE] = "invalid format",
+               [HXD_EDRAINED - HXD_EBASE] = "unit drains buffer",
+               [HXD_ENOTSUPP - HXD_EBASE] = "unsupported conversion sequence",
+               [HXD_EOOPS - HXD_EBASE] = "machine traps",
+       };
+
+       if (error >= 0)
+               return strerror(error);
+
+       if (error >= HXD_EBASE && error < HXD_ELAST) {
+               error -= HXD_EBASE;
+
+               if (error < (int)countof(txt) && txt[error])
+                       return txt[error];
+       }
+
+       return "unknown error (hexdump)";
+} /* hxd_strerror() */
+
+
+int hxd_version(void) {
+       return HXD_VERSION;
+} /* hxd_version() */
+
+
+const char *hxd_vendor(void) {
+       return HXD_VENDOR;
+} /* hxd_vendor() */
+
+
+int hxd_v_rel(void) {
+       return HXD_V_REL;
+} /* hxd_v_rel() */
+
+
+int hxd_v_abi(void) {
+       return HXD_V_ABI;
+} /* hxd_v_abi() */
+
+
+int hxd_v_api(void) {
+       return HXD_V_API;
+} /* hxd_v_api() */
+
+
+#if HEXDUMP_LUALIB
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#if WITH_LUA_VERSION_NUM && WITH_LUA_VERSION_NUM != LUA_VERSION_NUM
+#error Lua headers do not implement expected API
+#endif
+
+#define HEXDUMP_CLASS "HEXDUMP*"
+
+
+static inline struct hexdump *hxdL_checkudata(lua_State *L, int index) {
+       return *(struct hexdump **)luaL_checkudata(L, index, HEXDUMP_CLASS);
+} /* hxdL_checkudata() */
+
+
+static struct hexdump *hxdL_push(lua_State *L) {
+       struct hexdump **X;
+       int error;
+
+       X = lua_newuserdata(L, sizeof *X);
+       *X = NULL;
+
+       luaL_getmetatable(L, HEXDUMP_CLASS);
+       lua_setmetatable(L, -2);
+
+       if (!(*X = hxd_open(&error)))
+               luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+       return *X;
+} /* hxdL_push() */
+
+
+static int hxdL_apply(lua_State *L) {
+       const char *fmt, *p, *pe;
+       size_t n;
+       struct hexdump *X;
+       luaL_Buffer B;
+       int top = lua_gettop(L), data = 2, flags = 0;
+       int error;
+
+       fmt = luaL_checkstring(L, 1);
+
+       if (lua_type(L, 2) == LUA_TNUMBER) {
+               flags = lua_tointeger(L, 2);
+               data = 3;
+       }
+
+       lua_pushvalue(L, 1);
+       lua_rawget(L, lua_upvalueindex(1));
+
+       if (lua_isnil(L, -1)) {
+               lua_pop(L, 1);
+
+               lua_newtable(L);
+
+               lua_pushvalue(L, 1);
+               lua_pushvalue(L, -2);
+               lua_rawset(L, lua_upvalueindex(1));
+       }
+
+       lua_rawgeti(L, -1, flags);
+
+       if (lua_isnil(L, -1)) {
+               lua_pop(L, 1);
+
+               X = hxdL_push(L);
+
+               if ((error = hxd_compile(X, fmt, flags)))
+                       goto error;
+
+               lua_pushvalue(L, -1);
+               lua_rawseti(L, -3, flags);
+       } else {
+               X = hxdL_checkudata(L, -1);
+       }
+
+       hxd_reset(X);
+
+       luaL_buffinit(L, &B);
+
+       for (; data <= top; data++) {
+               p = luaL_checklstring(L, data, &n);
+               pe = p + n;
+
+               while (p < pe) {
+                       n = MIN(pe - p, 1024);
+
+                       if ((error = hxd_write(X, p, n)))
+                               goto error;
+
+                       p += n;
+
+                       while ((n = hxd_read(X, luaL_prepbuffer(&B), 
LUAL_BUFFERSIZE)))
+                               luaL_addsize(&B, n);
+               }
+       }
+
+       if ((error = hxd_flush(X)))
+               goto error;
+
+       while ((n = hxd_read(X, luaL_prepbuffer(&B), LUAL_BUFFERSIZE)))
+               luaL_addsize(&B, n);
+
+       luaL_pushresult(&B);
+
+       return 1;
+error:
+       return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+} /* hxdL_apply() */
+
+
+static int hxdL__call(lua_State *L) {
+       lua_pushvalue(L, lua_upvalueindex(1));
+       lua_replace(L, 1);
+
+       lua_call(L, lua_gettop(L) - 1, 1);
+
+       return 1;
+} /* hxdL__call() */
+
+
+static int hxdL_new(lua_State *L) {
+       hxdL_push(L);
+
+       return 1;
+} /* hxdL_new() */
+
+
+static int hxdL_compile(lua_State *L) {
+       struct hexdump *X = hxdL_checkudata(L, 1);
+       const char *fmt = luaL_checkstring(L, 2);
+       int flags = (int)luaL_optinteger(L, 3, 0);
+       int error;
+
+       if ((error = hxd_compile(X, fmt, flags)))
+               return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+       lua_pushboolean(L, 1);
+
+       return 1;
+} /* hxdL_compile() */
+
+
+static int hxdL_blocksize(lua_State *L) {
+       struct hexdump *X = hxdL_checkudata(L, 1);
+
+       lua_pushnumber(L, hxd_blocksize(X));
+
+       return 1;
+} /* hxdL_blocksize() */
+
+
+static int hxdL_write(lua_State *L) {
+       struct hexdump *X = hxdL_checkudata(L, 1);
+       const char *data;
+       size_t size;
+       int error;
+
+       data = luaL_checklstring(L, 2, &size);
+
+       if ((error = hxd_write(X, data, size)))
+               return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+       lua_pushboolean(L, 1);
+
+       return 1;
+} /* hxdL_write() */
+
+
+static int hxdL_flush(lua_State *L) {
+       struct hexdump *X = hxdL_checkudata(L, 1);
+       int error;
+
+       if ((error = hxd_flush(X)))
+               return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+       lua_pushboolean(L, 1);
+
+       return 1;
+} /* hxdL_flush() */
+
+
+static int hxdL_read(lua_State *L) {
+       struct hexdump *X = hxdL_checkudata(L, 1);
+       luaL_Buffer B;
+       size_t count;
+
+       luaL_buffinit(L, &B);
+
+       while ((count = hxd_read(X, luaL_prepbuffer(&B), LUAL_BUFFERSIZE)))
+               luaL_addsize(&B, count);
+
+       luaL_pushresult(&B);
+
+       return 1;
+} /* hxdL_read() */
+
+
+static int hxdL__gc(lua_State *L) {
+       struct hexdump **X = luaL_checkudata(L, 1, HEXDUMP_CLASS);
+
+       hxd_close(*X);
+       *X = NULL;
+
+       return 0;
+} /* hxdL__gc() */
+
+
+static const luaL_Reg hxdL_methods[] = {
+       { "compile",   &hxdL_compile },
+       { "blocksize", &hxdL_blocksize },
+       { "write",     &hxdL_write },
+       { "flush",     &hxdL_flush },
+       { "read",      &hxdL_read },
+       { NULL,        NULL },
+}; /* hxdL_methods[] */
+
+
+static const luaL_Reg hxdL_metatable[] = {
+       { "__gc",  &hxdL__gc },
+       { NULL,    NULL },
+}; /* hxdL_metatable[] */
+
+
+static const luaL_Reg hxdL_globals[] = {
+       { "new",  &hxdL_new },
+       { NULL,   NULL },
+}; /* hxdL_globals[] */
+
+
+static void hxdL_register(lua_State *L, const luaL_Reg *l) {
+#if LUA_VERSION_NUM >= 502
+       luaL_setfuncs(L, l, 0);
+#else
+       luaL_register(L, NULL, l);
+#endif
+} /* hxdL_register() */
+
+
+int luaopen_hexdump(lua_State *L) {
+       static const struct { const char *k; int v; } macro[] = {
+               { "NATIVE",        HXD_NATIVE },
+               { "NETWORK",       HXD_NETWORK },
+               { "BIG_ENDIAN",    HXD_BIG_ENDIAN },
+               { "LITTLE_ENDIAN", HXD_LITTLE_ENDIAN },
+               { "NOPADDING",     HXD_NOPADDING },
+       };
+       static const struct { const char *k; const char *v; } predef[] = {
+               { "b", HEXDUMP_b },
+               { "c", HEXDUMP_c },
+               { "C", HEXDUMP_C },
+               { "d", HEXDUMP_d },
+               { "o", HEXDUMP_o },
+               { "x", HEXDUMP_x },
+               { "i", HEXDUMP_i },
+       };
+       unsigned i;
+
+       if (luaL_newmetatable(L, HEXDUMP_CLASS)) {
+               hxdL_register(L, hxdL_metatable);
+               lua_newtable(L);
+               hxdL_register(L, hxdL_methods);
+               lua_setfield(L, -2, "__index");
+       }
+
+       lua_pop(L, 1);
+
+       lua_newtable(L);
+       hxdL_register(L, hxdL_globals);
+
+       lua_newtable(L); /* cache of compiled formats */
+       lua_pushcclosure(L, &hxdL_apply, 1);
+
+       lua_newtable(L); /* metatable of our global table */
+       lua_pushvalue(L, -2);
+       lua_pushcclosure(L, &hxdL__call, 1);
+       lua_setfield(L, -2, "__call");
+       lua_setmetatable(L, -3);
+
+       lua_setfield(L, -2, "apply"); /* global.apply */
+
+       for (i = 0; i < countof(macro); i++) {
+               lua_pushinteger(L, macro[i].v);
+               lua_setfield(L, -2, macro[i].k);
+       }
+
+       for (i = 0; i < countof(predef); i++) {
+               lua_pushstring(L, predef[i].v);
+               lua_setfield(L, -2, predef[i].k);
+       }
+
+       lua_pushinteger(L, HXD_VERSION);
+       lua_setfield(L, -2, "VERSION");
+
+       lua_pushstring(L, HXD_VENDOR);
+       lua_setfield(L, -2, "VENDOR");
+
+       return 1;
+} /* luaopen_hexdump() */
+
+#else
+
+int luaopen_hexdump() {
+       abort();
+} /* luaopen_hexdump() */
+
+#endif /* HEXDUMP_LUALIB */
+
+
+#if HEXDUMP_MAIN
+
+#include <errno.h>  /* ERANGE */
+#include <limits.h> /* LONG_MAX ULONG_MAX */
+#include <stdlib.h> /* strtoul(3) */
+#include <stdio.h>  /* FILE SEEK_SET stdout stderr stdin fprintf(3) fseek(3) */
+#include <string.h> /* strcmp(3) */
+
+#ifdef _WIN32
+#include <fcntl.h>  /* _fcntl(3) _setmode(3) _O_BINARY */
+#endif
+
+#ifndef HAVE_ERR
+#define HAVE_ERR (!_WIN32)
+#endif
+
+#ifndef HAVE_GETOPT
+#define HAVE_GETOPT (!_WIN32)
+#endif
+
+#if HAVE_ERR
+#include <err.h>    /* err(3) errx(3) */
+#else
+#include <stdarg.h> /* va_list va_start va_end */
+
+static void warn(const char *fmt, ...) {
+       int error = errno;
+       va_list ap;
+       va_start(ap, fmt);
+       fputs("hexdump: ", stderr);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, ": %s\n", strerror(error));
+       va_end(ap);
+}
+
+static void warnx(const char *fmt, ...) {
+       va_list ap;
+       va_start(ap, fmt);
+       fputs("hexdump: ", stderr);
+       vfprintf(stderr, fmt, ap);
+       fputc('\n', stderr);
+       va_end(ap);
+}
+
+#define err(status, ...) (warn(__VA_ARGS__), exit((status)))
+#define errx(status, ...) (warnx(__VA_ARGS__), exit((status)))
+
+#endif /* HAVE_ERR */
+
+#if HAVE_GETOPT
+#include <unistd.h> /* getopt(3) */
+#else
+char *optarg;
+int opterr = 1, optind = 1, optopt;
+
+#define ENTER                                                           \
+       do {                                                            \
+       static const int pc0 = __LINE__;                                \
+       switch (pc0 + pc) {                                             \
+       case __LINE__: (void)0
+
+#define SAVE_AND_DO(do_statement)                                       \
+       do {                                                            \
+               pc = __LINE__ - pc0;                                    \
+               do_statement;                                           \
+               case __LINE__: (void)0;                                 \
+       } while (0)
+
+#define YIELD(rv)                                                       \
+       SAVE_AND_DO(return (rv))
+
+#define LEAVE                                                           \
+       SAVE_AND_DO(break);                                             \
+       }                                                               \
+       } while (0)
+
+int getopt(int argc, char *const argv[], const char *shortopts) {
+       static unsigned pc;
+       static char *cp;
+
+       optopt = 0;
+       optarg = NULL;
+
+       ENTER;
+
+       while (optind < argc) {
+               cp = argv[optind];
+
+               if (*cp != '-' || !strcmp(cp, "-")) {
+                       break;
+               } else if (!strcmp(cp, "--")) {
+                       optind++;
+                       break;
+               }
+
+               for (;;) {
+                       char *shortopt;
+
+                       if (!(optopt = *++cp)) {
+                               optind++;
+                               break;
+                       } else if (!(shortopt = strchr(shortopts, optopt))) {
+                               if (*shortopts != ':' && opterr)
+                                       warnx("illegal option -- %c", optopt);
+                               YIELD('?');
+                       } else if (shortopt[1] != ':') {
+                               YIELD(optopt);
+                       } else if (cp[1]) {
+                               optarg = &cp[1];
+                               optind++;
+                               YIELD(optopt);
+                               break;
+                       } else if (optind + 1 < argc) {
+                               optarg = argv[optind + 1];
+                               optind += 2;
+                               YIELD(optopt);
+                               break;
+                       } else {
+                               if (*shortopts != ':' && opterr)
+                                       warnx("option requires an argument -- 
%c", optopt);
+                               optind++;
+                               YIELD((*shortopts == ':')? ':' : '?');
+                               break;
+                       }
+               }
+       }
+
+       LEAVE;
+
+       return -1;
+}
+#endif /* HAVE_GETOPT */
+
+
+static void run(struct hexdump *X, FILE *fp, _Bool flush, size_t *off, size_t 
*max) {
+       char buf[256];
+       size_t len;
+       int error;
+
+       /* TODO: need to update the dump address after skipping */
+       if (*off) {
+               long n = MIN(LONG_MAX, *off);
+               if (0 == fseek(fp, n, SEEK_SET))
+                       *off -= n;
+               while (*off && (len = fread(buf, 1, MIN(sizeof buf, *off), fp)))
+                       *off -= len;
+       }
+
+       while (*max && (len = fread(buf, 1, MIN(sizeof buf, *max), fp))) {
+               *max -= len;
+               if ((error = hxd_write(X, buf, len)))
+                       errx(EXIT_FAILURE, "%s", hxd_strerror(error));
+
+               while ((len = hxd_read(X, buf, sizeof buf)))
+                       fwrite(buf, 1, len, stdout);
+       }
+
+       if (flush) {
+               if ((error = hxd_flush(X)))
+                       errx(EXIT_FAILURE, "%s", hxd_strerror(error));
+
+               while ((len = hxd_read(X, buf, sizeof buf)))
+                       fwrite(buf, 1, len, stdout);
+       }
+} /* run() */
+
+
+static size_t tosize(const char *optarg) {
+       unsigned long lu;
+       char *argend;
+
+       errno = 0;
+       lu = strtoul(optarg, &argend, 0);
+       if (lu == ULONG_MAX && errno == ERANGE)
+               err(EXIT_FAILURE, "%s", optarg);
+       if (argend == optarg)
+               errx(EXIT_FAILURE, "%s: invalid number", optarg);
+
+       if (*argend) {
+               unsigned long scale = 0;
+
+               switch (*argend) {
+               case 'b':
+                       scale = 512;
+                       argend++;
+                       break;
+               case 'k':
+                       scale = 1024;
+                       argend++;
+                       break;
+               case 'm':
+                       scale = 1048576;
+                       argend++;
+                       break;
+               }
+
+               if (!scale || *argend)
+                       errx(EXIT_FAILURE, "%s: invalid number", optarg);
+
+               if (lu > ULONG_MAX / scale) {
+                       errno = ERANGE;
+                       err(EXIT_FAILURE, "%s", optarg);
+               }
+
+               lu *= scale;
+       }
+
+       return lu;
+}
+
+
+int main(int argc, char **argv) {
+       extern char *optarg;
+       extern int optind;
+       int opt, flags = 0;
+       _Bool dump = 0;
+       struct hexdump *X;
+       char *fmt = HEXDUMP_x, fmtbuf[512];
+       size_t len;
+       size_t max = (size_t)-1;
+       size_t off = 0;
+       int error;
+
+       while (-1 != (opt = getopt(argc, argv, "bcCde:f:n:os:xiBLPDVh"))) {
+               switch (opt) {
+               case 'b':
+                       fmt = HEXDUMP_b;
+
+                       break;
+               case 'c':
+                       fmt = HEXDUMP_c;
+
+                       break;
+               case 'C':
+                       fmt = HEXDUMP_C;
+                       break;
+               case 'd':
+                       fmt = HEXDUMP_d;
+
+                       break;
+               case 'e':
+                       fmt = optarg;
+
+                       break;
+               case 'f': {
+                       FILE *fp = (!strcmp(optarg, "-"))? stdin : 
fopen(optarg, "r");
+
+                       if (!fp)
+                               err(EXIT_FAILURE, "%s", optarg);
+
+                       len = fread(fmtbuf, 1, sizeof fmtbuf - 1, fp);
+                       fmtbuf[len] = '\0';
+
+                       if (fp != stdin)
+                               fclose(fp);
+
+                       fmt = fmtbuf;
+
+                       break;
+               }
+               case 'n':
+                       max = tosize(optarg);
+
+                       break;
+               case 'o':
+                       fmt = HEXDUMP_o;
+
+                       break;
+               case 's':
+                       off = tosize(optarg);
+
+                       break;
+               case 'x':
+                       fmt = HEXDUMP_x;
+
+                       break;
+               case 'i':
+                       fmt = HEXDUMP_i;
+
+                       break;
+               case 'B':
+                       flags |= HXD_BIG_ENDIAN;
+
+                       break;
+               case 'L':
+                       flags |= HXD_LITTLE_ENDIAN;
+
+                       break;
+               case 'P':
+                       flags |= HXD_NOPADDING;
+
+                       break;
+               case 'D':
+                       dump = 1;
+
+                       break;
+               case 'V':
+                       printf("%s (hexdump.c) %.8X\n", argv[0], hxd_version());
+                       printf("built   %s %s\n", __DATE__, __TIME__);
+                       printf("vendor  %s\n", hxd_vendor());
+                       printf("release %.8X\n", hxd_v_rel());
+                       printf("abi     %.8X\n", hxd_v_abi());
+                       printf("api     %.8X\n", hxd_v_api());
+
+                       return 0;
+               default: {
+                       FILE *fp = (opt == 'h')? stdout : stderr;
+
+                       fprintf(fp,
+                               "hexdump [-bcCde:f:n:os:xiBLPDVh] [file ...]\n" 
\
+                               "  -b       one-byte octal display\n" \
+                               "  -c       one-byte character display\n" \
+                               "  -C       canonical hex+ASCII display\n" \
+                               "  -d       two-byte decimal display\n" \
+                               "  -e FMT   hexdump string format\n" \
+                               "  -f PATH  path to hexdump format file\n" \
+                               "  -n NUM   dump maximum size\n" \
+                               "  -o       two-byte octal display\n" \
+                               "  -s NUM   skip offset bytes\n" \
+                               "  -x       two-byte hexadecimal display\n" \
+                               "  -i       one-byte hexadecimal like xxd -i\n" 
\
+                               "  -B       load words big-endian\n" \
+                               "  -L       load words little-endian\n" \
+                               "  -P       disable padding\n" \
+                               "  -D       dump the compiled machine\n" \
+                               "  -V       print version\n" \
+                               "  -h       print usage help\n" \
+                               "\n" \
+                               "Report bugs to <will...@25thandclement.com>\n"
+                       );
+
+                       return (opt == 'h')? 0 : EXIT_FAILURE;
+               }
+               } /* switch() */
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (!(X = hxd_open(&error)))
+               errx(EXIT_FAILURE, "open: %s", hxd_strerror(error));
+
+       if ((error = hxd_compile(X, fmt, flags)))
+               errx(EXIT_FAILURE, "%s: %s", fmt, hxd_strerror(error));
+
+       if (dump) {
+               vm_dump(&X->vm, stdout);
+
+               goto exit;
+       }
+
+       if (!argc) {
+#ifdef _WIN32
+               _setmode(_fileno(stdin), _O_BINARY);
+#endif
+               run(X, stdin, 1, &off, &max);
+       } else {
+               int i;
+
+               for (i = 0; i < argc; i++) {
+                       FILE *fp;
+
+                       if (!(fp = fopen(argv[i], "rb")))
+                               err(EXIT_FAILURE, "%s", argv[i]);
+
+                       run(X, fp, !argv[i + 1], &off, &max);
+
+                       fclose(fp);
+               }
+       }
+exit:
+       hxd_close(X);
+
+       return 0;
+} /* main() */
+
+#endif /* HEXDUMP_MAIN */


Property changes on: 
brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.h
===================================================================
--- brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.h              
                (rev 0)
+++ brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.h      
2020-03-25 22:15:48 UTC (rev 75109)
@@ -0,0 +1,215 @@
+/* ==========================================================================
+ * hexdump.h - hexdump.c
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2013  William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#ifndef HEXDUMP_H
+#define HEXDUMP_H
+
+
+/*
+ * H E X D U M P  V E R S I O N  I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define HXD_VERSION HXD_V_REL
+#define HXD_VENDOR "will...@25thandclement.com"
+
+#define HXD_V_REL 0x20181221
+#define HXD_V_ABI 0x20130210
+#define HXD_V_API 0x20130412
+
+int hxd_version(void);
+const char *hxd_vendor(void);
+
+int hxd_v_rel(void);
+int hxd_v_abi(void);
+int hxd_v_api(void);
+
+
+/*
+ * H E X D U M P  E R R O R  I N T E R F A C E S
+ *
+ * Hexdump internal error conditions are returned using regular int objects.
+ * System errors are loaded from errno as soon as encountered, and the value
+ * returned through the API like internal errors. DO NOT check errno, which
+ * may have been overwritten by subsequent error handling code. Internal
+ * errors are negative and utilize a simple high-order-byte namespacing
+ * protocol. This works because ISO C and POSIX guarantee that all system
+ * error codes are positive.
+ *
+ * hxd_strerror() will forward system errors to strerror(3).
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define HXD_EBASE -(('D' << 24) | ('U' << 16) | ('M' << 8) | 'P')
+#define HXD_ERROR(error) ((error) >= XD_EBASE && (error) < XD_ELAST)
+
+
+enum hxd_errors {
+       HXD_EFORMAT = HXD_EBASE,
+       /* a compile-time error signaling an invalid format string, format
+          unit, or conversion specification syntax */
+
+       HXD_EDRAINED,
+       /* a compile-time error signaling that preceding conversions have
+          already drained the input block */
+
+       HXD_ENOTSUPP,
+       /* a compile-time error returned for valid but unsupported
+          conversion specifications */
+
+       HXD_EOOPS,
+       /* something horrible happened */
+
+       HXD_ELAST
+}; /* enum hxd_errors */
+
+#define hxd_error_t int /* for documentation purposes only */
+
+const char *hxd_strerror(hxd_error_t);
+
+
+/*
+ * H E X D U M P  C O R E  I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct hexdump;
+
+struct hexdump *hxd_open(hxd_error_t *);
+
+void hxd_close(struct hexdump *);
+
+void hxd_reset(struct hexdump *);
+
+#define HXD_BYTEORDER(x)  (0x03 & (x))
+#define HXD_NATIVE         0x00
+#define HXD_NETWORK        HXD_BIG_ENDIAN
+#define HXD_BIG_ENDIAN     0x01
+#define HXD_LITTLE_ENDIAN  0x02
+#define HXD_NOPADDING      0x04
+
+hxd_error_t hxd_compile(struct hexdump *, const char *, int);
+
+const char *hxd_help(struct hexdump *);
+
+size_t hxd_blocksize(struct hexdump *);
+
+hxd_error_t hxd_write(struct hexdump *, const void *, size_t);
+
+hxd_error_t hxd_flush(struct hexdump *);
+
+size_t hxd_read(struct hexdump *, void *, size_t);
+
+
+/*
+ * H E X D U M P  C O M M O N  F O R M A T S
+ *
+ * Predefined formats for hexdump(1) -b, -c, -C, -d, -o, -x, and xxd(1) -i.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define HEXDUMP_b "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\""
+#define HEXDUMP_c "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\""
+#define HEXDUMP_C "\"%08.8_ax  \" 8/1 \"%02x \" \"  \" 8/1 \"%02x \"\n" \
+                  "\"  |\" 16/1 \"%_p\" \"|\\n\""
+#define HEXDUMP_d "\"%07.7_ax \" 8/2 \"  %05u \" \"\\n\""
+#define HEXDUMP_o "\"%07.7_ao   \" 8/2 \" %06o \" \"\\n\""
+#define HEXDUMP_x "\"%07.7_ax \" 8/2 \"   %04x \" \"\\n\""
+
+#define HEXDUMP_i "\"  \" 12/1? \"0x%02x, \" \"\\n\""
+
+
+/*
+ * H E X D U M P  L U A  I N T E R F A C E S
+ *
+ * When built with -DHEXDUMP_LUALIB then luaopen_hexdump() will return a
+ * Lua module table:
+ *
+ *   local hexdump = require"hexdump"
+ *
+ *   hexdump.NATIVE 
+ *   hexdump.NETWORK
+ *   hexdump.BIG_ENDIAN
+ *   hexdump.LITTLE_ENDIAN
+ *     Bitwise flags which configure word byte order. The default is the
+ *     native byte order.
+ *
+ *   hexdump.NOPADDING
+ *     Bitwise flag which disables padding; instead, formatting units are
+ *     skipped entirely when the block buffer is too short.
+ *
+ *   hexdump.b 
+ *   hexdump.c
+ *   hexdump.C
+ *   hexdump.d
+ *   hexdump.o
+ *   hexdump.x
+ *     Predefined format strings of the hexdump(1) options -x, -c, -C, -d,
+ *     -o, and -x, respectively.
+ *
+ *   hexdump.new()
+ *     Returns new context, just like hxd_open.
+ *
+ *   hexdump.apply(fmt:string, [flags:int,] data:string, ...)
+ *     Returns a formatted string, memoizing the context object for later
+ *     reuse with the same format.
+ *
+ * The module table also has a __call metamethod, which forwards to .apply.
+ * This allows doing require"hexdump"('/1 "%.2x"', "0123456789").
+ *
+ * Every context is a simple object with methods identical to the C library,
+ * including:
+ *
+ *   :reset()
+ *     Resets the internal buffers. Returns true.
+ *
+ *   :compile(fmt:string)
+ *     Parses and compiles the format string according to the rules of BSD
+ *     hexdump(1). Returns true on success, or throws an error on failure.
+ *
+ *   :blocksize()
+ *     Returns the block size of any compiled format string.
+ *
+ *   :write(data:string)
+ *     Processes the data string. The string DOES NOT have to be the same
+ *     length as the block size. It can be any size, although the formatted
+ *     output is buffered until drained with :read, so it's better to write
+ *     smallish chunks when processing large files. Returns true on success,
+ *     or throws an error on failure.
+ *
+ *   :flush()
+ *     Processes any data (less than the block size) in the input buffer
+ *     as-if EOF was received. Returns true on success, or throws an error
+ *     on failure.
+ *
+ *   :read()
+ *     Drains and returns the output buffer as a string.
+ * 
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int luaopen_hexdump(/* pointer to lua_State */);
+
+
+#endif /* HEXDUMP_H */


Property changes on: 
brlcad/branches/thirdparty_rework/misc/tools/hexdump/hexdump.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/thirdparty_rework/misc/tools/hexdump.dist
===================================================================
--- brlcad/branches/thirdparty_rework/misc/tools/hexdump.dist                   
        (rev 0)
+++ brlcad/branches/thirdparty_rework/misc/tools/hexdump.dist   2020-03-25 
22:15:48 UTC (rev 75109)
@@ -0,0 +1,6 @@
+set(hexdump_ignore_files
+CMakeLists.txt
+README.md
+hexdump.c
+hexdump.h
+)


Property changes on: brlcad/branches/thirdparty_rework/misc/tools/hexdump.dist
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
BRL-CAD Source Commits mailing list
brlcad-commits@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/brlcad-commits

Reply via email to