2017-08-25 02:22:50 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
// deps
|
|
|
|
const fs = require('graceful-fs');
|
|
|
|
const iconv = require('iconv-lite');
|
|
|
|
const async = require('async');
|
|
|
|
|
|
|
|
module.exports = class DescriptIonFile {
|
2018-06-22 05:15:04 +00:00
|
|
|
constructor() {
|
|
|
|
this.entries = new Map();
|
|
|
|
}
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
get(fileName) {
|
|
|
|
return this.entries.get(fileName);
|
|
|
|
}
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
getDescription(fileName) {
|
|
|
|
const entry = this.get(fileName);
|
|
|
|
if(entry) {
|
|
|
|
return entry.desc;
|
|
|
|
}
|
|
|
|
}
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
static createFromFile(path, cb) {
|
|
|
|
fs.readFile(path, (err, descData) => {
|
|
|
|
if(err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
const descIonFile = new DescriptIonFile();
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
// DESCRIPT.ION entries are terminated with a CR and/or LF
|
|
|
|
const lines = iconv.decode(descData, 'cp437').split(/\r?\n/g);
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
async.each(lines, (entryData, nextLine) => {
|
|
|
|
//
|
|
|
|
// We allow quoted (long) filenames or non-quoted filenames.
|
|
|
|
// FILENAME<SPC>DESC<0x04><program data><CR/LF>
|
|
|
|
//
|
|
|
|
const parts = entryData.match(/^(?:(?:"([^"]+)" )|(?:([^ ]+) ))([^\x04]+)\x04(.)[^\r\n]*$/); // eslint-disable-line no-control-regex
|
|
|
|
if(!parts) {
|
|
|
|
return nextLine(null);
|
|
|
|
}
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
const fileName = parts[1] || parts[2];
|
2017-09-02 15:59:38 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
|
|
|
// Un-escape CR/LF's
|
|
|
|
// - escapped \r and/or \n
|
|
|
|
// - BBBS style @n - See https://www.bbbs.net/sysop.html
|
|
|
|
//
|
|
|
|
const desc = parts[3].replace(/\\r\\n|\\n|[^@]@n/g, '\r\n');
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
descIonFile.entries.set(
|
|
|
|
fileName,
|
|
|
|
{
|
|
|
|
desc : desc,
|
|
|
|
programId : parts[4],
|
|
|
|
programData : parts[5],
|
|
|
|
}
|
|
|
|
);
|
2017-08-25 02:22:50 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
return nextLine(null);
|
|
|
|
},
|
|
|
|
() => {
|
|
|
|
return cb(null, descIonFile);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2017-08-25 02:22:50 +00:00
|
|
|
};
|
|
|
|
|