//
//
// Nimrod's Runtime Library
// (c) Copyright 2008 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit parseopt;
// A command line parser; the Nimrod version of this file
// will become part of the standard library.
interface
{$include 'config.inc'}
uses
nsystem, charsets, nos, strutils;
type
TCmdLineKind = (
cmdEnd, // end of command line reached
cmdArgument, // argument detected
cmdLongoption, // a long option ``--option`` detected
cmdShortOption // a short option ``-c`` detected
);
TOptParser = object(NObject)
cmd: string;
pos: int;
inShortState: bool;
kind: TCmdLineKind;
key, val: string;
end;
function init(const cmdline: string = ''): TOptParser;
procedure next(var p: TOptParser);
function getRestOfCommandLine(const p: TOptParser): string;
implementation
function init(const cmdline: string = ''): TOptParser;
var
i: int;
begin
result.pos := strStart;
result.inShortState := false;
if cmdline <> '' then
result.cmd := cmdline
else begin
result.cmd := '';
for i := 1 to ParamCount() do
result.cmd := result.cmd +{&} quoteIfContainsWhite(paramStr(i)) +{&} ' ';
{@ignore}
result.cmd := result.cmd + #0;
{@emit}
end;
result.kind := cmdEnd;
result.key := '';
result.val := '';
end;
function parseWord(const s: string; const i: int; var w: string;
const delim: TCharSet = {@set}[#9, ' ', #0]): int;
begin
result := i;
if s[result] = '"' then begin
inc(result);
while not (s[result] in [#0, '"']) do begin
addChar(w, s[result]);
inc(result);
end;
if s[result] = '"' then inc(result)
end
else begin
while not (s[result] in delim) do begin
addChar(w, s[result]);
inc(result);
end
end
end;
procedure handleShortOption(var p: TOptParser);
var
i: int;
begin
i := p.pos;
p.kind := cmdShortOption;
addChar(p.key, p.cmd[i]);
inc(i);
p.inShortState := true;
while p.cmd[i] in [#9, ' '] do begin
inc(i);
p.inShortState := false;
end;
if p.cmd[i] in [':', '='] then begin
inc(i); p.inShortState := false;
while p.cmd[i] in [#9, ' '] do inc(i);
i := parseWord(p.cmd, i, p.val);
end;
if p.cmd[i] = #0 then p.inShortState := false;
p.pos := i;
end;
procedure next(var p: TOptParser);
var
i: int;
begin
i := p.pos;
while p.cmd[i] in [#9, ' '] do inc(i);
p.pos := i;
setLength(p.key, 0);
setLength(p.val, 0);
if p.inShortState then begin
handleShortOption(p); exit
end;
case p.cmd[i] of
#0: p.kind := cmdEnd;
'-': begin
inc(i);
if p.cmd[i] = '-' then begin
p.kind := cmdLongOption;
inc(i);
i := parseWord(p.cmd, i, p.key, {@set}[#0, ' ', #9, ':', '=']);
while p.cmd[i] in [#9, ' '] do inc(i);
if p.cmd[i] in [':', '='] then begin
inc(i);
while p.cmd[i] in [#9, ' '] do inc(i);
p.pos := parseWord(p.cmd, i, p.val);
end
else
p.pos := i;
end
else begin
p.pos := i;
handleShortOption(p)
end
end;
else begin
p.kind := cmdArgument;
p.pos := parseWord(p.cmd, i, p.key);
end
end
end;
function getRestOfCommandLine(const p: TOptParser): string;
begin
result := strip(ncopy(p.cmd, p.pos+strStart, length(p.cmd)-1))
// always -1, because Pascal version uses a trailing zero here
end;
end.