1 { Version 040709. Copyright � Alexey A.Chernobaev, 1996-2004 }
2 
3 unit ParseCmd;
4 {
5   Command line parsing.
6   ������ ��������� ������.
7 
8   Example:
9   ������ �������������:
10 
11   CmdParser:=TCommandLineParser.Create([], False);
12   try
13     CmdParser.Parse(Windows.GetCommandLine, 1);
14 
15     for I:=0 to CmdParser.ParamCount - 1 do
16       if CmdParser.ParamStr(I) = ...
17 
18     for I:=0 to CmdParser.SimpleParamCount - 1 do
19       if CmdParser.SimpleParamStr(I) = ...
20 
21     for I:=0 to CmdParser.OptionCount - 1 do
22       if CmdParser.OptionStr(I) = ...
23 
24     if CmdParser.HasOption('from') then ...
25   finally
26     CmdParser.Free;
27   end;
28 }
29 
30 interface
31 
32 {$I VCheck.inc}
33 
34 uses
35   SysUtils, StrLst, VectStr;
36 
37 type
38   TCommandLineParser = class
39   protected
40     FOptionPrefix: TCharSet;
41     FParamList, FSimpleList, FOptionValues: TStrLst;
42     FOptionKeys: TStrLstObj;
43   public
44     constructor Create(OptionPrefix: TCharSet{$IFDEF V_DEFAULTS} = []{$ENDIF};
45       CaseSensitive: Boolean{$IFDEF V_DEFAULTS} = False{$ENDIF});
46     { OptionPrefix: �������� ����� ��������� ������; ���� [], �� �����������
47       �������� �� ��������� ['/', '-']; ���� CaseSensitive = True, �� �����,
48       ����������� � ������ ��������, ��������� ����������, ����� ��� ���������
49       �������������� }
50     { OptionPrefix: command line option prefixes; if [] then default values
51       ['/', '-'] are used; if CaseSensitive = True then options in different
52       case are interpreted as different, otherwise as equivalent. }
53     destructor Destroy; override;
54     procedure Parse(const CommandLine: String; FromIndex: Integer;
55       QuotesDoubling: Boolean{$IFDEF V_DEFAULTS} = False{$ENDIF});
56     { ��������� ������ ������ CommandLine; ��� ���� ����� ���������������
57       ������ FromIndex ����������; ���� QuotesDoubling = True, �� �����
58       ��������� ������ ����������� ������� '"', ������� ������ ���� ������� }
59     { parses the string CommandLine ignoring first FromIndex parameters; if
60       QuotesDoubling = True then the character '"' is allowed among the
61       elements of the string }
ParamCountnull62     function ParamCount: Integer;
63     { ����� ���������� ���������� }
64     { total number of parameters }
ParamStrnull65     function ParamStr(I: Integer): String;
66     { I-�� �������� (I = 0..ParamCount - 1); ���� I >= ParamCount, ��
67       ������������ ������ ������ }
68     { Ith parameter (I = 0..ParamCount - 1); if I >= ParamCount then returns
69       empty string }
SimpleParamCountnull70     function SimpleParamCount: Integer;
71     { ���������� ������� ���������� (�.�. �� ������������ � OptionPrefix) }
72     { number of simple parameters (i.e. ones not beginning with OptionPrefix) }
SimpleParamStrnull73     function SimpleParamStr(I: Integer): String;
74     { I-�� ������� �������� (I = 0..SimpleParamCount - 1); ����
75       I >= SimpleParamCount, �� ������������ ������ ������ }
76     { Ith simple parameter (I = 0..SimpleParamCount - 1); if
77       I >= SimpleParamCount then returns empty string }
OptionCountnull78     function OptionCount: Integer;
79     { ���������� ����� (�.�. ����������, ������������ � OptionPrefix) }
80     { number of options (i.e. parameters beginning with OptionPrefix) }
OptionStrnull81     function OptionStr(I: Integer): String;
82     { I-�� ����� (I = 0..OptionCount - 1) �������; ���� I >= OptionCount,
83       �� ������������ ������ ������ }
84     { Ith option (I = 0..OptionCount - 1); if I >= OptionCount then empty string
85       will be returned }
HasOptionnull86     function HasOption(const OptionName: String): Boolean;
87     { ���������� True, ���� � ��������� ������ ������ ����� OptionName;
88       ��������, ����� Parse('/option') ����� HasOption('option') ���������
89       True (���� '/' ������ � AnOptionPrefix) }
90     { returns True if the command line contains an option OptionName; e.g. after
91       Parse('/option') call to HasOption('option') will return True (if '/' is
92       in AnOptionPrefix) }
RemoveOptionnull93     function RemoveOption(const OptionName: String): Boolean;
94     { ���������, ���� �� � ��������� ������ �������� �����, � ���� ��, ��
95       ������� � �� ����������� ������ ����� � ���������� True, ����� ����������
96       False }
97     { checks whether the command line contains the given option and if true then
98       removes it from the internal list of options and returns True else returns
99       False }
OptionValuenull100     function OptionValue(const OptionName: String; Remove: Boolean
101       {$IFDEF V_DEFAULTS} = False{$ENDIF}): String;
102     { ���������� �������� ����� OptionName; ��������, �����
103       Parse('/option:value') ����� OptionValue('option') ��������� 'value';
104       ���� HasOption(OptionName) = False, �� ������������ ������ ������;
105       ���� Remove = True, �� ����� ����� ������� �� ����������� ������ ����� }
106     { returns a value of option OptionName; e.g. a call to OptionValue('option')
107       after Parse('/option:value') will return 'value'; if
108       HasOption(OptionName) = False then an empty string will be returned; if
109       Remove = True then the option will be removed from the internal list of
110       options }
111   end;
112 
113 implementation
114 
115 {$IFDEF CHECK_OBJECTS_FREE}
116 uses ChckFree;
117 {$ENDIF}
118 
119 constructor TCommandLineParser.Create(OptionPrefix: TCharSet; CaseSensitive: Boolean);
120 begin
121   inherited Create;
122   FParamList:=TStrLst.Create;
123   FSimpleList:=TStrLst.Create;
124   if CaseSensitive then
125     FOptionKeys:=TCaseSensStrLstObj.Create
126   else
127     FOptionKeys:=TStrLstObj.Create;
128   FOptionValues:=TStrLst.Create;
129   if OptionPrefix <> [] then
130     FOptionPrefix:=OptionPrefix
131   else
132     FOptionPrefix:=['/', '-'];
133   {$IFDEF CHECK_OBJECTS_FREE}
134   RegisterObjectCreate(Self);
135   {$ENDIF}
136 end;
137 
138 destructor TCommandLineParser.Destroy;
139 begin
140   {$IFDEF CHECK_OBJECTS_FREE}
141   RegisterObjectFree(Self);
142   {$ENDIF}
143   FParamList.Free;
144   FSimpleList.Free;
145   FOptionKeys.Free;
146   FOptionValues.Free;
147   inherited Destroy;
148 end;
149 
150 procedure TCommandLineParser.Parse(const CommandLine: String; FromIndex: Integer;
151   QuotesDoubling: Boolean);
152 var
153   ParamIndex: Integer;
154   S: String;
155 
156   procedure ProcessParam;
157   var
158     I, J: Integer;
159     S1, S2: String;
160   begin
161     if S <> '' then begin
162       Inc(ParamIndex);
163       if ParamIndex > FromIndex then begin
164         I:=FParamList.Add(S);
165         if S[1] in FOptionPrefix then begin
166           J:=CharPos(':', S, 1);
167           if J > 0 then begin
168             S1:=Copy(S, 2, J - 2);
169             S2:=Copy(S, J + 1, Length(S));
170             J:=FOptionKeys.IndexOf(S1);
171             if J < 0 then begin
172               FOptionKeys.AddObject(S1, Pointer(I));
173               FOptionValues.Add(S2);
174             end
175             else begin
176               FOptionKeys.Objects[J]:=Pointer(I);
177               FOptionValues.Items[J]:=S2;
178             end;
179           end
180           else begin
181             FOptionKeys.AddObject(Copy(S, 2, Length(S)), Pointer(I));
182             FOptionValues.Add('');
183           end;
184         end
185         else
186           FSimpleList.Add(S);
187       end;
188       S:='';
189     end;
190   end;
191 
192 var
193   I, L: Integer;
194   C: Char;
195   Quote: Boolean;
196 begin
197   FParamList.Clear;
198   FSimpleList.Clear;
199   FOptionKeys.Clear;
200   FOptionValues.Clear;
201   ParamIndex:=0;
202   S:='';
203   Quote:=False;
204   L:=Length(CommandLine);
205   I:=1;
206   while I <= L do begin
207     C:=CommandLine[I];
208     Case C of
209       '"':
210         begin
211           S:=S + C;
212           if Quote then begin
213             if QuotesDoubling and (I < L) and (CommandLine[I + 1] = '"') then begin
214               S:=S + '"';
215               Inc(I, 2);
216               Continue;
217             end;
218             ProcessParam;
219           end;
220           Quote:=not Quote;
221         end;
222       ' ':
223         if Quote then S:=S + C else ProcessParam;
224     Else
225       S:=S + C;
226     End;
227     Inc(I);
228   end;
229   ProcessParam;
230   FOptionKeys.SortWith(FOptionValues);
231 end;
232 
ParamCountnull233 function TCommandLineParser.ParamCount: Integer;
234 begin
235   Result:=FParamList.Count;
236 end;
237 
TCommandLineParser.ParamStrnull238 function TCommandLineParser.ParamStr(I: Integer): String;
239 begin
240   if (I >= 0) and (I < FParamList.Count) then
241     Result:=FParamList.Items[I]
242   else
243     Result:='';
244 end;
245 
TCommandLineParser.SimpleParamCountnull246 function TCommandLineParser.SimpleParamCount: Integer;
247 begin
248   Result:=FSimpleList.Count;
249 end;
250 
TCommandLineParser.SimpleParamStrnull251 function TCommandLineParser.SimpleParamStr(I: Integer): String;
252 begin
253   if (I >= 0) and (I < FSimpleList.Count) then
254     Result:=FSimpleList.Items[I]
255   else
256     Result:='';
257 end;
258 
OptionCountnull259 function TCommandLineParser.OptionCount: Integer;
260 begin
261   Result:=FOptionKeys.Count;
262 end;
263 
TCommandLineParser.OptionStrnull264 function TCommandLineParser.OptionStr(I: Integer): String;
265 begin
266   if (I >= 0) and (I < FOptionKeys.Count) then
267     Result:=FParamList.Items[Integer(FOptionKeys.Objects[I])]
268   else
269     Result:='';
270 end;
271 
TCommandLineParser.HasOptionnull272 function TCommandLineParser.HasOption(const OptionName: String): Boolean;
273 begin
274   Result:=FOptionKeys.FindInSorted(OptionName) >= 0;
275 end;
276 
TCommandLineParser.RemoveOptionnull277 function TCommandLineParser.RemoveOption(const OptionName: String): Boolean;
278 var
279   I: Integer;
280 begin
281   Result:=False;
282   I:=FOptionKeys.FindInSorted(OptionName);
283   if I >= 0 then begin
284     FOptionKeys.Delete(I);
285     FOptionValues.Delete(I);
286     Result:=True;
287   end;
288 end;
289 
TCommandLineParser.OptionValuenull290 function TCommandLineParser.OptionValue(const OptionName: String;
291   Remove: Boolean): String;
292 var
293   I: Integer;
294 begin
295   I:=FOptionKeys.FindInSorted(OptionName);
296   if I >= 0 then begin
297     Result:=FOptionValues.Items[I];
298     FOptionKeys.Delete(I);
299     FOptionValues.Delete(I);
300   end
301   else
302     Result:='';
303 end;
304 
305 end.
306