172 lines
4.4 KiB
JavaScript
172 lines
4.4 KiB
JavaScript
module.exports = parse;
|
|
|
|
// https://unix.stackexchange.com/questions/364383/confusion-about-changing-meaning-of-arguments-and-options-is-there-an-official
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap12.html#tag_12_01
|
|
|
|
// 'options' are --foo, -f
|
|
//
|
|
// 'option-arguments' are (non-option) things that follow an option, and that the option expects
|
|
// eg. 'filename.txt' in './example --input filename.txt'
|
|
//
|
|
// 'operands' are (non-option) things that follow an option, but aren't option-arguments
|
|
// eg 'filename.txt' in './example --verbose filename.txt'
|
|
|
|
|
|
/**
|
|
* @returns [ { string: boolean } | { string: string[] } ]
|
|
**/
|
|
function parse(argv) {
|
|
argv = process.argv.slice(2);
|
|
|
|
// Label everything in argv as either
|
|
// an 'option' or an 'arg_or_operand'
|
|
let mapped = argv.flatMap((a) => {
|
|
// A single long option
|
|
if (a.startsWith('--')) {
|
|
return { arg: a.substring(2), type: 'option' }
|
|
}
|
|
|
|
// A single short option
|
|
if (a.startsWith('-') && (a.length === 2)) {
|
|
return { arg: a.substring(1), type: 'option' }
|
|
}
|
|
|
|
// Multiple short options
|
|
if (a.startsWith('-')) {
|
|
let splitGroup = a.substring(1).split('');
|
|
return splitGroup.map((sg) => { return {arg: sg, type: 'option'} });
|
|
}
|
|
|
|
// An option-argument or operand
|
|
return { arg: a, type: 'arg_or_operand' };
|
|
});
|
|
|
|
// Group consecutive option-args/operands
|
|
let wrapped = mapped.map((a) => [a]);
|
|
let grouped = [];
|
|
wrapped.forEach((a, i) => {
|
|
if (i === 0 || a[0].type === 'option') {
|
|
grouped.push(a);
|
|
return;
|
|
}
|
|
let prev = grouped.pop();
|
|
if (prev[0].type === 'option') {
|
|
grouped.push(prev);
|
|
grouped.push(a);
|
|
return;
|
|
}
|
|
grouped.push(prev.concat(a));
|
|
return;
|
|
});
|
|
|
|
let out = []
|
|
grouped.forEach((a, i) => {
|
|
console.log('current', a);
|
|
let next = grouped[i+1];
|
|
console.log('next', next);
|
|
|
|
if ((a[0].type === 'option')
|
|
&& next
|
|
&& (next[0].type === 'arg_or_operand')) {
|
|
console.log('next is a value');
|
|
let next_shortened = next.map(n => n.arg);
|
|
console.log('short', next_shortened);
|
|
let o = {};
|
|
o[a[0].arg] = next_shortened;
|
|
out.push(o);
|
|
grouped.splice(i, 1);
|
|
} else {
|
|
let pair = {};
|
|
pair[a[0].arg] = true;
|
|
a.forEach((b) => {
|
|
let o = {};
|
|
o[b.arg] = true;
|
|
out.push(o);
|
|
});
|
|
}
|
|
});
|
|
console.log('out', out);
|
|
}
|
|
|
|
/*
|
|
// this is good, but doesn't handle multiple operands in a row
|
|
|
|
let out = [];
|
|
|
|
mapped.forEach((a, i) => {
|
|
console.log(a, i);
|
|
if ((a.type === 'flag') && (mapped[i+1].type === 'arg_or_operand')) {
|
|
console.log('next is a value');
|
|
let pair = {};
|
|
pair[a.arg] = mapped[i+1].arg;
|
|
out.push(pair);
|
|
mapped.splice(i, 1);
|
|
} else {
|
|
let pair = {};
|
|
pair[a.arg] = true;
|
|
out.push(pair);
|
|
}
|
|
});
|
|
//console.log(out);
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
/*
|
|
argv.forEach((arg) => {
|
|
// We'll treat `--long` flags and flags without dash prefixes the same,
|
|
// so just discard double-dash prefixes
|
|
if (arg.startsWith('--')) { arg = arg.substring(2); }
|
|
|
|
// Handle short flags, including
|
|
// un-grouping grouped ones (eg `ps -aux`)
|
|
if (arg.startsWith('-')) {
|
|
console.log('startswith -; length:', arg.length);
|
|
console.log('lose first char:', arg.substring(1));
|
|
console.log('split keys vals:', arg.split('='));
|
|
|
|
// If there's a key=value assignment,
|
|
// don't treat it like a group
|
|
if (arg.split('=').length > 1) {
|
|
let [key, value] = processArg(arg);
|
|
finalOutput[key] = value;
|
|
return;
|
|
}
|
|
|
|
if (arg.length < 2) {
|
|
// It's not grouped; just remove the '-' and treat it like the rest, later
|
|
arg = arg.substring(1);
|
|
let [key, value] = processArg(arg);
|
|
finalOutput[key] = value;
|
|
return;
|
|
} else {
|
|
arg = arg.substring(1);
|
|
let ungrouped = arg.split('');
|
|
ungrouped.forEach(a => output[a] = true);
|
|
console.log('ungr', ungrouped);
|
|
return;
|
|
}
|
|
}
|
|
|
|
console.log('out:', output);
|
|
|
|
|
|
output[arg] = true;
|
|
});
|
|
|
|
return finalOutput;
|
|
}
|
|
|
|
function processArg(a) {
|
|
let out = {};
|
|
let split = a.split('=')
|
|
if (split.length > 2) { throw new Error(`Too many '=' in argument ${a}`); }
|
|
let [key, value] = split;
|
|
if (split.length === 2) {
|
|
return [key, value];
|
|
} else {
|
|
return [key, true];
|
|
}
|
|
}
|
|
*/ |