Merge branch 'master' of ssh://numinibsd/git/base/enigma-bbs

This commit is contained in:
Bryan Ashby 2016-08-31 22:29:33 -06:00
commit ad963993d6
2 changed files with 61 additions and 244 deletions

View File

@ -2,19 +2,19 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
var conf = require('./config.js'); const Config = require('./config.js').config;
var miscUtil = require('./misc_util.js'); const miscUtil = require('./misc_util.js');
var ansi = require('./ansi_term.js'); const ansi = require('./ansi_term.js');
var aep = require('./ansi_escape_parser.js'); const aep = require('./ansi_escape_parser.js');
var sauce = require('./sauce.js'); const sauce = require('./sauce.js');
const farmhash = require('farmhash'); const farmhash = require('farmhash');
// deps // deps
var fs = require('fs'); const fs = require('fs');
var paths = require('path'); const paths = require('path');
var assert = require('assert'); const assert = require('assert');
var iconv = require('iconv-lite'); const iconv = require('iconv-lite');
var _ = require('lodash'); const _ = require('lodash');
exports.getArt = getArt; exports.getArt = getArt;
exports.getArtFromPath = getArtFromPath; exports.getArtFromPath = getArtFromPath;
@ -25,7 +25,7 @@ exports.defaultEncodingFromExtension = defaultEncodingFromExtension;
// :TODO: process SAUCE comments // :TODO: process SAUCE comments
// :TODO: return font + font mapped information from SAUCE // :TODO: return font + font mapped information from SAUCE
var SUPPORTED_ART_TYPES = { const SUPPORTED_ART_TYPES = {
// :TODO: the defualt encoding are really useless if they are all the same ... // :TODO: the defualt encoding are really useless if they are all the same ...
// perhaps .ansamiga and .ascamiga could be supported as well as overrides via conf // perhaps .ansamiga and .ascamiga could be supported as well as overrides via conf
'.ans' : { name : 'ANSI', defaultEncoding : 'cp437', eof : 0x1a }, '.ans' : { name : 'ANSI', defaultEncoding : 'cp437', eof : 0x1a },
@ -59,17 +59,16 @@ function sliceAtEOF(data, eofMarker) {
} }
function getArtFromPath(path, options, cb) { function getArtFromPath(path, options, cb) {
fs.readFile(path, function onData(err, data) { fs.readFile(path, (err, data) => {
if(err) { if(err) {
cb(err); return cb(err);
return;
} }
// //
// Convert from encodedAs -> j // Convert from encodedAs -> j
// //
var ext = paths.extname(path).toLowerCase(); const ext = paths.extname(path).toLowerCase();
var encoding = options.encodedAs || defaultEncodingFromExtension(ext); const encoding = options.encodedAs || defaultEncodingFromExtension(ext);
// :TODO: how are BOM's currently handled if present? Are they removed? Do we need to? // :TODO: how are BOM's currently handled if present? Are they removed? Do we need to?
@ -77,13 +76,13 @@ function getArtFromPath(path, options, cb) {
if(options.fullFile === true) { if(options.fullFile === true) {
return iconv.decode(data, encoding); return iconv.decode(data, encoding);
} else { } else {
var eofMarker = defaultEofFromExtension(ext); const eofMarker = defaultEofFromExtension(ext);
return iconv.decode(sliceAtEOF(data, eofMarker), encoding); return iconv.decode(sliceAtEOF(data, eofMarker), encoding);
} }
} }
function getResult(sauce) { function getResult(sauce) {
var result = { const result = {
data : sliceOfData(), data : sliceOfData(),
fromPath : path, fromPath : path,
}; };
@ -96,10 +95,11 @@ function getArtFromPath(path, options, cb) {
} }
if(options.readSauce === true) { if(options.readSauce === true) {
sauce.readSAUCE(data, function onSauce(err, sauce) { sauce.readSAUCE(data, (err, sauce) => {
if(err) { if(err) {
cb(null, getResult()); return cb(null, getResult());
} else { }
// //
// If a encoding was not provided & we have a mapping from // If a encoding was not provided & we have a mapping from
// the information provided by SAUCE, use that. // the information provided by SAUCE, use that.
@ -114,19 +114,18 @@ function getArtFromPath(path, options, cb) {
} }
*/ */
} }
cb(null, getResult(sauce)); return cb(null, getResult(sauce));
}
}); });
} else { } else {
cb(null, getResult()); return cb(null, getResult());
} }
}); });
} }
function getArt(name, options, cb) { function getArt(name, options, cb) {
var ext = paths.extname(name); const ext = paths.extname(name);
options.basePath = miscUtil.valueWithDefault(options.basePath, conf.config.paths.art); options.basePath = miscUtil.valueWithDefault(options.basePath, Config.paths.art);
options.asAnsi = miscUtil.valueWithDefault(options.asAnsi, true); options.asAnsi = miscUtil.valueWithDefault(options.asAnsi, true);
// :TODO: make use of asAnsi option and convert from supported -> ansi // :TODO: make use of asAnsi option and convert from supported -> ansi
@ -143,29 +142,27 @@ function getArt(name, options, cb) {
// If an extension is provided, just read the file now // If an extension is provided, just read the file now
if('' !== ext) { if('' !== ext) {
var directPath = paths.join(options.basePath, name); const directPath = paths.join(options.basePath, name);
getArtFromPath(directPath, options, cb); return getArtFromPath(directPath, options, cb);
return;
} }
fs.readdir(options.basePath, function onFiles(err, files) { fs.readdir(options.basePath, (err, files) => {
if(err) { if(err) {
cb(err); return cb(err);
return;
} }
var filtered = files.filter(function onFile(file) { const filtered = files.filter( file => {
// //
// Ignore anything not allowed in |options.types| // Ignore anything not allowed in |options.types|
// //
var fext = paths.extname(file); const fext = paths.extname(file);
if(options.types.indexOf(fext.toLowerCase()) < 0) { if(options.types.indexOf(fext.toLowerCase()) < 0) {
return false; return false;
} }
var bn = paths.basename(file, fext).toLowerCase(); const bn = paths.basename(file, fext).toLowerCase();
if(options.random) { if(options.random) {
var suppliedBn = paths.basename(name, fext).toLowerCase(); const suppliedBn = paths.basename(name, fext).toLowerCase();
// //
// Random selection enabled. We'll allow for // Random selection enabled. We'll allow for
// basename1.ext, basename2.ext, ... // basename1.ext, basename2.ext, ...
@ -173,7 +170,8 @@ function getArt(name, options, cb) {
if(bn.indexOf(suppliedBn) !== 0) { if(bn.indexOf(suppliedBn) !== 0) {
return false; return false;
} }
var num = bn.substr(suppliedBn.length);
const num = bn.substr(suppliedBn.length);
if(num.length > 0) { if(num.length > 0) {
if(isNaN(parseInt(num, 10))) { if(isNaN(parseInt(num, 10))) {
return false; return false;
@ -188,6 +186,7 @@ function getArt(name, options, cb) {
return false; return false;
} }
} }
return true; return true;
}); });
@ -197,7 +196,7 @@ function getArt(name, options, cb) {
// - Exactly (1) item in |filtered| if non-random // - Exactly (1) item in |filtered| if non-random
// - 1:n items in |filtered| to choose from if random // - 1:n items in |filtered| to choose from if random
// //
var readPath; let readPath;
if(options.random) { if(options.random) {
readPath = paths.join(options.basePath, filtered[Math.floor(Math.random() * filtered.length)]); readPath = paths.join(options.basePath, filtered[Math.floor(Math.random() * filtered.length)]);
} else { } else {
@ -205,19 +204,13 @@ function getArt(name, options, cb) {
readPath = paths.join(options.basePath, filtered[0]); readPath = paths.join(options.basePath, filtered[0]);
} }
getArtFromPath(readPath, options, cb); return getArtFromPath(readPath, options, cb);
} else {
return cb(new Error(`No matching art for supplied criteria: ${name}`));
} }
return cb(new Error(`No matching art for supplied criteria: ${name}`));
}); });
} }
// :TODO: need a showArt()
// - center (if term width > 81)
// - interruptable
// - pausable: by user key and/or by page size (e..g term height)
function defaultEncodingFromExtension(ext) { function defaultEncodingFromExtension(ext) {
return SUPPORTED_ART_TYPES[ext.toLowerCase()].defaultEncoding; return SUPPORTED_ART_TYPES[ext.toLowerCase()].defaultEncoding;
} }
@ -271,6 +264,7 @@ function display(client, art, options, cb) {
if(!options.disableMciCache) { if(!options.disableMciCache) {
// cache our MCI findings... // cache our MCI findings...
client.mciCache[artHash] = mciMap; client.mciCache[artHash] = mciMap;
client.log.trace( { artHash : artHash, mciMap : mciMap }, 'Added MCI map from cache');
} }
ansiParser.removeAllListeners(); // :TODO: Necessary??? ansiParser.removeAllListeners(); // :TODO: Necessary???
@ -292,7 +286,9 @@ function display(client, art, options, cb) {
} }
} }
if(!mciMap) { if(mciMap) {
client.log.trace( { artHash : artHash, mciMap : mciMap }, 'Loaded MCI map from cache');
} else {
// no cached MCI info // no cached MCI info
mciMap = {}; mciMap = {};
@ -375,186 +371,3 @@ function display(client, art, options, cb) {
ansiParser.reset(art); ansiParser.reset(art);
ansiParser.parse(); ansiParser.parse();
} }
function displayBACKUP(options, cb) {
assert(_.isObject(options));
assert(_.isObject(options.client));
assert(!_.isUndefined(options.art));
if(0 === options.art.length) {
cb(new Error('Empty art'));
return;
}
// pause = none/off | end | termHeight | [ "key1", "key2", ... ]
var cancelKeys = miscUtil.valueWithDefault(options.cancelKeys, []);
var pauseKeys = miscUtil.valueWithDefault(options.pauseKeys, []);
var pauseAtTermHeight = miscUtil.valueWithDefault(options.pauseAtTermHeight, false);
var mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, ' ');
var iceColors = options.iceColors;
if(_.isUndefined(options.iceColors)) {
// detect from SAUCE, if present
iceColors = false;
if(_.isObject(options.sauce) && _.isNumber(options.sauce.ansiFlags)) {
if(options.sauce.ansiFlags & (1 << 0)) {
iceColors = true;
}
}
}
//var iceColors = miscUtil.valueWithDefault(options.iceColors, false);
// :TODO: support pause/cancel & pause @ termHeight
var canceled = false;
var parser = new aep.ANSIEscapeParser({
mciReplaceChar : mciReplaceChar,
termHeight : options.client.term.termHeight,
termWidth : options.client.term.termWidth,
trailingLF : options.trailingLF,
});
var mciMap = {};
var mciPosQueue = [];
var parseComplete = false;
var generatedId = 100;
var cprListener = function(pos) {
if(mciPosQueue.length > 0) {
var forMapItem = mciPosQueue.shift();
mciMap[forMapItem].position = pos;
if(parseComplete && 0 === mciPosQueue.length) {
completed();
}
}
};
function completed() {
options.client.removeListener('cursor position report', cprListener);
parser.removeAllListeners(); // :TODO: Necessary???
if(iceColors) {
// options.client.term.write(ansi.blinkNormal());
}
var extraInfo = {
height : parser.row - 1,
};
cb(null, mciMap, extraInfo);
}
options.client.on('cursor position report', cprListener);
options.pause = 'termHeight'; // :TODO: remove!!
var nextPauseTermHeight = options.client.term.termHeight;
var continous = false;
/*
parser.on('row update', function rowUpdate(row) {
if(row >= nextPauseTermHeight) {
if(!continous && 'termHeight' === options.pause) {
// :TODO: Must use new key type (ch, key)
options.client.waitForKeyPress(function kp(k) {
// :TODO: Allow for configurable key(s) here; or none
if('C' === k || 'c' == k) {
continous = true;
}
parser.parse();
});
parser.stop();
}
nextPauseTermHeight += options.client.term.termHeight;
}
});
*/
parser.on('mci', function mciEncountered(mciInfo) {
/*
if('PA' === mciInfo.mci) {
// :TODO: can't do this until this thing is pausable anyway...
options.client.waitForKeyPress(function kp(k) {
console.log('got a key: ' + k);
});
return;
}
*/
// :TODO: ensure generatedId's do not conflict with any |id|
// :TODO: Bug here - should only generate & increment ID's for the initial entry, not the "focus" version
var id = !_.isNumber(mciInfo.id) ? generatedId++ : mciInfo.id;
var mapKey = mciInfo.mci + id;
var mapEntry = mciMap[mapKey];
if(mapEntry) {
mapEntry.focusSGR = mciInfo.SGR;
mapEntry.focusArgs = mciInfo.args;
} else {
mciMap[mapKey] = {
args : mciInfo.args,
SGR : mciInfo.SGR,
code : mciInfo.mci,
id : id,
};
mciPosQueue.push(mapKey);
options.client.term.write(ansi.queryPos(), false);
}
});
/*
parser.on('chunk', function onChunk(chunk) {
options.client.term.write(chunk, false);
});
*/
parser.on('literal', literal => {
options.client.term.write(literal, false);
});
parser.on('control', control => {
options.client.term.write(control, false);
});
parser.on('complete', function onComplete() {
parseComplete = true;
if(0 === mciPosQueue.length) {
completed();
}
});
// :TODO: If options.font, set the font via ANSI
// ...this should come from sauce, be passed in, or defaulted
var ansiFont = '';
if(options.font) {
ansiFont = ansi.setSyncTERMFont(options.font);
} else if(options.sauce) {
var fontName = getFontNameFromSAUCE(options.sauce);
if(fontName) {
fontName = ansi.getSyncTERMFontFromAlias(fontName);
}
// Don't set default (cp437) from SAUCE
if(fontName && 'cp437' !== fontName) {
ansiFont = ansi.setSyncTERMFont(fontName);
}
}
if(ansiFont.length > 1) {
options.client.term.write(ansiFont, false);
}
if(iceColors) {
options.client.term.write(ansi.blinkToBrightIntensity(), false);
}
parser.reset(options.art);
parser.parse();
}

View File

@ -297,6 +297,10 @@
} }
bbsList: { bbsList: {
config: {
listFormat: "|00|07{bbsName}"
focusListFormat: "|00|19|15{bbsName!styleFirstLower}"
}
0: { 0: {
mci: { mci: {
VM1: { VM1: {