strutils.pp 98 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537
  1. {
  2. Delphi/Kylix compatibility unit: String handling routines.
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2005 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$h+}
  13. {$inline on}
  14. {$modeswitch advancedrecords}
  15. {$IFNDEF FPC_DOTTEDUNITS}
  16. unit StrUtils;
  17. {$ENDIF FPC_DOTTEDUNITS}
  18. interface
  19. {$IFDEF FPC_DOTTEDUNITS}
  20. uses
  21. System.SysUtils, System.Types;
  22. {$ELSE FPC_DOTTEDUNITS}
  23. uses
  24. SysUtils, Types;
  25. {$ENDIF FPC_DOTTEDUNITS}
  26. { ---------------------------------------------------------------------
  27. Case insensitive search/replace
  28. ---------------------------------------------------------------------}
  29. Function AnsiResemblesText(const AText, AOther: AnsiString): Boolean;
  30. Function AnsiContainsText(const AText, ASubText: AnsiString): Boolean;
  31. Function AnsiStartsText(const ASubText, AText: AnsiString): Boolean;
  32. Function AnsiEndsText(const ASubText, AText: AnsiString): Boolean;
  33. function AnsiEndsText(const ASubText, AText: UnicodeString): Boolean;
  34. Function AnsiReplaceText(const AText, AFromText, AToText: AnsiString): AnsiString;inline;
  35. Function AnsiMatchText(const AText: AnsiString; const AValues: array of AnsiString): Boolean;inline;
  36. Function AnsiIndexText(const AText: AnsiString; const AValues: array of AnsiString): Integer;
  37. Function StartsText(const ASubText, AText: string): Boolean; inline;
  38. Function EndsText(const ASubText, AText: string): Boolean; inline;
  39. function ResemblesText(const AText, AOther: string): Boolean; inline;
  40. function ContainsText(const AText, ASubText: string): Boolean; inline;
  41. function MatchText(const AText: Ansistring; const AValues: array of Ansistring): Boolean; inline;
  42. function IndexText(const AText: Ansistring; const AValues: array of Ansistring): Integer; inline;
  43. { ---------------------------------------------------------------------
  44. Case sensitive search/replace
  45. ---------------------------------------------------------------------}
  46. Function AnsiContainsStr(const AText, ASubText: AnsiString): Boolean;inline;
  47. function AnsiContainsStr(const AText, ASubText: Unicodestring): Boolean; inline;
  48. Function AnsiStartsStr(const ASubText, AText: AnsiString): Boolean;
  49. Function AnsiStartsStr(const ASubText, AText: UnicodeString): Boolean;
  50. Function AnsiEndsStr(const ASubText, AText: AnsiString): Boolean;
  51. Function AnsiEndsStr(const ASubText, AText: UnicodeString): Boolean;
  52. Function AnsiReplaceStr(const AText, AFromText, AToText: AnsiString): AnsiString;inline;
  53. Function AnsiMatchStr(const AText: AnsiString; const AValues: array of AnsiString): Boolean;inline;
  54. Function AnsiIndexStr(const AText: Ansistring; const AValues: array of Ansistring): Integer;
  55. Function StartsStr(const ASubText, AText: string): Boolean;
  56. Function EndsStr(const ASubText, AText: string): Boolean;
  57. Function MatchStr(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  58. Function MatchText(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  59. Function IndexStr(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  60. Function IndexText(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  61. Operator in (const AText: Ansistring; const AValues: array of Ansistring):Boolean;inline;
  62. Operator in (const AText: UnicodeString; const AValues: array of UnicodeString):Boolean;inline;
  63. function ContainsStr(const AText, ASubText: string): Boolean; inline;
  64. function MatchStr(const AText: Ansistring; const AValues: array of Ansistring): Boolean; inline;
  65. function IndexStr(const AText: Ansistring; const AValues: array of Ansistring): Integer; inline;
  66. { ---------------------------------------------------------------------
  67. Miscellaneous
  68. ---------------------------------------------------------------------}
  69. Function DupeString(const AText: string; ACount: Integer): string;
  70. Function ReverseString(const AText: string): string;
  71. Function AnsiReverseString(const AText: AnsiString): AnsiString;inline;
  72. Function StuffString(const AText: string; AStart, ALength: Cardinal; const ASubText: string): string;
  73. Function RandomFrom(const AValues: array of string): string; overload;
  74. Function IfThen(AValue: Boolean; const ATrue: string; const AFalse: string = ''): string; overload;
  75. Function IfThen(AValue: Boolean; const ATrue: TStringDynArray; const AFalse: TStringDynArray = nil): TStringDynArray; overload;
  76. function NaturalCompareText (const S1 , S2 : string ): Integer ;
  77. function NaturalCompareText(const Str1, Str2: string; const ADecSeparator, AThousandSeparator: AnsiChar): Integer;
  78. function SplitString(const S, Delimiters: string): TRTLStringDynArray;
  79. { ---------------------------------------------------------------------
  80. VB emulations.
  81. ---------------------------------------------------------------------}
  82. Function LeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;inline;
  83. Function RightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  84. Function MidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;inline;
  85. Function RightBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;inline;
  86. Function MidBStr(const AText: AnsiString; const AByteStart, AByteCount: SizeInt): AnsiString;inline;
  87. Function AnsiLeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;inline;
  88. Function AnsiRightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;inline;
  89. Function AnsiMidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;inline;
  90. Function LeftBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;inline;
  91. Function LeftStr(const AText: WideString; const ACount: SizeInt): WideString;inline;
  92. Function RightStr(const AText: WideString; const ACount: SizeInt): WideString;
  93. Function MidStr(const AText: WideString; const AStart, ACount: SizeInt): WideString;inline;
  94. { ---------------------------------------------------------------------
  95. Extended search and replace
  96. ---------------------------------------------------------------------}
  97. const
  98. { Default word delimiters are any character except the core alphanumerics. }
  99. WordDelimiters: set of AnsiChar = [#0..#255] - ['a'..'z','A'..'Z','1'..'9','0'];
  100. resourcestring
  101. SErrAmountStrings = 'Amount of search and replace strings don''t match';
  102. type
  103. TStringSearchOption = (soDown, soMatchCase, soWholeWord);
  104. TStringSearchOptions = set of TStringSearchOption;
  105. TStringSeachOption = TStringSearchOption;
  106. Function SearchBuf(Buf: PAnsiChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String; Options: TStringSearchOptions): PAnsiChar;
  107. Function SearchBuf(Buf: PAnsiChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String): PAnsiChar;inline; // ; Options: TStringSearchOptions = [soDown]
  108. Function PosEx(const SubStr, S: Ansistring; Offset: SizeInt): SizeInt;inline;
  109. Function PosEx(const SubStr, S: Ansistring): SizeInt;inline;
  110. Function PosEx(c:AnsiChar; const S: AnsiString; Offset: SizeInt): SizeInt;inline;
  111. Function PosEx(const SubStr, S: UnicodeString; Offset: SizeInt): SizeInt;inline;
  112. Function PosEx(c: WideChar; const S: UnicodeString; Offset: SizeInt): SizeInt;inline;
  113. Function PosEx(const SubStr, S: UnicodeString): Sizeint;inline;
  114. function StringsReplace(const S: Ansistring; OldPattern, NewPattern: array of Ansistring; Flags: TReplaceFlags): string;
  115. { ---------------------------------------------------------------------
  116. Delphi compat
  117. ---------------------------------------------------------------------}
  118. Function ReplaceStr(const AText, AFromText, AToText: string): string;inline;
  119. Function ReplaceText(const AText, AFromText, AToText: string): string;inline;
  120. { ---------------------------------------------------------------------
  121. Soundex Functions.
  122. ---------------------------------------------------------------------}
  123. type
  124. TSoundexLength = 1..MaxInt;
  125. Function Soundex(const AText: string; ALength: TSoundexLength): string;
  126. Function Soundex(const AText: string): string;inline; // ; ALength: TSoundexLength = 4
  127. type
  128. TSoundexIntLength = 1..8;
  129. Function SoundexInt(const AText: string; ALength: TSoundexIntLength): Integer;
  130. Function SoundexInt(const AText: string): Integer;inline; //; ALength: TSoundexIntLength = 4
  131. Function DecodeSoundexInt(AValue: Integer): string;
  132. Function SoundexWord(const AText: string): Word;
  133. Function DecodeSoundexWord(AValue: Word): string;
  134. Function SoundexSimilar(const AText, AOther: string; ALength: TSoundexLength): Boolean;inline;
  135. Function SoundexSimilar(const AText, AOther: string): Boolean;inline; //; ALength: TSoundexLength = 4
  136. Function SoundexCompare(const AText, AOther: string; ALength: TSoundexLength): Integer;inline;
  137. Function SoundexCompare(const AText, AOther: string): Integer;inline; //; ALength: TSoundexLength = 4
  138. Function SoundexProc(const AText, AOther: string): Boolean;
  139. type
  140. TCompareTextProc = Function(const AText, AOther: string): Boolean;
  141. Const
  142. AnsiResemblesProc: TCompareTextProc = @SoundexProc;
  143. ResemblesProc: TCompareTextProc = @SoundexProc;
  144. { ---------------------------------------------------------------------
  145. Other functions, based on RxStrUtils.
  146. ---------------------------------------------------------------------}
  147. type
  148. TRomanConversionStrictness = (rcsStrict, rcsRelaxed, rcsDontCare);
  149. resourcestring
  150. SInvalidRomanNumeral = '%s is not a valid Roman numeral';
  151. function IsEmptyStr(const S: string; const EmptyChars: TSysCharSet): Boolean;
  152. function DelSpace(const S: string): string;
  153. function DelChars(const S: string; Chr: Char): string;
  154. function DelChars(const S: string; Chars: TSysCharSet): string;
  155. function DelSpace1(const S: string): string;
  156. function Tab2Space(const S: string; Numb: Byte): string;
  157. function NPos(const C: string; const S: string; N: Integer): SizeInt;
  158. Function RPosEx(C:AnsiChar;const S : AnsiString;offs:SizeInt):SizeInt; overload;
  159. Function RPosEx(C:Unicodechar;const S : UnicodeString;offs:SizeInt):SizeInt; overload;
  160. Function RPosEx(Const Substr : AnsiString; Const Source : AnsiString;offs:SizeInt) : SizeInt; overload;
  161. Function RPosEx(Const Substr : UnicodeString; Const Source : UnicodeString;offs:SizeInt) : SizeInt; overload;
  162. Function RPos(c:AnsiChar;const S : AnsiString):SizeInt; overload; inline;
  163. Function RPos(c:Unicodechar;const S : UnicodeString):SizeInt; overload; inline;
  164. Function RPos(Const Substr : AnsiString; Const Source : AnsiString) : SizeInt; overload; inline;
  165. Function RPos(Const Substr : UnicodeString; Const Source : UnicodeString) : SizeInt; overload; inline;
  166. function AddChar(C: AnsiChar; const S: string; N: Integer): string;
  167. function AddCharR(C: AnsiChar; const S: string; N: Integer): string;
  168. function PadLeft(const S: string; N: Integer): string;inline;
  169. function PadRight(const S: string; N: Integer): string;inline;
  170. function PadCenter(const S: string; Len: SizeInt): string;
  171. function Copy2Symb(const S: string; Symb: AnsiChar): string;
  172. function Copy2SymbDel(var S: string; Symb: AnsiChar): string;
  173. function Copy2Space(const S: string): string;inline;
  174. function Copy2SpaceDel(var S: string): string;inline;
  175. function AnsiProperCase(const S: string; const WordDelims: TSysCharSet): string;
  176. function WordCount(const S: string; const WordDelims: TSysCharSet): SizeInt;
  177. function WordPosition(const N: Integer; const S: string; const WordDelims: TSysCharSet): SizeInt;
  178. function ExtractWord(N: Integer; const S: string; const WordDelims: TSysCharSet): string;inline;
  179. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  180. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; out Pos: SizeInt): string;
  181. {$ENDIF}
  182. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; out Pos: Integer): string;
  183. function ExtractDelimited(N: Integer; const S: string; const Delims: TSysCharSet): string;
  184. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  185. function ExtractSubstr(const S: string; var Pos: SizeInt; const Delims: TSysCharSet): string;
  186. {$ENDIF}
  187. function ExtractSubstr(const S: string; var Pos: Integer; const Delims: TSysCharSet): string;
  188. function IsWordPresent(const W, S: string; const WordDelims: TSysCharSet): Boolean;
  189. function FindPart(const HelpWilds, InputStr: string): SizeInt;
  190. function IsWild(InputStr, Wilds: string; IgnoreCase: Boolean): Boolean;
  191. function XorString(const Key, Src: ShortString): ShortString;
  192. function XorEncode(const Key, Source: Ansistring): Ansistring;
  193. function XorDecode(const Key, Source: Ansistring): Ansistring;
  194. function GetCmdLineArg(const Switch: string; SwitchChars: TSysCharSet): string;
  195. function Numb2USA(const S: string): string;
  196. function Hex2Dec(const S: string): Longint;
  197. function Hex2Dec64(const S: string): int64;
  198. function Dec2Numb(N: Longint; Len, Base: Byte): string;
  199. function Numb2Dec(S: string; Base: Byte): Longint;
  200. function IntToBin(Value: Longint; Digits, Spaces: Integer): string;
  201. function IntToBin(Value: Longint; Digits: Integer): string;
  202. function IntToBin(Value: int64; Digits:integer): string;
  203. function IntToRoman(Value: Longint): string;
  204. function TryRomanToInt(S: String; out N: LongInt; Strictness: TRomanConversionStrictness = rcsRelaxed): Boolean;
  205. function RomanToInt(const S: string; Strictness: TRomanConversionStrictness = rcsRelaxed): Longint;
  206. function RomanToIntDef(Const S : String; const ADefault: Longint = 0; Strictness: TRomanConversionStrictness = rcsRelaxed): Longint;
  207. procedure BinToHex(const BinBuffer: TBytes; BinBufOffset: Integer; var HexBuffer: TBytes; HexBufOffset: Integer; Count: Integer); overload;
  208. procedure BinToHex(BinValue: Pointer; HexValue: PWideChar; BinBufSize: Integer); overload;
  209. procedure BinToHex(const BinValue; HexValue: PWideChar; BinBufSize: Integer); overload;
  210. procedure BinToHex(BinValue: PAnsiChar; HexValue: PAnsiChar; BinBufSize: Integer); overload;
  211. procedure BinToHex(BinValue: PAnsiChar; HexValue: PWideChar; BinBufSize: Integer); overload;
  212. procedure BinToHex(const BinValue; HexValue: PAnsiChar; BinBufSize: Integer); overload;
  213. procedure BinToHex(BinValue: Pointer; HexValue: PAnsiChar; BinBufSize: Integer); overload;
  214. function HexToBin(HexText: PAnsiChar; BinBuffer: PAnsiChar; BinBufSize: Integer): Integer; overload;
  215. function HexToBin(const HexText: PWideChar; HexTextOffset: Integer; var BinBuffer: TBytes; BinBufOffset: Integer; Count: Integer): Integer; overload;
  216. function HexToBin(const HexText: TBytes; HexTextOffset: Integer; var BinBuffer: TBytes; BinBufOffset: Integer; Count: Integer): Integer; overload;
  217. function HexToBin(HexText: PWideChar; BinBuffer: Pointer; BinBufSize: Integer): Integer; overload;
  218. function HexToBin(const HexText: PWideChar; var BinBuffer; BinBufSize: Integer): Integer; overload;
  219. function HexToBin(HexText: PWideChar; BinBuffer: PAnsiChar; BinBufSize: Integer): Integer; overload;
  220. function HexToBin(HexText: PAnsiChar; var BinBuffer; BinBufSize: Integer): Integer; overload;
  221. function HexToBin(const HexText: PAnsiChar; BinBuffer: Pointer; BinBufSize: Integer): Integer; overload;
  222. const
  223. DigitChars = ['0'..'9'];
  224. Brackets = ['(',')','[',']','{','}'];
  225. StdWordDelims = [#0..' ',',','.',';','/','\',':','''','"','`'] + Brackets;
  226. StdSwitchChars = ['-','/'];
  227. function PosSet (const c:TSysCharSet;const s : ansistring ):SizeInt;
  228. function PosSet (const c:string;const s : ansistring ):SizeInt;
  229. function PosSetEx (const c:TSysCharSet;const s : ansistring;count:Integer ):SizeInt;
  230. function PosSetEx (const c:string;const s : ansistring;count:Integer ):SizeInt;
  231. Procedure RemoveLeadingChars(VAR S : AnsiString; Const CSet:TSysCharset);
  232. Procedure RemoveTrailingChars(VAR S : AnsiString;Const CSet:TSysCharset);
  233. Procedure RemoveLeadingChars(VAR S : UnicodeString; Const CSet:TSysCharset);
  234. Procedure RemoveTrailingChars(VAR S : UnicodeString;Const CSet:TSysCharset);
  235. Procedure RemovePadChars(VAR S : AnsiString;Const CSet:TSysCharset);
  236. procedure RemovePadChars(VAR S: UnicodeString; const CSet: TSysCharset);
  237. function TrimLeftSet(const S: String;const CSet:TSysCharSet): String;
  238. Function TrimRightSet(const S: String;const CSet:TSysCharSet): String;
  239. function TrimSet(const S: String;const CSet:TSysCharSet): String;
  240. type
  241. SizeIntArray = array of SizeInt;
  242. Function FindMatchesBoyerMooreCaseSensitive(const S,OldPattern: PAnsiChar; const SSize, OldPatternSize: SizeInt; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  243. Function FindMatchesBoyerMooreCaseSensitive(const S,OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  244. Function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: PAnsiChar; const SSize, OldPatternSize: SizeInt; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  245. Function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  246. Type
  247. TStringReplaceAlgorithm = (sraDefault, // Default algoritm as used in StringUtils.
  248. sraManySmall, // Algorithm optimized for many small replacements.
  249. sraBoyerMoore // Algorithm optimized for long replacements.
  250. );
  251. Function StringReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags; out aCount : Integer; Algorithm : TStringReplaceAlgorithm = sraDefault): string; overload;
  252. Function StringReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags; Algorithm : TStringReplaceAlgorithm = sraDefault): string; overload;
  253. { We need these for backwards compatibility:
  254. The compiler will stop searching and convert to ansistring if the widestring version of stringreplace is used.
  255. They currently simply refer to sysutils, till the new mechanisms are proven to work with unicode.}
  256. {$IF SIZEOF(CHAR)=1}
  257. Function StringReplace(const S, OldPattern, NewPattern: unicodestring; Flags: TReplaceFlags): unicodestring; overload;
  258. Function StringReplace(const S, OldPattern, NewPattern: widestring; Flags: TReplaceFlags): widestring; overload;
  259. {$ENDIF}
  260. Type
  261. TRawByteStringArray = Array of RawByteString;
  262. TUnicodeStringArray = Array of UnicodeString;
  263. Function SplitCommandLine(S : RawByteString) : TRawByteStringArray;
  264. Function SplitCommandLine(S : UnicodeString) : TUnicodeStringArray;
  265. implementation
  266. {$IFDEF FPC_DOTTEDUNITS}
  267. uses System.SysConst; // HexDigits
  268. {$ELSE FPC_DOTTEDUNITS}
  269. uses sysconst; // HexDigits
  270. {$ENDIF FPC_DOTTEDUNITS}
  271. type
  272. // Shared between case-sensitive and case-insensitive versions.
  273. BoyerMoore = record
  274. DeltaJumpTable1: array[AnsiChar] of SizeInt;
  275. DeltaJumpTable2: SizeIntArray;
  276. MatchesCount: SizeInt; //Stores the amount of replaces that will take place
  277. MatchesAllocatedLimit: SizeInt; //Currently allocated space for matches.
  278. Matches: ^SizeIntArray;
  279. class var
  280. LCaseArray: array[AnsiChar] of AnsiChar; //Array of lowercased alphabet
  281. LCaseArrayPrepared: boolean;
  282. procedure Init(var aMatches: SizeIntArray); inline;
  283. procedure MakeDeltaJumpTables(aPattern: PAnsiChar; aPatternSize: SizeInt);
  284. procedure AddMatch(aPosition: SizeInt);
  285. class function Max(a, b: SizeInt): SizeInt; static; inline;
  286. class procedure IncrementEverything(var aMatches: array of SizeInt); static; // Make positions 1-based.
  287. class procedure PrepareLCaseArray; static;
  288. end;
  289. procedure BoyerMoore.Init(var aMatches: SizeIntArray);
  290. begin
  291. MatchesCount:=0;
  292. MatchesAllocatedLimit:=0;
  293. Matches:=@aMatches;
  294. end;
  295. procedure BoyerMoore.MakeDeltaJumpTables(aPattern: PAnsiChar; aPatternSize: SizeInt);
  296. var
  297. i, Position, LastPrefixIndex, SuffixLength: SizeInt;
  298. begin
  299. {$if sizeof(SizeInt)=sizeof(word)} FillWord
  300. {$elseif sizeof(SizeInt)=sizeof(dword)} FillDWord
  301. {$elseif sizeof(SizeInt)=sizeof(qword)} FillQWord
  302. {$else} {$error unknown SizeInt size}
  303. {$endif}
  304. (DeltaJumpTable1, Length(DeltaJumpTable1), SizeUint(aPatternSize));
  305. //Last AnsiChar do not enter in the equation
  306. for i := 0 to aPatternSize - 1 - 1 do
  307. DeltaJumpTable1[aPattern[i]]:=aPatternSize -1 - i;
  308. SetLength(DeltaJumpTable2, aPatternSize);
  309. LastPrefixIndex:=aPatternSize-1;
  310. Position:=aPatternSize-1;
  311. while Position>=0 do begin
  312. if CompareByte(aPattern^, aPattern[Position+1], (aPatternSize-Position-1)*SizeOf(aPattern[0]))=0 then
  313. LastPrefixIndex := Position+1;
  314. DeltaJumpTable2[Position] := LastPrefixIndex + (aPatternSize-1 - Position);
  315. Dec(Position);
  316. end;
  317. Position:=0;
  318. while Position<aPatternSize-1 do begin
  319. SuffixLength:=0;
  320. while (SuffixLength<Position) and (aPattern[Position-SuffixLength] = aPattern[aPatternSize-1-SuffixLength]) do
  321. inc(SuffixLength);
  322. if aPattern[Position-SuffixLength] <> aPattern[aPatternSize-1-SuffixLength] then
  323. DeltaJumpTable2[aPatternSize - 1 - SuffixLength] := aPatternSize - 1 - Position + SuffixLength;
  324. Inc(Position);
  325. end;
  326. end;
  327. procedure BoyerMoore.AddMatch(aPosition: SizeInt);
  328. begin
  329. if MatchesCount=MatchesAllocatedLimit then begin
  330. MatchesAllocatedLimit:=MatchesAllocatedLimit+4+SizeInt(SizeUint(MatchesAllocatedLimit) div 4+SizeUint(MatchesAllocatedLimit) div 8); // const + 37.5%
  331. SetLength(Matches^,MatchesAllocatedLimit);
  332. end;
  333. Matches^[MatchesCount]:=aPosition;
  334. inc(MatchesCount);
  335. end;
  336. class function BoyerMoore.Max(a, b: SizeInt): SizeInt;
  337. begin
  338. if a>b then Result:=a else Result:=b;
  339. end;
  340. class procedure BoyerMoore.IncrementEverything(var aMatches: array of SizeInt); static;
  341. var
  342. i: SizeInt;
  343. begin
  344. for i:=0 to High(aMatches) do
  345. Inc(aMatches[i]);
  346. end;
  347. class procedure BoyerMoore.PrepareLCaseArray;
  348. var
  349. c: AnsiChar;
  350. begin
  351. for c in AnsiChar do
  352. LCaseArray[c]:=AnsiLowerCase(c)[1];
  353. WriteBarrier; // Write LCaseArrayPrepared only after LCaseArray contents.
  354. LCaseArrayPrepared:=true;
  355. end;
  356. (*
  357. FindMatchesBoyerMooreCaseSensitive
  358. Finds one or many ocurrences of an ansistring in another ansistring.
  359. It is case sensitive.
  360. * Parameters:
  361. S: The PAnsiChar to be searched in. (Read only).
  362. OldPattern: The PAnsiChar to be searched. (Read only).
  363. SSize: The size of S in Chars. (Read only).
  364. OldPatternSize: The size of OldPatter in chars. (Read only).
  365. aMatches: SizeInt array where match indexes are returned (zero based) (write only).
  366. aMatchAll: Finds all matches, not just the first one. (Read only).
  367. * Returns:
  368. True if at least one occurence was found.
  369. The function is based in the Boyer-Moore algorithm.
  370. *)
  371. function FindMatchesBoyerMooreCaseSensitive(const S, OldPattern: PAnsiChar;
  372. const SSize, OldPatternSize: SizeInt; out aMatches: SizeIntArray;
  373. const aMatchAll: Boolean) : Boolean;
  374. var
  375. i,j: SizeInt;
  376. bm: BoyerMoore;
  377. begin
  378. aMatches:=nil;
  379. if OldPatternSize=0 then
  380. Exit(False);
  381. bm.Init(aMatches);
  382. bm.MakeDeltaJumpTables(OldPattern,OldPatternSize);
  383. i:=OldPatternSize-1;
  384. while i < SSize do begin
  385. j:=OldPatternSize-1;
  386. while (j>=0) and (S[i] = OldPattern[j]) do begin
  387. dec(i);
  388. dec(j);
  389. end;
  390. if (j<0) then begin
  391. bm.AddMatch(i+1);
  392. //Only first match ?
  393. if not aMatchAll then break;
  394. inc(i,bm.DeltaJumpTable2[0]+1);
  395. end else
  396. i:=i + bm.Max(bm.DeltaJumpTable1[s[i]],bm.DeltaJumpTable2[j]);
  397. end;
  398. SetLength(aMatches,bm.MatchesCount);
  399. Result:=bm.MatchesCount>0;
  400. end;
  401. function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: PAnsiChar; const SSize, OldPatternSize: SizeInt; out
  402. aMatches: SizeIntArray; const aMatchAll: Boolean): Boolean;
  403. var
  404. i,j: SizeInt;
  405. lPattern: PAnsiChar; //Lowercased OldPattern
  406. bm: BoyerMoore;
  407. lPatternStore: ansistring;
  408. begin
  409. aMatches:=nil;
  410. if OldPatternSize=0 then
  411. Exit(False);
  412. //Build an internal array of lowercase version of every possible AnsiChar.
  413. if not bm.LCaseArrayPrepared then
  414. bm.PrepareLCaseArray;
  415. ReadBarrier; // Read LCaseArray contents only after LCaseArrayPrepared.
  416. //Create the new lowercased pattern. Or avoid and reuse OldPattern if nothing to lowercase!
  417. lPattern:=OldPattern;
  418. for i := 0 to OldPatternSize-1 do
  419. if bm.LCaseArray[OldPattern[i]]<>OldPattern[i] then begin
  420. SetLength(lPatternStore,OldPatternSize);
  421. lPattern:=PAnsiChar(Pointer(lPatternStore));
  422. Move(OldPattern^,lPattern^,i*sizeof(AnsiChar));
  423. for j := i to OldPatternSize-1 do
  424. lPattern[j]:=bm.LCaseArray[OldPattern[j]];
  425. break;
  426. end;
  427. bm.Init(aMatches);
  428. bm.MakeDeltaJumpTables(lPattern,OldPatternSize);
  429. i:=OldPatternSize-1;
  430. while i < SSize do begin
  431. j:=OldPatternSize-1;
  432. while (j>=0) and (bm.LCaseArray[S[i]] = lPattern[j]) do begin
  433. dec(i);
  434. dec(j);
  435. end;
  436. if (j<0) then begin
  437. bm.AddMatch(i+1);
  438. //Only first match ?
  439. if not aMatchAll then break;
  440. inc(i,bm.DeltaJumpTable2[0]+1);
  441. end else
  442. i:=i + bm.Max(bm.DeltaJumpTable1[bm.LCaseArray[s[i]]],bm.DeltaJumpTable2[j]);
  443. end;
  444. SetLength(aMatches,bm.MatchesCount);
  445. Result:=bm.MatchesCount>0;
  446. end;
  447. function StringReplaceFast(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags; out aCount : Integer): string;
  448. const
  449. MATCHESCOUNTRESIZER=100; //Arbitrary value. Memory used = MATCHESCOUNTRESIZER * sizeof(SizeInt)
  450. var
  451. //Stores where a replace will take place
  452. Matches: array of SizeInt;
  453. //Stores the amount of replaces that will take place
  454. MatchesCount: SizeInt;
  455. //Currently allocated space for matches.
  456. MatchesAllocatedLimit: SizeInt;
  457. //Uppercase version of pattern
  458. PatternUppercase: string;
  459. //Lowercase version of pattern
  460. PatternLowerCase: string;
  461. //Index
  462. MatchIndex: SizeInt;
  463. MatchLimit: SizeInt;
  464. MatchInternal: SizeInt;
  465. MatchTarget: SizeInt;
  466. AdvanceIndex: SizeInt;
  467. //Miscelanous variables
  468. OldPatternSize: SizeInt;
  469. NewPatternSize: SizeInt;
  470. //Resizes the allocated space for replacement index
  471. procedure ResizeAllocatedMatches;
  472. begin
  473. MatchesAllocatedLimit:=MatchesCount+MATCHESCOUNTRESIZER;
  474. SetLength(Matches,MatchesAllocatedLimit);
  475. end;
  476. //Add a match to be replaced
  477. procedure AddMatch(const aPosition: SizeInt); inline;
  478. begin
  479. if MatchesCount = MatchesAllocatedLimit then begin
  480. ResizeAllocatedMatches;
  481. end;
  482. Matches[MatchesCount]:=aPosition;
  483. inc(MatchesCount);
  484. end;
  485. begin
  486. aCount:=0;
  487. if (OldPattern='') or (Length(OldPattern)>Length(S)) then begin
  488. //This cases will never match nothing.
  489. Result:=S;
  490. exit;
  491. end;
  492. Result:='';
  493. OldPatternSize:=Length(OldPattern);
  494. MatchesCount:=0;
  495. MatchesAllocatedLimit:=0;
  496. if rfIgnoreCase in Flags then begin
  497. //Different algorithm for case sensitive and insensitive
  498. //This is insensitive, so 2 new ansistrings are created for search pattern, one upper and one lower case.
  499. //It is easy, usually, to create 2 versions of the match pattern than uppercased and lowered case each
  500. //character in the "to be matched" string.
  501. PatternUppercase:=AnsiUpperCase(OldPattern);
  502. PatternLowerCase:=AnsiLowerCase(OldPattern);
  503. MatchIndex:=Length(OldPattern);
  504. MatchLimit:=Length(S);
  505. NewPatternSize:=Length(NewPattern);
  506. while MatchIndex <= MatchLimit do begin
  507. if (S[MatchIndex]=PatternLowerCase[OldPatternSize]) or (S[MatchIndex]=PatternUppercase[OldPatternSize]) then begin
  508. //Match backwards...
  509. MatchInternal:=OldPatternSize-1;
  510. MatchTarget:=MatchIndex-1;
  511. while MatchInternal>=1 do begin
  512. if (S[MatchTarget]=PatternLowerCase[MatchInternal]) or (S[MatchTarget]=PatternUppercase[MatchInternal]) then begin
  513. dec(MatchInternal);
  514. dec(MatchTarget);
  515. end else begin
  516. break;
  517. end;
  518. end;
  519. if MatchInternal=0 then begin
  520. //Match found, all AnsiChar meet the sequence
  521. //MatchTarget points to AnsiChar before, so matching is +1
  522. AddMatch(MatchTarget+1);
  523. inc(MatchIndex,OldPatternSize);
  524. if not (rfReplaceAll in Flags) then begin
  525. break;
  526. end;
  527. end else begin
  528. //Match not found
  529. inc(MatchIndex);
  530. end;
  531. end else begin
  532. inc(MatchIndex);
  533. end;
  534. end;
  535. end else begin
  536. //Different algorithm for case sensitive and insensitive
  537. //This is sensitive, so just 1 binary comprare
  538. MatchIndex:=Length(OldPattern);
  539. MatchLimit:=Length(S);
  540. NewPatternSize:=Length(NewPattern);
  541. while MatchIndex <= MatchLimit do begin
  542. if (S[MatchIndex]=OldPattern[OldPatternSize]) then begin
  543. //Match backwards...
  544. MatchInternal:=OldPatternSize-1;
  545. MatchTarget:=MatchIndex-1;
  546. while MatchInternal>=1 do begin
  547. if (S[MatchTarget]=OldPattern[MatchInternal]) then begin
  548. dec(MatchInternal);
  549. dec(MatchTarget);
  550. end else begin
  551. break;
  552. end;
  553. end;
  554. if MatchInternal=0 then begin
  555. //Match found, all AnsiChar meet the sequence
  556. //MatchTarget points to AnsiChar before, so matching is +1
  557. AddMatch(MatchTarget+1);
  558. inc(MatchIndex,OldPatternSize);
  559. if not (rfReplaceAll in Flags) then begin
  560. break;
  561. end;
  562. end else begin
  563. //Match not found
  564. inc(MatchIndex);
  565. end;
  566. end else begin
  567. inc(MatchIndex);
  568. end;
  569. end;
  570. end;
  571. //Create room enough for the result string
  572. aCount:=MatchesCount;
  573. SetLength(Result,Length(S)-OldPatternSize*MatchesCount+NewPatternSize*MatchesCount);
  574. MatchIndex:=1;
  575. MatchTarget:=1;
  576. //Matches[x] are 1 based offsets
  577. for MatchInternal := 0 to Pred(MatchesCount) do begin
  578. //Copy information up to next match
  579. AdvanceIndex:=Matches[MatchInternal]-MatchIndex;
  580. if AdvanceIndex>0 then begin
  581. move(S[MatchIndex],Result[MatchTarget],AdvanceIndex);
  582. inc(MatchTarget,AdvanceIndex);
  583. inc(MatchIndex,AdvanceIndex);
  584. end;
  585. //Copy the new replace information string
  586. if NewPatternSize>0 then begin
  587. move(NewPattern[1],Result[MatchTarget],NewPatternSize);
  588. inc(MatchTarget,NewPatternSize);
  589. end;
  590. inc(MatchIndex,OldPatternSize);
  591. end;
  592. if MatchTarget<=Length(Result) then begin
  593. //Add remain data at the end of source.
  594. move(S[MatchIndex],Result[MatchTarget],Length(Result)-MatchTarget+1);
  595. end;
  596. end;
  597. (*
  598. StringReplaceBoyerMoore
  599. Replaces one or many ocurrences of an ansistring in another ansistring by a new one.
  600. It can perform the compare ignoring case (ansi).
  601. * Parameters (Read only):
  602. S: The string to be searched in.
  603. OldPattern: The string to be searched.
  604. NewPattern: The string to replace OldPattern matches.
  605. Flags:
  606. rfReplaceAll: Replace all occurrences.
  607. rfIgnoreCase: Ignore case in OldPattern matching.
  608. * Returns:
  609. The modified string (if needed).
  610. It is memory conservative, just sizeof(SizeInt) per match in blocks off 100 matches
  611. plus Length(OldPattern)*2 in the case of ignoring case.
  612. Memory copies are the minimun necessary.
  613. Algorithm based in the Boyer-Moore string search algorithm.
  614. It is faster when the "S" string is very long and the OldPattern is also
  615. very big. As much big the OldPattern is, faster the search is too.
  616. It uses 2 different helper versions of Boyer-Moore algorithm, one for case
  617. sensitive and one for case INsensitive for speed reasons.
  618. *)
  619. function StringReplaceBoyerMoore(const S, OldPattern, NewPattern: string;Flags: TReplaceFlags; out aCount : Integer): string;
  620. var
  621. Matches: SizeIntArray;
  622. OldPatternSize: SizeInt;
  623. NewPatternSize: SizeInt;
  624. MatchesCount: SizeInt;
  625. MatchIndex: SizeInt;
  626. MatchTarget: SizeInt;
  627. MatchInternal: SizeInt;
  628. AdvanceIndex: SizeInt;
  629. begin
  630. aCount:=0;
  631. OldPatternSize:=Length(OldPattern);
  632. NewPatternSize:=Length(NewPattern);
  633. if (OldPattern='') or (Length(OldPattern)>Length(S)) then begin
  634. Result:=S;
  635. exit;
  636. end;
  637. if rfIgnoreCase in Flags then begin
  638. FindMatchesBoyerMooreCaseINSensitive(@s[1],@OldPattern[1],Length(S),Length(OldPattern),Matches, rfReplaceAll in Flags);
  639. end else begin
  640. FindMatchesBoyerMooreCaseSensitive(@s[1],@OldPattern[1],Length(S),Length(OldPattern),Matches, rfReplaceAll in Flags);
  641. end;
  642. MatchesCount:=Length(Matches);
  643. aCount:=MatchesCount;
  644. //Create room enougth for the result string
  645. SetLength(Result,Length(S)-OldPatternSize*MatchesCount+NewPatternSize*MatchesCount);
  646. MatchIndex:=1;
  647. MatchTarget:=1;
  648. //Matches[x] are 0 based offsets
  649. for MatchInternal := 0 to Pred(MatchesCount) do begin
  650. //Copy information up to next match
  651. AdvanceIndex:=Matches[MatchInternal]+1-MatchIndex;
  652. if AdvanceIndex>0 then begin
  653. move(S[MatchIndex],Result[MatchTarget],AdvanceIndex);
  654. inc(MatchTarget,AdvanceIndex);
  655. inc(MatchIndex,AdvanceIndex);
  656. end;
  657. //Copy the new replace information string
  658. if NewPatternSize>0 then begin
  659. move(NewPattern[1],Result[MatchTarget],NewPatternSize);
  660. inc(MatchTarget,NewPatternSize);
  661. end;
  662. inc(MatchIndex,OldPatternSize);
  663. end;
  664. if MatchTarget<=Length(Result) then begin
  665. //Add remain data at the end of source.
  666. move(S[MatchIndex],Result[MatchTarget],Length(Result)-MatchTarget+1);
  667. end;
  668. end;
  669. function StringReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags; Algorithm: TStringReplaceAlgorithm): string;
  670. Var
  671. C : Integer;
  672. begin
  673. Result:=StringReplace(S, OldPattern, NewPattern, Flags,C,Algorithm);
  674. end;
  675. Function StringReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags; out aCount : Integer; Algorithm : TStringReplaceAlgorithm = sraDefault): string; overload;
  676. begin
  677. Case Algorithm of
  678. sraDefault : Result:={$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.StringReplace(S,OldPattern,NewPattern,Flags,aCount);
  679. sraManySmall : Result:=StringReplaceFast(S,OldPattern,NewPattern,Flags,aCount);
  680. sraBoyerMoore : Result:=StringReplaceBoyerMoore(S,OldPattern,NewPattern,Flags,aCount);
  681. end;
  682. end;
  683. {$IF SIZEOF(CHAR)=1}
  684. function StringReplace(const S, OldPattern, NewPattern: unicodestring; Flags: TReplaceFlags): unicodestring;
  685. begin
  686. Result:={$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.StringReplace(S,OldPattern,NewPattern,Flags);
  687. end;
  688. function StringReplace(const S, OldPattern, NewPattern: widestring; Flags: TReplaceFlags): widestring;
  689. begin
  690. Result:={$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.StringReplace(S,OldPattern,NewPattern,Flags);
  691. end;
  692. {$ENDIF}
  693. function FindMatchesBoyerMooreCaseSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean
  694. ): Boolean;
  695. begin
  696. Result:=FindMatchesBoyerMooreCaseSensitive(PAnsiChar(S),PAnsiChar(OldPattern),Length(S),Length(OldPattern),aMatches,aMatchAll);
  697. BoyerMoore.IncrementEverything(AMatches);
  698. end;
  699. function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean
  700. ): Boolean;
  701. begin
  702. Result:=FindMatchesBoyerMooreCaseInSensitive(PAnsiChar(S),PAnsiChar(OldPattern),Length(S),Length(OldPattern),aMatches,aMatchAll);
  703. BoyerMoore.IncrementEverything(AMatches);
  704. end;
  705. { ---------------------------------------------------------------------
  706. Possibly Exception raising functions
  707. ---------------------------------------------------------------------}
  708. function Hex2Dec(const S: string): Longint;
  709. var
  710. HexStr: string;
  711. begin
  712. if Pos('$',S)=0 then
  713. HexStr:='$'+ S
  714. else
  715. HexStr:=S;
  716. Result:=StrToInt(HexStr);
  717. end;
  718. function Hex2Dec64(const S: string): int64;
  719. var
  720. HexStr: string;
  721. begin
  722. if Pos('$',S)=0 then
  723. HexStr:='$'+ S
  724. else
  725. HexStr:=S;
  726. Result:=StrToInt64(HexStr);
  727. end;
  728. {
  729. We turn off implicit exceptions, since these routines are tested, and it
  730. saves 20% codesize (and some speed) and don't throw exceptions, except maybe
  731. heap related. If they don't, that is consider a bug.
  732. In the future, be wary with routines that use strtoint, floating point
  733. and/or format() derivatives. And check every divisor for 0.
  734. }
  735. {$IMPLICITEXCEPTIONS OFF}
  736. { ---------------------------------------------------------------------
  737. Case insensitive search/replace
  738. ---------------------------------------------------------------------}
  739. function AnsiResemblesText(const AText, AOther: AnsiString): Boolean;
  740. begin
  741. if Assigned(AnsiResemblesProc) then
  742. Result:=AnsiResemblesProc(AText,AOther)
  743. else
  744. Result:=False;
  745. end;
  746. function AnsiContainsText(const AText, ASubText: AnsiString): Boolean;
  747. begin
  748. AnsiContainsText:=AnsiPos(AnsiUppercase(ASubText),AnsiUppercase(AText))>0;
  749. end;
  750. function AnsiStartsText(const ASubText, AText: AnsiString): Boolean;
  751. begin
  752. Result := (ASubText = '') or AnsiSameText(LeftStr(AText, Length(ASubText)), ASubText);
  753. end;
  754. function AnsiEndsText(const ASubText, AText: AnsiString): Boolean;
  755. begin
  756. Result := (ASubText = '') or AnsiSameText(RightStr(AText, Length(ASubText)), ASubText);
  757. end;
  758. function AnsiEndsText(const ASubText, AText: UnicodeString): Boolean;
  759. begin
  760. Result := (ASubText = '') or SameText(RightStr(AText, Length(ASubText)), ASubText);
  761. end;
  762. function StartsText(const ASubText, AText: String): Boolean; inline;
  763. begin
  764. Result := AnsiStartsText(ASubText, AText);
  765. end;
  766. function EndsText(const ASubText, AText: string): Boolean;
  767. begin
  768. Result := AnsiEndsText(ASubText, AText);
  769. end;
  770. function ResemblesText(const AText, AOther: string): Boolean;
  771. begin
  772. if Assigned(ResemblesProc) then
  773. Result := ResemblesProc(AText, AOther)
  774. else
  775. Result := False;
  776. end;
  777. function ContainsText(const AText, ASubText: string): Boolean;
  778. begin
  779. Result := AnsiContainsText(AText, ASubText);
  780. end;
  781. function MatchText(const AText: Ansistring; const AValues: array of Ansistring): Boolean;
  782. begin
  783. Result := AnsiMatchText(AText, AValues);
  784. end;
  785. function IndexText(const AText: Ansistring; const AValues: array of Ansistring): Integer;
  786. begin
  787. Result := AnsiIndexText(AText, AValues);
  788. end;
  789. function ContainsStr(const AText, ASubText: String): Boolean;
  790. begin
  791. Result := AnsiContainsStr(AText, ASubText);
  792. end;
  793. function MatchStr(const AText: Ansistring; const AValues: array of Ansistring): Boolean;
  794. begin
  795. Result := AnsiMatchStr(AText, AValues);
  796. end;
  797. function IndexStr(const AText: AnsiString; const AValues: array of AnsiString): Integer;
  798. begin
  799. Result := AnsiIndexStr(AText, AValues);
  800. end;
  801. function AnsiReplaceText(const AText, AFromText, AToText: Ansistring): Ansistring;
  802. begin
  803. Result := StringReplace(AText,AFromText,AToText,[rfReplaceAll,rfIgnoreCase]);
  804. end;
  805. function AnsiMatchText(const AText: Ansistring; const AValues: array of AnsiString): Boolean;
  806. begin
  807. Result:=(AnsiIndexText(AText,AValues)<>-1)
  808. end;
  809. function AnsiIndexText(const AText: AnsiString; const AValues: array of Ansistring): Integer;
  810. begin
  811. for Result := Low(AValues) to High(AValues) do
  812. if AnsiSameText(AValues[Result], AText) then
  813. Exit;
  814. Result := -1;
  815. end;
  816. { ---------------------------------------------------------------------
  817. Case sensitive search/replace
  818. ---------------------------------------------------------------------}
  819. function AnsiContainsStr(const AText, ASubText: Ansistring): Boolean;
  820. begin
  821. Result := AnsiPos(ASubText,AText)>0;
  822. end;
  823. function AnsiContainsStr(const AText, ASubText: Unicodestring): Boolean;
  824. begin
  825. Result := AnsiPos(ASubText,AText)>0;
  826. end;
  827. function AnsiStartsStr(const ASubText, AText: AnsiString): Boolean;
  828. begin
  829. Result := (ASubText = '') or (LeftStr(AText, Length(ASubText)) = ASubText);
  830. end;
  831. function AnsiStartsStr(const ASubText, AText: UnicodeString): Boolean;
  832. begin
  833. Result := (ASubText = '') or (LeftStr(AText, Length(ASubText)) = ASubText);
  834. end;
  835. function AnsiEndsStr(const ASubText, AText: AnsiString): Boolean;
  836. begin
  837. Result := (ASubText = '') or (RightStr(AText, Length(ASubText)) = ASubText);
  838. end;
  839. function AnsiEndsStr(const ASubText, AText: UnicodeString): Boolean;
  840. begin
  841. Result := (ASubText = '') or (RightStr(AText, Length(ASubText)) = ASubText);
  842. end;
  843. function StartsStr(const ASubText, AText: string): Boolean;
  844. begin
  845. if (Length(AText) >= Length(ASubText)) and (ASubText <> '') then
  846. Result := StrLComp(PChar(ASubText), PChar(AText), Length(ASubText)) = 0
  847. else
  848. Result := (AsubText='');
  849. end;
  850. function EndsStr(const ASubText, AText: string): Boolean;
  851. begin
  852. if Length(AText) >= Length(ASubText) then
  853. Result := StrLComp(PChar(ASubText),
  854. PChar(AText) + Length(AText) - Length(ASubText), Length(ASubText)) = 0
  855. else
  856. Result := False;
  857. end;
  858. function AnsiReplaceStr(const AText, AFromText, AToText: AnsiString): AnsiString;
  859. begin
  860. Result := StringReplace(AText,AFromText,AToText,[rfReplaceAll]);
  861. end;
  862. function AnsiMatchStr(const AText: AnsiString; const AValues: array of AnsiString): Boolean;
  863. begin
  864. Result:=AnsiIndexStr(AText,Avalues)<>-1;
  865. end;
  866. function AnsiIndexStr(const AText: AnsiString; const AValues: array of AnsiString): Integer;
  867. begin
  868. for result:=low(AValues) to High(Avalues) do
  869. if (avalues[result]=AText) Then
  870. exit; // make sure it is the first val.
  871. result:=-1;
  872. end;
  873. function MatchStr(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  874. begin
  875. Result := IndexStr(AText,AValues) <> -1;
  876. end;
  877. function MatchText(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  878. begin
  879. Result := IndexText(AText,AValues) <> -1;
  880. end;
  881. function IndexStr(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  882. begin
  883. for Result := Low(AValues) to High(AValues) do
  884. if (avalues[Result] = AText) Then
  885. exit; // make sure it is the first val.
  886. Result := -1;
  887. end;
  888. function IndexText(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  889. begin
  890. for Result:=Low(AValues) to High(AValues) do
  891. if UnicodeCompareText(avalues[Result],atext)=0 Then
  892. exit; // make sure it is the first val.
  893. Result:=-1;
  894. end;
  895. operator in(const AText: AnsiString; const AValues: array of AnsiString): Boolean;
  896. begin
  897. Result := AnsiIndexStr(AText,AValues) <>-1;
  898. end;
  899. operator in(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  900. begin
  901. Result := IndexStr(AText,AValues) <> -1;
  902. end;
  903. { ---------------------------------------------------------------------
  904. Playthingies
  905. ---------------------------------------------------------------------}
  906. function DupeString(const AText: string; ACount: Integer): string;
  907. var
  908. ResLen, Rp, ToCopy: SizeInt;
  909. begin
  910. if (AText = '') or (ACount <= 0) then
  911. Exit('');
  912. if ACount = 1 then
  913. Exit(AText);
  914. Rp := Length(AText);
  915. ResLen := ACount * Rp;
  916. SetLength(Result, ResLen);
  917. Move(Pointer(AText)^, Pointer(Result)^, Rp * SizeOf(AText[1]));
  918. repeat
  919. ToCopy := ResLen - Rp;
  920. if Rp < ToCopy then
  921. ToCopy := Rp;
  922. Move(Pointer(Result)^, PChar(Pointer(Result))[Rp], ToCopy * SizeOf(AText[1]));
  923. Inc(Rp, ToCopy);
  924. until Rp = ResLen;
  925. end;
  926. function ReverseString(const AText: string): string;
  927. var
  928. i,j : SizeInt;
  929. begin
  930. setlength(result,length(atext));
  931. i:=1; j:=length(atext);
  932. while (i<=j) do
  933. begin
  934. result[i]:=atext[j-i+1];
  935. inc(i);
  936. end;
  937. end;
  938. function AnsiReverseString(const AText: AnsiString): AnsiString;
  939. begin
  940. Result:=ReverseString(AText);
  941. end;
  942. function StuffString(const AText: string; AStart, ALength: Cardinal; const ASubText: string): string;
  943. var i,j,k : SizeUInt;
  944. begin
  945. j:=length(ASubText);
  946. i:=length(AText);
  947. if AStart>i then
  948. aStart:=i+1;
  949. k:=i+1-AStart;
  950. if ALength> k then
  951. ALength:=k;
  952. SetLength(Result,i+j-ALength);
  953. move (AText[1],result[1],(AStart-1)*SizeOf(Char));
  954. move (ASubText[1],result[AStart],j*SizeOf(Char));
  955. move (AText[AStart+ALength], Result[AStart+j],(i+1-AStart-ALength)*SizeOf(Char));
  956. end;
  957. function RandomFrom(const AValues: array of string): string;
  958. begin
  959. if high(AValues)=-1 then exit('');
  960. result:=Avalues[random(High(AValues)+1)];
  961. end;
  962. function IfThen(AValue: Boolean; const ATrue: string; const AFalse: string): string;
  963. begin
  964. if avalue then
  965. result:=atrue
  966. else
  967. result:=afalse;
  968. end;
  969. Function IfThen(AValue: Boolean; const ATrue: TStringDynArray; const AFalse: TStringDynArray = nil): TStringDynArray; overload;
  970. begin
  971. if avalue then
  972. result:=atrue
  973. else
  974. result:=afalse;
  975. end;
  976. function NaturalCompareText(const Str1, Str2: string; const ADecSeparator, AThousandSeparator: AnsiChar): Integer;
  977. {
  978. NaturalCompareBase compares strings in a collated order and
  979. so numbers are sorted too. It sorts like this:
  980. 01
  981. 001
  982. 0001
  983. and
  984. 0
  985. 00
  986. 000
  987. 000_A
  988. 000_B
  989. in a intuitive order.
  990. }
  991. var
  992. Num1, Num2: double;
  993. pStr1, pStr2: PAnsiChar;
  994. Len1, Len2: SizeInt;
  995. TextLen1, TextLen2: SizeInt;
  996. TextStr1: string = '';
  997. TextStr2: string = '';
  998. i: SizeInt;
  999. j: SizeInt;
  1000. function Sign(const AValue: sizeint): integer;inline;
  1001. begin
  1002. If Avalue<0 then
  1003. Result:=-1
  1004. else If Avalue>0 then
  1005. Result:=1
  1006. else
  1007. Result:=0;
  1008. end;
  1009. function IsNumber(ch: AnsiChar): boolean;
  1010. begin
  1011. Result := ch in ['0'..'9'];
  1012. end;
  1013. function GetInteger(var pch: PAnsiChar; var Len: sizeint): double;
  1014. begin
  1015. Result := 0;
  1016. while (pch^ <> #0) and IsNumber(pch^) do
  1017. begin
  1018. Result := Result * 10 + Ord(pch^) - Ord('0');
  1019. Inc(Len);
  1020. Inc(pch);
  1021. end;
  1022. end;
  1023. procedure GetChars;
  1024. begin
  1025. TextLen1 := 0;
  1026. while not ((pStr1 + TextLen1)^ in ['0'..'9']) and ((pStr1 + TextLen1)^ <> #0) do
  1027. Inc(TextLen1);
  1028. SetLength(TextStr1, TextLen1);
  1029. i := 1;
  1030. j := 0;
  1031. while i <= TextLen1 do
  1032. begin
  1033. TextStr1[i] := (pStr1 + j)^;
  1034. Inc(i);
  1035. Inc(j);
  1036. end;
  1037. TextLen2 := 0;
  1038. while not ((pStr2 + TextLen2)^ in ['0'..'9']) and ((pStr2 + TextLen2)^ <> #0) do
  1039. Inc(TextLen2);
  1040. SetLength(TextStr2, TextLen2);
  1041. i := 1;
  1042. j := 0;
  1043. while i <= TextLen2 do
  1044. begin
  1045. TextStr2[i] := (pStr2 + j)^;
  1046. Inc(i);
  1047. Inc(j);
  1048. end;
  1049. end;
  1050. begin
  1051. if (Str1 <> '') and (Str2 <> '') then
  1052. begin
  1053. pStr1 := PAnsiChar(Str1);
  1054. pStr2 := PAnsiChar(Str2);
  1055. Result := 0;
  1056. while not ((pStr1^ = #0) or (pStr2^ = #0)) do
  1057. begin
  1058. TextLen1 := 1;
  1059. TextLen2 := 1;
  1060. Len1 := 0;
  1061. Len2 := 0;
  1062. while (pStr1^ = ' ') do
  1063. begin
  1064. Inc(pStr1);
  1065. Inc(Len1);
  1066. end;
  1067. while (pStr2^ = ' ') do
  1068. begin
  1069. Inc(pStr2);
  1070. Inc(Len2);
  1071. end;
  1072. if IsNumber(pStr1^) and IsNumber(pStr2^) then
  1073. begin
  1074. Num1 := GetInteger(pStr1, Len1);
  1075. Num2 := GetInteger(pStr2, Len2);
  1076. if Num1 < Num2 then
  1077. Result := -1
  1078. else if Num1 > Num2 then
  1079. Result := 1
  1080. else
  1081. begin
  1082. Result := Sign(Len1 - Len2);
  1083. end;
  1084. Dec(pStr1);
  1085. Dec(pStr2);
  1086. end
  1087. else
  1088. begin
  1089. GetChars;
  1090. if TextStr1 <> TextStr2 then
  1091. Result := WideCompareText(UTF8Decode(TextStr1), UTF8Decode(TextStr2))
  1092. else
  1093. Result := 0;
  1094. end;
  1095. if Result <> 0 then
  1096. Break;
  1097. Inc(pStr1, TextLen1);
  1098. Inc(pStr2, TextLen2);
  1099. end;
  1100. end;
  1101. Num1 := Length(Str1);
  1102. Num2 := Length(Str2);
  1103. if (Result = 0) and (Num1 <> Num2) then
  1104. begin
  1105. if Num1 < Num2 then
  1106. Result := -1
  1107. else
  1108. Result := 1;
  1109. end;
  1110. end;
  1111. function SplitString(const S, Delimiters: string): TRTLStringDynArray;
  1112. Var
  1113. a : Array of Char;
  1114. I : Integer;
  1115. begin
  1116. SetLength(A,Length(Delimiters));
  1117. For I:=1 to Length(Delimiters) do
  1118. A[I-1]:=Delimiters[i];
  1119. Result := S.Split(A);
  1120. end;
  1121. function NaturalCompareText (const S1 , S2 : string ): Integer ;
  1122. begin
  1123. Result := NaturalCompareText(S1, S2,
  1124. DefaultFormatSettings.DecimalSeparator,
  1125. DefaultFormatSettings.ThousandSeparator);
  1126. end;
  1127. { ---------------------------------------------------------------------
  1128. VB emulations.
  1129. ---------------------------------------------------------------------}
  1130. function LeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1131. begin
  1132. Result:=Copy(AText,1,ACount);
  1133. end;
  1134. function RightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1135. var j,l:SizeInt;
  1136. begin
  1137. l:=length(atext);
  1138. j:=ACount;
  1139. if j>l then j:=l;
  1140. Result:=Copy(AText,l-j+1,j);
  1141. end;
  1142. function MidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;
  1143. begin
  1144. if (ACount=0) or (AStart>length(atext)) then
  1145. exit('');
  1146. Result:=Copy(AText,AStart,ACount);
  1147. end;
  1148. function LeftBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;
  1149. begin
  1150. Result:=LeftStr(AText,AByteCount);
  1151. end;
  1152. function RightBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;
  1153. begin
  1154. Result:=RightStr(Atext,AByteCount);
  1155. end;
  1156. function MidBStr(const AText: AnsiString; const AByteStart, AByteCount: SizeInt): AnsiString;
  1157. begin
  1158. Result:=MidStr(AText,AByteStart,AByteCount);
  1159. end;
  1160. function AnsiLeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1161. begin
  1162. Result := copy(AText,1,ACount);
  1163. end;
  1164. function AnsiRightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1165. begin
  1166. Result := copy(AText,length(AText)-ACount+1,ACount);
  1167. end;
  1168. function AnsiMidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;
  1169. begin
  1170. Result:=Copy(AText,AStart,ACount);
  1171. end;
  1172. function LeftStr(const AText: WideString; const ACount: SizeInt): WideString;
  1173. begin
  1174. Result:=Copy(AText,1,ACount);
  1175. end;
  1176. function RightStr(const AText: WideString; const ACount: SizeInt): WideString;
  1177. var
  1178. j,l:SizeInt;
  1179. begin
  1180. l:=length(atext);
  1181. j:=ACount;
  1182. if j>l then j:=l;
  1183. Result:=Copy(AText,l-j+1,j);
  1184. end;
  1185. function MidStr(const AText: WideString; const AStart, ACount: SizeInt): WideString;
  1186. begin
  1187. Result:=Copy(AText,AStart,ACount);
  1188. end;
  1189. { ---------------------------------------------------------------------
  1190. Extended search and replace
  1191. ---------------------------------------------------------------------}
  1192. type
  1193. TEqualFunction = function (const a,b : AnsiChar) : boolean;
  1194. function EqualWithCase (const a,b : AnsiChar) : boolean;
  1195. begin
  1196. result := (a = b);
  1197. end;
  1198. function EqualWithoutCase (const a,b : AnsiChar) : boolean;
  1199. begin
  1200. result := (lowerCase(a) = lowerCase(b));
  1201. end;
  1202. function IsWholeWord (bufstart, bufend, wordstart, wordend : PAnsiChar) : boolean;
  1203. begin
  1204. // Check start
  1205. result := ((wordstart = bufstart) or ((wordstart-1)^ in worddelimiters)) and
  1206. // Check end
  1207. ((wordend = bufend) or ((wordend+1)^ in worddelimiters));
  1208. end;
  1209. function SearchDown(buf,aStart,endchar:PAnsiChar; SearchString:string;
  1210. Equals : TEqualFunction; WholeWords:boolean) : PAnsiChar;
  1211. var Found : boolean;
  1212. s, c : PAnsiChar;
  1213. begin
  1214. result := aStart;
  1215. Found := false;
  1216. while not Found and (result <= endchar) do
  1217. begin
  1218. // Search first letter
  1219. while (result <= endchar) and not Equals(result^,SearchString[1]) do
  1220. inc (result);
  1221. // Check if following is searchstring
  1222. c := result;
  1223. s := @(Searchstring[1]);
  1224. Found := true;
  1225. while (c <= endchar) and (s^ <> #0) and Found do
  1226. begin
  1227. Found := Equals(c^, s^);
  1228. inc (c);
  1229. inc (s);
  1230. end;
  1231. if s^ <> #0 then
  1232. Found := false;
  1233. // Check if it is a word
  1234. if Found and WholeWords then
  1235. Found := IsWholeWord(buf,endchar,result,c-1);
  1236. if not found then
  1237. inc (result);
  1238. end;
  1239. if not Found then
  1240. result := nil;
  1241. end;
  1242. function SearchUp(buf,aStart,endchar:PAnsiChar; SearchString:string;
  1243. equals : TEqualFunction; WholeWords:boolean) : PAnsiChar;
  1244. var Found : boolean;
  1245. s, c, l : PAnsiChar;
  1246. begin
  1247. result := aStart;
  1248. Found := false;
  1249. l := @(SearchString[length(SearchString)]);
  1250. while not Found and (result >= buf) do
  1251. begin
  1252. // Search last letter
  1253. while (result >= buf) and not Equals(result^,l^) do
  1254. dec (result);
  1255. // Check if before is searchstring
  1256. c := result;
  1257. s := l;
  1258. Found := true;
  1259. while (c >= buf) and (s >= @SearchString[1]) and Found do
  1260. begin
  1261. Found := Equals(c^, s^);
  1262. dec (c);
  1263. dec (s);
  1264. end;
  1265. if (s >= @(SearchString[1])) then
  1266. Found := false;
  1267. // Check if it is a word
  1268. if Found and WholeWords then
  1269. Found := IsWholeWord(buf,endchar,c+1,result);
  1270. if found then
  1271. result := c+1
  1272. else
  1273. dec (result);
  1274. end;
  1275. if not Found then
  1276. result := nil;
  1277. end;
  1278. //function SearchDown(buf,aStart,endchar:PAnsiChar; SearchString:string; equal : TEqualFunction; WholeWords:boolean) : PAnsiChar;
  1279. function SearchBuf(Buf: PAnsiChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String; Options: TStringSearchOptions
  1280. ): PAnsiChar;
  1281. var
  1282. equal : TEqualFunction;
  1283. begin
  1284. SelStart := SelStart + SelLength;
  1285. if (SearchString = '') or (SelStart > BufLen) or (SelStart < 0) then
  1286. result := nil
  1287. else
  1288. begin
  1289. if soMatchCase in Options then
  1290. Equal := @EqualWithCase
  1291. else
  1292. Equal := @EqualWithoutCase;
  1293. if soDown in Options then
  1294. result := SearchDown(buf,buf+SelStart,Buf+(BufLen-1), SearchString, Equal, (soWholeWord in Options))
  1295. else
  1296. result := SearchUp(buf,buf+SelStart,Buf+(Buflen-1), SearchString, Equal, (soWholeWord in Options));
  1297. end;
  1298. end;
  1299. function SearchBuf(Buf: PAnsiChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String): PAnsiChar; // ; Options: TStringSearchOptions = [soDown]
  1300. begin
  1301. Result:=SearchBuf(Buf,BufLen,SelStart,SelLength,SearchString,[soDown]);
  1302. end;
  1303. function PosEx(const SubStr, S: AnsiString; Offset: SizeInt): SizeInt;
  1304. begin
  1305. Result := Pos(SubStr, S, Offset);
  1306. end;
  1307. function PosEx(c: AnsiChar; const S: Ansistring; Offset: SizeInt): SizeInt;
  1308. begin
  1309. Result := Pos(c, S, Offset);
  1310. end;
  1311. function PosEx(const SubStr, S: Ansistring): SizeInt;
  1312. begin
  1313. Result := Pos(SubStr, S);
  1314. end;
  1315. function PosEx(const SubStr, S: UnicodeString; Offset: SizeInt): SizeInt;
  1316. begin
  1317. Result := Pos(SubStr, S, Offset);
  1318. end;
  1319. function PosEx(c: WideChar; const S: UnicodeString; Offset: SizeInt): SizeInt;
  1320. begin
  1321. Result := Pos(c, S, Offset);
  1322. end;
  1323. function PosEx(const SubStr, S: UnicodeString): Sizeint;
  1324. begin
  1325. Result := Pos(SubStr, S);
  1326. end;
  1327. function StringsReplace(const S: AnsiString; OldPattern, NewPattern: array of AnsiString; Flags: TReplaceFlags): string;
  1328. var pc,lastpc,litStart : PAnsiChar;
  1329. iPattern,Rp,Ra,iFirstPattern,OldPatternLen : SizeInt;
  1330. // Heads of the linked lists of patterns starting with a character whose code has this residue modulo length(firstPattern).
  1331. // Length must be power of two, less = slower for large cases, more = slower for small cases.
  1332. // 0 .. 255 (or directly [AnsiChar]) might be a bit too much because the array is initialized every time; and generalizes worse to S: UnicodeString :)
  1333. firstPattern : array[0 .. 63] of SizeInt;
  1334. nextPattern : PSizeInt; // Next pattern starting with the same character.
  1335. nextPatternStatic: array[0 .. 63] of SizeInt;
  1336. CompStr : ansistring;
  1337. {$if sizeof(char) <> sizeof(ansichar)}
  1338. tempStr : string;
  1339. {$endif}
  1340. procedure Append(P: PChar; N: SizeInt);
  1341. begin
  1342. if N>Ra-Rp then
  1343. begin
  1344. Ra:=Rp+N+4+Ra shr 1+Ra shr 2; // + N + const + 37.5%
  1345. SetLength(Result,Ra);
  1346. end;
  1347. Move(P^,PChar(Pointer(Result))[Rp],N*SizeOf(Char));
  1348. Rp:=Rp+N;
  1349. end;
  1350. // Mostly exists to force better register allocation for the main "pc < lastpc" loop, hotter than this procedure. :)
  1351. // Returns the length of the found and replaced OldPattern item, or -1 if not found.
  1352. function TryMatchAndReplace(pc,lastpc: PAnsiChar; iPattern: SizeInt): SizeInt;
  1353. var
  1354. pcc: PAnsiChar;
  1355. OldPatternLen: SizeInt;
  1356. begin
  1357. repeat
  1358. OldPatternLen:=Length(OldPattern[iPattern]);
  1359. if (OldPatternLen <= (lastpc-pc)) and
  1360. (CompareByte(OldPattern[iPattern,1],pc^,OldPatternLen*SizeOf(AnsiChar))=0) then
  1361. begin
  1362. pcc:=PAnsiChar(Pointer(S))+(pc-PAnsiChar(Pointer(CompStr)));
  1363. {$if sizeof(char)=sizeof(ansichar)}
  1364. Append(litStart,pcc-litStart);
  1365. Append(PChar(Pointer(NewPattern[iPattern])), Length(NewPattern[iPattern]));
  1366. {$else}
  1367. tempStr := Copy(S,1+litStart-PAnsiChar(Pointer(S)),pcc-litStart);
  1368. Append(PChar(Pointer(tempStr)), Length(tempStr));
  1369. tempStr := NewPattern[iPattern];
  1370. Append(PChar(Pointer(tempStr)), Length(tempStr));
  1371. {$endif}
  1372. litStart := pcc+OldPatternLen;
  1373. exit(OldPatternLen);
  1374. end;
  1375. iPattern := nextPattern[iPattern];
  1376. until iPattern < 0;
  1377. result := -1;
  1378. end;
  1379. begin
  1380. if High(OldPattern) <> High(NewPattern) then
  1381. raise exception.Create(SErrAmountStrings);
  1382. FillChar(firstPattern, sizeof(firstPattern), byte(-1));
  1383. if High(OldPattern) <= High(nextPatternStatic) then
  1384. nextPattern := PSizeInt(nextPatternStatic)
  1385. else
  1386. nextPattern := GetMem(Length(OldPattern) * sizeof(SizeInt));
  1387. FillChar(nextPattern^, Length(OldPattern) * sizeof(SizeInt), byte(-1));
  1388. if rfIgnoreCase in Flags then
  1389. begin
  1390. CompStr := AnsiUpperCase(S);
  1391. for iPattern := 0 to High(OldPattern) do
  1392. OldPattern[iPattern] := AnsiUpperCase(OldPattern[iPattern]);
  1393. end
  1394. else
  1395. CompStr := S;
  1396. // The element added to the linked list last will be checked first, so add in reverse order.
  1397. for iPattern := High(OldPattern) downto 0 do
  1398. if OldPattern[iPattern] <> '' then
  1399. begin
  1400. iFirstPattern := ord(OldPattern[iPattern,1]) and High(firstPattern);
  1401. nextPattern[iPattern] := firstPattern[iFirstPattern];
  1402. firstPattern[iFirstPattern] := iPattern;
  1403. end;
  1404. Ra := Length(S); // Preallocation heuristic.
  1405. SetLength(result, Ra);
  1406. Rp := 0;
  1407. pc := PAnsiChar(Pointer(CompStr));
  1408. litStart := PAnsiChar(Pointer(S));
  1409. lastpc := pc+Length(S);
  1410. while pc < lastpc do
  1411. begin
  1412. iPattern := firstPattern[ord(pc^) and High(firstPattern)];
  1413. inc(pc);
  1414. if iPattern >= 0 then
  1415. begin
  1416. OldPatternLen := TryMatchAndReplace(pc-1, lastpc, iPattern);
  1417. if OldPatternLen >= 0 then
  1418. begin
  1419. pc := pc-1+OldPatternLen;
  1420. if not (rfReplaceAll in Flags) then
  1421. break;
  1422. end;
  1423. end;
  1424. end;
  1425. if nextPattern <> PSizeInt(nextPattern) then
  1426. FreeMem(nextPattern);
  1427. if litStart = PAnsiChar(Pointer(S)) then
  1428. exit(S); // Unchanged string.
  1429. {$if sizeof(char)=sizeof(ansichar)}
  1430. Append(litStart,PAnsiChar(Pointer(S))+(lastpc-PAnsiChar(Pointer(CompStr)))-litStart);
  1431. {$else}
  1432. tempStr := Copy(S,1+litStart-PAnsiChar(Pointer(S)),PAnsiChar(Pointer(S))+(lastpc-PAnsiChar(Pointer(CompStr)))-litStart);
  1433. Append(PChar(Pointer(tempStr)), Length(tempStr));
  1434. {$endif}
  1435. SetLength(result,Rp);
  1436. end;
  1437. { ---------------------------------------------------------------------
  1438. Delphi compat
  1439. ---------------------------------------------------------------------}
  1440. function ReplaceStr(const AText, AFromText, AToText: string): string;
  1441. begin
  1442. result:=AnsiReplaceStr(AText, AFromText, AToText);
  1443. end;
  1444. function ReplaceText(const AText, AFromText, AToText: string): string;
  1445. begin
  1446. result:=AnsiReplaceText(AText, AFromText, AToText);
  1447. end;
  1448. { ---------------------------------------------------------------------
  1449. Soundex Functions.
  1450. ---------------------------------------------------------------------}
  1451. Const
  1452. SScore : array[1..255] of AnsiChar =
  1453. ('0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 1..32
  1454. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 33..64
  1455. '0','1','2','3','0','1','2','i','0','2','2','4','5','5','0','1','2','6','2','3','0','1','i','2','i','2', // 65..90
  1456. '0','0','0','0','0','0', // 91..96
  1457. '0','1','2','3','0','1','2','i','0','2','2','4','5','5','0','1','2','6','2','3','0','1','i','2','i','2', // 97..122
  1458. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 123..154
  1459. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 155..186
  1460. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 187..218
  1461. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 219..250
  1462. '0','0','0','0','0'); // 251..255
  1463. function Soundex(const AText: string; ALength: TSoundexLength): string;
  1464. Var
  1465. S,PS : AnsiChar;
  1466. I,L : SizeInt;
  1467. begin
  1468. Result:='';
  1469. PS:=#0;
  1470. If Length(AText)>0 then
  1471. begin
  1472. Result:=Upcase(AText[1]);
  1473. I:=2;
  1474. L:=Length(AText);
  1475. While (I<=L) and (Length(Result)<ALength) do
  1476. begin
  1477. S:=SScore[Ord(AText[i])];
  1478. If Not (S in ['0','i',PS]) then
  1479. Result:=Result+S;
  1480. If (S<>'i') then
  1481. PS:=S;
  1482. Inc(I);
  1483. end;
  1484. end;
  1485. L:=Length(Result);
  1486. If (L<ALength) then
  1487. Result:=Result+StringOfChar('0',Alength-L);
  1488. end;
  1489. function Soundex(const AText: string): string; // ; ALength: TSoundexLength = 4
  1490. begin
  1491. Result:=Soundex(AText,4);
  1492. end;
  1493. Const
  1494. Ord0 = Ord('0');
  1495. OrdA = Ord('A');
  1496. function SoundexInt(const AText: string; ALength: TSoundexIntLength): Integer;
  1497. var
  1498. SE: string;
  1499. I: SizeInt;
  1500. begin
  1501. Result:=-1;
  1502. SE:=Soundex(AText,ALength);
  1503. If Length(SE)>0 then
  1504. begin
  1505. Result:=Ord(SE[1])-OrdA;
  1506. if ALength > 1 then
  1507. begin
  1508. Result:=Result*26+(Ord(SE[2])-Ord0);
  1509. for I:=3 to ALength do
  1510. Result:=(Ord(SE[I])-Ord0)+Result*7;
  1511. end;
  1512. Result:=ALength+Result*9;
  1513. end;
  1514. end;
  1515. function SoundexInt(const AText: string): Integer; //; ALength: TSoundexIntLength = 4
  1516. begin
  1517. Result:=SoundexInt(AText,4);
  1518. end;
  1519. function DecodeSoundexInt(AValue: Integer): string;
  1520. var
  1521. I, Len: Integer;
  1522. begin
  1523. Result := '';
  1524. Len := AValue mod 9;
  1525. AValue := AValue div 9;
  1526. for I:=Len downto 3 do
  1527. begin
  1528. Result:=Chr(Ord0+(AValue mod 7))+Result;
  1529. AValue:=AValue div 7;
  1530. end;
  1531. if Len>1 then
  1532. begin
  1533. Result:=Chr(Ord0+(AValue mod 26))+Result;
  1534. AValue:=AValue div 26;
  1535. end;
  1536. Result:=Chr(OrdA+AValue)+Result;
  1537. end;
  1538. function SoundexWord(const AText: string): Word;
  1539. Var
  1540. S : String;
  1541. begin
  1542. S:=SoundEx(Atext,4);
  1543. Result:=Ord(S[1])-OrdA;
  1544. Result:=Result*26+ord(S[2])-48;
  1545. Result:=Result*7+ord(S[3])-48;
  1546. Result:=Result*7+ord(S[4])-48;
  1547. end;
  1548. function DecodeSoundexWord(AValue: Word): string;
  1549. begin
  1550. Result := Chr(Ord0+ (AValue mod 7));
  1551. AValue := AValue div 7;
  1552. Result := Chr(Ord0+ (AValue mod 7)) + Result;
  1553. AValue := AValue div 7;
  1554. Result := IntToStr(AValue mod 26) + Result;
  1555. AValue := AValue div 26;
  1556. Result := Chr(OrdA+AValue) + Result;
  1557. end;
  1558. function SoundexSimilar(const AText, AOther: string; ALength: TSoundexLength): Boolean;
  1559. begin
  1560. Result:=Soundex(AText,ALength)=Soundex(AOther,ALength);
  1561. end;
  1562. function SoundexSimilar(const AText, AOther: string): Boolean; //; ALength: TSoundexLength = 4
  1563. begin
  1564. Result:=SoundexSimilar(AText,AOther,4);
  1565. end;
  1566. function SoundexCompare(const AText, AOther: string; ALength: TSoundexLength): Integer;
  1567. begin
  1568. Result:=AnsiCompareStr(Soundex(AText,ALength),Soundex(AOther,ALength));
  1569. end;
  1570. function SoundexCompare(const AText, AOther: string): Integer; //; ALength: TSoundexLength = 4
  1571. begin
  1572. Result:=SoundexCompare(AText,AOther,4);
  1573. end;
  1574. function SoundexProc(const AText, AOther: string): Boolean;
  1575. begin
  1576. Result:=SoundexSimilar(AText,AOther);
  1577. end;
  1578. { ---------------------------------------------------------------------
  1579. RxStrUtils-like functions.
  1580. ---------------------------------------------------------------------}
  1581. function IsEmptyStr(const S: string; const EmptyChars: TSysCharSet): Boolean;
  1582. var
  1583. i,l: SizeInt;
  1584. begin
  1585. l:=Length(S);
  1586. i:=1;
  1587. Result:=True;
  1588. while Result and (i<=l) do
  1589. begin
  1590. Result:=(S[i] in EmptyChars);
  1591. Inc(i);
  1592. end;
  1593. end;
  1594. function DelSpace(const S: string): string;
  1595. begin
  1596. Result:=DelChars(S,' ');
  1597. end;
  1598. function IndexCharSized(p: PChar; nchars: SizeInt; charv: SizeUint): SizeInt; inline;
  1599. begin
  1600. result :=
  1601. {$if sizeof(char) = sizeof(byte)} IndexByte
  1602. {$elseif sizeof(char) = sizeof(word)} IndexWord
  1603. {$else} {$error unknown char size}
  1604. {$endif}
  1605. (p^, nchars, charv);
  1606. end;
  1607. procedure FillCharSized(p: PChar; nchars: SizeInt; charv: SizeUint); inline;
  1608. begin
  1609. {$if sizeof(char) = sizeof(byte)} FillChar
  1610. {$elseif sizeof(char) = sizeof(word)} FillWord
  1611. {$else} {$error unknown char size}
  1612. {$endif}
  1613. (p^, nchars, charv);
  1614. end;
  1615. function DelChars(const S: string; Chr: Char): string;
  1616. var
  1617. Sp, Se, Rp: PChar;
  1618. ToCopy: SizeInt;
  1619. begin
  1620. Sp := PChar(Pointer(S));
  1621. Se := Sp + Length(S);
  1622. ToCopy := IndexCharSized(Sp, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char), ord(Chr));
  1623. if ToCopy < 0 then
  1624. exit(S); // Unchanged string.
  1625. SetLength(result, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char));
  1626. Rp := PChar(Pointer(Result));
  1627. repeat
  1628. Move(Sp^, Rp^, ToCopy * sizeof(Char));
  1629. Inc(Sp, ToCopy);
  1630. Inc(Rp, ToCopy);
  1631. repeat
  1632. Inc(Sp); // Can increment to Se + 1.
  1633. until (Sp >= Se) or (Sp^ <> Chr);
  1634. if Sp >= Se then
  1635. break;
  1636. ToCopy := IndexCharSized(Sp, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char), ord(Chr));
  1637. if ToCopy < 0 then
  1638. ToCopy := SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char);
  1639. until false;
  1640. SetLength(result, SizeUint(Pointer(Rp) - Pointer(Result)) div sizeof(Char));
  1641. end;
  1642. function DelChars(const S: string; Chars: TSysCharSet): string;
  1643. var
  1644. Ss, Sp, Se, Rp: PChar;
  1645. aDelta : Integer;
  1646. begin
  1647. Ss := PChar(Pointer(S));
  1648. Sp := Ss;
  1649. Se := Sp + Length(S);
  1650. while (Sp < Se) and not (Sp^ in Chars) do
  1651. Inc(Sp);
  1652. if Sp >= Se then
  1653. Exit(S); // Unchanged string.
  1654. SetLength(result, SizeUint(Pointer(Se) - Pointer(Ss)) div sizeof(Char));
  1655. Rp := PChar(Pointer(Result));
  1656. repeat
  1657. aDelta:=(Pointer(Sp) - Pointer(Ss));
  1658. Move(Ss^, Rp^, aDelta);
  1659. Inc(Pointer(Rp), aDelta);
  1660. repeat
  1661. Inc(Sp); // Can increment to Se + 1.
  1662. until (Sp >= Se) or not (Sp^ in Chars);
  1663. if Sp >= Se then
  1664. break;
  1665. Ss := Sp;
  1666. repeat
  1667. Inc(Sp);
  1668. until (Sp >= Se) or (Sp^ in Chars);
  1669. until false;
  1670. SetLength(result, SizeUint(Pointer(Rp) - Pointer(Result)) div sizeof(Char));
  1671. end;
  1672. function FindSpacePrecededBySpace(Sp, Se: PChar): PChar;
  1673. var
  1674. SpacePos: SizeInt;
  1675. begin
  1676. repeat
  1677. SpacePos := IndexCharSized(Sp, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char), ord(' '));
  1678. if SpacePos < 0 then
  1679. Exit(Se);
  1680. Inc(Sp, SpacePos+1);
  1681. until (Sp>=Se) or (Sp^=' ');
  1682. Result:=Sp;
  1683. end;
  1684. function DelSpace1(const S: string): string;
  1685. var
  1686. Ss, Sp, Se, Rp: PChar;
  1687. begin
  1688. Ss := PChar(Pointer(S));
  1689. Se := Ss + Length(S);
  1690. Sp := FindSpacePrecededBySpace(Ss, Se);
  1691. if Sp >= Se then
  1692. Exit(S); // Unchanged string.
  1693. SetLength(result, SizeUint(Pointer(Se) - Pointer(Ss)) div sizeof(Char));
  1694. Rp := PChar(Pointer(Result));
  1695. repeat
  1696. Move(Ss^, Rp^, Pointer(Sp) - Pointer(Ss));
  1697. Inc(Pointer(Rp), Pointer(Sp) - Pointer(Ss));
  1698. repeat
  1699. Inc(Sp); // Can increment to Se + 1.
  1700. until (Sp >= Se) or (Sp^ <> ' ');
  1701. if Sp >= Se then
  1702. break;
  1703. Ss := Sp;
  1704. Sp := FindSpacePrecededBySpace(Sp, Se);
  1705. until false;
  1706. SetLength(result, SizeUint(Pointer(Rp) - Pointer(Result)) div sizeof(Char));
  1707. end;
  1708. function Tab2Space(const S: string; Numb: Byte): string;
  1709. var
  1710. I: SizeInt;
  1711. begin
  1712. I:=1;
  1713. Result:=S;
  1714. while I <= Length(Result) do
  1715. if Result[I]<>Chr(9) then
  1716. inc(I)
  1717. else
  1718. begin
  1719. Result[I]:=' ';
  1720. If (Numb>1) then
  1721. Insert(StringOfChar(' ',Numb-1),Result,I);
  1722. Inc(I,Numb);
  1723. end;
  1724. end;
  1725. function NPos(const C: string; const S: string; N: Integer): SizeInt;
  1726. begin
  1727. Result:=0;
  1728. if N>=1 then
  1729. Repeat
  1730. Result:=Pos(C,S,Result+1);
  1731. dec(N);
  1732. until (N=0) or (Result=0);
  1733. end;
  1734. function AddChar(C: AnsiChar; const S: string; N: Integer): string;
  1735. Var
  1736. l : SizeInt;
  1737. begin
  1738. l:=Length(S);
  1739. if l>=N then
  1740. Exit(S);
  1741. SetLength(Result,N);
  1742. FillCharSized(Pointer(Result),N-l,ord(C));
  1743. Move(Pointer(S)^,PChar(Pointer(Result))[N-l],l*sizeof(Char));
  1744. end;
  1745. function AddCharR(C: AnsiChar; const S: string; N: Integer): string;
  1746. Var
  1747. l : SizeInt;
  1748. begin
  1749. l:=Length(S);
  1750. if l>=N then
  1751. Exit(S);
  1752. SetLength(Result,N);
  1753. Move(Pointer(S)^,Pointer(Result)^,l*sizeof(Char));
  1754. FillCharSized(PChar(Pointer(Result))+l,N-l,ord(C));
  1755. end;
  1756. function PadRight(const S: string; N: Integer): string;inline;
  1757. begin
  1758. Result:=AddCharR(' ',S,N);
  1759. end;
  1760. function PadLeft(const S: string; N: Integer): string;inline;
  1761. begin
  1762. Result:=AddChar(' ',S,N);
  1763. end;
  1764. function Copy2Symb(const S: string; Symb: AnsiChar): string;
  1765. var
  1766. p: SizeInt;
  1767. begin
  1768. p:=Pos(Symb,S);
  1769. if p=0 then
  1770. p:=Length(S)+1;
  1771. Result:=Copy(S,1,p-1);
  1772. end;
  1773. function Copy2SymbDel(var S: string; Symb: AnsiChar): string;
  1774. var
  1775. p: SizeInt;
  1776. begin
  1777. p:=Pos(Symb,S);
  1778. if p=0 then
  1779. begin
  1780. result:=s;
  1781. s:='';
  1782. end
  1783. else
  1784. begin
  1785. Result:=Copy(S,1,p-1);
  1786. delete(s,1,p);
  1787. end;
  1788. end;
  1789. function Copy2Space(const S: string): string;inline;
  1790. begin
  1791. Result:=Copy2Symb(S,' ');
  1792. end;
  1793. function Copy2SpaceDel(var S: string): string;inline;
  1794. begin
  1795. Result:=Copy2SymbDel(S,' ');
  1796. end;
  1797. function AnsiProperCase(const S: string; const WordDelims: TSysCharSet): string;
  1798. var
  1799. P,PE : PAnsiChar;
  1800. begin
  1801. Result:=AnsiLowerCase(S);
  1802. P:=PAnsiChar(pointer(Result));
  1803. PE:=P+Length(Result);
  1804. while (P<PE) do
  1805. begin
  1806. while (P<PE) and (P^ in WordDelims) do
  1807. inc(P);
  1808. if (P<PE) then
  1809. P^:=UpCase(P^);
  1810. while (P<PE) and not (P^ in WordDelims) do
  1811. inc(P);
  1812. end;
  1813. end;
  1814. function WordCount(const S: string; const WordDelims: TSysCharSet): SizeInt;
  1815. var
  1816. P,PE : PAnsiChar;
  1817. WasDelim, NowDelim : Boolean;
  1818. begin
  1819. Result:=0;
  1820. P:=PAnsiChar(pointer(S));
  1821. PE:=P+Length(S);
  1822. WasDelim:=true;
  1823. while (P<PE) do
  1824. begin
  1825. NowDelim := P^ in WordDelims;
  1826. Inc(Result,ord(WasDelim and not NowDelim));
  1827. WasDelim := NowDelim;
  1828. Inc(P);
  1829. end;
  1830. end;
  1831. function WordPosition(const N: Integer; const S: string; const WordDelims: TSysCharSet): SizeInt;
  1832. var
  1833. P,PE : PChar;
  1834. Count: Integer;
  1835. begin
  1836. P:=PChar(pointer(S));
  1837. PE:=P+Length(S);
  1838. Count:=N;
  1839. while (P<PE) and (P^ in WordDelims) do
  1840. Inc(P);
  1841. while (P<PE) do
  1842. begin
  1843. Dec(Count);
  1844. if Count<=0 then
  1845. break;
  1846. repeat
  1847. Inc(P);
  1848. until (P>=PE) or (P^ in WordDelims);
  1849. repeat
  1850. Inc(P); // Can increment to PE + 1.
  1851. until (P>=PE) or not (P^ in WordDelims);
  1852. end;
  1853. if (P<PE) and (Count=0) then
  1854. exit(P-PChar(pointer(S))+1);
  1855. Result:=0;
  1856. end;
  1857. function ExtractWord(N: Integer; const S: string; const WordDelims: TSysCharSet): string;inline;
  1858. var
  1859. i: SizeInt;
  1860. begin
  1861. Result:=ExtractWordPos(N,S,WordDelims,i);
  1862. end;
  1863. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; out Pos: Integer): string;
  1864. var
  1865. i,j: SizeInt;
  1866. begin
  1867. i:=WordPosition(N, S, WordDelims);
  1868. if not ((I>0) and (I<=High(Integer))) then
  1869. begin
  1870. Result:='';
  1871. Pos:=0;
  1872. Exit;
  1873. end;
  1874. Pos:=i;
  1875. j:=PosSetEx(WordDelims, S, i+1);
  1876. if j=0 then
  1877. j:=Length(S)+1;
  1878. Result:=Copy(S,i,j-i);
  1879. end;
  1880. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  1881. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; Out Pos: SizeInt): string;
  1882. var
  1883. i,j: SizeInt;
  1884. begin
  1885. i:=WordPosition(N, S, WordDelims);
  1886. Pos:=i;
  1887. if I<=0 then
  1888. begin
  1889. Result:='';
  1890. Exit;
  1891. end;
  1892. j:=PosSetEx(WordDelims, S, i+1);
  1893. if j=0 then
  1894. j:=Length(S)+1;
  1895. Result:=Copy(S,i,j-i);
  1896. end;
  1897. {$ENDIF}
  1898. function ExtractDelimited(N: Integer; const S: string; const Delims: TSysCharSet): string;
  1899. var
  1900. i,start,len: SizeInt;
  1901. begin
  1902. i:=1;
  1903. len:=Length(S);
  1904. while (i<=len) and (N>1) do
  1905. begin
  1906. dec(N,ord(S[i] in Delims));
  1907. inc(i);
  1908. end;
  1909. if N<>1 then
  1910. exit('');
  1911. start:=i;
  1912. while (i<=len) and not (S[i] in Delims) do
  1913. inc(i);
  1914. exit(Copy(S,start,i-start));
  1915. end;
  1916. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  1917. function ExtractSubstr(const S: string; var Pos: SizeInt; const Delims: TSysCharSet): string;
  1918. var
  1919. i,l: SizeInt;
  1920. begin
  1921. i:=Pos;
  1922. l:=Length(S);
  1923. while (i<=l) and not (S[i] in Delims) do
  1924. inc(i);
  1925. Result:=Copy(S,Pos,i-Pos);
  1926. while (i<=l) and (S[i] in Delims) do
  1927. inc(i);
  1928. Pos:=i;
  1929. end;
  1930. {$ENDIF}
  1931. function ExtractSubstr(const S: string; var Pos: Integer; const Delims: TSysCharSet): string;
  1932. var
  1933. i,l: SizeInt;
  1934. begin
  1935. i:=Pos;
  1936. l:=Length(S);
  1937. while (i<=l) and not (S[i] in Delims) do
  1938. inc(i);
  1939. Result:=Copy(S,Pos,i-Pos);
  1940. while (i<=l) and (S[i] in Delims) do
  1941. inc(i);
  1942. if I>MaxInt then
  1943. Pos:=MaxInt
  1944. else
  1945. Pos:=i;
  1946. end;
  1947. function IsWordPresent(const W, S: string; const WordDelims: TSysCharSet): Boolean;
  1948. var
  1949. P,PE,WordStart : PChar;
  1950. Wbytes : SizeInt;
  1951. begin
  1952. Wbytes:=Length(W)*sizeof(char);
  1953. P:=PChar(pointer(S));
  1954. PE:=P+Length(S);
  1955. while (P<PE) and (P^ in WordDelims) do
  1956. Inc(P);
  1957. while (P<PE) do
  1958. begin
  1959. WordStart:=P;
  1960. repeat
  1961. Inc(P);
  1962. until (P>=PE) or (P^ in WordDelims);
  1963. if (pointer(P)-pointer(WordStart)=Wbytes) and (CompareByte(Pointer(W)^,WordStart^,Wbytes)=0) then
  1964. exit(true);
  1965. repeat
  1966. Inc(P); // Can increment to PE + 1.
  1967. until (P>=PE) or not (P^ in WordDelims);
  1968. end;
  1969. result:=false;
  1970. end;
  1971. function Numb2USA(const S: string): string;
  1972. var
  1973. i, NA: Integer;
  1974. begin
  1975. i:=Length(S);
  1976. Result:=S;
  1977. NA:=0;
  1978. while (i > 0) do begin
  1979. if ((Length(Result) - i + 1 - NA) mod 3 = 0) and (i <> 1) then
  1980. begin
  1981. insert(',', Result, i);
  1982. inc(NA);
  1983. end;
  1984. Dec(i);
  1985. end;
  1986. end;
  1987. function PadCenter(const S: string; Len: SizeInt): string;
  1988. var
  1989. Ns,Nfirstspaces: SizeInt;
  1990. begin
  1991. Ns:=Length(S);
  1992. if Ns>=Len then
  1993. exit(S);
  1994. SetLength(Result,Len);
  1995. Nfirstspaces:=SizeUint(Len) div 2-SizeUint(Ns) div 2;
  1996. FillCharSized(Pointer(Result),Nfirstspaces,ord(' '));
  1997. FillCharSized(PChar(Pointer(Result))+Ns+Nfirstspaces,Len-Ns-Nfirstspaces,ord(' '));
  1998. Move(Pointer(S)^,PChar(Pointer(Result))[Nfirstspaces],Ns*sizeof(char));
  1999. end;
  2000. function Dec2Numb(N: Longint; Len, Base: Byte): string;
  2001. var
  2002. C: Integer;
  2003. Number: Longint;
  2004. begin
  2005. if N=0 then
  2006. Result:='0'
  2007. else
  2008. begin
  2009. Number:=N;
  2010. Result:='';
  2011. while Number>0 do
  2012. begin
  2013. C:=Number mod Base;
  2014. if C>9 then
  2015. C:=C+55
  2016. else
  2017. C:=C+48;
  2018. Result:=Chr(C)+Result;
  2019. Number:=Number div Base;
  2020. end;
  2021. end;
  2022. if (Result<>'') then
  2023. Result:=AddChar('0',Result,Len);
  2024. end;
  2025. function Numb2Dec(S: string; Base: Byte): Longint;
  2026. var
  2027. i, P: sizeint;
  2028. begin
  2029. i:=Length(S);
  2030. Result:=0;
  2031. S:=UpperCase(S);
  2032. P:=1;
  2033. while (i>=1) do
  2034. begin
  2035. if (S[i]>'@') then
  2036. Result:=Result+(Ord(S[i])-55)*P
  2037. else
  2038. Result:=Result+(Ord(S[i])-48)*P;
  2039. Dec(i);
  2040. P:=P*Base;
  2041. end;
  2042. end;
  2043. function RomanToIntDontCare(const S: String): Longint;
  2044. {This was the original implementation of RomanToInt,
  2045. it is internally used in TryRomanToInt when Strictness = rcsDontCare}
  2046. const
  2047. RomanChars = ['C','D','I','L','M','V','X'];
  2048. RomanValues : array['C'..'X'] of Word
  2049. = (100,500,0,0,0,0,1,0,0,50,1000,0,0,0,0,0,0,0,0,5,0,10);
  2050. var
  2051. index, Next: AnsiChar;
  2052. i,l: SizeInt;
  2053. Negative: Boolean;
  2054. begin
  2055. Result:=0;
  2056. i:=0;
  2057. Negative:=(Length(S)>0) and (S[1]='-');
  2058. if Negative then
  2059. inc(i);
  2060. l:=Length(S);
  2061. while (i<l) do
  2062. begin
  2063. inc(i);
  2064. index:=UpCase(S[i]);
  2065. if index in RomanChars then
  2066. begin
  2067. if Succ(i)<=l then
  2068. Next:=UpCase(S[i+1])
  2069. else
  2070. Next:=#0;
  2071. if (Next in RomanChars) and (RomanValues[index]<RomanValues[Next]) then
  2072. begin
  2073. inc(Result, RomanValues[Next]);
  2074. Dec(Result, RomanValues[index]);
  2075. inc(i);
  2076. end
  2077. else
  2078. inc(Result, RomanValues[index]);
  2079. end
  2080. else
  2081. begin
  2082. Result:=0;
  2083. Exit;
  2084. end;
  2085. end;
  2086. if Negative then
  2087. Result:=-Result;
  2088. end;
  2089. { TryRomanToInt: try to convert a roman numeral to an integer
  2090. Parameters:
  2091. S: Roman numeral (like: 'MCMXXII')
  2092. N: Integer value of S (only meaningfull if the function succeeds)
  2093. Stricness: controls how strict the parsing of S is
  2094. - rcsStrict:
  2095. * Follow common subtraction rules
  2096. - only 1 preceding subtraction character allowed: IX = 9, but IIX <> 8
  2097. - from M you can only subtract C
  2098. - from D you can only subtract C
  2099. - from C you can only subtract X
  2100. - from L you can only subtract X
  2101. - from X you can only subtract I
  2102. - from V you can only subtract I
  2103. * The numeral is parsed in "groups" (first M's, then D's etc.), the next group to be parsed
  2104. must always be of a lower denomination than the previous one.
  2105. Example: 'MMDCCXX' is allowed but 'MMCCXXDD' is not
  2106. * There can only ever be 3 consecutive M's, C's, X's or I's
  2107. * There can only ever be 1 D, 1 L and 1 V
  2108. * After IX or IV there can be no more characters
  2109. * Negative numbers are not supported
  2110. // As a consequence the maximum allowed Roman numeral is MMMCMXCIX = 3999, also N can never become 0 (zero)
  2111. - rcsRelaxed: Like rcsStrict but with the following exceptions:
  2112. * An infinite number of (leading) M's is allowed
  2113. * Up to 4 consecutive M's, C's, X's and I's are allowed
  2114. // So this is allowed: 'MMMMMMCXIIII' = 6124
  2115. - rcsDontCare:
  2116. * no checking on the order of "groups" is done
  2117. * there are no restrictions on the number of consecutive chars
  2118. * negative numbers are supported
  2119. * an empty string as input will return True and N will be 0
  2120. * invalid input will return false
  2121. // for backwards comatibility: it supports rather ludicrous input like '-IIIMIII' -> -(2+(1000-1)+3)=-1004
  2122. }
  2123. function TryRomanToInt(S: String; out N: LongInt; Strictness: TRomanConversionStrictness = rcsRelaxed): Boolean;
  2124. var
  2125. i, Len: SizeInt;
  2126. Terminated: Boolean;
  2127. begin
  2128. Result := (False);
  2129. S := UpperCase(S); //don't use AnsiUpperCase please
  2130. Len := Length(S);
  2131. if (Strictness = rcsDontCare) then
  2132. begin
  2133. N := RomanToIntDontCare(S);
  2134. if (N = 0) then
  2135. begin
  2136. Result := (Len = 0);
  2137. end
  2138. else
  2139. Result := True;
  2140. Exit;
  2141. end;
  2142. if (Len = 0) then
  2143. begin
  2144. Result:=true;
  2145. N:=0;
  2146. Exit;
  2147. end;
  2148. i := 1;
  2149. N := 0;
  2150. Terminated := False;
  2151. //leading M's
  2152. while (i <= Len) and ((Strictness <> rcsStrict) or (i < 4)) and (S[i] = 'M') do
  2153. begin
  2154. //writeln('TryRomanToInt: Found 1000');
  2155. Inc(i);
  2156. N := N + 1000;
  2157. end;
  2158. //then CM or or CD or D or (C, CC, CCC, CCCC)
  2159. if (i <= Len) and (S[i] = 'D') then
  2160. begin
  2161. //writeln('TryRomanToInt: Found 500');
  2162. Inc(i);
  2163. N := N + 500;
  2164. end
  2165. else if (i + 1 <= Len) and (S[i] = 'C') then
  2166. begin
  2167. if (S[i+1] = 'M') then
  2168. begin
  2169. //writeln('TryRomanToInt: Found 900');
  2170. Inc(i,2);
  2171. N := N + 900;
  2172. end
  2173. else if (S[i+1] = 'D') then
  2174. begin
  2175. //writeln('TryRomanToInt: Found 400');
  2176. Inc(i,2);
  2177. N := N + 400;
  2178. end;
  2179. end ;
  2180. //next max 4 or 3 C's, depending on Strictness
  2181. if (i <= Len) and (S[i] = 'C') then
  2182. begin
  2183. //find max 4 C's
  2184. //writeln('TryRomanToInt: Found 100');
  2185. Inc(i);
  2186. N := N + 100;
  2187. if (i <= Len) and (S[i] = 'C') then
  2188. begin
  2189. //writeln('TryRomanToInt: Found 100');
  2190. Inc(i);
  2191. N := N + 100;
  2192. end;
  2193. if (i <= Len) and (S[i] = 'C') then
  2194. begin
  2195. //writeln('TryRomanToInt: Found 100');
  2196. Inc(i);
  2197. N := N + 100;
  2198. end;
  2199. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'C') then
  2200. begin
  2201. //writeln('TryRomanToInt: Found 100');
  2202. Inc(i);
  2203. N := N + 100;
  2204. end;
  2205. end;
  2206. //then XC or XL
  2207. if (i + 1 <= Len) and (S[i] = 'X') then
  2208. begin
  2209. if (S[i+1] = 'C') then
  2210. begin
  2211. //writeln('TryRomanToInt: Found 90');
  2212. Inc(i,2);
  2213. N := N + 90;
  2214. end
  2215. else if (S[i+1] = 'L') then
  2216. begin
  2217. //writeln('TryRomanToInt: Found 40');
  2218. Inc(i,2);
  2219. N := N + 40;
  2220. end;
  2221. end;
  2222. //then L
  2223. if (i <= Len) and (S[i] = 'L') then
  2224. begin
  2225. //writeln('TryRomanToInt: Found 50');
  2226. Inc(i);
  2227. N := N + 50;
  2228. end;
  2229. //then (X, xx, xxx, xxxx)
  2230. if (i <= Len) and (S[i] = 'X') then
  2231. begin
  2232. //find max 3 or 4 X's, depending on Strictness
  2233. //writeln('TryRomanToInt: Found 10');
  2234. Inc(i);
  2235. N := N + 10;
  2236. if (i <= Len) and (S[i] = 'X') then
  2237. begin
  2238. //writeln('TryRomanToInt: Found 10');
  2239. Inc(i);
  2240. N := N + 10;
  2241. end;
  2242. if (i <= Len) and (S[i] = 'X') then
  2243. begin
  2244. //writeln('TryRomanToInt: Found 10');
  2245. Inc(i);
  2246. N := N + 10;
  2247. end;
  2248. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'X') then
  2249. begin
  2250. //writeln('TryRomanToInt: Found 10');
  2251. Inc(i);
  2252. N := N + 10;
  2253. end;
  2254. end;
  2255. //then IX or IV
  2256. if (i + 1 <= Len) and (S[i] = 'I') then
  2257. begin
  2258. if (S[i+1] = 'X') then
  2259. begin
  2260. Terminated := (True);
  2261. //writeln('TryRomanToInt: Found 9');
  2262. Inc(i,2);
  2263. N := N + 9;
  2264. end
  2265. else if (S[i+1] = 'V') then
  2266. begin
  2267. Terminated := (True);
  2268. //writeln('TryRomanToInt: Found 4');
  2269. Inc(i,2);
  2270. N := N + 4;
  2271. end;
  2272. end;
  2273. //then V
  2274. if (not Terminated) and (i <= Len) and (S[i] = 'V') then
  2275. begin
  2276. //writeln('TryRomanToInt: Found 5');
  2277. Inc(i);
  2278. N := N + 5;
  2279. end;
  2280. //then I
  2281. if (not Terminated) and (i <= Len) and (S[i] = 'I') then
  2282. begin
  2283. Terminated := (True);
  2284. //writeln('TryRomanToInt: Found 1');
  2285. Inc(i);
  2286. N := N + 1;
  2287. //Find max 2 or 3 closing I's, depending on strictness
  2288. if (i <= Len) and (S[i] = 'I') then
  2289. begin
  2290. //writeln('TryRomanToInt: Found 1');
  2291. Inc(i);
  2292. N := N + 1;
  2293. end;
  2294. if (i <= Len) and (S[i] = 'I') then
  2295. begin
  2296. //writeln('TryRomanToInt: Found 1');
  2297. Inc(i);
  2298. N := N + 1;
  2299. end;
  2300. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'I') then
  2301. begin
  2302. //writeln('TryRomanToInt: Found 1');
  2303. Inc(i);
  2304. N := N + 1;
  2305. end;
  2306. end;
  2307. //writeln('TryRomanToInt: Len = ',Len,' i = ',i);
  2308. Result := (i > Len);
  2309. //if Result then writeln('TryRomanToInt: N = ',N);
  2310. end;
  2311. function RomanToInt(const S: string; Strictness: TRomanConversionStrictness = rcsRelaxed): Longint;
  2312. begin
  2313. if not TryRomanToInt(S, Result, Strictness) then
  2314. raise EConvertError.CreateFmt(SInvalidRomanNumeral,[S]);
  2315. end;
  2316. function RomanToIntDef(const S: String; const ADefault: Longint;
  2317. Strictness: TRomanConversionStrictness): Longint;
  2318. begin
  2319. if not TryRomanToInt(S, Result, Strictness) then
  2320. Result := ADefault;
  2321. end;
  2322. function IntToRoman(Value: Longint): string;
  2323. const
  2324. Arabics : Array[1..13] of Integer
  2325. = (1,4,5,9,10,40,50,90,100,400,500,900,1000);
  2326. Romans : Array[1..13] of String
  2327. = ('I','IV','V','IX','X','XL','L','XC','C','CD','D','CM','M');
  2328. var
  2329. i: Integer;
  2330. begin
  2331. Result:='';
  2332. for i:=13 downto 1 do
  2333. while (Value >= Arabics[i]) do
  2334. begin
  2335. Value:=Value-Arabics[i];
  2336. Result:=Result+Romans[i];
  2337. end;
  2338. end;
  2339. function IntToBin(Value: Longint; Digits, Spaces: Integer): string;
  2340. var endpos : integer;
  2341. p,p2:PChar;
  2342. k: integer;
  2343. begin
  2344. Result:='';
  2345. if (Digits>32) then
  2346. Digits:=32;
  2347. if (spaces=0) then
  2348. begin
  2349. result:=inttobin(value,digits);
  2350. exit;
  2351. end;
  2352. endpos:=digits+ (digits-1) div spaces;
  2353. setlength(result,endpos);
  2354. p:=@result[endpos];
  2355. p2:=@result[1];
  2356. k:=spaces;
  2357. while (p>=p2) do
  2358. begin
  2359. if k=0 then
  2360. begin
  2361. p^:=' ';
  2362. dec(p);
  2363. k:=spaces;
  2364. end;
  2365. p^:=chr(48+(cardinal(value) and 1));
  2366. value:=cardinal(value) shr 1;
  2367. dec(p);
  2368. dec(k);
  2369. end;
  2370. end;
  2371. function IntToBin(Value: Longint; Digits: Integer): string;
  2372. var p,p2 : PChar;
  2373. begin
  2374. result:='';
  2375. if digits<=0 then exit;
  2376. setlength(result,digits);
  2377. p:=PChar(pointer(@result[digits]));
  2378. p2:=PChar(pointer(@result[1]));
  2379. // typecasts because we want to keep intto* delphi compat and take an integer
  2380. while (p>=p2) and (cardinal(value)>0) do
  2381. begin
  2382. p^:=chr(48+(cardinal(value) and 1));
  2383. value:=cardinal(value) shr 1;
  2384. dec(p);
  2385. end;
  2386. digits:=p-p2+1;
  2387. if digits>0 then
  2388. fillchar(result[1],digits,#48);
  2389. end;
  2390. function intToBin(Value: int64; Digits:integer): string;
  2391. var p,p2 : PChar;
  2392. begin
  2393. result:='';
  2394. if digits<=0 then exit;
  2395. setlength(result,digits);
  2396. p:=PChar(pointer(@result[digits]));
  2397. p2:=PChar(pointer(@result[1]));
  2398. // typecasts because we want to keep intto* delphi compat and take a signed val
  2399. // and avoid warnings
  2400. while (p>=p2) and (qword(value)>0) do
  2401. begin
  2402. p^:=chr(48+(cardinal(value) and 1));
  2403. value:=qword(value) shr 1;
  2404. dec(p);
  2405. end;
  2406. digits:=p-p2+1;
  2407. if digits>0 then
  2408. fillchar(result[1],digits,#48);
  2409. end;
  2410. function FindPart(const HelpWilds, InputStr: string): SizeInt;
  2411. var
  2412. i, J, NWilds: SizeInt;
  2413. begin
  2414. if Pos('?',HelpWilds)=0 then
  2415. Exit(Pos(HelpWilds, inputStr));
  2416. NWilds:=Length(HelpWilds);
  2417. for i:=0 to Length(inputStr) - NWilds do
  2418. begin
  2419. J:=1;
  2420. while (J<=NWilds) and ((inputStr[i + J] = HelpWilds[J]) or (HelpWilds[J] = '?')) do
  2421. Inc(J);
  2422. if J>NWilds then
  2423. Exit(i+1);
  2424. end;
  2425. Result:=0;
  2426. end;
  2427. function IsWild(InputStr, Wilds: string; IgnoreCase: Boolean): Boolean;
  2428. var
  2429. Wp,We,Ip,Ie,WpBack,IpBack: PChar;
  2430. begin
  2431. if ignoreCase then { upcase all letters }
  2432. begin
  2433. inputStr:=AnsiUpperCase(inputStr);
  2434. Wilds:=AnsiUpperCase(Wilds);
  2435. end;
  2436. Wp:=PChar(Pointer(Wilds));
  2437. We:=Wp+Length(Wilds);
  2438. Ip:=PChar(Pointer(InputStr));
  2439. Ie:=Ip+Length(InputStr);
  2440. WpBack:=nil;
  2441. while Ip<Ie do
  2442. begin
  2443. if Wp<We then
  2444. if (Wp^=Ip^) or (Wp^='?') then
  2445. begin
  2446. Inc(Ip);
  2447. Inc(Wp);
  2448. continue;
  2449. end
  2450. else if Wp^='*' then
  2451. begin
  2452. Inc(Wp);
  2453. WpBack:=Wp;
  2454. IpBack:=Ip;
  2455. continue;
  2456. end;
  2457. if not Assigned(WpBack) then
  2458. exit(false);
  2459. Wp:=WpBack;
  2460. Inc(IpBack);
  2461. Ip:=IpBack;
  2462. end;
  2463. while (Wp<We) and (Wp^='*') do
  2464. Inc(Wp);
  2465. Result:=Wp=We;
  2466. end;
  2467. function XorString(const Key, Src: ShortString): ShortString;
  2468. var
  2469. i: SizeInt;
  2470. begin
  2471. Result:=Src;
  2472. if Length(Key) > 0 then
  2473. for i:=1 to Length(Src) do
  2474. Result[i]:=Chr(Byte(Key[1 + ((i - 1) mod Length(Key))]) xor Ord(Src[i]));
  2475. end;
  2476. function XorEncode(const Key, Source: Ansistring): Ansistring;
  2477. var
  2478. i: Integer;
  2479. C: Byte;
  2480. begin
  2481. Result:='';
  2482. for i:=1 to Length(Source) do
  2483. begin
  2484. if Length(Key) > 0 then
  2485. C:=Byte(Key[1 + ((i - 1) mod Length(Key))]) xor Byte(Source[i])
  2486. else
  2487. C:=Byte(Source[i]);
  2488. Result:=Result+AnsiLowerCase(intToHex(C, 2));
  2489. end;
  2490. end;
  2491. function XorDecode(const Key, Source: Ansistring): Ansistring;
  2492. var
  2493. i: Integer;
  2494. C: AnsiChar;
  2495. begin
  2496. Result:='';
  2497. for i:=0 to Length(Source) div 2 - 1 do
  2498. begin
  2499. C:=Chr(StrTointDef('$' + Copy(Source, (i * 2) + 1, 2), Ord(' ')));
  2500. if Length(Key) > 0 then
  2501. C:=Chr(Byte(Key[1 + (i mod Length(Key))]) xor Byte(C));
  2502. Result:=Result + C;
  2503. end;
  2504. end;
  2505. function GetCmdLineArg(const Switch: string; SwitchChars: TSysCharSet): string;
  2506. var
  2507. i: Integer;
  2508. S: string;
  2509. begin
  2510. i:=1;
  2511. Result:='';
  2512. while (Result='') and (i<=ParamCount) do
  2513. begin
  2514. S:=ParamStr(i);
  2515. if (SwitchChars=[]) or ((S[1] in SwitchChars) and (Length(S) > 1)) and
  2516. (AnsiCompareText(Copy(S,2,Length(S)-1),Switch)=0) then
  2517. begin
  2518. inc(i);
  2519. if i<=ParamCount then
  2520. Result:=ParamStr(i);
  2521. end;
  2522. inc(i);
  2523. end;
  2524. end;
  2525. function RPosEx(C: AnsiChar; const S: AnsiString; offs: SizeInt): SizeInt;
  2526. var p,p2: PAnsiChar;
  2527. Begin
  2528. If (offs>0) and (offs<=Length(S)) Then
  2529. begin
  2530. p:=@s[offs];
  2531. p2:=@s[1];
  2532. while (p2<=p) and (p^<>c) do dec(p);
  2533. RPosEx:=(p-p2)+1;
  2534. end
  2535. else
  2536. RPosEX:=0;
  2537. End;
  2538. function RPos(c: AnsiChar; const S: AnsiString): SizeInt;
  2539. Begin
  2540. Result:=RPosEx(c,S,Length(S)); { Length(S) must be used because character version returns 0 on offs > length. }
  2541. End;
  2542. function RPos(const Substr: AnsiString; const Source: AnsiString): SizeInt;
  2543. begin
  2544. Result:=RPosEx(Substr,Source,High(Result)); { High(Result) is possible because string version clamps offs > length to offs = length. }
  2545. end;
  2546. function RPosEx(const Substr: AnsiString; const Source: AnsiString; offs: SizeInt): SizeInt;
  2547. var
  2548. MaxLen,llen : SizeInt;
  2549. c : AnsiChar;
  2550. pc,pc2 : PAnsiChar;
  2551. begin
  2552. llen:=Length(SubStr);
  2553. maxlen:=length(source);
  2554. if offs<maxlen then maxlen:=offs;
  2555. if (llen>0) and (maxlen>0) and ( llen<=maxlen) then
  2556. begin
  2557. pc:=@source[maxlen-llen+1];
  2558. pc2:=@source[1];
  2559. c:=substr[1];
  2560. repeat
  2561. if (c=pc^) and
  2562. (CompareChar(Substr[1],pc^,llen)=0) then
  2563. begin
  2564. rPosex:=pc-pc2+1;
  2565. exit;
  2566. end;
  2567. dec(pc);
  2568. until pc<pc2;
  2569. end;
  2570. rPosex:=0;
  2571. end;
  2572. function RPosEx(C: unicodechar; const S: UnicodeString; offs: SizeInt): SizeInt;
  2573. var p,p2: PUnicodeChar;
  2574. Begin
  2575. If (offs>0) and (offs<=Length(S)) Then
  2576. begin
  2577. p:=@s[offs];
  2578. p2:=@s[1];
  2579. while (p2<=p) and (p^<>c) do dec(p);
  2580. RPosEx:=SizeUint(pointer(p)-pointer(p2)) div sizeof(unicodechar)+1; { p-p2+1 but avoids signed division... }
  2581. end
  2582. else
  2583. RPosEX:=0;
  2584. End;
  2585. function RPos(c: Unicodechar; const S: UnicodeString): SizeInt;
  2586. Begin
  2587. Result:=RPosEx(c,S,Length(S)); { Length(S) must be used because character version returns 0 on offs > length. }
  2588. End;
  2589. function RPos(const Substr: UnicodeString; const Source: UnicodeString): SizeInt;
  2590. begin
  2591. Result:=RPosEx(Substr,Source,High(Result)); { High(Result) is possible because string version clamps offs > length to offs = length. }
  2592. end;
  2593. function RPosEx(const Substr: UnicodeString; const Source: UnicodeString; offs: SizeInt): SizeInt;
  2594. var
  2595. MaxLen,llen : SizeInt;
  2596. c : unicodechar;
  2597. pc,pc2 : punicodechar;
  2598. begin
  2599. llen:=Length(SubStr);
  2600. maxlen:=length(source);
  2601. if offs<maxlen then maxlen:=offs;
  2602. if (llen>0) and (maxlen>0) and ( llen<=maxlen) then
  2603. begin
  2604. pc:=@source[maxlen-llen+1];
  2605. pc2:=@source[1];
  2606. c:=substr[1];
  2607. repeat
  2608. if (c=pc^) and
  2609. (Compareword(Substr[1],pc^,llen)=0) then
  2610. begin
  2611. rPosex:=SizeUint(pointer(pc)-pointer(pc2)) div sizeof(unicodechar)+1; { pc-pc2+1 but avoids signed division... }
  2612. exit;
  2613. end;
  2614. dec(pc);
  2615. until pc<pc2;
  2616. end;
  2617. rPosex:=0;
  2618. end;
  2619. procedure BinToHex(BinValue: PAnsiChar; HexValue: PAnsiChar; BinBufSize: Integer);
  2620. var
  2621. i : longint;
  2622. begin
  2623. for i:=0 to BinBufSize-1 do
  2624. begin
  2625. HexValue[0]:=HexDigits[((Ord(BinValue[i]) shr 4))];
  2626. HexValue[1]:=HexDigits[((Ord(BinValue[i]) and 15))];
  2627. Inc(HexValue,2);
  2628. end;
  2629. end;
  2630. procedure BinToHex(BinValue: PAnsiChar; HexValue: PWideChar; BinBufSize: Integer);
  2631. var
  2632. i : longint;
  2633. begin
  2634. for i:=0 to BinBufSize-1 do
  2635. begin
  2636. HexValue[0]:=HexDigitsW[((Ord(BinValue[i]) shr 4))];
  2637. HexValue[1]:=HexDigitsW[((Ord(BinValue[i]) and 15))];
  2638. Inc(HexValue,2);
  2639. end;
  2640. end;
  2641. procedure BinToHex(const BinBuffer: TBytes; BinBufOffset: Integer; var HexBuffer: TBytes; HexBufOffset: Integer; Count: Integer);
  2642. var
  2643. i : longint;
  2644. begin
  2645. for i:=0 to Count-1 do
  2646. begin
  2647. HexBuffer[HexBufOffset+2*i+0]:=Byte(HexDigits[(BinBuffer[BinBufOffset + i] shr 4)]);
  2648. HexBuffer[HexBufOffset+2*i+1]:=Byte(HexDigits[(BinBuffer[BinBufOffset + i] and 15)]);
  2649. end;
  2650. end;
  2651. procedure BinToHex(BinValue: Pointer; HexValue: PAnsiChar; BinBufSize: Integer);
  2652. begin
  2653. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2654. end;
  2655. procedure BinToHex(BinValue: Pointer; HexValue: PWideChar; BinBufSize: Integer);
  2656. begin
  2657. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2658. end;
  2659. procedure BinToHex(const BinValue; HexValue: PAnsiChar; BinBufSize: Integer);
  2660. begin
  2661. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2662. end;
  2663. procedure BinToHex(const BinValue; HexValue: PWideChar; BinBufSize: Integer);
  2664. begin
  2665. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2666. end;
  2667. function HexToBin(const HexText: PWideChar; HexTextOffset: Integer; var BinBuffer: TBytes; BinBufOffset: Integer; Count: Integer): Integer;
  2668. var
  2669. i : Integer;
  2670. PText : PWideChar;
  2671. PBinBuf : PAnsiChar;
  2672. begin
  2673. PText:=HexText+HexTextOffset;
  2674. PBinBuf:=PAnsiChar(BinBuffer)+BinBufOffset;
  2675. i:=Count;
  2676. Result:=HexToBin(PText, PBinBuf, i);
  2677. end;
  2678. function HexToBin(const HexText: TBytes; HexTextOffset: Integer; var BinBuffer: TBytes; BinBufOffset: Integer; Count: Integer): Integer;
  2679. var
  2680. i : Integer;
  2681. PText : PAnsiChar;
  2682. PBinBuf : PAnsiChar;
  2683. begin
  2684. PText:=PAnsiChar(HexText)+HexTextOffset;
  2685. PBinBuf:=PAnsiChar(BinBuffer)+BinBufOffset;
  2686. i:=Count;
  2687. Result:=HexToBin(PText, PBinBuf, i);
  2688. end;
  2689. function HexToBin(HexText: PWideChar; BinBuffer: Pointer; BinBufSize: Integer): Integer;
  2690. begin
  2691. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2692. end;
  2693. function HexToBin(const HexText: PWideChar; var BinBuffer; BinBufSize: Integer): Integer;
  2694. begin
  2695. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2696. end;
  2697. function HexToBin(HexText: PAnsiChar; BinBuffer: PAnsiChar; BinBufSize: Integer): Integer;
  2698. var
  2699. i,num : integer;
  2700. begin
  2701. i:=BinBufSize;
  2702. while (i>0) do
  2703. begin
  2704. // get value of first character
  2705. case HexText^ of
  2706. '0'..'9':
  2707. num:=ord(HexText^)-ord('0');
  2708. 'a'..'f':
  2709. num:=ord(HexText^)-(ord('a')-10);
  2710. 'A'..'F':
  2711. num:=ord(HexText^)-(ord('A')-10);
  2712. else
  2713. break;
  2714. end;
  2715. // add value of second character
  2716. case HexText[1] of
  2717. '0'..'9':
  2718. num:=num shl 4 or (ord(HexText[1])-ord('0'));
  2719. 'a'..'f':
  2720. num:=num shl 4 or (ord(HexText[1])-(ord('a')-10));
  2721. 'A'..'F':
  2722. num:=num shl 4 or (ord(HexText[1])-(ord('A')-10));
  2723. else
  2724. break;
  2725. end;
  2726. BinBuffer^:=AnsiChar(num);
  2727. inc(BinBuffer);
  2728. inc(HexText,2);
  2729. dec(i);
  2730. end;
  2731. Result:=BinBufSize-i;
  2732. end;
  2733. function HexToBin(HexText: PWideChar; BinBuffer: PAnsiChar; BinBufSize: Integer): Integer;
  2734. var
  2735. i,num : integer;
  2736. begin
  2737. i:=BinBufSize;
  2738. while (i>0) do
  2739. begin
  2740. // get value of first character
  2741. case HexText^ of
  2742. '0'..'9':
  2743. num:=ord(HexText^)-ord('0');
  2744. 'a'..'f':
  2745. num:=ord(HexText^)-(ord('a')-10);
  2746. 'A'..'F':
  2747. num:=ord(HexText^)-(ord('A')-10);
  2748. else // this includes >#255.
  2749. break;
  2750. end;
  2751. // add value of second character
  2752. case HexText[1] of
  2753. '0'..'9':
  2754. num:=num shl 4 or (ord(HexText[1])-ord('0'));
  2755. 'a'..'f':
  2756. num:=num shl 4 or (ord(HexText[1])-(ord('a')-10));
  2757. 'A'..'F':
  2758. num:=num shl 4 or (ord(HexText[1])-(ord('A')-10));
  2759. else // this includes >#255.
  2760. break;
  2761. end;
  2762. BinBuffer^:=AnsiChar(num);
  2763. inc(BinBuffer);
  2764. inc(HexText,2);
  2765. dec(i);
  2766. end;
  2767. Result:=BinBufSize-i;
  2768. end;
  2769. function HexToBin(HexText: PAnsiChar; var BinBuffer; BinBufSize: Integer): Integer;
  2770. begin
  2771. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2772. end;
  2773. function HexToBin(const HexText: PAnsiChar; BinBuffer: Pointer; BinBufSize: Integer): Integer;
  2774. begin
  2775. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2776. end;
  2777. function PosSetEx(const c: TSysCharSet; const s: ansistring; count: Integer): SizeInt;
  2778. var i,j:SizeInt;
  2779. begin
  2780. i:=length(s);
  2781. j:=count;
  2782. while (j<=i) and (not (s[j] in c)) do inc(j);
  2783. if (j>i) then
  2784. j:=0; // not found.
  2785. result:=j;
  2786. end;
  2787. function PosSet(const c: TSysCharSet; const s: ansistring): SizeInt;
  2788. begin
  2789. result:=possetex(c,s,1);
  2790. end;
  2791. function StringToCharset(const c: string): TSysCharSet;
  2792. var
  2793. i: SizeInt;
  2794. begin
  2795. result:=[];
  2796. for i:=1 to length(c) do
  2797. include(result,c[i]);
  2798. end;
  2799. function PosSetEx(const c: string; const s: ansistring; count: Integer): SizeInt;
  2800. begin
  2801. result:=0;
  2802. if length(c)>0 then
  2803. result:=possetex(StringToCharset(c),s,count);
  2804. end;
  2805. function PosSet(const c: string; const s: ansistring): SizeInt;
  2806. begin
  2807. result:=0;
  2808. if length(c)>0 then
  2809. result:=possetex(StringToCharset(c),s,1);
  2810. end;
  2811. procedure Removeleadingchars(VAR S: AnsiString; const CSet: TSysCharset);
  2812. VAR I,J : Longint;
  2813. Begin
  2814. I:=Length(S);
  2815. J:=1;
  2816. While (J<=I) And (S[J] IN CSet) DO
  2817. INC(J);
  2818. IF J>1 Then
  2819. Delete(S,1,J-1);
  2820. End;
  2821. procedure Removeleadingchars(VAR S: UnicodeString; const CSet: TSysCharset);
  2822. VAR I,J : Longint;
  2823. Begin
  2824. I:=Length(S);
  2825. J:=1;
  2826. While (J<=I) And (S[J] IN CSet) DO
  2827. INC(J);
  2828. IF J>1 Then
  2829. Delete(S,1,J-1);
  2830. End;
  2831. function TrimLeftSet(const S: String;const CSet:TSysCharSet): String;
  2832. begin
  2833. result:=s;
  2834. removeleadingchars(result,cset);
  2835. end;
  2836. procedure RemoveTrailingChars(VAR S: AnsiString; const CSet: TSysCharset);
  2837. VAR I,J: LONGINT;
  2838. Begin
  2839. I:=Length(S);
  2840. J:=I;
  2841. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2842. IF J<>I Then
  2843. SetLength(S,J);
  2844. End;
  2845. procedure RemoveTrailingChars(VAR S: UnicodeString; const CSet: TSysCharset);
  2846. VAR I,J: LONGINT;
  2847. Begin
  2848. I:=Length(S);
  2849. J:=I;
  2850. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2851. IF J<>I Then
  2852. SetLength(S,J);
  2853. End;
  2854. function TrimRightSet(const S: String; const CSet: TSysCharSet): String;
  2855. begin
  2856. result:=s;
  2857. RemoveTrailingchars(result,cset);
  2858. end;
  2859. procedure RemovePadChars(VAR S: AnsiString; const CSet: TSysCharset);
  2860. VAR J,K: SizeInt;
  2861. Begin
  2862. J:=Length(S);
  2863. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2864. k:=1;
  2865. While (k<=J) And (S[k] IN CSet) DO
  2866. INC(k);
  2867. IF k>1 Then
  2868. move(s[k],s[1],(j-k+1)*sizeof(S[1]));
  2869. setlength(s,j-k+1);
  2870. End;
  2871. procedure RemovePadChars(VAR S: UnicodeString; const CSet: TSysCharset);
  2872. VAR J,K: SizeInt;
  2873. Begin
  2874. J:=Length(S);
  2875. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2876. k:=1;
  2877. While (k<=J) And (S[k] IN CSet) DO
  2878. INC(k);
  2879. IF k>1 Then
  2880. move(s[k],s[1],(j-k+1)*sizeof(S[1]));
  2881. setlength(s,j-k+1);
  2882. End;
  2883. function TrimSet(const S: String;const CSet:TSysCharSet): String;
  2884. begin
  2885. result:=s;
  2886. RemovePadChars(result,cset);
  2887. end;
  2888. Function SplitCommandLine(S : RawByteString) : TRawByteStringArray;
  2889. Function GetNextWord : RawByteString;
  2890. Const
  2891. WhiteSpace = [' ',#9,#10,#13];
  2892. Literals = ['"',''''];
  2893. Var
  2894. Wstart,wend : Integer;
  2895. InLiteral : Boolean;
  2896. LastLiteral : AnsiChar;
  2897. Procedure AppendToResult;
  2898. begin
  2899. Result:=Result+Copy(S,WStart,WEnd-WStart);
  2900. WStart:=Wend+1;
  2901. end;
  2902. begin
  2903. Result:='';
  2904. WStart:=1;
  2905. While (WStart<=Length(S)) and charinset(S[WStart],WhiteSpace) do
  2906. Inc(WStart);
  2907. WEnd:=WStart;
  2908. InLiteral:=False;
  2909. LastLiteral:=#0;
  2910. While (Wend<=Length(S)) and (Not charinset(S[Wend],WhiteSpace) or InLiteral) do
  2911. begin
  2912. if charinset(S[Wend],Literals) then
  2913. If InLiteral then
  2914. begin
  2915. InLiteral:=Not (S[Wend]=LastLiteral);
  2916. if not InLiteral then
  2917. AppendToResult;
  2918. end
  2919. else
  2920. begin
  2921. InLiteral:=True;
  2922. LastLiteral:=S[Wend];
  2923. AppendToResult;
  2924. end;
  2925. inc(wend);
  2926. end;
  2927. AppendToResult;
  2928. While (WEnd<=Length(S)) and (S[Wend] in WhiteSpace) do
  2929. inc(Wend);
  2930. Delete(S,1,WEnd-1);
  2931. end;
  2932. Var
  2933. W : RawByteString;
  2934. len : Integer;
  2935. begin
  2936. Len:=0;
  2937. Result:=Default(TRawByteStringArray);
  2938. SetLength(Result,(Length(S) div 2)+1);
  2939. While Length(S)>0 do
  2940. begin
  2941. W:=GetNextWord;
  2942. If (W<>'') then
  2943. begin
  2944. Result[Len]:=W;
  2945. Inc(Len);
  2946. end;
  2947. end;
  2948. SetLength(Result,Len);
  2949. end;
  2950. Function SplitCommandLine(S : UnicodeString) : TUnicodeStringArray;
  2951. Function GetNextWord : UnicodeString;
  2952. Const
  2953. WhiteSpace = [' ',#9,#10,#13];
  2954. Literals = ['"',''''];
  2955. Var
  2956. Wstart,wend : Integer;
  2957. InLiteral : Boolean;
  2958. LastLiteral : AnsiChar;
  2959. Procedure AppendToResult;
  2960. begin
  2961. Result:=Result+Copy(S,WStart,WEnd-WStart);
  2962. WStart:=Wend+1;
  2963. end;
  2964. begin
  2965. Result:='';
  2966. WStart:=1;
  2967. While (WStart<=Length(S)) and charinset(S[WStart],WhiteSpace) do
  2968. Inc(WStart);
  2969. WEnd:=WStart;
  2970. InLiteral:=False;
  2971. LastLiteral:=#0;
  2972. While (Wend<=Length(S)) and (Not charinset(S[Wend],WhiteSpace) or InLiteral) do
  2973. begin
  2974. if charinset(S[Wend],Literals) then
  2975. If InLiteral then
  2976. begin
  2977. InLiteral:=Not (S[Wend]=LastLiteral);
  2978. if not InLiteral then
  2979. AppendToResult;
  2980. end
  2981. else
  2982. begin
  2983. InLiteral:=True;
  2984. LastLiteral:=S[Wend];
  2985. AppendToResult;
  2986. end;
  2987. inc(wend);
  2988. end;
  2989. AppendToResult;
  2990. While (WEnd<=Length(S)) and (S[Wend] in WhiteSpace) do
  2991. inc(Wend);
  2992. Delete(S,1,WEnd-1);
  2993. end;
  2994. Var
  2995. W : UnicodeString;
  2996. len : Integer;
  2997. begin
  2998. Len:=0;
  2999. Result:=Default(TUnicodeStringArray);
  3000. SetLength(Result,(Length(S) div 2)+1);
  3001. While Length(S)>0 do
  3002. begin
  3003. W:=GetNextWord;
  3004. If (W<>'') then
  3005. begin
  3006. Result[Len]:=W;
  3007. Inc(Len);
  3008. end;
  3009. end;
  3010. SetLength(Result,Len);
  3011. end;
  3012. end.