// // Copyright (c) 2019 Dimitar Toshkov Zhekov // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // 'use strict'; class Params { constructor() { this.excstk = false; } } class Options { constructor(needArgs, helpText, versionText) { needArgs.forEach(name => { if (!name.match(/^(-[^-]|--[^=]+)$/)) { throw new Error(`invalid option name "${name}"`); } }); this.needArgs = needArgs; this.helpText = helpText; this.versionText = versionText; } posixlyCorrect() { // eslint-disable-line class-methods-use-this return process.env['POSIXLY_CORRECT'] != null; } needsArg(name) { return this.needArgs.includes(name); } fallback(name, params) { if (name === '--excstk') { params.excstk = true; } else if (name === '--help' && this.helpText != null) { process.stdout.write(this.helpText); process.exit(0); } else if (name === '--version' && this.versionText != null) { process.stdout.write(this.versionText); process.exit(0); } else { let suffix = this.needsArg(name) ? ' (taking an argument?)' : ''; suffix += (this.helpText != null) ? ', try --help' : ''; throw new Error(`unknown option "${name}"${suffix}`); } } reader(args, skip = 2) { return new Options.Reader(this, args, skip); } } Options.Reader = class { constructor(options, args, skip) { this.options = options; this.args = args; this.skip = skip; } forEach(callback) { let optind; for (optind = this.skip; optind < this.args.length; optind++) { let arg = this.args[optind]; if (arg === '-' || !arg.startsWith('-')) { if (this.options.posixlyCorrect()) { break; } callback(null, arg); } else if (arg === '--') { optind++; break; } else { let name, value; if (!arg.startsWith('--')) { for (;;) { name = arg.substring(0, 2); value = (name !== arg) ? arg.substring(2) : null; if (this.options.needsArg(name) || value == null) { break; } callback(name, null); arg = '-' + value; } } else if (arg.indexOf('=') >= 3) { name = arg.split('=', 1)[0]; if (!this.options.needsArg(name)) { throw new Error(`option "${name}" does not take an argument`); } value = arg.substring(name.length + 1); } else { name = arg; value = null; } if (value === null && Number(this.options.needsArg(name)) > 0) { if (++optind === this.args.length) { throw new Error(`option "${name}" requires an argument`); } value = this.args[optind]; } callback(name, value); } } this.args.slice(optind).forEach(value => callback(null, value)); } }; Object.defineProperty(Options, 'Reader', { 'enumerable': false }); Object.defineProperty(Options.Reader, 'name', { value: 'Reader' }); function start(programName, options, params, mainProgram) { // eslint-disable-line consistent-return let parsed = (params != null) ? params : new Params(); try { const version = process.version.match(/^v?(\d+)\.(\d+)/); if (version.length < 3) { throw new Error('unable to obtain node version'); } else if ((parseInt(version[1]) * 1000 + parseInt(version[2])) < 6009) { throw new Error('node version 6.9.0 or later required'); } if (params == null) { return mainProgram(options.reader(process.argv), name => options.fallback(name, parsed)); } else { let nonopt = []; options.reader(process.argv).forEach((name, value) => { if (name == null) { nonopt.push(value); } else { options.parse(name, value, parsed); } }); return mainProgram(nonopt, parsed); } } catch (e) { if (parsed.excstk) { throw e; } else { process.stderr.write(`${process.argv.length >= 2 ? process.argv[1] : programName}: ${e.message}\n`); process.exit(1); } } } module.exports = Object.freeze({ Params, Options, start });