strutils.pp 99 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528
  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. // All indices are zero-based to be used with PChar(Pointer(...))[Sp] form,
  992. // which allows to omit Sp < Length check, instead reading terminating #0 at Sp = Length.
  993. type
  994. TRunningNumberCompare = record
  995. S: string;
  996. Sp: SizeInt;
  997. end;
  998. function ScanText(const S: string; Sp: SizeInt): SizeInt;
  999. begin
  1000. Result := Sp;
  1001. repeat
  1002. while not (PChar(Pointer(S))[Result] in ['0' .. '9', #0]) do
  1003. Inc(Result);
  1004. // End?
  1005. if Result >= Length(S) then
  1006. exit;
  1007. // Undo spaces if there is a number.
  1008. if PChar(Pointer(S))[Result] in ['0' .. '9'] then
  1009. begin
  1010. while (Result > Sp) and (PChar(Pointer(S))[Result - 1] in [' ']) do
  1011. Dec(Result);
  1012. exit;
  1013. end;
  1014. // Embedded #0.
  1015. Inc(Result);
  1016. until false;
  1017. end;
  1018. function InitNumber(out C: TRunningNumberCompare; const S: string; Sp: SizeInt): boolean;
  1019. begin
  1020. C.S := S;
  1021. C.Sp := Sp;
  1022. while PChar(Pointer(S))[C.Sp] in [' '] do
  1023. Inc(C.Sp);
  1024. while (PChar(Pointer(S))[C.Sp] in ['0']) and (PChar(Pointer(S))[C.Sp + 1] in ['0' .. '9']) do
  1025. Inc(C.Sp);
  1026. Result := PChar(Pointer(S))[C.Sp] in ['0' .. '9'];
  1027. end;
  1028. function NextDigit(var C: TRunningNumberCompare): Integer;
  1029. var
  1030. Ch: Char;
  1031. begin
  1032. Ch := PChar(Pointer(C.S))[C.Sp];
  1033. if Ch in ['0' .. '9'] then
  1034. begin
  1035. Result := Ord(Ch) - Ord('0');
  1036. Inc(C.Sp);
  1037. end else
  1038. Result := -1;
  1039. end;
  1040. function ScanAndCompareTexts(const S1: string; var S1p: SizeInt; const S2: string; var S2p: SizeInt): Integer;
  1041. var
  1042. S1e, S2e: SizeInt;
  1043. begin
  1044. S1e := ScanText(S1, S1p);
  1045. S2e := ScanText(S2, S2p);
  1046. Result := (S1e - S1p) - (S2e - S2p);
  1047. if Result = 0 then { Shortcut same strings (file0000, file0001). }
  1048. Result := CompareByte(S1[1 + S1p], S2[1 + S2p], (S1e - S1p) * SizeOf(Char));
  1049. if Result <> 0 then
  1050. Result := {$if sizeof(char) = 1} AnsiCompareText {$else} UnicodeCompareText {$endif}
  1051. (Copy(S1, 1 + S1p, S1e - S1p), Copy(S2, 1 + S2p, S2e - S2p));
  1052. S1p := S1e;
  1053. S2p := S2e;
  1054. end;
  1055. function ScanAndCompareNumbers(const S1: string; var S1p: SizeInt; const S2: string; var S2p: SizeInt): Integer;
  1056. var
  1057. C1, C2: TRunningNumberCompare;
  1058. Digit1, Digit2: Integer;
  1059. begin
  1060. if not InitNumber(C1, S1, S1p) or not InitNumber(C2, S2, S2p) then
  1061. Exit(0);
  1062. Result := 0;
  1063. repeat
  1064. Digit1 := NextDigit(C1);
  1065. Digit2 := NextDigit(C2);
  1066. if (Digit1 < 0) <> (Digit2 < 0) then
  1067. Exit(2 * Ord(Digit2 < 0) - 1); // C1 > C2 if C2 ended first, and vice versa.
  1068. if Digit1 < 0 then
  1069. Break;
  1070. // 'Result' remembers the result of comparison between most significant different digits, used if it turns out that amounts of digits are equal.
  1071. if Result = 0 then
  1072. Result := Ord(Digit1 > Digit2) - Ord(Digit1 < Digit2);
  1073. until false;
  1074. // Compare lengths if numbers are equal (but not characters, so '01' and ' 1' are equal).
  1075. if Result = 0 then
  1076. Result := Ord(C1.Sp - S1p > C2.Sp - S2p) - Ord(C1.Sp - S1p < C2.Sp - S2p);
  1077. S1p := C1.Sp;
  1078. S2p := C2.Sp;
  1079. end;
  1080. var
  1081. S1p, S1n, S2p, S2n: SizeInt;
  1082. begin
  1083. S1p := 0;
  1084. S2p := 0;
  1085. S1n := Length(Str1);
  1086. S2n := Length(Str2);
  1087. while (S1p < S1n) and (S2p < S2n) do
  1088. begin
  1089. Result := ScanAndCompareTexts(Str1, S1p, Str2, S2p);
  1090. if Result <> 0 then
  1091. Exit;
  1092. Result := ScanAndCompareNumbers(Str1, S1p, Str2, S2p);
  1093. if Result <> 0 then
  1094. Exit;
  1095. end;
  1096. Result := Ord(S1p < S1n) - Ord(S2p < S2n);
  1097. end;
  1098. function SplitString(const S, Delimiters: string): TRTLStringDynArray;
  1099. Var
  1100. a : Array of Char;
  1101. I : Integer;
  1102. begin
  1103. SetLength(A,Length(Delimiters));
  1104. For I:=1 to Length(Delimiters) do
  1105. A[I-1]:=Delimiters[i];
  1106. Result := S.Split(A);
  1107. end;
  1108. function NaturalCompareText (const S1 , S2 : string ): Integer ;
  1109. begin
  1110. Result := NaturalCompareText(S1, S2,
  1111. DefaultFormatSettings.DecimalSeparator,
  1112. DefaultFormatSettings.ThousandSeparator);
  1113. end;
  1114. { ---------------------------------------------------------------------
  1115. VB emulations.
  1116. ---------------------------------------------------------------------}
  1117. function LeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1118. begin
  1119. Result:=Copy(AText,1,ACount);
  1120. end;
  1121. function RightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1122. var j,l:SizeInt;
  1123. begin
  1124. l:=length(atext);
  1125. j:=ACount;
  1126. if j>l then j:=l;
  1127. Result:=Copy(AText,l-j+1,j);
  1128. end;
  1129. function MidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;
  1130. begin
  1131. if (ACount=0) or (AStart>length(atext)) then
  1132. exit('');
  1133. Result:=Copy(AText,AStart,ACount);
  1134. end;
  1135. function LeftBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;
  1136. begin
  1137. Result:=LeftStr(AText,AByteCount);
  1138. end;
  1139. function RightBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;
  1140. begin
  1141. Result:=RightStr(Atext,AByteCount);
  1142. end;
  1143. function MidBStr(const AText: AnsiString; const AByteStart, AByteCount: SizeInt): AnsiString;
  1144. begin
  1145. Result:=MidStr(AText,AByteStart,AByteCount);
  1146. end;
  1147. function AnsiLeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1148. begin
  1149. Result := copy(AText,1,ACount);
  1150. end;
  1151. function AnsiRightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1152. begin
  1153. Result := copy(AText,length(AText)-ACount+1,ACount);
  1154. end;
  1155. function AnsiMidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;
  1156. begin
  1157. Result:=Copy(AText,AStart,ACount);
  1158. end;
  1159. function LeftStr(const AText: WideString; const ACount: SizeInt): WideString;
  1160. begin
  1161. Result:=Copy(AText,1,ACount);
  1162. end;
  1163. function RightStr(const AText: WideString; const ACount: SizeInt): WideString;
  1164. var
  1165. j,l:SizeInt;
  1166. begin
  1167. l:=length(atext);
  1168. j:=ACount;
  1169. if j>l then j:=l;
  1170. Result:=Copy(AText,l-j+1,j);
  1171. end;
  1172. function MidStr(const AText: WideString; const AStart, ACount: SizeInt): WideString;
  1173. begin
  1174. Result:=Copy(AText,AStart,ACount);
  1175. end;
  1176. { ---------------------------------------------------------------------
  1177. Extended search and replace
  1178. ---------------------------------------------------------------------}
  1179. type
  1180. TEqualFunction = function (const a,b : AnsiChar) : boolean;
  1181. function EqualWithCase (const a,b : AnsiChar) : boolean;
  1182. begin
  1183. result := (a = b);
  1184. end;
  1185. function EqualWithoutCase (const a,b : AnsiChar) : boolean;
  1186. begin
  1187. result := (lowerCase(a) = lowerCase(b));
  1188. end;
  1189. function IsWholeWord (bufstart, bufend, wordstart, wordend : PAnsiChar) : boolean;
  1190. begin
  1191. // Check start
  1192. result := ((wordstart = bufstart) or ((wordstart-1)^ in worddelimiters)) and
  1193. // Check end
  1194. ((wordend = bufend) or ((wordend+1)^ in worddelimiters));
  1195. end;
  1196. function SearchDown(buf,aStart,endchar:PAnsiChar; SearchString:string;
  1197. Equals : TEqualFunction; WholeWords:boolean) : PAnsiChar;
  1198. var Found : boolean;
  1199. s, c : PAnsiChar;
  1200. begin
  1201. result := aStart;
  1202. Found := false;
  1203. while not Found and (result <= endchar) do
  1204. begin
  1205. // Search first letter
  1206. while (result <= endchar) and not Equals(result^,SearchString[1]) do
  1207. inc (result);
  1208. // Check if following is searchstring
  1209. c := result;
  1210. s := @(Searchstring[1]);
  1211. Found := true;
  1212. while (c <= endchar) and (s^ <> #0) and Found do
  1213. begin
  1214. Found := Equals(c^, s^);
  1215. inc (c);
  1216. inc (s);
  1217. end;
  1218. if s^ <> #0 then
  1219. Found := false;
  1220. // Check if it is a word
  1221. if Found and WholeWords then
  1222. Found := IsWholeWord(buf,endchar,result,c-1);
  1223. if not found then
  1224. inc (result);
  1225. end;
  1226. if not Found then
  1227. result := nil;
  1228. end;
  1229. function SearchUp(buf,aStart,endchar:PAnsiChar; SearchString:string;
  1230. equals : TEqualFunction; WholeWords:boolean) : PAnsiChar;
  1231. var Found : boolean;
  1232. s, c, l : PAnsiChar;
  1233. begin
  1234. result := aStart;
  1235. Found := false;
  1236. l := @(SearchString[length(SearchString)]);
  1237. while not Found and (result >= buf) do
  1238. begin
  1239. // Search last letter
  1240. while (result >= buf) and not Equals(result^,l^) do
  1241. dec (result);
  1242. // Check if before is searchstring
  1243. c := result;
  1244. s := l;
  1245. Found := true;
  1246. while (c >= buf) and (s >= @SearchString[1]) and Found do
  1247. begin
  1248. Found := Equals(c^, s^);
  1249. dec (c);
  1250. dec (s);
  1251. end;
  1252. if (s >= @(SearchString[1])) then
  1253. Found := false;
  1254. // Check if it is a word
  1255. if Found and WholeWords then
  1256. Found := IsWholeWord(buf,endchar,c+1,result);
  1257. if found then
  1258. result := c+1
  1259. else
  1260. dec (result);
  1261. end;
  1262. if not Found then
  1263. result := nil;
  1264. end;
  1265. //function SearchDown(buf,aStart,endchar:PAnsiChar; SearchString:string; equal : TEqualFunction; WholeWords:boolean) : PAnsiChar;
  1266. function SearchBuf(Buf: PAnsiChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String; Options: TStringSearchOptions
  1267. ): PAnsiChar;
  1268. var
  1269. equal : TEqualFunction;
  1270. begin
  1271. SelStart := SelStart + SelLength;
  1272. if (SearchString = '') or (SelStart > BufLen) or (SelStart < 0) then
  1273. result := nil
  1274. else
  1275. begin
  1276. if soMatchCase in Options then
  1277. Equal := @EqualWithCase
  1278. else
  1279. Equal := @EqualWithoutCase;
  1280. if soDown in Options then
  1281. result := SearchDown(buf,buf+SelStart,Buf+(BufLen-1), SearchString, Equal, (soWholeWord in Options))
  1282. else
  1283. result := SearchUp(buf,buf+SelStart,Buf+(Buflen-1), SearchString, Equal, (soWholeWord in Options));
  1284. end;
  1285. end;
  1286. function SearchBuf(Buf: PAnsiChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String): PAnsiChar; // ; Options: TStringSearchOptions = [soDown]
  1287. begin
  1288. Result:=SearchBuf(Buf,BufLen,SelStart,SelLength,SearchString,[soDown]);
  1289. end;
  1290. function PosEx(const SubStr, S: AnsiString; Offset: SizeInt): SizeInt;
  1291. begin
  1292. Result := Pos(SubStr, S, Offset);
  1293. end;
  1294. function PosEx(c: AnsiChar; const S: Ansistring; Offset: SizeInt): SizeInt;
  1295. begin
  1296. Result := Pos(c, S, Offset);
  1297. end;
  1298. function PosEx(const SubStr, S: Ansistring): SizeInt;
  1299. begin
  1300. Result := Pos(SubStr, S);
  1301. end;
  1302. function PosEx(const SubStr, S: UnicodeString; Offset: SizeInt): SizeInt;
  1303. begin
  1304. Result := Pos(SubStr, S, Offset);
  1305. end;
  1306. function PosEx(c: WideChar; const S: UnicodeString; Offset: SizeInt): SizeInt;
  1307. begin
  1308. Result := Pos(c, S, Offset);
  1309. end;
  1310. function PosEx(const SubStr, S: UnicodeString): Sizeint;
  1311. begin
  1312. Result := Pos(SubStr, S);
  1313. end;
  1314. function StringsReplace(const S: AnsiString; OldPattern, NewPattern: array of AnsiString; Flags: TReplaceFlags): string;
  1315. var pc,lastpc,litStart : PAnsiChar;
  1316. iPattern,Rp,Ra,iFirstPattern,OldPatternLen : SizeInt;
  1317. // Heads of the linked lists of patterns starting with a character whose code has this residue modulo length(firstPattern).
  1318. // Length must be power of two, less = slower for large cases, more = slower for small cases.
  1319. // 0 .. 255 (or directly [AnsiChar]) might be a bit too much because the array is initialized every time; and generalizes worse to S: UnicodeString :)
  1320. firstPattern : array[0 .. 63] of SizeInt;
  1321. nextPattern : PSizeInt; // Next pattern starting with the same character.
  1322. nextPatternStatic: array[0 .. 63] of SizeInt;
  1323. CompStr : ansistring;
  1324. {$if sizeof(char) <> sizeof(ansichar)}
  1325. tempStr : string;
  1326. {$endif}
  1327. procedure Append(P: PChar; N: SizeInt);
  1328. begin
  1329. if N>Ra-Rp then
  1330. begin
  1331. Ra:=Rp+N+4+Ra shr 1+Ra shr 2; // + N + const + 37.5%
  1332. SetLength(Result,Ra);
  1333. end;
  1334. Move(P^,PChar(Pointer(Result))[Rp],N*SizeOf(Char));
  1335. Rp:=Rp+N;
  1336. end;
  1337. // Mostly exists to force better register allocation for the main "pc < lastpc" loop, hotter than this procedure. :)
  1338. // Returns the length of the found and replaced OldPattern item, or -1 if not found.
  1339. function TryMatchAndReplace(pc,lastpc: PAnsiChar; iPattern: SizeInt): SizeInt;
  1340. var
  1341. pcc: PAnsiChar;
  1342. OldPatternLen: SizeInt;
  1343. begin
  1344. repeat
  1345. OldPatternLen:=Length(OldPattern[iPattern]);
  1346. if (OldPatternLen <= (lastpc-pc)) and
  1347. (CompareByte(OldPattern[iPattern,1],pc^,OldPatternLen*SizeOf(AnsiChar))=0) then
  1348. begin
  1349. pcc:=PAnsiChar(Pointer(S))+(pc-PAnsiChar(Pointer(CompStr)));
  1350. {$if sizeof(char)=sizeof(ansichar)}
  1351. Append(litStart,pcc-litStart);
  1352. Append(PChar(Pointer(NewPattern[iPattern])), Length(NewPattern[iPattern]));
  1353. {$else}
  1354. tempStr := Copy(S,1+litStart-PAnsiChar(Pointer(S)),pcc-litStart);
  1355. Append(PChar(Pointer(tempStr)), Length(tempStr));
  1356. tempStr := NewPattern[iPattern];
  1357. Append(PChar(Pointer(tempStr)), Length(tempStr));
  1358. {$endif}
  1359. litStart := pcc+OldPatternLen;
  1360. exit(OldPatternLen);
  1361. end;
  1362. iPattern := nextPattern[iPattern];
  1363. until iPattern < 0;
  1364. result := -1;
  1365. end;
  1366. begin
  1367. if High(OldPattern) <> High(NewPattern) then
  1368. raise exception.Create(SErrAmountStrings);
  1369. FillChar(firstPattern, sizeof(firstPattern), byte(-1));
  1370. if High(OldPattern) <= High(nextPatternStatic) then
  1371. nextPattern := PSizeInt(nextPatternStatic)
  1372. else
  1373. nextPattern := GetMem(Length(OldPattern) * sizeof(SizeInt));
  1374. FillChar(nextPattern^, Length(OldPattern) * sizeof(SizeInt), byte(-1));
  1375. if rfIgnoreCase in Flags then
  1376. begin
  1377. CompStr := AnsiUpperCase(S);
  1378. for iPattern := 0 to High(OldPattern) do
  1379. OldPattern[iPattern] := AnsiUpperCase(OldPattern[iPattern]);
  1380. end
  1381. else
  1382. CompStr := S;
  1383. // The element added to the linked list last will be checked first, so add in reverse order.
  1384. for iPattern := High(OldPattern) downto 0 do
  1385. if OldPattern[iPattern] <> '' then
  1386. begin
  1387. iFirstPattern := ord(OldPattern[iPattern,1]) and High(firstPattern);
  1388. nextPattern[iPattern] := firstPattern[iFirstPattern];
  1389. firstPattern[iFirstPattern] := iPattern;
  1390. end;
  1391. Ra := Length(S); // Preallocation heuristic.
  1392. SetLength(result, Ra);
  1393. Rp := 0;
  1394. pc := PAnsiChar(Pointer(CompStr));
  1395. litStart := PAnsiChar(Pointer(S));
  1396. lastpc := pc+Length(S);
  1397. while pc < lastpc do
  1398. begin
  1399. iPattern := firstPattern[ord(pc^) and High(firstPattern)];
  1400. inc(pc);
  1401. if iPattern >= 0 then
  1402. begin
  1403. OldPatternLen := TryMatchAndReplace(pc-1, lastpc, iPattern);
  1404. if OldPatternLen >= 0 then
  1405. begin
  1406. pc := pc-1+OldPatternLen;
  1407. if not (rfReplaceAll in Flags) then
  1408. break;
  1409. end;
  1410. end;
  1411. end;
  1412. if nextPattern <> PSizeInt(nextPattern) then
  1413. FreeMem(nextPattern);
  1414. if litStart = PAnsiChar(Pointer(S)) then
  1415. exit(S); // Unchanged string.
  1416. {$if sizeof(char)=sizeof(ansichar)}
  1417. Append(litStart,PAnsiChar(Pointer(S))+(lastpc-PAnsiChar(Pointer(CompStr)))-litStart);
  1418. {$else}
  1419. tempStr := Copy(S,1+litStart-PAnsiChar(Pointer(S)),PAnsiChar(Pointer(S))+(lastpc-PAnsiChar(Pointer(CompStr)))-litStart);
  1420. Append(PChar(Pointer(tempStr)), Length(tempStr));
  1421. {$endif}
  1422. SetLength(result,Rp);
  1423. end;
  1424. { ---------------------------------------------------------------------
  1425. Delphi compat
  1426. ---------------------------------------------------------------------}
  1427. function ReplaceStr(const AText, AFromText, AToText: string): string;
  1428. begin
  1429. result:=AnsiReplaceStr(AText, AFromText, AToText);
  1430. end;
  1431. function ReplaceText(const AText, AFromText, AToText: string): string;
  1432. begin
  1433. result:=AnsiReplaceText(AText, AFromText, AToText);
  1434. end;
  1435. { ---------------------------------------------------------------------
  1436. Soundex Functions.
  1437. ---------------------------------------------------------------------}
  1438. Const
  1439. SScore : array[1..255] of AnsiChar =
  1440. ('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
  1441. '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
  1442. '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
  1443. '0','0','0','0','0','0', // 91..96
  1444. '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
  1445. '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
  1446. '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
  1447. '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
  1448. '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
  1449. '0','0','0','0','0'); // 251..255
  1450. function Soundex(const AText: string; ALength: TSoundexLength): string;
  1451. Var
  1452. S,PS : AnsiChar;
  1453. I,L : SizeInt;
  1454. begin
  1455. Result:='';
  1456. PS:=#0;
  1457. If Length(AText)>0 then
  1458. begin
  1459. Result:=Upcase(AText[1]);
  1460. I:=2;
  1461. L:=Length(AText);
  1462. While (I<=L) and (Length(Result)<ALength) do
  1463. begin
  1464. S:=SScore[Ord(AText[i])];
  1465. If Not (S in ['0','i',PS]) then
  1466. Result:=Result+S;
  1467. If (S<>'i') then
  1468. PS:=S;
  1469. Inc(I);
  1470. end;
  1471. end;
  1472. L:=Length(Result);
  1473. If (L<ALength) then
  1474. Result:=Result+StringOfChar('0',Alength-L);
  1475. end;
  1476. function Soundex(const AText: string): string; // ; ALength: TSoundexLength = 4
  1477. begin
  1478. Result:=Soundex(AText,4);
  1479. end;
  1480. Const
  1481. Ord0 = Ord('0');
  1482. OrdA = Ord('A');
  1483. function SoundexInt(const AText: string; ALength: TSoundexIntLength): Integer;
  1484. var
  1485. SE: string;
  1486. I: SizeInt;
  1487. begin
  1488. Result:=-1;
  1489. SE:=Soundex(AText,ALength);
  1490. If Length(SE)>0 then
  1491. begin
  1492. Result:=Ord(SE[1])-OrdA;
  1493. if ALength > 1 then
  1494. begin
  1495. Result:=Result*26+(Ord(SE[2])-Ord0);
  1496. for I:=3 to ALength do
  1497. Result:=(Ord(SE[I])-Ord0)+Result*7;
  1498. end;
  1499. Result:=ALength+Result*9;
  1500. end;
  1501. end;
  1502. function SoundexInt(const AText: string): Integer; //; ALength: TSoundexIntLength = 4
  1503. begin
  1504. Result:=SoundexInt(AText,4);
  1505. end;
  1506. function DecodeSoundexInt(AValue: Integer): string;
  1507. var
  1508. I, Len: Integer;
  1509. begin
  1510. Result := '';
  1511. Len := AValue mod 9;
  1512. AValue := AValue div 9;
  1513. for I:=Len downto 3 do
  1514. begin
  1515. Result:=Chr(Ord0+(AValue mod 7))+Result;
  1516. AValue:=AValue div 7;
  1517. end;
  1518. if Len>1 then
  1519. begin
  1520. Result:=Chr(Ord0+(AValue mod 26))+Result;
  1521. AValue:=AValue div 26;
  1522. end;
  1523. Result:=Chr(OrdA+AValue)+Result;
  1524. end;
  1525. function SoundexWord(const AText: string): Word;
  1526. Var
  1527. S : String;
  1528. begin
  1529. S:=SoundEx(Atext,4);
  1530. Result:=Ord(S[1])-OrdA;
  1531. Result:=Result*26+ord(S[2])-48;
  1532. Result:=Result*7+ord(S[3])-48;
  1533. Result:=Result*7+ord(S[4])-48;
  1534. end;
  1535. function DecodeSoundexWord(AValue: Word): string;
  1536. begin
  1537. Result := Chr(Ord0+ (AValue mod 7));
  1538. AValue := AValue div 7;
  1539. Result := Chr(Ord0+ (AValue mod 7)) + Result;
  1540. AValue := AValue div 7;
  1541. Result := IntToStr(AValue mod 26) + Result;
  1542. AValue := AValue div 26;
  1543. Result := Chr(OrdA+AValue) + Result;
  1544. end;
  1545. function SoundexSimilar(const AText, AOther: string; ALength: TSoundexLength): Boolean;
  1546. begin
  1547. Result:=Soundex(AText,ALength)=Soundex(AOther,ALength);
  1548. end;
  1549. function SoundexSimilar(const AText, AOther: string): Boolean; //; ALength: TSoundexLength = 4
  1550. begin
  1551. Result:=SoundexSimilar(AText,AOther,4);
  1552. end;
  1553. function SoundexCompare(const AText, AOther: string; ALength: TSoundexLength): Integer;
  1554. begin
  1555. Result:=AnsiCompareStr(Soundex(AText,ALength),Soundex(AOther,ALength));
  1556. end;
  1557. function SoundexCompare(const AText, AOther: string): Integer; //; ALength: TSoundexLength = 4
  1558. begin
  1559. Result:=SoundexCompare(AText,AOther,4);
  1560. end;
  1561. function SoundexProc(const AText, AOther: string): Boolean;
  1562. begin
  1563. Result:=SoundexSimilar(AText,AOther);
  1564. end;
  1565. { ---------------------------------------------------------------------
  1566. RxStrUtils-like functions.
  1567. ---------------------------------------------------------------------}
  1568. function IsEmptyStr(const S: string; const EmptyChars: TSysCharSet): Boolean;
  1569. var
  1570. i,l: SizeInt;
  1571. begin
  1572. l:=Length(S);
  1573. i:=1;
  1574. Result:=True;
  1575. while Result and (i<=l) do
  1576. begin
  1577. Result:=(S[i] in EmptyChars);
  1578. Inc(i);
  1579. end;
  1580. end;
  1581. function DelSpace(const S: string): string;
  1582. begin
  1583. Result:=DelChars(S,' ');
  1584. end;
  1585. function IndexCharSized(p: PChar; nchars: SizeInt; charv: SizeUint): SizeInt; inline;
  1586. begin
  1587. result :=
  1588. {$if sizeof(char) = sizeof(byte)} IndexByte
  1589. {$elseif sizeof(char) = sizeof(word)} IndexWord
  1590. {$else} {$error unknown char size}
  1591. {$endif}
  1592. (p^, nchars, charv);
  1593. end;
  1594. procedure FillCharSized(p: PChar; nchars: SizeInt; charv: SizeUint); inline;
  1595. begin
  1596. {$if sizeof(char) = sizeof(byte)} FillChar
  1597. {$elseif sizeof(char) = sizeof(word)} FillWord
  1598. {$else} {$error unknown char size}
  1599. {$endif}
  1600. (p^, nchars, charv);
  1601. end;
  1602. function DelChars(const S: string; Chr: Char): string;
  1603. var
  1604. Sp, Se, Rp: PChar;
  1605. ToCopy: SizeInt;
  1606. begin
  1607. Sp := PChar(Pointer(S));
  1608. Se := Sp + Length(S);
  1609. ToCopy := IndexCharSized(Sp, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char), ord(Chr));
  1610. if ToCopy < 0 then
  1611. exit(S); // Unchanged string.
  1612. SetLength(result, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char));
  1613. Rp := PChar(Pointer(Result));
  1614. repeat
  1615. Move(Sp^, Rp^, ToCopy * sizeof(Char));
  1616. Inc(Sp, ToCopy);
  1617. Inc(Rp, ToCopy);
  1618. repeat
  1619. Inc(Sp); // Can increment to Se + 1.
  1620. until (Sp >= Se) or (Sp^ <> Chr);
  1621. if Sp >= Se then
  1622. break;
  1623. ToCopy := IndexCharSized(Sp, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char), ord(Chr));
  1624. if ToCopy < 0 then
  1625. ToCopy := SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char);
  1626. until false;
  1627. SetLength(result, SizeUint(Pointer(Rp) - Pointer(Result)) div sizeof(Char));
  1628. end;
  1629. function DelChars(const S: string; Chars: TSysCharSet): string;
  1630. var
  1631. Ss, Sp, Se, Rp: PChar;
  1632. aDelta : Integer;
  1633. begin
  1634. Ss := PChar(Pointer(S));
  1635. Sp := Ss;
  1636. Se := Sp + Length(S);
  1637. while (Sp < Se) and not (Sp^ in Chars) do
  1638. Inc(Sp);
  1639. if Sp >= Se then
  1640. Exit(S); // Unchanged string.
  1641. SetLength(result, SizeUint(Pointer(Se) - Pointer(Ss)) div sizeof(Char));
  1642. Rp := PChar(Pointer(Result));
  1643. repeat
  1644. aDelta:=(Pointer(Sp) - Pointer(Ss));
  1645. Move(Ss^, Rp^, aDelta);
  1646. Inc(Pointer(Rp), aDelta);
  1647. repeat
  1648. Inc(Sp); // Can increment to Se + 1.
  1649. until (Sp >= Se) or not (Sp^ in Chars);
  1650. if Sp >= Se then
  1651. break;
  1652. Ss := Sp;
  1653. repeat
  1654. Inc(Sp);
  1655. until (Sp >= Se) or (Sp^ in Chars);
  1656. until false;
  1657. SetLength(result, SizeUint(Pointer(Rp) - Pointer(Result)) div sizeof(Char));
  1658. end;
  1659. function FindSpacePrecededBySpace(Sp, Se: PChar): PChar;
  1660. var
  1661. SpacePos: SizeInt;
  1662. begin
  1663. repeat
  1664. SpacePos := IndexCharSized(Sp, SizeUint(Pointer(Se) - Pointer(Sp)) div sizeof(Char), ord(' '));
  1665. if SpacePos < 0 then
  1666. Exit(Se);
  1667. Inc(Sp, SpacePos+1);
  1668. until (Sp>=Se) or (Sp^=' ');
  1669. Result:=Sp;
  1670. end;
  1671. function DelSpace1(const S: string): string;
  1672. var
  1673. Ss, Sp, Se, Rp: PChar;
  1674. begin
  1675. Ss := PChar(Pointer(S));
  1676. Se := Ss + Length(S);
  1677. Sp := FindSpacePrecededBySpace(Ss, Se);
  1678. if Sp >= Se then
  1679. Exit(S); // Unchanged string.
  1680. SetLength(result, SizeUint(Pointer(Se) - Pointer(Ss)) div sizeof(Char));
  1681. Rp := PChar(Pointer(Result));
  1682. repeat
  1683. Move(Ss^, Rp^, Pointer(Sp) - Pointer(Ss));
  1684. Inc(Pointer(Rp), Pointer(Sp) - Pointer(Ss));
  1685. repeat
  1686. Inc(Sp); // Can increment to Se + 1.
  1687. until (Sp >= Se) or (Sp^ <> ' ');
  1688. if Sp >= Se then
  1689. break;
  1690. Ss := Sp;
  1691. Sp := FindSpacePrecededBySpace(Sp, Se);
  1692. until false;
  1693. SetLength(result, SizeUint(Pointer(Rp) - Pointer(Result)) div sizeof(Char));
  1694. end;
  1695. function Tab2Space(const S: string; Numb: Byte): string;
  1696. var
  1697. I: SizeInt;
  1698. begin
  1699. I:=1;
  1700. Result:=S;
  1701. while I <= Length(Result) do
  1702. if Result[I]<>Chr(9) then
  1703. inc(I)
  1704. else
  1705. begin
  1706. Result[I]:=' ';
  1707. If (Numb>1) then
  1708. Insert(StringOfChar(' ',Numb-1),Result,I);
  1709. Inc(I,Numb);
  1710. end;
  1711. end;
  1712. function NPos(const C: string; const S: string; N: Integer): SizeInt;
  1713. begin
  1714. Result:=0;
  1715. if N>=1 then
  1716. Repeat
  1717. Result:=Pos(C,S,Result+1);
  1718. dec(N);
  1719. until (N=0) or (Result=0);
  1720. end;
  1721. function AddChar(C: AnsiChar; const S: string; N: Integer): string;
  1722. Var
  1723. l : SizeInt;
  1724. begin
  1725. l:=Length(S);
  1726. if l>=N then
  1727. Exit(S);
  1728. SetLength(Result,N);
  1729. FillCharSized(Pointer(Result),N-l,ord(C));
  1730. Move(Pointer(S)^,PChar(Pointer(Result))[N-l],l*sizeof(Char));
  1731. end;
  1732. function AddCharR(C: AnsiChar; const S: string; N: Integer): string;
  1733. Var
  1734. l : SizeInt;
  1735. begin
  1736. l:=Length(S);
  1737. if l>=N then
  1738. Exit(S);
  1739. SetLength(Result,N);
  1740. Move(Pointer(S)^,Pointer(Result)^,l*sizeof(Char));
  1741. FillCharSized(PChar(Pointer(Result))+l,N-l,ord(C));
  1742. end;
  1743. function PadRight(const S: string; N: Integer): string;inline;
  1744. begin
  1745. Result:=AddCharR(' ',S,N);
  1746. end;
  1747. function PadLeft(const S: string; N: Integer): string;inline;
  1748. begin
  1749. Result:=AddChar(' ',S,N);
  1750. end;
  1751. function Copy2Symb(const S: string; Symb: AnsiChar): string;
  1752. var
  1753. p: SizeInt;
  1754. begin
  1755. p:=Pos(Symb,S);
  1756. if p=0 then
  1757. p:=Length(S)+1;
  1758. Result:=Copy(S,1,p-1);
  1759. end;
  1760. function Copy2SymbDel(var S: string; Symb: AnsiChar): string;
  1761. var
  1762. p: SizeInt;
  1763. begin
  1764. p:=Pos(Symb,S);
  1765. if p=0 then
  1766. begin
  1767. result:=s;
  1768. s:='';
  1769. end
  1770. else
  1771. begin
  1772. Result:=Copy(S,1,p-1);
  1773. delete(s,1,p);
  1774. end;
  1775. end;
  1776. function Copy2Space(const S: string): string;inline;
  1777. begin
  1778. Result:=Copy2Symb(S,' ');
  1779. end;
  1780. function Copy2SpaceDel(var S: string): string;inline;
  1781. begin
  1782. Result:=Copy2SymbDel(S,' ');
  1783. end;
  1784. function AnsiProperCase(const S: string; const WordDelims: TSysCharSet): string;
  1785. var
  1786. P,PE : PAnsiChar;
  1787. begin
  1788. Result:=AnsiLowerCase(S);
  1789. P:=PAnsiChar(pointer(Result));
  1790. PE:=P+Length(Result);
  1791. while (P<PE) do
  1792. begin
  1793. while (P<PE) and (P^ in WordDelims) do
  1794. inc(P);
  1795. if (P<PE) then
  1796. P^:=UpCase(P^);
  1797. while (P<PE) and not (P^ in WordDelims) do
  1798. inc(P);
  1799. end;
  1800. end;
  1801. function WordCount(const S: string; const WordDelims: TSysCharSet): SizeInt;
  1802. var
  1803. P,PE : PAnsiChar;
  1804. WasDelim, NowDelim : Boolean;
  1805. begin
  1806. Result:=0;
  1807. P:=PAnsiChar(pointer(S));
  1808. PE:=P+Length(S);
  1809. WasDelim:=true;
  1810. while (P<PE) do
  1811. begin
  1812. NowDelim := P^ in WordDelims;
  1813. Inc(Result,ord(WasDelim and not NowDelim));
  1814. WasDelim := NowDelim;
  1815. Inc(P);
  1816. end;
  1817. end;
  1818. function WordPosition(const N: Integer; const S: string; const WordDelims: TSysCharSet): SizeInt;
  1819. var
  1820. P,PE : PChar;
  1821. Count: Integer;
  1822. begin
  1823. P:=PChar(pointer(S));
  1824. PE:=P+Length(S);
  1825. Count:=N;
  1826. while (P<PE) and (P^ in WordDelims) do
  1827. Inc(P);
  1828. while (P<PE) do
  1829. begin
  1830. Dec(Count);
  1831. if Count<=0 then
  1832. break;
  1833. repeat
  1834. Inc(P);
  1835. until (P>=PE) or (P^ in WordDelims);
  1836. repeat
  1837. Inc(P); // Can increment to PE + 1.
  1838. until (P>=PE) or not (P^ in WordDelims);
  1839. end;
  1840. if (P<PE) and (Count=0) then
  1841. exit(P-PChar(pointer(S))+1);
  1842. Result:=0;
  1843. end;
  1844. function ExtractWord(N: Integer; const S: string; const WordDelims: TSysCharSet): string;inline;
  1845. var
  1846. i: SizeInt;
  1847. begin
  1848. Result:=ExtractWordPos(N,S,WordDelims,i);
  1849. end;
  1850. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; out Pos: Integer): string;
  1851. var
  1852. i,j: SizeInt;
  1853. begin
  1854. i:=WordPosition(N, S, WordDelims);
  1855. if not ((I>0) and (I<=High(Integer))) then
  1856. begin
  1857. Result:='';
  1858. Pos:=0;
  1859. Exit;
  1860. end;
  1861. Pos:=i;
  1862. j:=PosSetEx(WordDelims, S, i+1);
  1863. if j=0 then
  1864. j:=Length(S)+1;
  1865. Result:=Copy(S,i,j-i);
  1866. end;
  1867. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  1868. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; Out Pos: SizeInt): string;
  1869. var
  1870. i,j: SizeInt;
  1871. begin
  1872. i:=WordPosition(N, S, WordDelims);
  1873. Pos:=i;
  1874. if I<=0 then
  1875. begin
  1876. Result:='';
  1877. Exit;
  1878. end;
  1879. j:=PosSetEx(WordDelims, S, i+1);
  1880. if j=0 then
  1881. j:=Length(S)+1;
  1882. Result:=Copy(S,i,j-i);
  1883. end;
  1884. {$ENDIF}
  1885. function ExtractDelimited(N: Integer; const S: string; const Delims: TSysCharSet): string;
  1886. var
  1887. i,start,len: SizeInt;
  1888. begin
  1889. i:=1;
  1890. len:=Length(S);
  1891. while (i<=len) and (N>1) do
  1892. begin
  1893. dec(N,ord(S[i] in Delims));
  1894. inc(i);
  1895. end;
  1896. if N<>1 then
  1897. exit('');
  1898. start:=i;
  1899. while (i<=len) and not (S[i] in Delims) do
  1900. inc(i);
  1901. exit(Copy(S,start,i-start));
  1902. end;
  1903. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  1904. function ExtractSubstr(const S: string; var Pos: SizeInt; const Delims: TSysCharSet): string;
  1905. var
  1906. i,l: SizeInt;
  1907. begin
  1908. i:=Pos;
  1909. l:=Length(S);
  1910. while (i<=l) and not (S[i] in Delims) do
  1911. inc(i);
  1912. Result:=Copy(S,Pos,i-Pos);
  1913. while (i<=l) and (S[i] in Delims) do
  1914. inc(i);
  1915. Pos:=i;
  1916. end;
  1917. {$ENDIF}
  1918. function ExtractSubstr(const S: string; var Pos: Integer; const Delims: TSysCharSet): string;
  1919. var
  1920. i,l: SizeInt;
  1921. begin
  1922. i:=Pos;
  1923. l:=Length(S);
  1924. while (i<=l) and not (S[i] in Delims) do
  1925. inc(i);
  1926. Result:=Copy(S,Pos,i-Pos);
  1927. while (i<=l) and (S[i] in Delims) do
  1928. inc(i);
  1929. if I>MaxInt then
  1930. Pos:=MaxInt
  1931. else
  1932. Pos:=i;
  1933. end;
  1934. function IsWordPresent(const W, S: string; const WordDelims: TSysCharSet): Boolean;
  1935. var
  1936. P,PE,WordStart : PChar;
  1937. Wbytes : SizeInt;
  1938. begin
  1939. Wbytes:=Length(W)*sizeof(char);
  1940. P:=PChar(pointer(S));
  1941. PE:=P+Length(S);
  1942. while (P<PE) and (P^ in WordDelims) do
  1943. Inc(P);
  1944. while (P<PE) do
  1945. begin
  1946. WordStart:=P;
  1947. repeat
  1948. Inc(P);
  1949. until (P>=PE) or (P^ in WordDelims);
  1950. if (pointer(P)-pointer(WordStart)=Wbytes) and (CompareByte(Pointer(W)^,WordStart^,Wbytes)=0) then
  1951. exit(true);
  1952. repeat
  1953. Inc(P); // Can increment to PE + 1.
  1954. until (P>=PE) or not (P^ in WordDelims);
  1955. end;
  1956. result:=false;
  1957. end;
  1958. function Numb2USA(const S: string): string;
  1959. var
  1960. i, NA: Integer;
  1961. begin
  1962. i:=Length(S);
  1963. Result:=S;
  1964. NA:=0;
  1965. while (i > 0) do begin
  1966. if ((Length(Result) - i + 1 - NA) mod 3 = 0) and (i <> 1) then
  1967. begin
  1968. insert(',', Result, i);
  1969. inc(NA);
  1970. end;
  1971. Dec(i);
  1972. end;
  1973. end;
  1974. function PadCenter(const S: string; Len: SizeInt): string;
  1975. var
  1976. Ns,Nfirstspaces: SizeInt;
  1977. begin
  1978. Ns:=Length(S);
  1979. if Ns>=Len then
  1980. exit(S);
  1981. SetLength(Result,Len);
  1982. Nfirstspaces:=SizeUint(Len) div 2-SizeUint(Ns) div 2;
  1983. FillCharSized(Pointer(Result),Nfirstspaces,ord(' '));
  1984. FillCharSized(PChar(Pointer(Result))+Ns+Nfirstspaces,Len-Ns-Nfirstspaces,ord(' '));
  1985. Move(Pointer(S)^,PChar(Pointer(Result))[Nfirstspaces],Ns*sizeof(char));
  1986. end;
  1987. function Dec2Numb(N: Longint; Len, Base: Byte): string;
  1988. var
  1989. C: Integer;
  1990. Number: Longint;
  1991. begin
  1992. if N=0 then
  1993. Result:='0'
  1994. else
  1995. begin
  1996. Number:=N;
  1997. Result:='';
  1998. while Number>0 do
  1999. begin
  2000. C:=Number mod Base;
  2001. if C>9 then
  2002. C:=C+55
  2003. else
  2004. C:=C+48;
  2005. Result:=Chr(C)+Result;
  2006. Number:=Number div Base;
  2007. end;
  2008. end;
  2009. if (Result<>'') then
  2010. Result:=AddChar('0',Result,Len);
  2011. end;
  2012. function Numb2Dec(S: string; Base: Byte): Longint;
  2013. var
  2014. i, P: sizeint;
  2015. begin
  2016. i:=Length(S);
  2017. Result:=0;
  2018. S:=UpperCase(S);
  2019. P:=1;
  2020. while (i>=1) do
  2021. begin
  2022. if (S[i]>'@') then
  2023. Result:=Result+(Ord(S[i])-55)*P
  2024. else
  2025. Result:=Result+(Ord(S[i])-48)*P;
  2026. Dec(i);
  2027. P:=P*Base;
  2028. end;
  2029. end;
  2030. function RomanToIntDontCare(const S: String): Longint;
  2031. {This was the original implementation of RomanToInt,
  2032. it is internally used in TryRomanToInt when Strictness = rcsDontCare}
  2033. const
  2034. RomanChars = ['C','D','I','L','M','V','X'];
  2035. RomanValues : array['C'..'X'] of Word
  2036. = (100,500,0,0,0,0,1,0,0,50,1000,0,0,0,0,0,0,0,0,5,0,10);
  2037. var
  2038. index, Next: AnsiChar;
  2039. i,l: SizeInt;
  2040. Negative: Boolean;
  2041. begin
  2042. Result:=0;
  2043. i:=0;
  2044. Negative:=(Length(S)>0) and (S[1]='-');
  2045. if Negative then
  2046. inc(i);
  2047. l:=Length(S);
  2048. while (i<l) do
  2049. begin
  2050. inc(i);
  2051. index:=UpCase(S[i]);
  2052. if index in RomanChars then
  2053. begin
  2054. if Succ(i)<=l then
  2055. Next:=UpCase(S[i+1])
  2056. else
  2057. Next:=#0;
  2058. if (Next in RomanChars) and (RomanValues[index]<RomanValues[Next]) then
  2059. begin
  2060. inc(Result, RomanValues[Next]);
  2061. Dec(Result, RomanValues[index]);
  2062. inc(i);
  2063. end
  2064. else
  2065. inc(Result, RomanValues[index]);
  2066. end
  2067. else
  2068. begin
  2069. Result:=0;
  2070. Exit;
  2071. end;
  2072. end;
  2073. if Negative then
  2074. Result:=-Result;
  2075. end;
  2076. { TryRomanToInt: try to convert a roman numeral to an integer
  2077. Parameters:
  2078. S: Roman numeral (like: 'MCMXXII')
  2079. N: Integer value of S (only meaningfull if the function succeeds)
  2080. Stricness: controls how strict the parsing of S is
  2081. - rcsStrict:
  2082. * Follow common subtraction rules
  2083. - only 1 preceding subtraction character allowed: IX = 9, but IIX <> 8
  2084. - from M you can only subtract C
  2085. - from D you can only subtract C
  2086. - from C you can only subtract X
  2087. - from L you can only subtract X
  2088. - from X you can only subtract I
  2089. - from V you can only subtract I
  2090. * The numeral is parsed in "groups" (first M's, then D's etc.), the next group to be parsed
  2091. must always be of a lower denomination than the previous one.
  2092. Example: 'MMDCCXX' is allowed but 'MMCCXXDD' is not
  2093. * There can only ever be 3 consecutive M's, C's, X's or I's
  2094. * There can only ever be 1 D, 1 L and 1 V
  2095. * After IX or IV there can be no more characters
  2096. * Negative numbers are not supported
  2097. // As a consequence the maximum allowed Roman numeral is MMMCMXCIX = 3999, also N can never become 0 (zero)
  2098. - rcsRelaxed: Like rcsStrict but with the following exceptions:
  2099. * An infinite number of (leading) M's is allowed
  2100. * Up to 4 consecutive M's, C's, X's and I's are allowed
  2101. // So this is allowed: 'MMMMMMCXIIII' = 6124
  2102. - rcsDontCare:
  2103. * no checking on the order of "groups" is done
  2104. * there are no restrictions on the number of consecutive chars
  2105. * negative numbers are supported
  2106. * an empty string as input will return True and N will be 0
  2107. * invalid input will return false
  2108. // for backwards comatibility: it supports rather ludicrous input like '-IIIMIII' -> -(2+(1000-1)+3)=-1004
  2109. }
  2110. function TryRomanToInt(S: String; out N: LongInt; Strictness: TRomanConversionStrictness = rcsRelaxed): Boolean;
  2111. var
  2112. i, Len: SizeInt;
  2113. Terminated: Boolean;
  2114. begin
  2115. Result := (False);
  2116. S := UpperCase(S); //don't use AnsiUpperCase please
  2117. Len := Length(S);
  2118. if (Strictness = rcsDontCare) then
  2119. begin
  2120. N := RomanToIntDontCare(S);
  2121. if (N = 0) then
  2122. begin
  2123. Result := (Len = 0);
  2124. end
  2125. else
  2126. Result := True;
  2127. Exit;
  2128. end;
  2129. if (Len = 0) then
  2130. begin
  2131. Result:=true;
  2132. N:=0;
  2133. Exit;
  2134. end;
  2135. i := 1;
  2136. N := 0;
  2137. Terminated := False;
  2138. //leading M's
  2139. while (i <= Len) and ((Strictness <> rcsStrict) or (i < 4)) and (S[i] = 'M') do
  2140. begin
  2141. //writeln('TryRomanToInt: Found 1000');
  2142. Inc(i);
  2143. N := N + 1000;
  2144. end;
  2145. //then CM or or CD or D or (C, CC, CCC, CCCC)
  2146. if (i <= Len) and (S[i] = 'D') then
  2147. begin
  2148. //writeln('TryRomanToInt: Found 500');
  2149. Inc(i);
  2150. N := N + 500;
  2151. end
  2152. else if (i + 1 <= Len) and (S[i] = 'C') then
  2153. begin
  2154. if (S[i+1] = 'M') then
  2155. begin
  2156. //writeln('TryRomanToInt: Found 900');
  2157. Inc(i,2);
  2158. N := N + 900;
  2159. end
  2160. else if (S[i+1] = 'D') then
  2161. begin
  2162. //writeln('TryRomanToInt: Found 400');
  2163. Inc(i,2);
  2164. N := N + 400;
  2165. end;
  2166. end ;
  2167. //next max 4 or 3 C's, depending on Strictness
  2168. if (i <= Len) and (S[i] = 'C') then
  2169. begin
  2170. //find max 4 C's
  2171. //writeln('TryRomanToInt: Found 100');
  2172. Inc(i);
  2173. N := N + 100;
  2174. if (i <= Len) and (S[i] = 'C') then
  2175. begin
  2176. //writeln('TryRomanToInt: Found 100');
  2177. Inc(i);
  2178. N := N + 100;
  2179. end;
  2180. if (i <= Len) and (S[i] = 'C') then
  2181. begin
  2182. //writeln('TryRomanToInt: Found 100');
  2183. Inc(i);
  2184. N := N + 100;
  2185. end;
  2186. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'C') then
  2187. begin
  2188. //writeln('TryRomanToInt: Found 100');
  2189. Inc(i);
  2190. N := N + 100;
  2191. end;
  2192. end;
  2193. //then XC or XL
  2194. if (i + 1 <= Len) and (S[i] = 'X') then
  2195. begin
  2196. if (S[i+1] = 'C') then
  2197. begin
  2198. //writeln('TryRomanToInt: Found 90');
  2199. Inc(i,2);
  2200. N := N + 90;
  2201. end
  2202. else if (S[i+1] = 'L') then
  2203. begin
  2204. //writeln('TryRomanToInt: Found 40');
  2205. Inc(i,2);
  2206. N := N + 40;
  2207. end;
  2208. end;
  2209. //then L
  2210. if (i <= Len) and (S[i] = 'L') then
  2211. begin
  2212. //writeln('TryRomanToInt: Found 50');
  2213. Inc(i);
  2214. N := N + 50;
  2215. end;
  2216. //then (X, xx, xxx, xxxx)
  2217. if (i <= Len) and (S[i] = 'X') then
  2218. begin
  2219. //find max 3 or 4 X's, depending on Strictness
  2220. //writeln('TryRomanToInt: Found 10');
  2221. Inc(i);
  2222. N := N + 10;
  2223. if (i <= Len) and (S[i] = 'X') then
  2224. begin
  2225. //writeln('TryRomanToInt: Found 10');
  2226. Inc(i);
  2227. N := N + 10;
  2228. end;
  2229. if (i <= Len) and (S[i] = 'X') then
  2230. begin
  2231. //writeln('TryRomanToInt: Found 10');
  2232. Inc(i);
  2233. N := N + 10;
  2234. end;
  2235. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'X') then
  2236. begin
  2237. //writeln('TryRomanToInt: Found 10');
  2238. Inc(i);
  2239. N := N + 10;
  2240. end;
  2241. end;
  2242. //then IX or IV
  2243. if (i + 1 <= Len) and (S[i] = 'I') then
  2244. begin
  2245. if (S[i+1] = 'X') then
  2246. begin
  2247. Terminated := (True);
  2248. //writeln('TryRomanToInt: Found 9');
  2249. Inc(i,2);
  2250. N := N + 9;
  2251. end
  2252. else if (S[i+1] = 'V') then
  2253. begin
  2254. Terminated := (True);
  2255. //writeln('TryRomanToInt: Found 4');
  2256. Inc(i,2);
  2257. N := N + 4;
  2258. end;
  2259. end;
  2260. //then V
  2261. if (not Terminated) and (i <= Len) and (S[i] = 'V') then
  2262. begin
  2263. //writeln('TryRomanToInt: Found 5');
  2264. Inc(i);
  2265. N := N + 5;
  2266. end;
  2267. //then I
  2268. if (not Terminated) and (i <= Len) and (S[i] = 'I') then
  2269. begin
  2270. Terminated := (True);
  2271. //writeln('TryRomanToInt: Found 1');
  2272. Inc(i);
  2273. N := N + 1;
  2274. //Find max 2 or 3 closing I's, depending on strictness
  2275. if (i <= Len) and (S[i] = 'I') then
  2276. begin
  2277. //writeln('TryRomanToInt: Found 1');
  2278. Inc(i);
  2279. N := N + 1;
  2280. end;
  2281. if (i <= Len) and (S[i] = 'I') then
  2282. begin
  2283. //writeln('TryRomanToInt: Found 1');
  2284. Inc(i);
  2285. N := N + 1;
  2286. end;
  2287. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'I') then
  2288. begin
  2289. //writeln('TryRomanToInt: Found 1');
  2290. Inc(i);
  2291. N := N + 1;
  2292. end;
  2293. end;
  2294. //writeln('TryRomanToInt: Len = ',Len,' i = ',i);
  2295. Result := (i > Len);
  2296. //if Result then writeln('TryRomanToInt: N = ',N);
  2297. end;
  2298. function RomanToInt(const S: string; Strictness: TRomanConversionStrictness = rcsRelaxed): Longint;
  2299. begin
  2300. if not TryRomanToInt(S, Result, Strictness) then
  2301. raise EConvertError.CreateFmt(SInvalidRomanNumeral,[S]);
  2302. end;
  2303. function RomanToIntDef(const S: String; const ADefault: Longint;
  2304. Strictness: TRomanConversionStrictness): Longint;
  2305. begin
  2306. if not TryRomanToInt(S, Result, Strictness) then
  2307. Result := ADefault;
  2308. end;
  2309. function IntToRoman(Value: Longint): string;
  2310. const
  2311. Arabics : Array[1..13] of Integer
  2312. = (1,4,5,9,10,40,50,90,100,400,500,900,1000);
  2313. Romans : Array[1..13] of String
  2314. = ('I','IV','V','IX','X','XL','L','XC','C','CD','D','CM','M');
  2315. var
  2316. i: Integer;
  2317. begin
  2318. Result:='';
  2319. for i:=13 downto 1 do
  2320. while (Value >= Arabics[i]) do
  2321. begin
  2322. Value:=Value-Arabics[i];
  2323. Result:=Result+Romans[i];
  2324. end;
  2325. end;
  2326. function IntToBin(Value: Longint; Digits, Spaces: Integer): string;
  2327. var endpos : integer;
  2328. p,p2:PChar;
  2329. k: integer;
  2330. begin
  2331. Result:='';
  2332. if (Digits>32) then
  2333. Digits:=32;
  2334. if (spaces=0) then
  2335. begin
  2336. result:=inttobin(value,digits);
  2337. exit;
  2338. end;
  2339. endpos:=digits+ (digits-1) div spaces;
  2340. setlength(result,endpos);
  2341. p:=@result[endpos];
  2342. p2:=@result[1];
  2343. k:=spaces;
  2344. while (p>=p2) do
  2345. begin
  2346. if k=0 then
  2347. begin
  2348. p^:=' ';
  2349. dec(p);
  2350. k:=spaces;
  2351. end;
  2352. p^:=chr(48+(cardinal(value) and 1));
  2353. value:=cardinal(value) shr 1;
  2354. dec(p);
  2355. dec(k);
  2356. end;
  2357. end;
  2358. function IntToBin(Value: Longint; Digits: Integer): string;
  2359. var p,p2 : PChar;
  2360. begin
  2361. result:='';
  2362. if digits<=0 then exit;
  2363. setlength(result,digits);
  2364. p:=PChar(pointer(@result[digits]));
  2365. p2:=PChar(pointer(@result[1]));
  2366. // typecasts because we want to keep intto* delphi compat and take an integer
  2367. while (p>=p2) and (cardinal(value)>0) do
  2368. begin
  2369. p^:=chr(48+(cardinal(value) and 1));
  2370. value:=cardinal(value) shr 1;
  2371. dec(p);
  2372. end;
  2373. digits:=p-p2+1;
  2374. if digits>0 then
  2375. fillchar(result[1],digits,#48);
  2376. end;
  2377. function intToBin(Value: int64; Digits:integer): string;
  2378. var p,p2 : PChar;
  2379. begin
  2380. result:='';
  2381. if digits<=0 then exit;
  2382. setlength(result,digits);
  2383. p:=PChar(pointer(@result[digits]));
  2384. p2:=PChar(pointer(@result[1]));
  2385. // typecasts because we want to keep intto* delphi compat and take a signed val
  2386. // and avoid warnings
  2387. while (p>=p2) and (qword(value)>0) do
  2388. begin
  2389. p^:=chr(48+(cardinal(value) and 1));
  2390. value:=qword(value) shr 1;
  2391. dec(p);
  2392. end;
  2393. digits:=p-p2+1;
  2394. if digits>0 then
  2395. fillchar(result[1],digits,#48);
  2396. end;
  2397. function FindPart(const HelpWilds, InputStr: string): SizeInt;
  2398. var
  2399. i, J, NWilds: SizeInt;
  2400. begin
  2401. if Pos('?',HelpWilds)=0 then
  2402. Exit(Pos(HelpWilds, inputStr));
  2403. NWilds:=Length(HelpWilds);
  2404. for i:=0 to Length(inputStr) - NWilds do
  2405. begin
  2406. J:=1;
  2407. while (J<=NWilds) and ((inputStr[i + J] = HelpWilds[J]) or (HelpWilds[J] = '?')) do
  2408. Inc(J);
  2409. if J>NWilds then
  2410. Exit(i+1);
  2411. end;
  2412. Result:=0;
  2413. end;
  2414. function IsWild(InputStr, Wilds: string; IgnoreCase: Boolean): Boolean;
  2415. var
  2416. Wp,We,Ip,Ie,WpBack,IpBack: PChar;
  2417. begin
  2418. if ignoreCase then { upcase all letters }
  2419. begin
  2420. inputStr:=AnsiUpperCase(inputStr);
  2421. Wilds:=AnsiUpperCase(Wilds);
  2422. end;
  2423. Wp:=PChar(Pointer(Wilds));
  2424. We:=Wp+Length(Wilds);
  2425. Ip:=PChar(Pointer(InputStr));
  2426. Ie:=Ip+Length(InputStr);
  2427. WpBack:=nil;
  2428. while Ip<Ie do
  2429. begin
  2430. if Wp<We then
  2431. if (Wp^=Ip^) or (Wp^='?') then
  2432. begin
  2433. Inc(Ip);
  2434. Inc(Wp);
  2435. continue;
  2436. end
  2437. else if Wp^='*' then
  2438. begin
  2439. Inc(Wp);
  2440. WpBack:=Wp;
  2441. IpBack:=Ip;
  2442. continue;
  2443. end;
  2444. if not Assigned(WpBack) then
  2445. exit(false);
  2446. Wp:=WpBack;
  2447. Inc(IpBack);
  2448. Ip:=IpBack;
  2449. end;
  2450. while (Wp<We) and (Wp^='*') do
  2451. Inc(Wp);
  2452. Result:=Wp=We;
  2453. end;
  2454. function XorString(const Key, Src: ShortString): ShortString;
  2455. var
  2456. i: SizeInt;
  2457. begin
  2458. Result:=Src;
  2459. if Length(Key) > 0 then
  2460. for i:=1 to Length(Src) do
  2461. Result[i]:=Chr(Byte(Key[1 + ((i - 1) mod Length(Key))]) xor Ord(Src[i]));
  2462. end;
  2463. function XorEncode(const Key, Source: Ansistring): Ansistring;
  2464. var
  2465. i: Integer;
  2466. C: Byte;
  2467. begin
  2468. Result:='';
  2469. for i:=1 to Length(Source) do
  2470. begin
  2471. if Length(Key) > 0 then
  2472. C:=Byte(Key[1 + ((i - 1) mod Length(Key))]) xor Byte(Source[i])
  2473. else
  2474. C:=Byte(Source[i]);
  2475. Result:=Result+AnsiLowerCase(intToHex(C, 2));
  2476. end;
  2477. end;
  2478. function XorDecode(const Key, Source: Ansistring): Ansistring;
  2479. var
  2480. i: Integer;
  2481. C: AnsiChar;
  2482. begin
  2483. Result:='';
  2484. for i:=0 to Length(Source) div 2 - 1 do
  2485. begin
  2486. C:=Chr(StrTointDef('$' + Copy(Source, (i * 2) + 1, 2), Ord(' ')));
  2487. if Length(Key) > 0 then
  2488. C:=Chr(Byte(Key[1 + (i mod Length(Key))]) xor Byte(C));
  2489. Result:=Result + C;
  2490. end;
  2491. end;
  2492. function GetCmdLineArg(const Switch: string; SwitchChars: TSysCharSet): string;
  2493. var
  2494. i: Integer;
  2495. S: string;
  2496. begin
  2497. i:=1;
  2498. Result:='';
  2499. while (Result='') and (i<=ParamCount) do
  2500. begin
  2501. S:=ParamStr(i);
  2502. if (SwitchChars=[]) or ((S[1] in SwitchChars) and (Length(S) > 1)) and
  2503. (AnsiCompareText(Copy(S,2,Length(S)-1),Switch)=0) then
  2504. begin
  2505. inc(i);
  2506. if i<=ParamCount then
  2507. Result:=ParamStr(i);
  2508. end;
  2509. inc(i);
  2510. end;
  2511. end;
  2512. function RPosEx(C: AnsiChar; const S: AnsiString; offs: SizeInt): SizeInt;
  2513. var p,p2: PAnsiChar;
  2514. Begin
  2515. If (offs>0) and (offs<=Length(S)) Then
  2516. begin
  2517. p:=@s[offs];
  2518. p2:=@s[1];
  2519. while (p2<=p) and (p^<>c) do dec(p);
  2520. RPosEx:=(p-p2)+1;
  2521. end
  2522. else
  2523. RPosEX:=0;
  2524. End;
  2525. function RPos(c: AnsiChar; const S: AnsiString): SizeInt;
  2526. Begin
  2527. Result:=RPosEx(c,S,Length(S)); { Length(S) must be used because character version returns 0 on offs > length. }
  2528. End;
  2529. function RPos(const Substr: AnsiString; const Source: AnsiString): SizeInt;
  2530. begin
  2531. Result:=RPosEx(Substr,Source,High(Result)); { High(Result) is possible because string version clamps offs > length to offs = length. }
  2532. end;
  2533. function RPosEx(const Substr: AnsiString; const Source: AnsiString; offs: SizeInt): SizeInt;
  2534. var
  2535. MaxLen,llen : SizeInt;
  2536. c : AnsiChar;
  2537. pc,pc2 : PAnsiChar;
  2538. begin
  2539. llen:=Length(SubStr);
  2540. maxlen:=length(source);
  2541. if offs<maxlen then maxlen:=offs;
  2542. if (llen>0) and (maxlen>0) and ( llen<=maxlen) then
  2543. begin
  2544. pc:=@source[maxlen-llen+1];
  2545. pc2:=@source[1];
  2546. c:=substr[1];
  2547. repeat
  2548. if (c=pc^) and
  2549. (CompareChar(Substr[1],pc^,llen)=0) then
  2550. begin
  2551. rPosex:=pc-pc2+1;
  2552. exit;
  2553. end;
  2554. dec(pc);
  2555. until pc<pc2;
  2556. end;
  2557. rPosex:=0;
  2558. end;
  2559. function RPosEx(C: unicodechar; const S: UnicodeString; offs: SizeInt): SizeInt;
  2560. var p,p2: PUnicodeChar;
  2561. Begin
  2562. If (offs>0) and (offs<=Length(S)) Then
  2563. begin
  2564. p:=@s[offs];
  2565. p2:=@s[1];
  2566. while (p2<=p) and (p^<>c) do dec(p);
  2567. RPosEx:=SizeUint(pointer(p)-pointer(p2)) div sizeof(unicodechar)+1; { p-p2+1 but avoids signed division... }
  2568. end
  2569. else
  2570. RPosEX:=0;
  2571. End;
  2572. function RPos(c: Unicodechar; const S: UnicodeString): SizeInt;
  2573. Begin
  2574. Result:=RPosEx(c,S,Length(S)); { Length(S) must be used because character version returns 0 on offs > length. }
  2575. End;
  2576. function RPos(const Substr: UnicodeString; const Source: UnicodeString): SizeInt;
  2577. begin
  2578. Result:=RPosEx(Substr,Source,High(Result)); { High(Result) is possible because string version clamps offs > length to offs = length. }
  2579. end;
  2580. function RPosEx(const Substr: UnicodeString; const Source: UnicodeString; offs: SizeInt): SizeInt;
  2581. var
  2582. MaxLen,llen : SizeInt;
  2583. c : unicodechar;
  2584. pc,pc2 : punicodechar;
  2585. begin
  2586. llen:=Length(SubStr);
  2587. maxlen:=length(source);
  2588. if offs<maxlen then maxlen:=offs;
  2589. if (llen>0) and (maxlen>0) and ( llen<=maxlen) then
  2590. begin
  2591. pc:=@source[maxlen-llen+1];
  2592. pc2:=@source[1];
  2593. c:=substr[1];
  2594. repeat
  2595. if (c=pc^) and
  2596. (Compareword(Substr[1],pc^,llen)=0) then
  2597. begin
  2598. rPosex:=SizeUint(pointer(pc)-pointer(pc2)) div sizeof(unicodechar)+1; { pc-pc2+1 but avoids signed division... }
  2599. exit;
  2600. end;
  2601. dec(pc);
  2602. until pc<pc2;
  2603. end;
  2604. rPosex:=0;
  2605. end;
  2606. procedure BinToHex(BinValue: PAnsiChar; HexValue: PAnsiChar; BinBufSize: Integer);
  2607. var
  2608. i : longint;
  2609. begin
  2610. for i:=0 to BinBufSize-1 do
  2611. begin
  2612. HexValue[0]:=HexDigits[((Ord(BinValue[i]) shr 4))];
  2613. HexValue[1]:=HexDigits[((Ord(BinValue[i]) and 15))];
  2614. Inc(HexValue,2);
  2615. end;
  2616. end;
  2617. procedure BinToHex(BinValue: PAnsiChar; HexValue: PWideChar; BinBufSize: Integer);
  2618. var
  2619. i : longint;
  2620. begin
  2621. for i:=0 to BinBufSize-1 do
  2622. begin
  2623. HexValue[0]:=HexDigitsW[((Ord(BinValue[i]) shr 4))];
  2624. HexValue[1]:=HexDigitsW[((Ord(BinValue[i]) and 15))];
  2625. Inc(HexValue,2);
  2626. end;
  2627. end;
  2628. procedure BinToHex(const BinBuffer: TBytes; BinBufOffset: Integer; var HexBuffer: TBytes; HexBufOffset: Integer; Count: Integer);
  2629. var
  2630. i : longint;
  2631. begin
  2632. for i:=0 to Count-1 do
  2633. begin
  2634. HexBuffer[HexBufOffset+2*i+0]:=Byte(HexDigits[(BinBuffer[BinBufOffset + i] shr 4)]);
  2635. HexBuffer[HexBufOffset+2*i+1]:=Byte(HexDigits[(BinBuffer[BinBufOffset + i] and 15)]);
  2636. end;
  2637. end;
  2638. procedure BinToHex(BinValue: Pointer; HexValue: PAnsiChar; BinBufSize: Integer);
  2639. begin
  2640. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2641. end;
  2642. procedure BinToHex(BinValue: Pointer; HexValue: PWideChar; BinBufSize: Integer);
  2643. begin
  2644. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2645. end;
  2646. procedure BinToHex(const BinValue; HexValue: PAnsiChar; BinBufSize: Integer);
  2647. begin
  2648. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2649. end;
  2650. procedure BinToHex(const BinValue; HexValue: PWideChar; BinBufSize: Integer);
  2651. begin
  2652. BinToHex(PAnsiChar(BinValue), HexValue, BinBufSize);
  2653. end;
  2654. function HexToBin(const HexText: PWideChar; HexTextOffset: Integer; var BinBuffer: TBytes; BinBufOffset: Integer; Count: Integer): Integer;
  2655. var
  2656. i : Integer;
  2657. PText : PWideChar;
  2658. PBinBuf : PAnsiChar;
  2659. begin
  2660. PText:=HexText+HexTextOffset;
  2661. PBinBuf:=PAnsiChar(BinBuffer)+BinBufOffset;
  2662. i:=Count;
  2663. Result:=HexToBin(PText, PBinBuf, i);
  2664. end;
  2665. function HexToBin(const HexText: TBytes; HexTextOffset: Integer; var BinBuffer: TBytes; BinBufOffset: Integer; Count: Integer): Integer;
  2666. var
  2667. i : Integer;
  2668. PText : PAnsiChar;
  2669. PBinBuf : PAnsiChar;
  2670. begin
  2671. PText:=PAnsiChar(HexText)+HexTextOffset;
  2672. PBinBuf:=PAnsiChar(BinBuffer)+BinBufOffset;
  2673. i:=Count;
  2674. Result:=HexToBin(PText, PBinBuf, i);
  2675. end;
  2676. function HexToBin(HexText: PWideChar; BinBuffer: Pointer; BinBufSize: Integer): Integer;
  2677. begin
  2678. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2679. end;
  2680. function HexToBin(const HexText: PWideChar; var BinBuffer; BinBufSize: Integer): Integer;
  2681. begin
  2682. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2683. end;
  2684. function HexToBin(HexText: PAnsiChar; BinBuffer: PAnsiChar; BinBufSize: Integer): Integer;
  2685. var
  2686. i,num : integer;
  2687. begin
  2688. i:=BinBufSize;
  2689. while (i>0) do
  2690. begin
  2691. // get value of first character
  2692. case HexText^ of
  2693. '0'..'9':
  2694. num:=ord(HexText^)-ord('0');
  2695. 'a'..'f':
  2696. num:=ord(HexText^)-(ord('a')-10);
  2697. 'A'..'F':
  2698. num:=ord(HexText^)-(ord('A')-10);
  2699. else
  2700. break;
  2701. end;
  2702. // add value of second character
  2703. case HexText[1] of
  2704. '0'..'9':
  2705. num:=num shl 4 or (ord(HexText[1])-ord('0'));
  2706. 'a'..'f':
  2707. num:=num shl 4 or (ord(HexText[1])-(ord('a')-10));
  2708. 'A'..'F':
  2709. num:=num shl 4 or (ord(HexText[1])-(ord('A')-10));
  2710. else
  2711. break;
  2712. end;
  2713. BinBuffer^:=AnsiChar(num);
  2714. inc(BinBuffer);
  2715. inc(HexText,2);
  2716. dec(i);
  2717. end;
  2718. Result:=BinBufSize-i;
  2719. end;
  2720. function HexToBin(HexText: PWideChar; BinBuffer: PAnsiChar; BinBufSize: Integer): Integer;
  2721. var
  2722. i,num : integer;
  2723. begin
  2724. i:=BinBufSize;
  2725. while (i>0) do
  2726. begin
  2727. // get value of first character
  2728. case HexText^ of
  2729. '0'..'9':
  2730. num:=ord(HexText^)-ord('0');
  2731. 'a'..'f':
  2732. num:=ord(HexText^)-(ord('a')-10);
  2733. 'A'..'F':
  2734. num:=ord(HexText^)-(ord('A')-10);
  2735. else // this includes >#255.
  2736. break;
  2737. end;
  2738. // add value of second character
  2739. case HexText[1] of
  2740. '0'..'9':
  2741. num:=num shl 4 or (ord(HexText[1])-ord('0'));
  2742. 'a'..'f':
  2743. num:=num shl 4 or (ord(HexText[1])-(ord('a')-10));
  2744. 'A'..'F':
  2745. num:=num shl 4 or (ord(HexText[1])-(ord('A')-10));
  2746. else // this includes >#255.
  2747. break;
  2748. end;
  2749. BinBuffer^:=AnsiChar(num);
  2750. inc(BinBuffer);
  2751. inc(HexText,2);
  2752. dec(i);
  2753. end;
  2754. Result:=BinBufSize-i;
  2755. end;
  2756. function HexToBin(HexText: PAnsiChar; var BinBuffer; BinBufSize: Integer): Integer;
  2757. begin
  2758. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2759. end;
  2760. function HexToBin(const HexText: PAnsiChar; BinBuffer: Pointer; BinBufSize: Integer): Integer;
  2761. begin
  2762. Result:=HexToBin(HexText, PAnsiChar(BinBuffer), BinBufSize);
  2763. end;
  2764. function PosSetEx(const c: TSysCharSet; const s: ansistring; count: Integer): SizeInt;
  2765. var i,j:SizeInt;
  2766. begin
  2767. i:=length(s);
  2768. j:=count;
  2769. while (j<=i) and (not (s[j] in c)) do inc(j);
  2770. if (j>i) then
  2771. j:=0; // not found.
  2772. result:=j;
  2773. end;
  2774. function PosSet(const c: TSysCharSet; const s: ansistring): SizeInt;
  2775. begin
  2776. result:=possetex(c,s,1);
  2777. end;
  2778. function StringToCharset(const c: string): TSysCharSet;
  2779. var
  2780. i: SizeInt;
  2781. begin
  2782. result:=[];
  2783. for i:=1 to length(c) do
  2784. include(result,c[i]);
  2785. end;
  2786. function PosSetEx(const c: string; const s: ansistring; count: Integer): SizeInt;
  2787. begin
  2788. result:=0;
  2789. if length(c)>0 then
  2790. result:=possetex(StringToCharset(c),s,count);
  2791. end;
  2792. function PosSet(const c: string; const s: ansistring): SizeInt;
  2793. begin
  2794. result:=0;
  2795. if length(c)>0 then
  2796. result:=possetex(StringToCharset(c),s,1);
  2797. end;
  2798. procedure Removeleadingchars(VAR S: AnsiString; const CSet: TSysCharset);
  2799. VAR I,J : Longint;
  2800. Begin
  2801. I:=Length(S);
  2802. J:=1;
  2803. While (J<=I) And (S[J] IN CSet) DO
  2804. INC(J);
  2805. IF J>1 Then
  2806. Delete(S,1,J-1);
  2807. End;
  2808. procedure Removeleadingchars(VAR S: UnicodeString; const CSet: TSysCharset);
  2809. VAR I,J : Longint;
  2810. Begin
  2811. I:=Length(S);
  2812. J:=1;
  2813. While (J<=I) And (S[J] IN CSet) DO
  2814. INC(J);
  2815. IF J>1 Then
  2816. Delete(S,1,J-1);
  2817. End;
  2818. function TrimLeftSet(const S: String;const CSet:TSysCharSet): String;
  2819. begin
  2820. result:=s;
  2821. removeleadingchars(result,cset);
  2822. end;
  2823. procedure RemoveTrailingChars(VAR S: AnsiString; const CSet: TSysCharset);
  2824. VAR I,J: LONGINT;
  2825. Begin
  2826. I:=Length(S);
  2827. J:=I;
  2828. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2829. IF J<>I Then
  2830. SetLength(S,J);
  2831. End;
  2832. procedure RemoveTrailingChars(VAR S: UnicodeString; const CSet: TSysCharset);
  2833. VAR I,J: LONGINT;
  2834. Begin
  2835. I:=Length(S);
  2836. J:=I;
  2837. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2838. IF J<>I Then
  2839. SetLength(S,J);
  2840. End;
  2841. function TrimRightSet(const S: String; const CSet: TSysCharSet): String;
  2842. begin
  2843. result:=s;
  2844. RemoveTrailingchars(result,cset);
  2845. end;
  2846. procedure RemovePadChars(VAR S: AnsiString; const CSet: TSysCharset);
  2847. VAR J,K: SizeInt;
  2848. Begin
  2849. J:=Length(S);
  2850. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2851. k:=1;
  2852. While (k<=J) And (S[k] IN CSet) DO
  2853. INC(k);
  2854. IF k>1 Then
  2855. move(s[k],s[1],(j-k+1)*sizeof(S[1]));
  2856. setlength(s,j-k+1);
  2857. End;
  2858. procedure RemovePadChars(VAR S: UnicodeString; const CSet: TSysCharset);
  2859. VAR J,K: SizeInt;
  2860. Begin
  2861. J:=Length(S);
  2862. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2863. k:=1;
  2864. While (k<=J) And (S[k] IN CSet) DO
  2865. INC(k);
  2866. IF k>1 Then
  2867. move(s[k],s[1],(j-k+1)*sizeof(S[1]));
  2868. setlength(s,j-k+1);
  2869. End;
  2870. function TrimSet(const S: String;const CSet:TSysCharSet): String;
  2871. begin
  2872. result:=s;
  2873. RemovePadChars(result,cset);
  2874. end;
  2875. Function SplitCommandLine(S : RawByteString) : TRawByteStringArray;
  2876. Function GetNextWord : RawByteString;
  2877. Const
  2878. WhiteSpace = [' ',#9,#10,#13];
  2879. Literals = ['"',''''];
  2880. Var
  2881. Wstart,wend : Integer;
  2882. InLiteral : Boolean;
  2883. LastLiteral : AnsiChar;
  2884. Procedure AppendToResult;
  2885. begin
  2886. Result:=Result+Copy(S,WStart,WEnd-WStart);
  2887. WStart:=Wend+1;
  2888. end;
  2889. begin
  2890. Result:='';
  2891. WStart:=1;
  2892. While (WStart<=Length(S)) and charinset(S[WStart],WhiteSpace) do
  2893. Inc(WStart);
  2894. WEnd:=WStart;
  2895. InLiteral:=False;
  2896. LastLiteral:=#0;
  2897. While (Wend<=Length(S)) and (Not charinset(S[Wend],WhiteSpace) or InLiteral) do
  2898. begin
  2899. if charinset(S[Wend],Literals) then
  2900. If InLiteral then
  2901. begin
  2902. InLiteral:=Not (S[Wend]=LastLiteral);
  2903. if not InLiteral then
  2904. AppendToResult;
  2905. end
  2906. else
  2907. begin
  2908. InLiteral:=True;
  2909. LastLiteral:=S[Wend];
  2910. AppendToResult;
  2911. end;
  2912. inc(wend);
  2913. end;
  2914. AppendToResult;
  2915. While (WEnd<=Length(S)) and (S[Wend] in WhiteSpace) do
  2916. inc(Wend);
  2917. Delete(S,1,WEnd-1);
  2918. end;
  2919. Var
  2920. W : RawByteString;
  2921. len : Integer;
  2922. begin
  2923. Len:=0;
  2924. Result:=Default(TRawByteStringArray);
  2925. SetLength(Result,(Length(S) div 2)+1);
  2926. While Length(S)>0 do
  2927. begin
  2928. W:=GetNextWord;
  2929. If (W<>'') then
  2930. begin
  2931. Result[Len]:=W;
  2932. Inc(Len);
  2933. end;
  2934. end;
  2935. SetLength(Result,Len);
  2936. end;
  2937. Function SplitCommandLine(S : UnicodeString) : TUnicodeStringArray;
  2938. Function GetNextWord : UnicodeString;
  2939. Const
  2940. WhiteSpace = [' ',#9,#10,#13];
  2941. Literals = ['"',''''];
  2942. Var
  2943. Wstart,wend : Integer;
  2944. InLiteral : Boolean;
  2945. LastLiteral : AnsiChar;
  2946. Procedure AppendToResult;
  2947. begin
  2948. Result:=Result+Copy(S,WStart,WEnd-WStart);
  2949. WStart:=Wend+1;
  2950. end;
  2951. begin
  2952. Result:='';
  2953. WStart:=1;
  2954. While (WStart<=Length(S)) and charinset(S[WStart],WhiteSpace) do
  2955. Inc(WStart);
  2956. WEnd:=WStart;
  2957. InLiteral:=False;
  2958. LastLiteral:=#0;
  2959. While (Wend<=Length(S)) and (Not charinset(S[Wend],WhiteSpace) or InLiteral) do
  2960. begin
  2961. if charinset(S[Wend],Literals) then
  2962. If InLiteral then
  2963. begin
  2964. InLiteral:=Not (S[Wend]=LastLiteral);
  2965. if not InLiteral then
  2966. AppendToResult;
  2967. end
  2968. else
  2969. begin
  2970. InLiteral:=True;
  2971. LastLiteral:=S[Wend];
  2972. AppendToResult;
  2973. end;
  2974. inc(wend);
  2975. end;
  2976. AppendToResult;
  2977. While (WEnd<=Length(S)) and (S[Wend] in WhiteSpace) do
  2978. inc(Wend);
  2979. Delete(S,1,WEnd-1);
  2980. end;
  2981. Var
  2982. W : UnicodeString;
  2983. len : Integer;
  2984. begin
  2985. Len:=0;
  2986. Result:=Default(TUnicodeStringArray);
  2987. SetLength(Result,(Length(S) div 2)+1);
  2988. While Length(S)>0 do
  2989. begin
  2990. W:=GetNextWord;
  2991. If (W<>'') then
  2992. begin
  2993. Result[Len]:=W;
  2994. Inc(Len);
  2995. end;
  2996. end;
  2997. SetLength(Result,Len);
  2998. end;
  2999. end.