strutils.pp 97 KB

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