source: trunk/NewView/CmdLineParameterUnit.pas@ 67

Last change on this file since 67 was 67, checked in by RBRi, 19 years ago

again significant changes for the command line parser
TODO: write many more unit tests

  • Property svn:eol-style set to native
File size: 16.9 KB
RevLine 
[23]1Unit CmdLineParameterUnit;
2
3// NewView - a new OS/2 Help Viewer
4// Copyright 2006 Ronald Brill (rbri at rbri dot de)
[42]5// This software is released under the GNU Public License - see readme.txt
[23]6
7// Helper functions to address the command line parameters newview
8// is started with
9
10Interface
11
12uses
[32]13 Os2Def,
14 BseTib,
15 BseDos,
[23]16 SysUtils,
17 Classes,
18 PMWIN,
[42]19 StringUtilsUnit,
20 DebugUnit;
[23]21
22 CONST
23 SUCCESS = 0;
24 ERROR_UNMATCHED_QUOTE = -1;
25
[42]26 TYPE EParsingFailed=CLASS(Exception);
27
[23]28 TYPE
29 TWindowPosition = record
30 left: longint;
31 bottom: longint;
32 width: longint;
33 height: longint;
34 end;
35 TYPE
36 TCmdLineParameters = class
37 private
[42]38 commandLine : String;
[23]39 showUsageFlag : boolean;
[42]40 searchFlag : boolean;
41 globalSearchFlag : boolean;
[23]42 language : string;
43 helpManagerFlag : boolean;
44 helpManagerWindow : integer;
45 windowPositionFlag: boolean;
46 windowPosition: TWindowPosition;
47 ownerWindow : integer;
48 windowTitle : string;
[25]49 fileNames : string;
[45]50 fileNamesRaw : string;
[42]51 searchText : string;
[23]52
[67]53// FUNCTION ReadNextPart(const aParseString : String; const aSetOfDelimiterChars : TSetOfChars): String;
54 FUNCTION handleSwitchWithValue(const aSwitchString : String; const aSwitch : String; var aValue : String) : Boolean;
55 PROCEDURE parseSwitch(aSwitchString : String);
[65]56 PROPERTY getFileNames : string read fileNames;
57 PROPERTY getSearchText : string read searchText;
[42]58
[23]59 public
[42]60 PROPERTY getCommandLine : String read commandLine;
[32]61 PROPERTY getShowUsageFlag : boolean read showUsageFlag;
[42]62 PROPERTY getSearchFlag : boolean read searchFlag;
63 PROPERTY getGlobalSearchFlag : boolean read globalSearchFlag;
[32]64 PROPERTY getLanguage : string read language;
65 PROPERTY getHelpManagerFlag : boolean read helpManagerFlag;
[25]66 FUNCTION setHelpManagerFlag(aNewValue : boolean) : boolean;
[32]67 PROPERTY getHelpManagerWindow : integer read helpManagerWindow;
68 PROPERTY getWindowPositionFlag : boolean read windowPositionFlag;
69 PROPERTY getWindowPosition : TWindowPosition read windowPosition;
70 PROPERTY getOwnerWindow : integer read ownerWindow;
71 PROPERTY getWindowTitle : string read windowTitle;
[45]72 PROPERTY getFileNamesRaw : string read fileNamesRaw;
[42]73
[65]74 PROCEDURE writeDetailsTo(aStrings : TStrings);
[42]75 PROCEDURE parseCmdLine(aCmdLineString : String);
[54]76
77 FUNCTION getInterpretedFileNames: String;
78 FUNCTION getInterpretedSearchText: String;
[23]79 end;
80
[54]81 FUNCTION getOwnHelpFileName: String;
82
[42]83 // returns a string containing the whole
84 // command line parametes
[54]85 FUNCTION nativeOS2GetCmdLineParameter : String;
[23]86
87
88Implementation
[54]89uses
90 ACLFileUtility;
[23]91
[65]92 PROCEDURE TCmdLineParameters.writeDetailsTo(aStrings : TStrings);
93 var
94 tmpWindowPosition : TWindowPosition;
95 begin
[67]96 aStrings.Add('''' + getCommandLine + '''');
[65]97 aStrings.Add('parsed infos:');
98
99 aStrings.Add('getShowUsageFlag: ' + boolToStr(getShowUsageFlag));
100 aStrings.Add('getSearchFlag: ' + boolToStr(getSearchFlag));
101 aStrings.Add('getSearchText: ' + getSearchText);
102 aStrings.Add('getGlobalSearchFlag: ' + boolToStr(getGlobalSearchFlag));
103 aStrings.Add('getLanguage: ' + getLanguage);
104 aStrings.Add('getHelpManagerFlag: ' + boolToStr(getHelpManagerFlag));
105 aStrings.Add('getHelpManagerFlag: ' + boolToStr(getHelpManagerFlag));
106 aStrings.Add('getHelpManagerWindow: ' + intToStr(getHelpManagerWindow));
107 aStrings.Add('getWindowPositionFlag: ' + boolToStr(getWindowPositionFlag));
108 aStrings.Add('getFileNames: ' + getFileNames);
109 aStrings.Add('getInterpretedSearchText: ' + getInterpretedSearchText);
110 aStrings.Add('getInterpretedFileNames: ' + getInterpretedFileNames);
111
112 tmpWindowPosition := getWindowPosition;
113 aStrings.Add('getWindowPosition: '
114 + intToStr(tmpWindowPosition.left) + ', '
115 + intToStr(tmpWindowPosition.bottom) + ', '
116 + intToStr(tmpWindowPosition.width) + ', '
117 + intToStr(tmpWindowPosition.height)
118 );
119 aStrings.Add('getOwnerWindow: ' + intToStr(getOwnerWindow));
120 aStrings.Add('getWindowTitle: ' + getWindowTitle);
121 end;
122
123
[67]124 Function TCmdLineParameters.getInterpretedFileNames: String;
[54]125 var
126 tmpOwnHelpFileName : String;
127 begin
128 result := getFileNames;
129
130 if getGlobalSearchFlag
131 AND (getSearchText = '')
132 then
133 begin
134 result := '';
135 exit;
136 end;
137
138
139 tmpOwnHelpFileName := FindDefaultLanguageHelpFile('NewView');
140 if (result = '') AND
141 FileExists(tmpOwnHelpFileName)
142 then
143 result := tmpOwnHelpFileName;
144 end;
145
146
[67]147 Function TCmdLineParameters.getInterpretedSearchText: String;
[54]148 begin
149 result := getSearchText;
150
151 if getGlobalSearchFlag
152 AND (result = '')
153 then
154 result := getFileNamesRaw;
[65]155
156 if not getGlobalSearchFlag
157 AND (not getSearchFlag)
158 then
159 begin
160 result := StrTrim(result);
161 result := StrTrimChars(result, ['"']);
162 end;
163
[54]164 end;
165
166
[67]167 Function TCmdLineParameters.setHelpManagerFlag(aNewValue : boolean) : boolean;
[25]168 begin
169 helpManagerFlag := aNewValue;
170 result := helpManagerFlag;
171 end;
172
173
[67]174 Procedure TCmdLineParameters.parseCmdLine(aCmdLineString : String);
[23]175 var
[67]176 tmpState : (WHITESPACE, QUOTE, SWITCH, FILENAME, TEXT);
177 tmpCurrentParsePosition : integer;
178 tmpQuoted : boolean;
[42]179 tmpCurrentChar : char;
[67]180 tmpWhitespace : String;
181 tmpQuote : String;
182 tmpSwitch : String;
[23]183 begin
[42]184 LogEvent(LogStartup, 'ParseCommandLine: "' + aCmdLineString + '"');
[25]185
[42]186 // store the original string for debugging
187 commandLine := aCmdLineString;
[23]188
[42]189 // reset the whole object
190 showUsageFlag := false;
191 searchFlag := false;
192 globalSearchFlag := false;
193 language := '';
194 helpManagerFlag := false;
195 helpManagerWindow := 0;
196 windowPositionFlag := false;
197 ownerWindow := 0;
198 windowTitle := '';
199 searchText := '';
[45]200 fileNames := '';
201 fileNamesRaw := '';
[23]202
[42]203 try
204 // start parsing
[67]205 tmpState := WHITESPACE;
206 tmpWhitespace := '';
207 tmpSwitch := '';
208 tmpQuote := '';
209 tmpQuoted := false;
210 tmpCurrentParsePosition := 1;
211 while tmpCurrentParsePosition <= length(aCmdLineString) do
[42]212 begin
[67]213 tmpCurrentChar := aCmdLineString[tmpCurrentParsePosition];
[23]214
[42]215 Case tmpCurrentChar of
216 ' ' :
[67]217 begin
218 Case tmpState of
219
220 WHITESPACE :
221 begin
222 tmpWhitespace := tmpWhitespace + tmpCurrentChar;
223 end;
224
225 QUOTE :
226 begin
227 tmpQuote := tmpQuote + tmpCurrentChar;
228 end;
229
230 SWITCH :
231 begin
232 if tmpQuoted then
[42]233 begin
[67]234 tmpSwitch := tmpSwitch + tmpCurrentChar;
235 end
236 else
[42]237 begin
[67]238 parseSwitch(tmpSwitch);
239 tmpState := WHITESPACE;
240 tmpWhitespace := tmpCurrentChar;
241 end
242 end;
243
244 FILENAME :
245 begin
246 if tmpQuoted then
[42]247 begin
[45]248 fileNames := fileNames + tmpCurrentChar;
249 fileNamesRaw := fileNamesRaw + tmpCurrentChar;
[67]250 end
251 else
[42]252 begin
[67]253 tmpState := WHITESPACE;
254 tmpWhitespace := tmpCurrentChar;
255 end
256 end;
257
258 TEXT :
259 begin
260 if tmpQuoted then
261 begin
[42]262 searchText := searchText + tmpCurrentChar;
[67]263 end
264 else
265 begin
266 tmpState := WHITESPACE;
267 tmpWhitespace := tmpCurrentChar;
268 end
[23]269 end;
[42]270 end;
[67]271 end;
[25]272
[42]273 '/', '-' :
[67]274 begin
275 Case tmpState of
276 WHITESPACE :
277 begin
278 tmpState := SWITCH;
279 tmpSwitch := '';
280 end;
281
282 QUOTE :
283 begin
284 tmpState := SWITCH;
285 tmpSwitch := '';
286 end;
287
288 SWITCH :
289 begin
290 parseSwitch(tmpSwitch);
291 tmpSwitch := '';
292 end;
293
294 FILENAME :
295 begin
296 fileNames := fileNames + tmpCurrentChar;
297 fileNamesRaw := fileNamesRaw + tmpCurrentChar;
298 end;
299
300 TEXT :
301 begin
302 searchText := searchText + tmpCurrentChar;
303 end;
304 end;
305 end;
306
307 '"' :
308 begin
309 if tmpQuoted then
[42]310 begin
[67]311 tmpQuoted := false;
[23]312 Case tmpState of
[42]313 FILENAME :
[23]314 begin
[45]315 fileNamesRaw := fileNamesRaw + tmpCurrentChar;
[23]316 end;
317 end;
[67]318 end
319 else
[42]320 begin
[23]321 Case tmpState of
[67]322 WHITESPACE :
[23]323 begin
[67]324 tmpState := QUOTE;
325 tmpQuote := tmpCurrentChar;
[23]326 end;
[42]327 FILENAME :
[23]328 begin
[45]329 fileNamesRaw := fileNamesRaw + tmpCurrentChar;
[23]330 end;
[42]331 end;
[67]332 tmpQuoted := true;
[42]333 end;
[67]334 end;
[42]335
[67]336 // any other char
[42]337 else
[67]338 begin
339 Case tmpState of
340
341 WHITESPACE :
342 begin
343 if length(fileNames) > 0 then
[23]344 begin
[67]345 tmpState := TEXT;
346 searchText := searchText + tmpWhitespace + tmpCurrentChar;
347 end
348 else
[23]349 begin
[67]350 tmpState := FILENAME;
[42]351 fileNames := fileNames + tmpCurrentChar;
[45]352 fileNamesRaw := fileNamesRaw + tmpCurrentChar;
[23]353 end;
[67]354 end;
355
356 QUOTE :
357 begin
358 if length(fileNames) > 0 then
[23]359 begin
[67]360 tmpState := TEXT;
361 searchText := searchText + tmpWhitespace + tmpCurrentChar;
362 end
363 else
364 begin
365 tmpState := FILENAME;
[45]366 fileNames := fileNames + tmpCurrentChar;
[67]367 fileNamesRaw := fileNamesRaw + tmpQuote + tmpCurrentChar;
[23]368 end;
[67]369 end;
370
371 SWITCH :
[42]372 begin
[67]373 tmpSwitch := tmpSwitch + tmpCurrentChar;
374 end;
375
376 FILENAME :
377 begin
378 fileNames := fileNames + tmpCurrentChar;
379 fileNamesRaw := fileNamesRaw + tmpCurrentChar;
380 end;
381
382 TEXT :
383 begin
[42]384 searchText := searchText + tmpCurrentChar;
[23]385 end;
[42]386 end;
387 end;
388 end;
[67]389 inc(tmpCurrentParsePosition);
[42]390 end;
[23]391
[67]392 // ok all chars are processed, but maybe we have something to do
393 Case tmpState of
394 SWITCH :
395 begin
396 parseSwitch(tmpSwitch);
397 end;
398 end;
399
400
401 // TODO remove interpreted
402
[42]403 except
404 on e:EParsingFailed do
405 begin
406 showUsageFlag := true;
407 end;
408 end;
409
410 // remove leading blanks from search text
411 searchText := StrTrim(searchText);
412
413 LogEvent(LogStartup, 'Parameters parsed');
414 LogEvent(LogStartup, ' Filename(s): "' + fileNames + '"');
415 LogEvent(LogStartup, ' Search Text: "' + searchText + '"');
416 end;
417
418
[67]419{
[42]420 FUNCTION TCmdLineParameters.ReadNextPart(const aParseString : String; const aSetOfDelimiterChars : TSetOfChars): String;
421 VAR
422 i : integer;
423 tmpChar : char;
424 BEGIN
425 result := '';
426 for i:= currentParsePosition to length(aParseString) do
427 begin
428 tmpChar := aParseString[i];
429 if tmpChar in aSetOfDelimiterChars then
430 begin
431 i := length(aParseString); // stop parsing
432 end
433 else
434 begin
435 result := result + tmpChar;
436 end;
437 end;
[23]438 END;
[67]439}
[23]440
441
[67]442 Function TCmdLineParameters.handleSwitchWithValue(const aSwitchString : String; const aSwitch : String; var aValue : String) : Boolean;
[42]443 var
444 tmpText : String;
[67]445 tmpValueStartPos : integer;
[42]446 tmpSwitchLength : integer;
447 begin
448 tmpSwitchLength := Length(aSwitch);
[67]449 tmpText := copy(aSwitchString, 1, tmpSwitchLength);
[42]450 tmpText := lowercase(tmpText);
[23]451
[42]452 if (lowercase(aSwitch) = tmpText) then
453 begin
[67]454 tmpValueStartPos := tmpSwitchLength;
455 inc(tmpValueStartPos);
456 if aSwitchString[tmpValueStartPos] = ':' then
[42]457 begin
[67]458 inc(tmpValueStartPos);
[42]459 end;
[23]460
[67]461 aValue := copy(aSwitchString, tmpValueStartPos, Length(aSwitchString) - tmpValueStartPos+ 1);
[42]462 result := true;
463 exit;
464 end;
465 result := false;
466 end;
[23]467
468
[42]469 Function ParseWindowPositionPart(const aPart: String; const aScreenDimension: longint): longint;
470 Var
471 tmpPart : String;
472 Begin
473 if aPart = '' then
474 raise EParsingFailed.Create('Missing position element');
[23]475
[42]476 if StrEndsWithIgnoringCase(aPart, 'P') then
477 begin
478 tmpPart := copy(aPart, 1, length(aPart)-1);
479 if tmpPart = '' then
480 raise EParsingFailed.Create('Missing position element');
[23]481
[42]482 Result := StrToInt(tmpPart);
483 if Result < 0 then
484 Result := 0;
485 if Result > 100 then
486 Result := 100;
487 Result := Round(Result / 100 * aScreenDimension);
488 end
489 else
490 begin
491 Result := StrToInt(aPart);
492 end;
493 end;
[23]494
[42]495 Function ParseWindowPosition(const aParamValue: String): TWindowPosition;
496 Var
497 tmpParts : TStringList;
498 Begin
499 tmpParts := TStringList.Create;
500 StrExtractStrings(tmpParts, aParamValue, [','], '\');
[23]501
[42]502 result.Left := ParseWindowPositionPart(tmpParts[0], WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN));
503 result.Bottom := ParseWindowPositionPart(tmpParts[1], WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN));
[23]504
[42]505 result.Width := ParseWindowPositionPart(tmpParts[2], WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN));
506 if result.Width < 50 then
507 result.Width := 50;
[23]508
[42]509 result.Height := ParseWindowPositionPart(tmpParts[3], WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN));
510 if result.Height < 50 then
511 result.Height := 50;
512
513 tmpParts.Destroy;
[23]514 end;
515
516
[67]517 Procedure TCmdLineParameters.parseSwitch(aSwitchString : String);
[42]518 var
519 tmpCurrentChar : char;
520 tmpText : String;
521 tmpValue : String;
522 begin
523 // lang
[67]524 if handleSwitchWithValue(aSwitchString, 'lang', tmpValue) then
[42]525 begin
526 language := tmpValue;
527 exit;
528 end;
529
530 // title
[67]531 if handleSwitchWithValue(aSwitchString, 'title', tmpValue) then
[42]532 begin
533 windowTitle := tmpValue;
534 exit;
535 end;
536
537 // HM
[67]538 if handleSwitchWithValue(aSwitchString, 'hm', tmpValue) then
[42]539 begin
540 try
541 helpManagerWindow := StrToInt(tmpValue);
542 helpManagerFlag := true;
543 except
544 on e:Exception do
545 begin
546 showUsageFlag := true;
547 end;
548 end;
549 exit;
550 end;
551
552 // owner
[67]553 if handleSwitchWithValue(aSwitchString, 'owner', tmpValue) then
[42]554 begin
555 try
556 ownerWindow := StrToInt(tmpValue);
557 except
558 on e:Exception do
559 begin
560 showUsageFlag := true;
561 end;
562 end;
563 exit;
564 end;
565
566 // pos
[67]567 if handleSwitchWithValue(aSwitchString, 'pos', tmpValue) then
[42]568 begin
569 windowPosition := ParseWindowPosition(tmpValue);
570 windowPositionFlag := true;
571 exit;
572 end;
573
574 // check the next char
[67]575 // TODO check for other invalid chars
576 tmpCurrentChar := aSwitchString[1];
[42]577 Case tmpCurrentChar of
578 'h', 'H', '?' :
579 begin
580 showUsageFlag := true;
581
582 // check for 'help'
[67]583// tmpText := copy(aCmdLineString, 2, 3);
584// tmpText := lowercase(tmpText);
[42]585
[67]586// if ('elp' = tmpText) then
587// begin
588// end;
[42]589 end;
590
591 's', 'S' :
592 begin
593 searchFlag := true;
594 end;
595
596 'g', 'G' :
597 begin
598 globalSearchFlag := true;
599 end;
600
601 else
602 begin
603 raise EParsingFailed.Create('Unsupported switch');
604 end;
605 end;
[23]606 end;
607
[25]608
[54]609 FUNCTION getOwnHelpFileName: String;
610 begin
611 result := FindDefaultLanguageHelpFile('NewView');
612 end;
613
614
[42]615 FUNCTION nativeOS2GetCmdLineParameter : STRING;
616 VAR
617 tmpPtib : PTIB; // thread information block
618 tmpPpib : PPIB; // process information block
619 tmpCmd : PCHAR;
620 tmpResult : PCHAR;
621
622 BEGIN
623 // ask the system
624 DosGetInfoBlocks(tmpPtib, tmpPpib);
625 tmpCmd := tmpPpib^.pib_pchcmd;
626 // the fist element (null terminated) is the
627 // called command itself
628 // skip to the next null terminated string
629 // these are the parameters
630 tmpResult := tmpCmd + StrLen(tmpCmd) + 1;
631 result := StrPas(tmpResult);
632 END;
[23]633END.
Note: See TracBrowser for help on using the repository browser.