294 lines
7.8 KiB
Plaintext
294 lines
7.8 KiB
Plaintext
/*
|
|
* A port of xxd as distributed with Vim 8.2 (on Ubuntu 22.04 LTS).
|
|
*
|
|
* That utility's source code is distributed with the following notice:
|
|
*
|
|
* (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com)
|
|
*
|
|
* I hereby grant permission to distribute and use xxd
|
|
* under X11-MIT or GPL-2.0 (at the user's choice).
|
|
*
|
|
* Contributions by Bram Moolenaar et al.
|
|
*
|
|
* Only the defaults are supported at this time. The argument processing
|
|
* logic is mostly absent.
|
|
*
|
|
* The choices made here (and there are few--we primarily aim for the source
|
|
* to correspond to the routines just as they appear in xxd-8.2.3995.c) are
|
|
* done with the main aim being to reconcile the impedance mismatch between
|
|
* the C implementation and the dialect used here for the programming
|
|
* environment we are targeting. Modifications have been made where necessary
|
|
* with the foremost goal being to get the code to work. It is not meant to
|
|
* be efficient nor to satisfy some other measure of good. This is bad code.
|
|
*
|
|
* This class doesn't actually output anything (or read anything, for that
|
|
* matter). It does its work with the `input` and `output` buffers. There is
|
|
* a `toString` implementation to reify the completed dump as a string.
|
|
*/
|
|
|
|
export
|
|
class XXD {
|
|
exec() {
|
|
const { EOF } = XXD;
|
|
|
|
const { HEX_NORMAL } = XXD;
|
|
const { HEX_POSTSCRIPT } = XXD;
|
|
const { HEX_CINCLUDE } = XXD;
|
|
const { HEX_BITS } = XXD;
|
|
const { HEX_LITTLEENDIAN } = XXD;
|
|
|
|
const { COLS } = XXD;
|
|
const { LLEN } = XXD;
|
|
|
|
const _puts = ((...args) => (this._putOrDie(...args)));
|
|
const _exit = ((...args) => (this._exit(...args)));
|
|
|
|
const $ = ((c) => (c.charCodeAt(0)));
|
|
const $$ = ((s) => (Uint8Array.from(s, $)));
|
|
|
|
const hexx = $$("0123456789abcdef0123456789ABCDEF");
|
|
|
|
let fp = this.input;
|
|
let fpo = this.output;
|
|
|
|
let c, e, p = 0;
|
|
|
|
let relseek = 1;
|
|
let negseek = 0;
|
|
let revert = 0;
|
|
|
|
let cols = 0;
|
|
let nonzero = 0;
|
|
let autoskip = 0;
|
|
let hextype = HEX_NORMAL;
|
|
|
|
let capitalize, decimaloff = 0;
|
|
let ebcdic = 0;
|
|
let octspergrp = -1;
|
|
let grplen;
|
|
let length = -1, n = 0, seekoff = 0;
|
|
let displayoff = 0;
|
|
|
|
let l = new Uint8Array(LLEN + 1);
|
|
|
|
let pp;
|
|
let addrlen = 9;
|
|
|
|
if (!cols) {
|
|
switch (hextype) {
|
|
case HEX_POSTSCRIPT: cols = 30; break;
|
|
case HEX_CINCLUDE: cols = 12; break;
|
|
case HEX_BITS: cols = 6; break;
|
|
case HEX_NORMAL:
|
|
case HEX_LITTLENDIAN:
|
|
default: cols = 16; break;
|
|
}
|
|
}
|
|
|
|
if (octspergrp < 0) {
|
|
switch (hextype) {
|
|
case HEX_BITS: octspergrp = 1; break;
|
|
case HEX_NORMAL: octspergrp = 2; break;
|
|
case HEX_LITTLENDIAN: octspergrp = 4; break;
|
|
case HEX_POSTSCRIPT:
|
|
case HEX_CINCLUDE:
|
|
default: octspergrp = 0; break;
|
|
}
|
|
}
|
|
|
|
if (cols < 1 || ((hextype == HEX_NORMAL || hextype == HEX_BITS ||
|
|
hextype == HEX_LITTLEENDIAN) && (cols > COLS))) {
|
|
return void this._exit(
|
|
1, null, pname + ": invalid number of columns (max. " + COLS + ")"
|
|
);
|
|
}
|
|
|
|
if (octspergrp < 1 || octspergrp > cols) {
|
|
octspergrp = cols;
|
|
} else if (hextype == HEX_LITTLEENDIAN) {
|
|
if (octspergrp & (octspergrp - 1)) {
|
|
return void this._exit(
|
|
1, "number of octets per group must be a power of 2 with -e."
|
|
);
|
|
}
|
|
}
|
|
|
|
if (revert) {
|
|
if (hextype && hextype != HEX_POSTSCRIPT) {
|
|
return void this._exit(-1, "Sorry, cannot revert this type of hexdump");
|
|
}
|
|
return void this._exit(XXD.UNIMPLEMENTED); // XXX
|
|
}
|
|
|
|
if (seekoff || negseek || !relseek) {
|
|
if (relseek) {
|
|
e = _fseek(fp, (negseek) ? -seekoff : seekoff, SEEK_CUR);
|
|
} else {
|
|
e = _fseek(fp, (negseek) ? -seekoff : seekoff,
|
|
(negseek) ? SEEK_END : SEEK_SET);
|
|
}
|
|
|
|
if (e < 0 && negseek) {
|
|
return void this._exit(4, "Sorry, cannot seek.");
|
|
} else if (e >= 0) {
|
|
seekoff = _ftell(fp);
|
|
} else {
|
|
let s = seekoff;
|
|
while (s--) {
|
|
let e = this._getOrDie(fp);
|
|
if (e == EOF) return void this._exit(4, "Sorry, cannot seek.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hextype == HEX_CINCLUDE) {
|
|
return void this._exit(XXD.UNIMPLEMENTED); // XXX
|
|
} else if (hextype == HEX_POSTSCRIPT) {
|
|
return void this._exit(XXD.UNIMPLEMENTED); // XXX
|
|
}
|
|
|
|
if (hextype != HEX_BITS) {
|
|
grplen = octspergrp + octspergrp + 1;
|
|
} else {
|
|
grplen = 8 * octspergrp + 1;
|
|
}
|
|
|
|
while (length < 0 || n < length) {
|
|
e = this._getOrDie(fp);
|
|
if (e == EOF) break;
|
|
|
|
let x;
|
|
|
|
if (p == 0) {
|
|
addrlen = _sprintf(
|
|
l, (decimaloff) ? "%08ld:" : "%08lx:",
|
|
n + seekoff + displayoff
|
|
);
|
|
|
|
for (c = addrlen; c < LLEN; l[c++] = $(' '));
|
|
}
|
|
x = (hextype == HEX_LITTLEENDIAN) ? (p ^ (octspergrp - 1)) : p;
|
|
c = addrlen + 1 + ~~((grplen * x) / octspergrp);
|
|
if (hextype == HEX_NORMAL || hextype == HEX_LITTLEENDIAN) {
|
|
l[c+0] = hexx[(e >> 4) & 0x0F];
|
|
l[++c] = hexx[(e >> 0) & 0x0F];
|
|
} else { // HEX_BITS
|
|
for (let i = 7; i >= 0; --i) {
|
|
l[c++] = (e & (1 << i)) ? $('1') : $('0');
|
|
}
|
|
}
|
|
|
|
if (e) nonzero++;
|
|
|
|
if (ebcdic) return void this._exit(XXD.UNIMPLEMENTED); // XXX
|
|
|
|
c = addrlen + 3 + ~~((grplen * cols - 1) / octspergrp) + p;
|
|
|
|
if (31 < e && e < 127) {
|
|
l[c++] = e;
|
|
} else {
|
|
l[c++] = $('.');
|
|
}
|
|
|
|
++n;
|
|
|
|
if (++p == cols) {
|
|
l[c+0] = $('\n');
|
|
l[++c] = $('\0');
|
|
_xxdline(fpo, l , (autoskip) ? nonzero : 1);
|
|
nonzero = 0;
|
|
p = 0;
|
|
}
|
|
}
|
|
|
|
if (p) {
|
|
l[c+0] = $('\n');
|
|
l[++c] = $('\0');
|
|
_xxdline(fpo, l, 1);
|
|
} else if (autoskip) {
|
|
_xxdline(fpo, l, -1);
|
|
}
|
|
|
|
function _xxdline(fp, l, nz, _static = _xxdline) {
|
|
if (!nz && (_static .s) == 1) {
|
|
_strcpy(_static .z, l);
|
|
}
|
|
|
|
if (nz || !(_static .s++)) {
|
|
if (nz) {
|
|
if (nz < 0) (_static .s)--;
|
|
if ((_static .s) == 2) _puts(_static .z, fp);
|
|
if ((_static .s) > 2) _puts($$("*\n"), fp);
|
|
}
|
|
if (nz >= 0 || (_static .s) > 0) _puts(l, fp);
|
|
if (nz) _static .s = 0;
|
|
}
|
|
}
|
|
_static: _xxdline .s = 0; // was `zero_seen` in C
|
|
_static: _xxdline .z = new Uint8Array(LLEN + 1);
|
|
|
|
function _strcpy(dest, src) {
|
|
let stop = src.indexOf(0);
|
|
if (stop >= 0) {
|
|
dest.set(new Uint8Array(src.buffer, 0, stop));
|
|
} else {
|
|
return void _exit(XXD.IMPLEMENTATION_ERROR); // XXX
|
|
}
|
|
}
|
|
|
|
function _sprintf(bytes, format, ...args) {
|
|
if (args.length == 1 && format == "%08lx:") {
|
|
let what = args[0].toString(16).padStart(8, "0") + ":";
|
|
bytes.set($$(what));
|
|
return what.length;
|
|
}
|
|
return void _exit(XXD.UNIMPLEMENTED); // XXX
|
|
}
|
|
}
|
|
|
|
constructor(input, output = []) {
|
|
this.input = input;
|
|
this.output = output;
|
|
|
|
this._place = 0;
|
|
}
|
|
|
|
toString() {
|
|
return this.output.map((x) => (String.fromCharCode(x))).join("");
|
|
}
|
|
|
|
_getOrDie(fp) {
|
|
if (fp == this.input) {
|
|
if (this._place < this.input.length) {
|
|
return this.input[this._place++];
|
|
}
|
|
return XXD.EOF;
|
|
}
|
|
return void this._exit(XXD.UNIMPLEMENTED); // XXX
|
|
}
|
|
|
|
_putOrDie(bytes, fp) {
|
|
if (fp == this.output) {
|
|
for (let i = 0, n = bytes.length; i < n; ++i) {
|
|
if (bytes[i] == 0) return;
|
|
this.output.push(bytes[i]);
|
|
}
|
|
}
|
|
return void this._exit(XXD.UNIMPLEMENTED); // XXX
|
|
}
|
|
}
|
|
|
|
XXD.HEX_NORMAL = 0;
|
|
XXD.HEX_POSTSCRIPT = 1;
|
|
XXD.HEX_CINCLUDE = 2;
|
|
XXD.HEX_BITS = 3;
|
|
XXD.HEX_LITTLEENDIAN = 4;
|
|
|
|
XXD.COLS = 256;
|
|
XXD.LLEN = 2573; // XXX (2 * 4) + 4 + (9 * COLS - 1) + COLS + 2
|
|
|
|
XXD.EOF = -1;
|
|
|
|
XXD.UNIMPLEMENTED = 90;
|
|
XXD.IMPLEMENTATION_ERROR = 91;
|