diff --git a/WHATSNEW.md b/WHATSNEW.md index 61b7235f..5b08e2b8 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -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 * NetMail support! You can now send and receive NetMail. To send a NetMail address a external user using `Name
` 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 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 * 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. diff --git a/core/files_bbs_file.js b/core/files_bbs_file.js index cfbd5a96..6e186ab4 100644 --- a/core/files_bbs_file.js +++ b/core/files_bbs_file.js @@ -8,6 +8,12 @@ const fs = require('graceful-fs'); const iconv = require('iconv-lite'); 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 { constructor() { this.entries = new Map(); @@ -34,6 +40,10 @@ module.exports = class FilesBBSFile { const lines = iconv.decode(descData, 'cp437').split(/\r?\n/g); 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, // 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 timestamp = moment(hdr[2], 'MM/DD/YY'); - if(!timestamp.isValid()) { + if(isBadDescription(desc) || !timestamp.isValid()) { continue; } filesBbs.entries.set(fileName, { timestamp, desc } ); @@ -121,6 +131,10 @@ module.exports = class FilesBBSFile { const desc = long.join('\r\n'); const fileName = hdr[1]; + if(isBadDescription(desc)) { + continue; + } + filesBbs.entries.set(fileName, { desc } ); } } @@ -157,6 +171,10 @@ module.exports = class FilesBBSFile { const desc = long.join('\r\n'); const fileName = hdr[1]; + if(isBadDescription(desc)) { + continue; + } + filesBbs.entries.set(fileName, { desc } ); } } @@ -182,10 +200,6 @@ module.exports = class FilesBBSFile { } const firstDescLine = hdr[4].trimRight(); - // ugly kludge: - if('No ID File Found For This Archive File.' === firstDescLine) { - continue; - } const long = [ firstDescLine ]; for(let j = i + 1; j < lines.length; ++j) { line = lines[j]; @@ -201,7 +215,7 @@ module.exports = class FilesBBSFile { const size = parseInt(hdr[2]); const timestamp = moment(hdr[3], 'MM-DD-YY'); - if(isNaN(size) || !timestamp.isValid()) { + if(isBadDescription(desc) || isNaN(size) || !timestamp.isValid()) { continue; } @@ -235,7 +249,7 @@ module.exports = class FilesBBSFile { const fileName = hdr[1].trim(); const desc = hdr[2].trim(); - if(desc) { + if(desc && !isBadDescription(desc)) { filesBbs.entries.set(fileName, { desc } ); } }); diff --git a/core/oputil/oputil_file_base.js b/core/oputil/oputil_file_base.js index 18113cc0..b12c4d69 100644 --- a/core/oputil/oputil_file_base.js +++ b/core/oputil/oputil_file_base.js @@ -413,11 +413,11 @@ function dumpFileInfo(shaOrFileId, cb) { ); } -function displayFileAreaInfo() { +function displayFileOrAreaInfo() { // AREA_TAG[@STORAGE_TAG] - // SHA256|PARTIAL + // SHA256|PARTIAL|FILE_ID|FILENAME_WILDCARD // if sha: dump file info - // if area/stoarge dump area(s) + + // if area/storage dump area(s) + 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 errUsage() { @@ -924,7 +993,7 @@ function handleFileBaseCommand() { const action = argv._[1]; return ({ - info : displayFileAreaInfo, + info : displayFileOrAreaInfo, scan : scanFileAreas, mv : moveFiles, @@ -936,5 +1005,8 @@ function handleFileBaseCommand() { delete : removeFiles, 'import-areas' : importFileAreas, + + desc : setFileDescription, + description : setFileDescription, }[action] || errUsage)(); } \ No newline at end of file diff --git a/core/oputil/oputil_help.js b/core/oputil/oputil_help.js index 59693ad3..f8a0d42c 100644 --- a/core/oputil/oputil_help.js +++ b/core/oputil/oputil_help.js @@ -57,9 +57,7 @@ actions: for example: scan some_area *.zip info CRITERIA display information about areas and/or files - where CRITERIA is one of the following: - AREA_TAG|SHA|FILE_ID|FILENAME_WC - SHA may be a full or partial SHA-256 + matching CRITERIA. mv SRC [SRC...] DST move entry(s) from SRC to DST 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 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 scan args: @@ -93,6 +94,9 @@ import-areas args: general information: AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag 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 SHA full or partial SHA-256 diff --git a/docs/admin/oputil.md b/docs/admin/oputil.md index dae9b44c..bb6dcacf 100644 --- a/docs/admin/oputil.md +++ b/docs/admin/oputil.md @@ -99,9 +99,7 @@ actions: for example: scan some_area *.zip info CRITERIA display information about areas and/or files - where CRITERIA is one of the following: - AREA_TAG|SHA|FILE_ID|FILENAME_WC - SHA may be a full or partial SHA-256 + matching CRITERIA. mv SRC [SRC...] DST move entry(s) from SRC to DST 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 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 scan args: @@ -133,6 +134,9 @@ import-areas args: general information: AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag 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 SHA full or partial SHA-256