summary refs log tree commit diff stats
path: root/nim/parseopt.pas
blob: 0ca87bd37abb032b96b0b8b7b65bfdc11df13d1c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//
//
//            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.