summary refs log blame commit diff stats
path: root/nim/parseopt.pas
blob: 0ca87bd37abb032b96b0b8b7b65bfdc11df13d1c (plain) (tree)




















































                                                           
                                                                               


































































































                                                                       
//
//
//            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.