Add oputil fb desc
This commit is contained in:
parent
f4088303ca
commit
c6e176f5bd
|
@ -41,7 +41,8 @@ This document attempts to track **major** changes and additions in ENiGMA½. For
|
||||||
* Correly parse oddball `INTL`, `TOPT`, `FMPT`, `Via`, etc. FTN kludge lines
|
* Correly parse oddball `INTL`, `TOPT`, `FMPT`, `Via`, etc. FTN kludge lines
|
||||||
* NetMail support! You can now send and receive NetMail. To send a NetMail address a external user using `Name <address>` format from your personal email menu. For example, `Foo Bar <123:123/123>`. The system also detects other formats such asa `Name @ address` (`Foo Bar@123:123/123`)
|
* NetMail support! You can now send and receive NetMail. To send a NetMail address a external user using `Name <address>` format from your personal email menu. For example, `Foo Bar <123:123/123>`. The system also detects other formats such asa `Name @ address` (`Foo Bar@123:123/123`)
|
||||||
* `oputil.js`: Added `mb areafix` command to quickly send AreaFix messages from the command line. You can manually send them from personal mail as well.
|
* `oputil.js`: Added `mb areafix` command to quickly send AreaFix messages from the command line. You can manually send them from personal mail as well.
|
||||||
* `oputil.js fb rm|remove|del|delete` functionality to remove file base entries
|
* `oputil.js fb rm|remove|del|delete` functionality to remove file base entries.
|
||||||
|
* `oputil.js fb desc` for setting/updating a file entry description.
|
||||||
* Users can now (re)set File and Message base pointers
|
* Users can now (re)set File and Message base pointers
|
||||||
* Add `--update` option to `oputil.js fb scan`
|
* Add `--update` option to `oputil.js fb scan`
|
||||||
* Fix @watch path support for event scheduler including FTN, e.g. when looking for a `toss!.now` file produced by Binkd.
|
* Fix @watch path support for event scheduler including FTN, e.g. when looking for a `toss!.now` file produced by Binkd.
|
||||||
|
|
|
@ -8,6 +8,12 @@ const fs = require('graceful-fs');
|
||||||
const iconv = require('iconv-lite');
|
const iconv = require('iconv-lite');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
|
// Descriptions found in the wild that mean "no description" /facepalm.
|
||||||
|
const IgnoredDescriptions = [
|
||||||
|
'No description available',
|
||||||
|
'No ID File Found For This Archive File.',
|
||||||
|
];
|
||||||
|
|
||||||
module.exports = class FilesBBSFile {
|
module.exports = class FilesBBSFile {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.entries = new Map();
|
this.entries = new Map();
|
||||||
|
@ -34,6 +40,10 @@ module.exports = class FilesBBSFile {
|
||||||
const lines = iconv.decode(descData, 'cp437').split(/\r?\n/g);
|
const lines = iconv.decode(descData, 'cp437').split(/\r?\n/g);
|
||||||
const filesBbs = new FilesBBSFile();
|
const filesBbs = new FilesBBSFile();
|
||||||
|
|
||||||
|
const isBadDescription = (desc) => {
|
||||||
|
return IgnoredDescriptions.find(d => desc.startsWith(d)) ? true : false;
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Contrary to popular belief, there is not a FILES.BBS standard. Instead,
|
// Contrary to popular belief, there is not a FILES.BBS standard. Instead,
|
||||||
// many formats have been used over the years. We'll try to support as much
|
// many formats have been used over the years. We'll try to support as much
|
||||||
|
@ -84,7 +94,7 @@ module.exports = class FilesBBSFile {
|
||||||
const fileName = hdr[1];
|
const fileName = hdr[1];
|
||||||
const timestamp = moment(hdr[2], 'MM/DD/YY');
|
const timestamp = moment(hdr[2], 'MM/DD/YY');
|
||||||
|
|
||||||
if(!timestamp.isValid()) {
|
if(isBadDescription(desc) || !timestamp.isValid()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
filesBbs.entries.set(fileName, { timestamp, desc } );
|
filesBbs.entries.set(fileName, { timestamp, desc } );
|
||||||
|
@ -121,6 +131,10 @@ module.exports = class FilesBBSFile {
|
||||||
const desc = long.join('\r\n');
|
const desc = long.join('\r\n');
|
||||||
const fileName = hdr[1];
|
const fileName = hdr[1];
|
||||||
|
|
||||||
|
if(isBadDescription(desc)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
filesBbs.entries.set(fileName, { desc } );
|
filesBbs.entries.set(fileName, { desc } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,6 +171,10 @@ module.exports = class FilesBBSFile {
|
||||||
const desc = long.join('\r\n');
|
const desc = long.join('\r\n');
|
||||||
const fileName = hdr[1];
|
const fileName = hdr[1];
|
||||||
|
|
||||||
|
if(isBadDescription(desc)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
filesBbs.entries.set(fileName, { desc } );
|
filesBbs.entries.set(fileName, { desc } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,10 +200,6 @@ module.exports = class FilesBBSFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstDescLine = hdr[4].trimRight();
|
const firstDescLine = hdr[4].trimRight();
|
||||||
// ugly kludge:
|
|
||||||
if('No ID File Found For This Archive File.' === firstDescLine) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const long = [ firstDescLine ];
|
const long = [ firstDescLine ];
|
||||||
for(let j = i + 1; j < lines.length; ++j) {
|
for(let j = i + 1; j < lines.length; ++j) {
|
||||||
line = lines[j];
|
line = lines[j];
|
||||||
|
@ -201,7 +215,7 @@ module.exports = class FilesBBSFile {
|
||||||
const size = parseInt(hdr[2]);
|
const size = parseInt(hdr[2]);
|
||||||
const timestamp = moment(hdr[3], 'MM-DD-YY');
|
const timestamp = moment(hdr[3], 'MM-DD-YY');
|
||||||
|
|
||||||
if(isNaN(size) || !timestamp.isValid()) {
|
if(isBadDescription(desc) || isNaN(size) || !timestamp.isValid()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +249,7 @@ module.exports = class FilesBBSFile {
|
||||||
const fileName = hdr[1].trim();
|
const fileName = hdr[1].trim();
|
||||||
const desc = hdr[2].trim();
|
const desc = hdr[2].trim();
|
||||||
|
|
||||||
if(desc) {
|
if(desc && !isBadDescription(desc)) {
|
||||||
filesBbs.entries.set(fileName, { desc } );
|
filesBbs.entries.set(fileName, { desc } );
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -413,11 +413,11 @@ function dumpFileInfo(shaOrFileId, cb) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayFileAreaInfo() {
|
function displayFileOrAreaInfo() {
|
||||||
// AREA_TAG[@STORAGE_TAG]
|
// AREA_TAG[@STORAGE_TAG]
|
||||||
// SHA256|PARTIAL
|
// SHA256|PARTIAL|FILE_ID|FILENAME_WILDCARD
|
||||||
// if sha: dump file info
|
// if sha: dump file info
|
||||||
// if area/stoarge dump area(s) +
|
// if area/storage dump area(s) +
|
||||||
|
|
||||||
async.series(
|
async.series(
|
||||||
[
|
[
|
||||||
|
@ -908,6 +908,75 @@ function importFileAreas() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFileDescription() {
|
||||||
|
//
|
||||||
|
// ./oputil.js fb set-desc CRITERIA # will prompt
|
||||||
|
// ./oputil.js fb set-desc CRITERIA "The new description"
|
||||||
|
//
|
||||||
|
let fileCriteria;
|
||||||
|
let desc;
|
||||||
|
if(argv._.length > 3) {
|
||||||
|
fileCriteria = argv._[argv._.length - 2];
|
||||||
|
desc = argv._[argv._.length - 1];
|
||||||
|
} else {
|
||||||
|
fileCriteria = argv._[argv._.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
(callback) => {
|
||||||
|
return initConfigAndDatabases(callback);
|
||||||
|
},
|
||||||
|
(callback) => {
|
||||||
|
getFileEntries(fileCriteria, (err, entries) => {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entries.length > 1) {
|
||||||
|
return callback(Errors.General('Criteria not specific enough.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, entries[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(fileEntry, callback) => {
|
||||||
|
if(desc) {
|
||||||
|
return callback(null, fileEntry, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnswers([
|
||||||
|
{
|
||||||
|
name : 'userDesc',
|
||||||
|
message : 'Description:',
|
||||||
|
type : 'editor',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
answers => {
|
||||||
|
if(!answers.userDesc) {
|
||||||
|
return callback(Errors.General('User canceled'));
|
||||||
|
}
|
||||||
|
return callback(null, fileEntry, answers.userDesc);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(fileEntry, newDesc, callback) => {
|
||||||
|
fileEntry.desc = newDesc;
|
||||||
|
fileEntry.persist(true, err => { // true=isUpdate
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
process.exitCode = ExitCodes.ERROR;
|
||||||
|
console.error(err.message);
|
||||||
|
} else {
|
||||||
|
console.info('Description updated.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function handleFileBaseCommand() {
|
function handleFileBaseCommand() {
|
||||||
|
|
||||||
function errUsage() {
|
function errUsage() {
|
||||||
|
@ -924,7 +993,7 @@ function handleFileBaseCommand() {
|
||||||
const action = argv._[1];
|
const action = argv._[1];
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
info : displayFileAreaInfo,
|
info : displayFileOrAreaInfo,
|
||||||
scan : scanFileAreas,
|
scan : scanFileAreas,
|
||||||
|
|
||||||
mv : moveFiles,
|
mv : moveFiles,
|
||||||
|
@ -936,5 +1005,8 @@ function handleFileBaseCommand() {
|
||||||
delete : removeFiles,
|
delete : removeFiles,
|
||||||
|
|
||||||
'import-areas' : importFileAreas,
|
'import-areas' : importFileAreas,
|
||||||
|
|
||||||
|
desc : setFileDescription,
|
||||||
|
description : setFileDescription,
|
||||||
}[action] || errUsage)();
|
}[action] || errUsage)();
|
||||||
}
|
}
|
|
@ -57,9 +57,7 @@ actions:
|
||||||
for example: scan some_area *.zip
|
for example: scan some_area *.zip
|
||||||
|
|
||||||
info CRITERIA display information about areas and/or files
|
info CRITERIA display information about areas and/or files
|
||||||
where CRITERIA is one of the following:
|
matching CRITERIA.
|
||||||
AREA_TAG|SHA|FILE_ID|FILENAME_WC
|
|
||||||
SHA may be a full or partial SHA-256
|
|
||||||
|
|
||||||
mv SRC [SRC...] DST move entry(s) from SRC to DST
|
mv SRC [SRC...] DST move entry(s) from SRC to DST
|
||||||
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
||||||
|
@ -67,6 +65,9 @@ actions:
|
||||||
|
|
||||||
rm SRC [SRC...] remove entry(s) from the system matching SRC
|
rm SRC [SRC...] remove entry(s) from the system matching SRC
|
||||||
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
||||||
|
desc CRITERIA sets a new file description for file base entry
|
||||||
|
matching CRITERIA. Launches an external editor using
|
||||||
|
$VISUAL, $EDITOR, or vim/notepad.
|
||||||
import-areas FILEGATE.ZXX import file base areas using FileGate RAID type format
|
import-areas FILEGATE.ZXX import file base areas using FileGate RAID type format
|
||||||
|
|
||||||
scan args:
|
scan args:
|
||||||
|
@ -94,6 +95,9 @@ general information:
|
||||||
AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag
|
AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag
|
||||||
example: retro@bbs
|
example: retro@bbs
|
||||||
|
|
||||||
|
CRITERIA file base entry criteria. in general, can be AREA_TAG, SHA,
|
||||||
|
FILE_ID, or FILENAME_WC.
|
||||||
|
|
||||||
FILENAME_WC filename with * and ? wildcard support. may match 0:n entries
|
FILENAME_WC filename with * and ? wildcard support. may match 0:n entries
|
||||||
SHA full or partial SHA-256
|
SHA full or partial SHA-256
|
||||||
FILE_ID a file identifier. see file.sqlite3
|
FILE_ID a file identifier. see file.sqlite3
|
||||||
|
|
|
@ -99,9 +99,7 @@ actions:
|
||||||
for example: scan some_area *.zip
|
for example: scan some_area *.zip
|
||||||
|
|
||||||
info CRITERIA display information about areas and/or files
|
info CRITERIA display information about areas and/or files
|
||||||
where CRITERIA is one of the following:
|
matching CRITERIA.
|
||||||
AREA_TAG|SHA|FILE_ID|FILENAME_WC
|
|
||||||
SHA may be a full or partial SHA-256
|
|
||||||
|
|
||||||
mv SRC [SRC...] DST move entry(s) from SRC to DST
|
mv SRC [SRC...] DST move entry(s) from SRC to DST
|
||||||
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
||||||
|
@ -109,6 +107,9 @@ actions:
|
||||||
|
|
||||||
rm SRC [SRC...] remove entry(s) from the system matching SRC
|
rm SRC [SRC...] remove entry(s) from the system matching SRC
|
||||||
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
|
||||||
|
desc CRITERIA sets a new file description for file base entry
|
||||||
|
matching CRITERIA. Launches an external editor using
|
||||||
|
$VISUAL, $EDITOR, or vim/notepad.
|
||||||
import-areas FILEGATE.ZXX import file base areas using FileGate RAID type format
|
import-areas FILEGATE.ZXX import file base areas using FileGate RAID type format
|
||||||
|
|
||||||
scan args:
|
scan args:
|
||||||
|
@ -134,6 +135,9 @@ general information:
|
||||||
AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag
|
AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag
|
||||||
example: retro@bbs
|
example: retro@bbs
|
||||||
|
|
||||||
|
CRITERIA file base entry criteria. in general, can be AREA_TAG, SHA,
|
||||||
|
FILE_ID, or FILENAME_WC.
|
||||||
|
|
||||||
FILENAME_WC filename with * and ? wildcard support. may match 0:n entries
|
FILENAME_WC filename with * and ? wildcard support. may match 0:n entries
|
||||||
SHA full or partial SHA-256
|
SHA full or partial SHA-256
|
||||||
FILE_ID a file identifier. see file.sqlite3
|
FILE_ID a file identifier. see file.sqlite3
|
||||||
|
|
Loading…
Reference in New Issue