| Description | uses | Classes, Interfaces, Objects and Records | Functions and Procedures | Types | Constants | Variables |
Parsing command-line arguments.
Terminology:
Command-line parameters are given directly by the OS to our program. Implementation of this unit obtains them from Parameters.
Options are encoded by the user as parameters. Examples:
Command-line
view3dscene --navigation Walk
passes two parameters (--navigation and Walk) for view3dscene, and these two parameters form one option: --navigation=Walk.
Command-line
view3dscene -hv
passes one parameter (-hv) for view3dscene, and inside this parameter two options are encoded: -h and -v.
Pewne zalety uzywania w ogole funkcji do parsowania parametrow (w rodzaju getopts czy mojego ParseParameters) ponad robieniem parsowania argumentow za kazdym razem recznie w programie:
Trudniej sie pomylic, nie trzeba juz pisac tych wszystkich Parameters.Delete(i, 1), ParDeleteAndGetNext(i), Inc(i).
Uwzglednianie takich rzeczy jak specjalny parametr '--' jest juz automatyczne i nie powoduje komplikacji w kodzie programu.
Automatycznie zglaszamy bledy "zla opcja", np. teraz
view3dscene --whatever scena.wrl
spowoduje blad "invalid long option --whatever" podczas gdy wczesniej powodowal blad "expected one file name as parameter", bo wczesniej view3dscene widzial "--whatever" po prostu jako kolejna nazwe pliku i wydawalo mu sie ze dostaje w parametrach dwie nazwy pliku.
Oczywiscie teraz moze sie wydawac to niedogodnoscia, bo np. kiedys jesli miales plik o nazwie "--scene-wrl" (a wiec plik ktorego nazwa zaczyna sie od "-" i wyglada jak opcja) to mogles go podac view3dscene i view3dscene nie uzna tego za opcje, tzn.
view3dscene --scene-wrl
zadziala ok. Natomiast teraz
view3dscene --scene-wrl
spowoduje blad "wrong long option –scene-wrl" i musisz zrobic tak jak w standardowych UNIXowych programach:
view3dscene -- --scene-wrl
Na dluzsza mete obecne rozwiazanie jest jednak duzo bardziej eleganckie: parametr "--" powoduje ze w 1-znaczny sposob wszystko co jest za nim nie jest traktowane jako opcja, wiec np. jesli masz plik o nazwie "--camera-radius", tzn. jego nazwa nie tylko wyglada na opcje ale tez _jest_ rzeczywista opcja view3dscene to wczesniej nie bylo zupelnie zadnego sposobu zeby przekazac ten plik jako parametr bo view3dscene zawsze widzial --camera-radius jako opcje. Teraz natomiast
view3dscene -- --camera-radius
dziala jak nalezy. To sa moze drobne subtelnosci (w koncu rzadko spotyka sie pliki o nazwach zaczynajacych sie na "-", ktore na dodatek sa rzeczywistymi opcjami programu) ale sprawiaja one ze na pierwszy rzut oka widac ze uzyta semantyka parametrow jest pelna i pozwala wyrazic doslownie wszystko co kiedykolwiek ktos moglby chciec wyrazic.
Kombinowanie krotkich opcji (-abc) tez jest robione tutaj automatycznie, nie odbywa sie kosztem komplikacji kodu programu.
Nazewnictwo: wszedzie w tym module, w komentarzach i w identyfikatorach (typow, nazw funkcji, stalych... wszystkiego) staram sie konsekwentnie uzywac okreslenia "parametr" na parametr podany w linii polecen przez usera (a dokladniej rzecz biorac, to sa stringi ktore zostaly nam przekazane przez program ktory nas wywolal) oraz "opcja" na jakas regule wedlug ktorej zamierzamy parsowac (czyli po prostu interpretowac, wszelkie dokladniejsze definicje parsera nie maja tu racji bytu) parametry. Np. wywolanie
view3dscene --camera-radius 0.5 plik.3ds
ma 4 parametry : parametr numer 0 to "view3dscene" (chociaz niekoniecznie, to zalezy od OSa, np. Windows wymusza postac parametru numer 0), potem "--camera-radius", potem "0.5", potem "plik.3ds". Jezeli teraz zasam regule "jest jedna opcja, --camera-radius, i ma ona jeden argument" to mozemy teraz zinterpretowac parametry i otrzymujemy wynik: napotkana jedna opcja --camera-radius, jej argument to 0.5, a poza tym zostaja dwa parametry ktore nie zostaly wlaczone do zadnej opcji :
view3dscene plik.3ds
Zadaniem interfejsu tego modulu jest przede wszystkim zdefiniowac scisle "czym jest opcja" oraz "jaki jest zwiazek miedzy parametrami a opcjami". Zadaniem implementacji jest przeprowadzic odpowiednia konwersje : wez parametry, wez opcje, wyrzuc wynik (sparsowane paczki "opcja+zwiazane z nia argumenty") usuwajac zinterpretowane parametry z listy parametrow. Takie nazewnictwo jest konsekwentne z getopts i z Pascalowymi ParamStr/Count.
| Name | Description |
|---|---|
Class EInvalidShortOption |
Niniejsza funkcja stanowi odpowiednik znanych funkcji z rodziny getopts z libc, modulu GetOpts z FPC i innych. |
Class EInvalidLongOption |
|
Class EWrongOptionArgument |
|
Class EExcessiveOptionArgument |
|
Class EMissingOptionArgument |
|
record TOption |
|
Class TDynArray_2 |
|
record TParsedOption |
Jeszcze inna wersja ParseParameters, o nieco innym interfejsie |
Class TDynArray_1 |
procedure ParseParameters( Options: POption_Array; OptionsCount: Integer; OptionProc: TOptionProc; OptionProcData: Pointer; ParseOnlyKnownLongOptions: boolean =false ); overload; |
procedure ParseParameters( const Options: array of TOption; OptionProc: TOptionProc; OptionProcData: Pointer; ParseOnlyKnownLongOptions: boolean =false ); overload; |
procedure ParseParameters( Options: TDynOptionArray; OptionProc: TOptionProc; OptionProcData: Pointer; ParseOnlyKnownLongOptions: boolean =false ); overload; |
function ParseParameters( const Options: array of TOption; ParseOnlyKnownLongOptions: boolean =false ) : TDynParsedOptionArray; overload; |
function ParseParameters( Options: POption_Array; OptionsCount: Integer; ParseOnlyKnownLongOptions: boolean =false ) : TDynParsedOptionArray; overload; |
function OptionSeparateArgumentToCount(const v: TOptionSeparateArgument): Integer; |
function SeparateArgsToVector3Single(const v: TSeparateArgs): TVector3Single; |
oaRequiredSeparateFirst = oaRequired2Separate; |
oaRequiredSeparateLast = oaRequired9Separate; |
RequiredSeparateFirstCount = 2; |
RequiredSeparateLastCount = RequiredSeparateFirstCount
+ Ord(oaRequiredSeparateLast) - Ord(oaRequiredSeparateFirst); |
OptionArgumentsRequiredSeparate: TOptionArguments =
[oaRequiredSeparateFirst .. oaRequiredSeparateLast]; |
EmptySeparateArgs: TSeparateArgs = ('','','', '','','', '','',''); |
OnlyHelpOptions: array[0..1]of TOption = (
(Short: 'h'; Long: 'help'; Argument: oaNone),
(Short: #0; Long: ''; Argument: oaNone)
); |
HelpOptionHelp =
' -h / --help Print this help message and exit'; |
VersionOptionHelp =
' -v / --version Print the version number and exit'; |
procedure ParseParameters( Options: POption_Array; OptionsCount: Integer; OptionProc: TOptionProc; OptionProcData: Pointer; ParseOnlyKnownLongOptions: boolean =false ); overload; |
procedure ParseParameters( const Options: array of TOption; OptionProc: TOptionProc; OptionProcData: Pointer; ParseOnlyKnownLongOptions: boolean =false ); overload; |
procedure ParseParameters( Options: TDynOptionArray; OptionProc: TOptionProc; OptionProcData: Pointer; ParseOnlyKnownLongOptions: boolean =false ); overload; |
function ParseParameters( const Options: array of TOption; ParseOnlyKnownLongOptions: boolean =false ) : TDynParsedOptionArray; overload; |
function ParseParameters( Options: POption_Array; OptionsCount: Integer; ParseOnlyKnownLongOptions: boolean =false ) : TDynParsedOptionArray; overload; |
function OptionSeparateArgumentToCount(const v: TOptionSeparateArgument): Integer; |
|
some simple helper utilities ———————————————- |
function SeparateArgsToVector3Single(const v: TSeparateArgs): TVector3Single; |
TOptionArgument = (...); |
Values
|
TOptionArguments = set of TOptionArgument; |
TOptionSeparateArgument = oaRequiredSeparateFirst .. oaRequiredSeparateLast; |
TSeparateArgs = array[1..RequiredSeparateLastCount]of string; |
TOptionProc = procedure (OptionNum: Integer; HasArgument: boolean; const Argument: string; const SeparateArgs: TSeparateArgs; Data: Pointer); |
|
Gdy ta funkcja bedzie wywolywana z funkcji ParseParameters to znaczenie parametrow bedzie nastepujace : OptionNum = bedzie numer opcji z tablicy Options (zero-based). HasArgument = false, jesli Options[ParamNum].Argument in [oaNone, oaRequired?Separate] lub (oaOptional i nie podano argumentu). true wpp. (a wiec gdy Options[ParamNum].Argument = oaRequired lub (oaOptional i podano argument). Argument = jesli HasArgument to jest to argument podany przy opcji, wpp. '' (notka: zwroc uwage ze nic nie przeszkadza userowi podac argument = ''. Dlatego wlasnie potrzebny jest parametr HasArgument zeby opcje oaOptional mogly rozpoznac czy user nie podal argumentu (HasArgument=false) czy tez podal argument rowny '' (HasArgument = true, Argument = ''). SeparateArgs = jezeli Options[ParamNum].Argument in oaRequired?Separate to odpowiednia ilosc poczatkowych SeparateArgs bedzie usatwiona na poczatkowe argumenty, np. dla oaRequired2Separate powinienes odczytac argumenty z SeparateArgs[1] i SeparateArgs[2]. Wszystkie pozostale SeparateArgs (w powyzszym przykladzie, SeparateArgs[3] .. SeparateArgs[High(SeparateArgs)]) beda rowne ''. Jesli Options[ParamNum].Argument nie byl rowny oaRequired?Separate to wszystkie SeparateArgs[] beda rowne '' (czyli SeparateArgs = EmptySeparateArgs). Data = OptionProcData podane do ParseParameters |
POption = ˆTOption; |
TDynArrayItem_2 = TOption; |
PDynArrayItem_2 = POption; |
TInfiniteArray_2 = array[0..MaxInt div SizeOf(TDynArrayItem_2)-1]of TDynArrayItem_2; |
PInfiniteArray_2 = ˆTInfiniteArray_2; |
TDynArrayItemIsSmallerFunc_2 = function (const a, b: TDynArrayItem_2): boolean; |
TDynArrayItemIsSmallerFuncByObject_2 = function (const a, b: TDynArrayItem_2): boolean of object; |
TDynOptionArray = TDynArray_2; |
TOption_Array = TInfiniteArray_2; |
POption_Array = PInfiniteArray_2; |
PParsedOption = ˆTParsedOption; |
TDynArrayItem_1 = TParsedOption; |
PDynArrayItem_1 = PParsedOption; |
TInfiniteArray_1 = array[0..MaxInt div SizeOf(TDynArrayItem_1)-1]of TDynArrayItem_1; |
PInfiniteArray_1 = ˆTInfiniteArray_1; |
TDynArrayItemIsSmallerFunc_1 = function (const a, b: TDynArrayItem_1): boolean; |
TDynArrayItemIsSmallerFuncByObject_1 = function (const a, b: TDynArrayItem_1): boolean of object; |
TDynParsedOptionArray = TDynArray_1; |
oaRequiredSeparateFirst = oaRequired2Separate; |
oaRequiredSeparateLast = oaRequired9Separate; |
RequiredSeparateFirstCount = 2; |
RequiredSeparateLastCount = RequiredSeparateFirstCount
+ Ord(oaRequiredSeparateLast) - Ord(oaRequiredSeparateFirst); |
OptionArgumentsRequiredSeparate: TOptionArguments =
[oaRequiredSeparateFirst .. oaRequiredSeparateLast]; |
EmptySeparateArgs: TSeparateArgs = ('','','', '','','', '','',''); |
OnlyHelpOptions: array[0..1]of TOption = (
(Short: 'h'; Long: 'help'; Argument: oaNone),
(Short: #0; Long: ''; Argument: oaNone)
); |
VersionOptionHelp =
' -v / --version Print the version number and exit'; |