1
0
Fork 0
zmodemjs/bin/zmodemjs-sz.js
Daniel Baumann 4d3e0bf859
Adding upstream version 0.1.10+dfsg.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-04-22 16:48:36 +02:00

140 lines
3.8 KiB
JavaScript
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
// A proof-of-concept CLI implementation of “sz” using zmodem.js.
// This is not tested extensively and isnt really meant for production use.
const process = require('process');
const fs = require('fs');
const Zmodem = require('../src/zmodem');
var paths = process.argv.slice(1);
// Accommodate “node $script …”
if (paths[0] === __filename) {
paths = paths.slice(1);
}
if (!paths.length) {
console.error("Need at least one path!");
process.exit(1);
}
// Cant be to the same terminal as STDOUT.
// npms “ttyname” can tell us, but its annoying to require
// a module for this.
const DEBUG = false;
if (DEBUG) {
var outtype = fs.fstatSync(1).mode & fs.constants.S_IFMT;
var errtype = fs.fstatSync(1).mode & fs.constants.S_IFMT;
if (outtype === errtype && outtype === fs.constants.S_IFCHR) {
console.error("STDOUT and STDERR cant both be to a terminal when debugging is on.");
process.exit(1);
}
}
function _debug() {
DEBUG && console.warn.apply( console, arguments );
}
_debug("PID:", process.pid);
_debug("Paths to send:", paths);
//----------------------------------------------------------------------
var path_fd = {};
paths.forEach( (path) => path_fd[path] = fs.openSync(path, 'r') );
// TODO: This should maybe be in its own module?
// The notion of starting a session in JS wasnt envisioned when
// this module was written.
const initial_bytes = Zmodem.Header.build("ZRQINIT").to_hex();
process.stdout.write(Buffer.from(initial_bytes));
_debug('Sent ZRQINIT');
// We need a binary stdin.
var stdin = fs.createReadStream( "", { fd: 0 } );
function send_files(zsession, paths) {
function send_next() {
var path = paths.shift();
if (path) {
_debug("Sending offer: ", path);
var fd = path_fd[path];
var fstat = fs.fstatSync(fd);
var filename = path.match(/.+\/(.+)/);
filename = filename ? filename[0] : path;
return zsession.send_offer( {
name: filename,
size: fstat.size,
mtime: Math.round( fstat.mtimeMs / 1000 ),
} ).then( (xfer) => {
if (!xfer) {
_debug("Offer was rejected.");
return send_next();
}
_debug("Offer was accepted.");
var stream = fs.createReadStream( "", {
fd: fd,
} );
stream.on('data', (chunk) => {
_debug("Sending chunk.");
xfer.send(chunk);
} );
return new Promise( (res, rej) => {
stream.on('end', () => {
_debug("Reached EOF; sending end.");
xfer.end().then( () => {;
res( send_next() );
} );
} );
} );
} );
}
else {
_debug("Reached end of files batch.");
}
}
return send_next();
}
var zsession;
stdin.on('data', (chunk) => {
var octets = Array.from(chunk)
if (zsession) {
zsession.consume(octets);
}
else {
_debug("Received on STDIN; checking for session.", octets);
zsession = Zmodem.Session.parse(octets);
if (zsession) {
_debug("Got session.");
// It seems like .parse() should strip out the header bytes,
// but thats not how it works.
// zsession.consume(octets);
zsession.set_sender( (octets) => process.stdout.write( Buffer.from(octets) ) );
send_files(zsession, paths).then( () => zsession.close() );
}
else {
_debug("No session yet …");
}
}
});