/* * 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;