JclStrings.pas 149 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418
  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclStrings.pas. }
  14. { }
  15. { The Initial Developer of the Original Code is Marcel van Brakel. }
  16. { Portions created by Marcel van Brakel are Copyright (C) Marcel van Brakel. All rights reserved. }
  17. { }
  18. { Contributor(s): }
  19. { Alexander Radchenko }
  20. { Andreas Hausladen (ahuser) }
  21. { Anthony Steele }
  22. { Azret Botash }
  23. { Barry Kelly }
  24. { Huanlin Tsai }
  25. { Jack N.A. Bakker }
  26. { Jean-Fabien Connault (cycocrew) }
  27. { John C Molyneux }
  28. { Kiriakos Vlahos }
  29. { Leonard Wennekers }
  30. { Marcel Bestebroer }
  31. { Martin Kimmings }
  32. { Martin Kubecka }
  33. { Massimo Maria Ghisalberti }
  34. { Matthias Thoma (mthoma) }
  35. { Michael Winter }
  36. { Nick Hodges }
  37. { Olivier Sannier (obones) }
  38. { Pelle F. S. Liljendal }
  39. { Petr Vones (pvones) }
  40. { Rik Barker (rikbarker) }
  41. { Robert Lee }
  42. { Robert Marquardt (marquardt) }
  43. { Robert Rossmair (rrossmair) }
  44. { Andreas Schmidt }
  45. { Sean Farrow (sfarrow) }
  46. { }
  47. {**************************************************************************************************}
  48. { }
  49. { Various character and string routines (searching, testing and transforming) }
  50. { }
  51. {**************************************************************************************************}
  52. { }
  53. { Last modified: $Date:: $ }
  54. { Revision: $Rev:: $ }
  55. { Author: $Author:: $ }
  56. { }
  57. {**************************************************************************************************}
  58. unit JclStrings;
  59. {$I jcl.inc}
  60. interface
  61. uses
  62. {$IFDEF UNITVERSIONING}
  63. JclUnitVersioning,
  64. {$ENDIF UNITVERSIONING}
  65. {$IFDEF HAS_UNITSCOPE}
  66. {$IFDEF MSWINDOWS}
  67. Winapi.Windows,
  68. {$ENDIF MSWINDOWS}
  69. {$IFDEF UNICODE_RTL_DATABASE}
  70. System.Character,
  71. {$ENDIF UNICODE_RTL_DATABASE}
  72. System.Classes, System.SysUtils,
  73. {$ELSE ~HAS_UNITSCOPE}
  74. {$IFDEF MSWINDOWS}
  75. Windows,
  76. {$ENDIF MSWINDOWS}
  77. {$IFDEF UNICODE_RTL_DATABASE}
  78. Character,
  79. {$ENDIF UNICODE_RTL_DATABASE}
  80. Classes, SysUtils,
  81. {$ENDIF ~HAS_UNITSCOPE}
  82. JclAnsiStrings,
  83. JclWideStrings,
  84. JclBase;
  85. // Exceptions
  86. type
  87. EJclStringError = class(EJclError);
  88. // Character constants and sets
  89. const
  90. // Misc. often used character definitions
  91. NativeNull = Char(#0);
  92. NativeSoh = Char(#1);
  93. NativeStx = Char(#2);
  94. NativeEtx = Char(#3);
  95. NativeEot = Char(#4);
  96. NativeEnq = Char(#5);
  97. NativeAck = Char(#6);
  98. NativeBell = Char(#7);
  99. NativeBackspace = Char(#8);
  100. NativeTab = Char(#9);
  101. NativeLineFeed = JclBase.NativeLineFeed;
  102. NativeVerticalTab = Char(#11);
  103. NativeFormFeed = Char(#12);
  104. NativeCarriageReturn = JclBase.NativeCarriageReturn;
  105. NativeCrLf = JclBase.NativeCrLf;
  106. NativeSo = Char(#14);
  107. NativeSi = Char(#15);
  108. NativeDle = Char(#16);
  109. NativeDc1 = Char(#17);
  110. NativeDc2 = Char(#18);
  111. NativeDc3 = Char(#19);
  112. NativeDc4 = Char(#20);
  113. NativeNak = Char(#21);
  114. NativeSyn = Char(#22);
  115. NativeEtb = Char(#23);
  116. NativeCan = Char(#24);
  117. NativeEm = Char(#25);
  118. NativeEndOfFile = Char(#26);
  119. NativeEscape = Char(#27);
  120. NativeFs = Char(#28);
  121. NativeGs = Char(#29);
  122. NativeRs = Char(#30);
  123. NativeUs = Char(#31);
  124. NativeSpace = Char(' ');
  125. NativeComma = Char(',');
  126. NativeBackslash = Char('\');
  127. NativeForwardSlash = Char('/');
  128. NativeDoubleQuote = Char('"');
  129. NativeSingleQuote = Char('''');
  130. NativeLineBreak = JclBase.NativeLineBreak;
  131. const
  132. // CharType return values
  133. C1_UPPER = $0001; // Uppercase
  134. C1_LOWER = $0002; // Lowercase
  135. C1_DIGIT = $0004; // Decimal digits
  136. C1_SPACE = $0008; // Space characters
  137. C1_PUNCT = $0010; // Punctuation
  138. C1_CNTRL = $0020; // Control characters
  139. C1_BLANK = $0040; // Blank characters
  140. C1_XDIGIT = $0080; // Hexadecimal digits
  141. C1_ALPHA = $0100; // Any linguistic character: alphabetic, syllabary, or ideographic
  142. {$IFDEF MSWINDOWS}
  143. {$IFDEF SUPPORTS_EXTSYM}
  144. {$EXTERNALSYM C1_UPPER}
  145. {$EXTERNALSYM C1_LOWER}
  146. {$EXTERNALSYM C1_DIGIT}
  147. {$EXTERNALSYM C1_SPACE}
  148. {$EXTERNALSYM C1_PUNCT}
  149. {$EXTERNALSYM C1_CNTRL}
  150. {$EXTERNALSYM C1_BLANK}
  151. {$EXTERNALSYM C1_XDIGIT}
  152. {$EXTERNALSYM C1_ALPHA}
  153. {$ENDIF SUPPORTS_EXTSYM}
  154. {$ENDIF MSWINDOWS}
  155. type
  156. TCharValidator = function(const C: Char): Boolean;
  157. function ArrayContainsChar(const Chars: array of Char; const C: Char): Boolean; overload;
  158. function ArrayContainsChar(const Chars: array of Char; const C: Char; out Index: SizeInt): Boolean; overload;
  159. // String Test Routines
  160. function StrIsAlpha(const S: string): Boolean;
  161. function StrIsAlphaNum(const S: string): Boolean;
  162. function StrIsAlphaNumUnderscore(const S: string): Boolean;
  163. function StrContainsChars(const S: string; const Chars: TCharValidator; CheckAll: Boolean): Boolean; overload;
  164. function StrContainsChars(const S: string; const Chars: array of Char; CheckAll: Boolean): Boolean; overload;
  165. function StrConsistsOfNumberChars(const S: string): Boolean;
  166. function StrIsDigit(const S: string): Boolean;
  167. function StrIsSubset(const S: string; const ValidChars: TCharValidator): Boolean; overload;
  168. function StrIsSubset(const S: string; const ValidChars: array of Char): Boolean; overload;
  169. function StrSame(const S1, S2: string; CaseSensitive: Boolean = False): Boolean;
  170. // String Transformation Routines
  171. function StrCenter(const S: string; L: SizeInt; C: Char = ' '): string;
  172. function StrCharPosLower(const S: string; CharPos: SizeInt): string;
  173. function StrCharPosUpper(const S: string; CharPos: SizeInt): string;
  174. function StrDoubleQuote(const S: string): string;
  175. function StrEnsureNoPrefix(const Prefix, Text: string): string;
  176. function StrEnsureNoSuffix(const Suffix, Text: string): string;
  177. function StrEnsurePrefix(const Prefix, Text: string): string;
  178. function StrEnsureSuffix(const Suffix, Text: string): string;
  179. function StrEscapedToString(const S: string): string;
  180. function StrLower(const S: string): string;
  181. procedure StrLowerInPlace(var S: string);
  182. procedure StrLowerBuff(S: PChar);
  183. procedure StrMove(var Dest: string; const Source: string; const ToIndex,
  184. FromIndex, Count: SizeInt);
  185. function StrPadLeft(const S: string; Len: SizeInt; C: Char = NativeSpace): string;
  186. function StrPadRight(const S: string; Len: SizeInt; C: Char = NativeSpace): string;
  187. function StrProper(const S: string): string;
  188. procedure StrProperBuff(S: PChar);
  189. function StrQuote(const S: string; C: Char): string;
  190. function StrRemoveChars(const S: string; const Chars: TCharValidator): string; overload;
  191. function StrRemoveChars(const S: string; const Chars: array of Char): string; overload;
  192. function StrRemoveLeadingChars(const S: string; const Chars: TCharValidator): string; overload;
  193. function StrRemoveLeadingChars(const S: string; const Chars: array of Char): string; overload;
  194. function StrRemoveEndChars(const S: string; const Chars: TCharValidator): string; overload;
  195. function StrRemoveEndChars(const S: string; const Chars: array of Char): string; overload;
  196. function StrKeepChars(const S: string; const Chars: TCharValidator): string; overload;
  197. function StrKeepChars(const S: string; const Chars: array of Char): string; overload;
  198. procedure StrReplace(var S: string; const Search, Replace: string; Flags: TReplaceFlags = []);
  199. function StrReplaceChar(const S: string; const Source, Replace: Char): string;
  200. function StrReplaceChars(const S: string; const Chars: TCharValidator; Replace: Char): string; overload;
  201. function StrReplaceChars(const S: string; const Chars: array of Char; Replace: Char): string; overload;
  202. function StrReplaceButChars(const S: string; const Chars: TCharValidator; Replace: Char): string; overload;
  203. function StrReplaceButChars(const S: string; const Chars: array of Char; Replace: Char): string; overload;
  204. function StrRepeat(const S: string; Count: SizeInt): string;
  205. function StrRepeatLength(const S: string; L: SizeInt): string;
  206. function StrReverse(const S: string): string;
  207. procedure StrReverseInPlace(var S: string);
  208. function StrSingleQuote(const S: string): string;
  209. procedure StrSkipChars(var S: PChar; const Chars: TCharValidator); overload;
  210. procedure StrSkipChars(var S: PChar; const Chars: array of Char); overload;
  211. procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: TCharValidator); overload;
  212. procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: array of Char); overload;
  213. function StrSmartCase(const S: string; const Delimiters: TCharValidator): string; overload;
  214. function StrSmartCase(const S: string; const Delimiters: array of Char): string; overload;
  215. function StrStringToEscaped(const S: string): string;
  216. function StrStripNonNumberChars(const S: string): string;
  217. function StrToHex(const Source: string): string;
  218. function StrTrimCharLeft(const S: string; C: Char): string;
  219. function StrTrimCharsLeft(const S: string; const Chars: TCharValidator): string; overload;
  220. function StrTrimCharsLeft(const S: string; const Chars: array of Char): string; overload;
  221. function StrTrimCharRight(const S: string; C: Char): string;
  222. function StrTrimCharsRight(const S: string; const Chars: TCharValidator): string; overload;
  223. function StrTrimCharsRight(const S: string; const Chars: array of Char): string; overload;
  224. function StrTrimQuotes(const S: string): string;
  225. function StrUpper(const S: string): string;
  226. procedure StrUpperInPlace(var S: string);
  227. procedure StrUpperBuff(S: PChar);
  228. // String Management
  229. procedure StrAddRef(var S: string);
  230. procedure StrDecRef(var S: string);
  231. function StrLength(const S: string): SizeInt;
  232. function StrRefCount(const S: string): SizeInt;
  233. // String Search and Replace Routines
  234. function StrCharCount(const S: string; C: Char): SizeInt; overload;
  235. function StrCharsCount(const S: string; const Chars: TCharValidator): SizeInt; overload;
  236. function StrCharsCount(const S: string; const Chars: array of Char): SizeInt; overload;
  237. function StrStrCount(const S, SubS: string): SizeInt;
  238. function StrCompare(const S1, S2: string; CaseSensitive: Boolean = False): SizeInt;
  239. function StrCompareRange(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean = True): SizeInt;
  240. function StrCompareRangeEx(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
  241. procedure StrFillChar(var S; Count: SizeInt; C: Char);
  242. function StrRepeatChar(C: Char; Count: SizeInt): string;
  243. function StrFind(const Substr, S: string; const Index: SizeInt = 1): SizeInt;
  244. function StrHasPrefix(const S: string; const Prefixes: array of string): Boolean;
  245. function StrHasSuffix(const S: string; const Suffixes: array of string): Boolean;
  246. function StrIndex(const S: string; const List: array of string; CaseSensitive: Boolean = False): SizeInt;
  247. function StrIHasPrefix(const S: string; const Prefixes: array of string): Boolean;
  248. function StrIHasSuffix(const S: string; const Suffixes: array of string): Boolean;
  249. function StrILastPos(const SubStr, S: string): SizeInt;
  250. function StrIPos(const SubStr, S: string): SizeInt;
  251. function StrIPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
  252. function StrIsOneOf(const S: string; const List: array of string): Boolean;
  253. function StrISuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
  254. function StrLastPos(const SubStr, S: string): SizeInt;
  255. function StrMatch(const Substr, S: string; Index: SizeInt = 1): SizeInt;
  256. function StrMatches(const Substr, S: string; const Index: SizeInt = 1): Boolean;
  257. function StrNIPos(const S, SubStr: string; N: SizeInt): SizeInt;
  258. function StrNPos(const S, SubStr: string; N: SizeInt): SizeInt;
  259. function StrPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
  260. function StrSearch(const Substr, S: string; const Index: SizeInt = 1): SizeInt;
  261. function StrSuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
  262. // String Extraction
  263. /// Returns the string after SubStr
  264. function StrAfter(const SubStr, S: string): string;
  265. /// Returns the String before SubStr
  266. function StrBefore(const SubStr, S: string): string;
  267. /// Splits a string at SubStr, returns true when SubStr is found, Left contains the
  268. /// string before the SubStr and Right the string behind SubStr
  269. function StrSplit(const SubStr, S: string;var Left, Right : string): boolean;
  270. /// Returns the string between Start and Stop
  271. function StrBetween(const S: string; const Start, Stop: Char): string;
  272. /// Returns all but rightmost N characters of the string
  273. function StrChopRight(const S: string; N: SizeInt): string;{$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
  274. /// Returns the left Count characters of the string
  275. function StrLeft(const S: string; Count: SizeInt): string; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
  276. /// Returns the string starting from position Start for the Count Characters
  277. function StrMid(const S: string; Start, Count: SizeInt): string; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
  278. /// Returns the string starting from position N to the end
  279. function StrRestOf(const S: string; N: SizeInt): string;{$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
  280. /// Returns the right Count characters of the string
  281. function StrRight(const S: string; Count: SizeInt): string;{$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
  282. // Character Test Routines
  283. function CharEqualNoCase(const C1, C2: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  284. function CharIsAlpha(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  285. function CharIsAlphaNum(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  286. function CharIsBlank(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  287. function CharIsControl(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  288. function CharIsDelete(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  289. function CharIsDigit(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  290. function CharIsFracDigit(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  291. function CharIsHexDigit(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  292. function CharIsLower(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  293. function CharIsNumberChar(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
  294. function CharIsNumber(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
  295. function CharIsPrintable(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  296. function CharIsPunctuation(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  297. function CharIsReturn(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  298. function CharIsSpace(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  299. function CharIsUpper(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  300. function CharIsValidIdentifierLetter(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  301. function CharIsWhiteSpace(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  302. function CharIsWildcard(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  303. function CharType(const C: Char): Word;
  304. // Character Transformation Routines
  305. function CharHex(const C: Char): Byte;
  306. function CharLower(const C: Char): Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  307. function CharUpper(const C: Char): Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  308. function CharToggleCase(const C: Char): Char;
  309. // Character Search and Replace
  310. function CharPos(const S: string; const C: Char; const Index: SizeInt = 1): SizeInt;
  311. function CharLastPos(const S: string; const C: Char; const Index: SizeInt = 1): SizeInt;
  312. function CharIPos(const S: string; C: Char; const Index: SizeInt = 1): SizeInt;
  313. function CharReplace(var S: string; const Search, Replace: Char): SizeInt;
  314. // PCharVector
  315. type
  316. PCharVector = ^PChar;
  317. function StringsToPCharVector(var Dest: PCharVector; const Source: TStrings): PCharVector;
  318. function PCharVectorCount(Source: PCharVector): SizeInt;
  319. procedure PCharVectorToStrings(const Dest: TStrings; Source: PCharVector);
  320. procedure FreePCharVector(var Dest: PCharVector);
  321. // MultiSz Routines
  322. type
  323. PMultiSz = PChar;
  324. PAnsiMultiSz = JclAnsiStrings.PAnsiMultiSz;
  325. PWideMultiSz = JclWideStrings.PWideMultiSz;
  326. TAnsiStrings = JclAnsiStrings.TJclAnsiStrings;
  327. TWideStrings = JclWideStrings.TJclWideStrings;
  328. TAnsiStringList = JclAnsiStrings.TJclAnsiStringList;
  329. TWideStringList = JclWideStrings.TJclWideStringList;
  330. function StringsToMultiSz(var Dest: PMultiSz; const Source: TStrings): PMultiSz;
  331. procedure MultiSzToStrings(const Dest: TStrings; const Source: PMultiSz);
  332. function MultiSzLength(const Source: PMultiSz): SizeInt;
  333. procedure AllocateMultiSz(var Dest: PMultiSz; Len: SizeInt);
  334. procedure FreeMultiSz(var Dest: PMultiSz);
  335. function MultiSzDup(const Source: PMultiSz): PMultiSz;
  336. function AnsiStringsToAnsiMultiSz(var Dest: PAnsiMultiSz; const Source: TAnsiStrings): PAnsiMultiSz;
  337. {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  338. procedure AnsiMultiSzToAnsiStrings(const Dest: TAnsiStrings; const Source: PAnsiMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  339. function AnsiMultiSzLength(const Source: PAnsiMultiSz): SizeInt; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  340. procedure AllocateAnsiMultiSz(var Dest: PAnsiMultiSz; Len: SizeInt); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  341. procedure FreeAnsiMultiSz(var Dest: PAnsiMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  342. function AnsiMultiSzDup(const Source: PAnsiMultiSz): PAnsiMultiSz; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  343. function WideStringsToWideMultiSz(var Dest: PWideMultiSz; const Source: TWideStrings): PWideMultiSz;
  344. {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  345. procedure WideMultiSzToWideStrings(const Dest: TWideStrings; const Source: PWideMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  346. function WideMultiSzLength(const Source: PWideMultiSz): SizeInt; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  347. procedure AllocateWideMultiSz(var Dest: PWideMultiSz; Len: SizeInt); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  348. procedure FreeWideMultiSz(var Dest: PWideMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  349. function WideMultiSzDup(const Source: PWideMultiSz): PWideMultiSz; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  350. // TStrings Manipulation
  351. procedure StrIToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
  352. procedure StrToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
  353. function StringsToStr(const List: TStrings; const Sep: string; const AllowEmptyString: Boolean = True): string; overload;
  354. function StringsToStr(const List: TStrings; const Sep: string; const NumberOfItems: SizeInt; const AllowEmptyString:
  355. Boolean = True): string; overload;
  356. procedure TrimStrings(const List: TStrings; DeleteIfEmpty: Boolean = True);
  357. procedure TrimStringsRight(const List: TStrings; DeleteIfEmpty: Boolean = True);
  358. procedure TrimStringsLeft(const List: TStrings; DeleteIfEmpty: Boolean = True);
  359. function AddStringToStrings(const S: string; Strings: TStrings; const Unique: Boolean): Boolean;
  360. // Miscellaneous
  361. // (OF) moved to JclSysUtils
  362. // function BooleanToStr(B: Boolean): string;
  363. // AnsiString here because it is binary data
  364. function FileToString(const FileName: string): {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
  365. procedure StringToFile(const FileName: string; const Contents: {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
  366. Append: Boolean = False);
  367. function StrToken(var S: string; Separator: Char): string;
  368. procedure StrTokens(const S: string; const List: TStrings);
  369. procedure StrTokenToStrings(S: string; Separator: Char; const List: TStrings);
  370. function StrWord(const S: string; var Index: SizeInt; out Word: string): Boolean; overload;
  371. function StrWord(var S: PChar; out Word: string): Boolean; overload;
  372. function StrIdent(const S: string; var Index: SizeInt; out Ident: string): Boolean; overload;
  373. function StrIdent(var S: PChar; out Ident: string): Boolean; overload;
  374. function StrToFloatSafe(const S: string): Float;
  375. function StrToIntSafe(const S: string): Integer;
  376. procedure StrNormIndex(const StrLen: SizeInt; var Index: SizeInt; var Count: SizeInt); overload;
  377. function ArrayOf(List: TStrings): TDynStringArray; overload;
  378. type
  379. FormatException = class(EJclError);
  380. ArgumentException = class(EJclError);
  381. ArgumentNullException = class(EJclError);
  382. ArgumentOutOfRangeException = class(EJclError);
  383. IToString = interface
  384. ['{C4ABABB4-1029-46E7-B5FA-99800F130C05}']
  385. function ToString: string;
  386. end;
  387. TCharDynArray = array of Char;
  388. // The TStringBuilder class is a Delphi implementation of the .NET
  389. // System.Text.StringBuilder.
  390. // It is zero based and the methods that have a TObject argument (Append, Insert,
  391. // AppendFormat) are limited to IToString implementors or Delphi 2009+ RTL.
  392. // This class is not threadsafe. Any instance of TStringBuilder should not
  393. // be used in different threads at the same time.
  394. TJclStringBuilder = class(TInterfacedObject, IToString)
  395. private
  396. FChars: TCharDynArray;
  397. FLength: SizeInt;
  398. FMaxCapacity: SizeInt;
  399. function GetCapacity: SizeInt;
  400. procedure SetCapacity(const Value: SizeInt);
  401. function GetChars(Index: SizeInt): Char;
  402. procedure SetChars(Index: SizeInt; const Value: Char);
  403. procedure Set_Length(const Value: SizeInt);
  404. protected
  405. function AppendPChar(Value: PChar; Count: SizeInt; RepeatCount: SizeInt = 1): TJclStringBuilder;
  406. function InsertPChar(Index: SizeInt; Value: PChar; Count: SizeInt; RepeatCount: SizeInt = 1): TJclStringBuilder;
  407. public
  408. constructor Create(const Value: string; Capacity: SizeInt = 16); overload;
  409. constructor Create(Capacity: SizeInt = 16; MaxCapacity: SizeInt = MaxInt); overload;
  410. constructor Create(const Value: string; StartIndex, Length, Capacity: SizeInt); overload;
  411. function Append(const Value: string): TJclStringBuilder; overload;
  412. function Append(const Value: string; StartIndex, Length: SizeInt): TJclStringBuilder; overload;
  413. function Append(Value: Boolean): TJclStringBuilder; overload;
  414. function Append(Value: Char; RepeatCount: SizeInt = 1): TJclStringBuilder; overload;
  415. function Append(const Value: array of Char): TJclStringBuilder; overload;
  416. function Append(const Value: array of Char; StartIndex, Length: SizeInt): TJclStringBuilder; overload;
  417. function Append(Value: Cardinal): TJclStringBuilder; overload;
  418. function Append(Value: Integer): TJclStringBuilder; overload;
  419. function Append(Value: Double): TJclStringBuilder; overload;
  420. function Append(Value: Int64): TJclStringBuilder; overload;
  421. function Append(Obj: TObject): TJclStringBuilder; overload;
  422. function AppendFormat(const Fmt: string; const Args: array of const): TJclStringBuilder; overload;
  423. function AppendFormat(const Fmt: string; Arg0: Variant): TJclStringBuilder; overload;
  424. function AppendFormat(const Fmt: string; Arg0, Arg1: Variant): TJclStringBuilder; overload;
  425. function AppendFormat(const Fmt: string; Arg0, Arg1, Arg2: Variant): TJclStringBuilder; overload;
  426. function Insert(Index: SizeInt; const Value: string; Count: SizeInt = 1): TJclStringBuilder; overload;
  427. function Insert(Index: SizeInt; Value: Boolean): TJclStringBuilder; overload;
  428. function Insert(Index: SizeInt; const Value: array of Char): TJclStringBuilder; overload;
  429. function Insert(Index: SizeInt; const Value: array of Char; StartIndex, Length: SizeInt): TJclStringBuilder;
  430. overload;
  431. function Insert(Index: SizeInt; Value: Cardinal): TJclStringBuilder; overload;
  432. function Insert(Index: SizeInt; Value: Integer): TJclStringBuilder; overload;
  433. function Insert(Index: SizeInt; Value: Double): TJclStringBuilder; overload;
  434. function Insert(Index: SizeInt; Value: Int64): TJclStringBuilder; overload;
  435. function Insert(Index: SizeInt; Obj: TObject): TJclStringBuilder; overload;
  436. function Replace(OldChar, NewChar: Char; StartIndex: SizeInt = 0; Count: SizeInt = -1): TJclStringBuilder;
  437. overload;
  438. function Replace(OldValue, NewValue: string; StartIndex: SizeInt = 0; Count: SizeInt = -1): TJclStringBuilder;
  439. overload;
  440. function Remove(StartIndex, Length: SizeInt): TJclStringBuilder;
  441. function EnsureCapacity(Capacity: SizeInt): SizeInt;
  442. procedure Clear;
  443. { IToString }
  444. function ToString: string; {$IFDEF RTL200_UP} override; {$ENDIF RTL200_UP}
  445. property __Chars__[Index: SizeInt]: Char read GetChars write SetChars; default;
  446. property Chars: TCharDynArray read FChars;
  447. property Length: SizeInt read FLength write Set_Length;
  448. property Capacity: SizeInt read GetCapacity write SetCapacity;
  449. property MaxCapacity: SizeInt read FMaxCapacity;
  450. end;
  451. {$IFDEF RTL200_UP}
  452. TStringBuilder = {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.TStringBuilder;
  453. {$ELSE ~RTL200_UP}
  454. TStringBuilder = TJclStringBuilder;
  455. {$ENDIF ~RTL200_UP}
  456. // DotNetFormat() uses the .NET format style: "{argX}"
  457. function DotNetFormat(const Fmt: string; const Args: array of const): string; overload;
  458. function DotNetFormat(const Fmt: string; const Arg0: Variant): string; overload;
  459. function DotNetFormat(const Fmt: string; const Arg0, Arg1: Variant): string; overload;
  460. function DotNetFormat(const Fmt: string; const Arg0, Arg1, Arg2: Variant): string; overload;
  461. // TJclTabSet
  462. type
  463. TJclTabSet = class (TInterfacedObject, IToString)
  464. private
  465. FData: TObject;
  466. function GetCount: SizeInt;
  467. function GetStops(Index: SizeInt): SizeInt;
  468. function GetTabWidth: SizeInt;
  469. function GetZeroBased: Boolean;
  470. procedure SetStops(Index, Value: SizeInt);
  471. procedure SetTabWidth(Value: SizeInt);
  472. procedure SetZeroBased(Value: Boolean);
  473. protected
  474. function FindStop(Column: SizeInt): SizeInt;
  475. function InternalTabStops: TDynSizeIntArray;
  476. function InternalTabWidth: SizeInt;
  477. procedure RemoveAt(Index: SizeInt);
  478. public
  479. constructor Create; overload;
  480. constructor Create(Data: TObject); overload;
  481. constructor Create(TabWidth: SizeInt); overload;
  482. constructor Create(const Tabstops: array of SizeInt; ZeroBased: Boolean); overload;
  483. constructor Create(const Tabstops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt); overload;
  484. destructor Destroy; override;
  485. // cloning and referencing
  486. function Clone: TJclTabSet;
  487. function NewReference: TJclTabSet;
  488. // Tab stops manipulation
  489. function Add(Column: SizeInt): SizeInt;
  490. function Delete(Column: SizeInt): SizeInt;
  491. // Usage
  492. function Expand(const S: string): string; overload;
  493. function Expand(const S: string; Column: SizeInt): string; overload;
  494. procedure OptimalFillInfo(StartColumn, TargetColumn: SizeInt; out TabsNeeded, SpacesNeeded: SizeInt);
  495. function Optimize(const S: string): string; overload;
  496. function Optimize(const S: string; Column: SizeInt): string; overload;
  497. function StartColumn: SizeInt;
  498. function TabFrom(Column: SizeInt): SizeInt;
  499. function UpdatePosition(const S: string): SizeInt; overload;
  500. function UpdatePosition(const S: string; Column: SizeInt): SizeInt; overload;
  501. function UpdatePosition(const S: string; var Column, Line: SizeInt): SizeInt; overload;
  502. { IToString }
  503. function ToString: string; overload; {$IFDEF RTL200_UP} override; {$ENDIF RTL200_UP}
  504. // Conversions
  505. function ToString(FormattingOptions: SizeInt): string; {$IFDEF RTL200_UP} reintroduce; {$ENDIF RTL200_UP} overload;
  506. class function FromString(const S: string): TJclTabSet; {$IFDEF SUPPORTS_STATIC} static; {$ENDIF SUPPORTS_STATIC}
  507. // Properties
  508. property ActualTabWidth: SizeInt read InternalTabWidth;
  509. property Count: SizeInt read GetCount;
  510. property TabStops[Index: SizeInt]: SizeInt read GetStops write SetStops; default;
  511. property TabWidth: SizeInt read GetTabWidth write SetTabWidth;
  512. property ZeroBased: Boolean read GetZeroBased write SetZeroBased;
  513. end;
  514. // Formatting constants
  515. const
  516. TabSetFormatting_SurroundStopsWithBrackets = 1;
  517. TabSetFormatting_EmptyBracketsIfNoStops = 2;
  518. TabSetFormatting_NoTabStops = 4;
  519. TabSetFormatting_NoTabWidth = 8;
  520. TabSetFormatting_AutoTabWidth = 16;
  521. // common combinations
  522. TabSetFormatting_Default = 0;
  523. TabSetFormatting_AlwaysUseBrackets = TabSetFormatting_SurroundStopsWithBrackets or
  524. TabSetFormatting_EmptyBracketsIfNoStops;
  525. TabSetFormatting_Full = TabSetFormatting_AlwaysUseBrackets or TabSetFormatting_AutoTabWidth;
  526. // aliases
  527. TabSetFormatting_StopsOnly = TabSetFormatting_NoTabWidth;
  528. TabSetFormatting_TabWidthOnly = TabSetFormatting_NoTabStops;
  529. TabSetFormatting_StopsWithoutBracketsAndTabWidth = TabSetFormatting_Default;
  530. // Tab expansion routines
  531. function StrExpandTabs(S: string): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
  532. function StrExpandTabs(S: string; TabWidth: SizeInt): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
  533. function StrExpandTabs(S: string; TabSet: TJclTabSet): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
  534. // Tab optimization routines
  535. function StrOptimizeTabs(S: string): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
  536. function StrOptimizeTabs(S: string; TabWidth: SizeInt): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
  537. function StrOptimizeTabs(S: string; TabSet: TJclTabSet): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
  538. // move to JclBase?
  539. type
  540. NullReferenceException = class(EJclError)
  541. public
  542. constructor Create; overload;
  543. end;
  544. procedure StrResetLength(var S: WideString); overload;
  545. procedure StrResetLength(var S: AnsiString); overload;
  546. procedure StrResetLength(S: TJclStringBuilder); overload;
  547. {$IFDEF SUPPORTS_UNICODE_STRING}
  548. procedure StrResetLength(var S: UnicodeString); overload;
  549. {$ENDIF SUPPORTS_UNICODE_STRING}
  550. // natural comparison functions
  551. function CompareNaturalStr(const S1, S2: string): SizeInt;
  552. function CompareNaturalText(const S1, S2: string): SizeInt;
  553. {$IFNDEF UNICODE_RTL_DATABASE}
  554. // internal structures published to make function inlining working
  555. const
  556. MaxStrCharCount = Ord(High(Char)) + 1; // # of chars in one set
  557. StrLoOffset = MaxStrCharCount * 0; // offset to lower case chars
  558. StrUpOffset = MaxStrCharCount * 1; // offset to upper case chars
  559. StrReOffset = MaxStrCharCount * 2; // offset to reverse case chars
  560. StrCaseMapSize = MaxStrCharCount * 3; // # of chars is a table
  561. var
  562. StrCaseMap: array [0..StrCaseMapSize - 1] of Char; // case mappings
  563. StrCaseMapReady: Boolean = False; // true if case map exists
  564. StrCharTypes: array [Char] of Word;
  565. {$ENDIF ~UNICODE_RTL_DATABASE}
  566. {$IFDEF UNITVERSIONING}
  567. const
  568. UnitVersioning: TUnitVersionInfo = (
  569. RCSfile: '$URL$';
  570. Revision: '$Revision$';
  571. Date: '$Date$';
  572. LogPath: 'JCL\source\common';
  573. Extra: '';
  574. Data: nil
  575. );
  576. {$ENDIF UNITVERSIONING}
  577. implementation
  578. uses
  579. {$IFDEF HAS_UNIT_LIBC}
  580. Libc,
  581. {$ENDIF HAS_UNIT_LIBC}
  582. {$IFDEF SUPPORTS_UNICODE}
  583. {$IFDEF HAS_UNITSCOPE}
  584. System.StrUtils,
  585. {$ELSE ~HAS_UNITSCOPE}
  586. StrUtils,
  587. {$ENDIF ~HAS_UNITSCOPE}
  588. {$ENDIF SUPPORTS_UNICODE}
  589. JclLogic, JclResources, JclStreams, JclSynch, JclSysUtils;
  590. //=== Internal ===============================================================
  591. type
  592. TStrRec = packed record
  593. RefCount: Integer;
  594. Length: Integer;
  595. end;
  596. PStrRec = ^TStrRec;
  597. {$IFNDEF UNICODE_RTL_DATABASE}
  598. procedure LoadCharTypes;
  599. var
  600. CurrChar: Char;
  601. CurrType: Word;
  602. begin
  603. for CurrChar := Low(CurrChar) to High(CurrChar) do
  604. begin
  605. {$IFDEF MSWINDOWS}
  606. CurrType := 0;
  607. GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, @CurrChar, 1, CurrType);
  608. {$DEFINE CHAR_TYPES_INITIALIZED}
  609. {$ENDIF MSWINDOWS}
  610. {$IFDEF LINUX}
  611. CurrType := 0;
  612. if isupper(Byte(CurrChar)) <> 0 then
  613. CurrType := CurrType or C1_UPPER;
  614. if islower(Byte(CurrChar)) <> 0 then
  615. CurrType := CurrType or C1_LOWER;
  616. if isdigit(Byte(CurrChar)) <> 0 then
  617. CurrType := CurrType or C1_DIGIT;
  618. if isspace(Byte(CurrChar)) <> 0 then
  619. CurrType := CurrType or C1_SPACE;
  620. if ispunct(Byte(CurrChar)) <> 0 then
  621. CurrType := CurrType or C1_PUNCT;
  622. if iscntrl(Byte(CurrChar)) <> 0 then
  623. CurrType := CurrType or C1_CNTRL;
  624. if isblank(Byte(CurrChar)) <> 0 then
  625. CurrType := CurrType or C1_BLANK;
  626. if isxdigit(Byte(CurrChar)) <> 0 then
  627. CurrType := CurrType or C1_XDIGIT;
  628. if isalpha(Byte(CurrChar)) <> 0 then
  629. CurrType := CurrType or C1_ALPHA;
  630. {$DEFINE CHAR_TYPES_INITIALIZED}
  631. {$ENDIF LINUX}
  632. StrCharTypes[CurrChar] := CurrType;
  633. {$IFNDEF CHAR_TYPES_INITIALIZED}
  634. Implement case map initialization here
  635. {$ENDIF ~CHAR_TYPES_INITIALIZED}
  636. end;
  637. end;
  638. procedure LoadCaseMap;
  639. var
  640. CurrChar, UpCaseChar, LoCaseChar, ReCaseChar: Char;
  641. begin
  642. if not StrCaseMapReady then
  643. begin
  644. for CurrChar := Low(Char) to High(Char) do
  645. begin
  646. {$IFDEF MSWINDOWS}
  647. LoCaseChar := CurrChar;
  648. UpCaseChar := CurrChar;
  649. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CharLowerBuff(@LoCaseChar, 1);
  650. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CharUpperBuff(@UpCaseChar, 1);
  651. {$DEFINE CASE_MAP_INITIALIZED}
  652. {$ENDIF MSWINDOWS}
  653. {$IFDEF LINUX}
  654. LoCaseChar := Char(tolower(Byte(CurrChar)));
  655. UpCaseChar := Char(toupper(Byte(CurrChar)));
  656. {$DEFINE CASE_MAP_INITIALIZED}
  657. {$ENDIF LINUX}
  658. {$IFNDEF CASE_MAP_INITIALIZED}
  659. Implement case map initialization here
  660. {$ENDIF ~CASE_MAP_INITIALIZED}
  661. if CharIsUpper(CurrChar) then
  662. ReCaseChar := LoCaseChar
  663. else
  664. if CharIsLower(CurrChar) then
  665. ReCaseChar := UpCaseChar
  666. else
  667. ReCaseChar := CurrChar;
  668. StrCaseMap[Ord(CurrChar) + StrLoOffset] := LoCaseChar;
  669. StrCaseMap[Ord(CurrChar) + StrUpOffset] := UpCaseChar;
  670. StrCaseMap[Ord(CurrChar) + StrReOffset] := ReCaseChar;
  671. end;
  672. StrCaseMapReady := True;
  673. end;
  674. end;
  675. // Uppercases or Lowercases a give string depending on the
  676. // passed offset. (UpOffset or LoOffset)
  677. procedure StrCase(var Str: string; const Offset: SizeInt);
  678. var
  679. P: PChar;
  680. I, L: SizeInt;
  681. begin
  682. L := Length(Str);
  683. if L > 0 then
  684. begin
  685. UniqueString(Str);
  686. P := PChar(Str);
  687. for I := 1 to L do
  688. begin
  689. P^ := StrCaseMap[Offset + Ord(P^)];
  690. Inc(P);
  691. end;
  692. end;
  693. end;
  694. // Internal utility function
  695. // Uppercases or Lowercases a give null terminated string depending on the
  696. // passed offset. (UpOffset or LoOffset)
  697. procedure StrCaseBuff(S: PChar; const Offset: SizeInt);
  698. var
  699. C: Char;
  700. begin
  701. if S <> nil then
  702. begin
  703. repeat
  704. C := S^;
  705. S^ := StrCaseMap[Offset + Ord(C)];
  706. Inc(S);
  707. until C = #0;
  708. end;
  709. end;
  710. {$ENDIF ~UNICODE_RTL_DATABASE}
  711. function StrEndW(Str: PWideChar): PWideChar;
  712. begin
  713. Result := Str;
  714. while Result^ <> #0 do
  715. Inc(Result);
  716. end;
  717. function ArrayContainsChar(const Chars: array of Char; const C: Char): Boolean;
  718. var
  719. idx: SizeInt;
  720. begin
  721. Result := ArrayContainsChar(Chars, C, idx);
  722. end;
  723. function ArrayContainsChar(const Chars: array of Char; const C: Char; out Index: SizeInt): Boolean;
  724. { optimized version for sorted arrays
  725. var
  726. I, L, H: SizeInt;
  727. begin
  728. L := Low(Chars);
  729. H := High(Chars);
  730. while L <= H do
  731. begin
  732. I := (L + H) div 2;
  733. if C = Chars[I] then
  734. begin
  735. Result := True;
  736. Exit;
  737. end
  738. else
  739. if C < Chars[I] then
  740. H := I - 1
  741. else
  742. // C > Chars[I]
  743. L := I + 1;
  744. end;
  745. Result := False;
  746. end;}
  747. begin
  748. Index := High(Chars);
  749. while (Index >= Low(Chars)) and (Chars[Index] <> C) do
  750. Dec(Index);
  751. Result := Index >= Low(Chars);
  752. end;
  753. // String Test Routines
  754. function StrIsAlpha(const S: string): Boolean;
  755. var
  756. I: SizeInt;
  757. begin
  758. Result := S <> '';
  759. for I := 1 to Length(S) do
  760. begin
  761. if not CharIsAlpha(S[I]) then
  762. begin
  763. Result := False;
  764. Exit;
  765. end;
  766. end;
  767. end;
  768. function StrIsAlphaNum(const S: string): Boolean;
  769. var
  770. I: SizeInt;
  771. begin
  772. Result := S <> '';
  773. for I := 1 to Length(S) do
  774. begin
  775. if not CharIsAlphaNum(S[I]) then
  776. begin
  777. Result := False;
  778. Exit;
  779. end;
  780. end;
  781. end;
  782. function StrConsistsofNumberChars(const S: string): Boolean;
  783. var
  784. I: SizeInt;
  785. begin
  786. Result := S <> '';
  787. for I := 1 to Length(S) do
  788. begin
  789. if not CharIsNumberChar(S[I]) then
  790. begin
  791. Result := False;
  792. Exit;
  793. end;
  794. end;
  795. end;
  796. function StrContainsChars(const S: string; const Chars: TCharValidator; CheckAll: Boolean): Boolean;
  797. var
  798. I: SizeInt;
  799. begin
  800. Result := False;
  801. if CheckAll then
  802. begin
  803. // this will not work with the current definition of the validator. The validator would need to check each character
  804. // it requires against the string (which is currently not provided to the Validator). The current implementation of
  805. // CheckAll will check if all characters in S will be accepted by the provided Validator, which is wrong and incon-
  806. // sistent with the documentation and the array-based overload.
  807. for I := 1 to Length(S) do
  808. begin
  809. Result := Chars(S[I]);
  810. if not Result then
  811. Break;
  812. end;
  813. end
  814. else
  815. begin
  816. for I := 1 to Length(S) do
  817. begin
  818. Result := Chars(S[I]);
  819. if Result then
  820. Break;
  821. end;
  822. end;
  823. end;
  824. function StrContainsChars(const S: string; const Chars: array of Char; CheckAll: Boolean): Boolean;
  825. var
  826. I: SizeInt;
  827. begin
  828. if CheckAll then
  829. begin
  830. Result := True;
  831. I := High(Chars);
  832. while (I >= 0) and Result do
  833. begin
  834. Result := CharPos(S, Chars[I]) > 0;
  835. Dec(I);
  836. end;
  837. end
  838. else
  839. begin
  840. Result := False;
  841. for I := 1 to Length(S) do
  842. begin
  843. Result := ArrayContainsChar(Chars, S[I]);
  844. if Result then
  845. Break;
  846. end;
  847. end;
  848. end;
  849. function StrIsAlphaNumUnderscore(const S: string): Boolean;
  850. var
  851. I: SizeInt;
  852. C: Char;
  853. begin
  854. for I := 1 to Length(S) do
  855. begin
  856. C := S[I];
  857. if not (CharIsAlphaNum(C) or (C = '_')) then
  858. begin
  859. Result := False;
  860. Exit;
  861. end;
  862. end;
  863. Result := Length(S) > 0;
  864. end;
  865. function StrIsDigit(const S: string): Boolean;
  866. var
  867. I: SizeInt;
  868. begin
  869. Result := S <> '';
  870. for I := 1 to Length(S) do
  871. begin
  872. if not CharIsDigit(S[I]) then
  873. begin
  874. Result := False;
  875. Exit;
  876. end;
  877. end;
  878. end;
  879. function StrIsSubset(const S: string; const ValidChars: TCharValidator): Boolean;
  880. var
  881. I: SizeInt;
  882. begin
  883. for I := 1 to Length(S) do
  884. begin
  885. Result := ValidChars(S[I]);
  886. if not Result then
  887. Exit;
  888. end;
  889. Result := Length(S) > 0;
  890. end;
  891. function StrIsSubset(const S: string; const ValidChars: array of Char): Boolean;
  892. var
  893. I: SizeInt;
  894. begin
  895. for I := 1 to Length(S) do
  896. begin
  897. Result := ArrayContainsChar(ValidChars, S[I]);
  898. if not Result then
  899. Exit;
  900. end;
  901. Result := Length(S) > 0;
  902. end;
  903. function StrSame(const S1, S2: string; CaseSensitive: Boolean): Boolean;
  904. begin
  905. Result := StrCompare(S1, S2, CaseSensitive) = 0;
  906. end;
  907. //=== String Transformation Routines =========================================
  908. function StrCenter(const S: string; L: SizeInt; C: Char = ' '): string;
  909. begin
  910. if Length(S) < L then
  911. begin
  912. Result := StringOfChar(C, (L - Length(S)) div 2) + S;
  913. Result := Result + StringOfChar(C, L - Length(Result));
  914. end
  915. else
  916. Result := S;
  917. end;
  918. function StrCharPosLower(const S: string; CharPos: SizeInt): string;
  919. begin
  920. Result := S;
  921. if (CharPos > 0) and (CharPos <= Length(S)) then
  922. Result[CharPos] := CharLower(Result[CharPos]);
  923. end;
  924. function StrCharPosUpper(const S: string; CharPos: SizeInt): string;
  925. begin
  926. Result := S;
  927. if (CharPos > 0) and (CharPos <= Length(S)) then
  928. Result[CharPos] := CharUpper(Result[CharPos]);
  929. end;
  930. function StrDoubleQuote(const S: string): string;
  931. begin
  932. Result := NativeDoubleQuote + S + NativeDoubleQuote;
  933. end;
  934. function StrEnsureNoPrefix(const Prefix, Text: string): string;
  935. var
  936. PrefixLen: SizeInt;
  937. begin
  938. PrefixLen := Length(Prefix);
  939. if Copy(Text, 1, PrefixLen) = Prefix then
  940. Result := Copy(Text, PrefixLen + 1, Length(Text))
  941. else
  942. Result := Text;
  943. end;
  944. function StrEnsureNoSuffix(const Suffix, Text: string): string;
  945. var
  946. SuffixLen: SizeInt;
  947. StrLength: SizeInt;
  948. begin
  949. SuffixLen := Length(Suffix);
  950. StrLength := Length(Text);
  951. if Copy(Text, StrLength - SuffixLen + 1, SuffixLen) = Suffix then
  952. Result := Copy(Text, 1, StrLength - SuffixLen)
  953. else
  954. Result := Text;
  955. end;
  956. function StrEnsurePrefix(const Prefix, Text: string): string;
  957. var
  958. PrefixLen: SizeInt;
  959. begin
  960. PrefixLen := Length(Prefix);
  961. if Copy(Text, 1, PrefixLen) = Prefix then
  962. Result := Text
  963. else
  964. Result := Prefix + Text;
  965. end;
  966. function StrEnsureSuffix(const Suffix, Text: string): string;
  967. var
  968. SuffixLen: SizeInt;
  969. begin
  970. SuffixLen := Length(Suffix);
  971. if Copy(Text, Length(Text) - SuffixLen + 1, SuffixLen) = Suffix then
  972. Result := Text
  973. else
  974. Result := Text + Suffix;
  975. end;
  976. function StrEscapedToString(const S: string): string;
  977. procedure HandleHexEscapeSeq(const S: string; var I: SizeInt; Len: SizeInt; var Dest: string);
  978. const
  979. HexDigits = string('0123456789abcdefABCDEF');
  980. var
  981. StartI, Val, N: SizeInt;
  982. begin
  983. StartI := I;
  984. N := Pos(S[I + 1], HexDigits) - 1;
  985. if N < 0 then
  986. // '\x' without hex digit following is not escape sequence
  987. Dest := Dest + '\x'
  988. else
  989. begin
  990. Inc(I); // Jump over x
  991. if N >= 16 then
  992. N := N - 6;
  993. Val := N;
  994. // Same for second digit
  995. if I < Len then
  996. begin
  997. N := Pos(S[I + 1], HexDigits) - 1;
  998. if N >= 0 then
  999. begin
  1000. Inc(I); // Jump over first digit
  1001. if N >= 16 then
  1002. N := N - 6;
  1003. Val := Val * 16 + N;
  1004. end;
  1005. end;
  1006. if Val > Ord(High(Char)) then
  1007. raise EJclStringError.CreateResFmt(@RsNumericConstantTooLarge, [Val, StartI]);
  1008. Dest := Dest + Char(Val);
  1009. end;
  1010. end;
  1011. procedure HandleOctEscapeSeq(const S: string; var I: SizeInt; Len: SizeInt; var Dest: string);
  1012. const
  1013. OctDigits = string('01234567');
  1014. var
  1015. StartI, Val, N: SizeInt;
  1016. begin
  1017. StartI := I;
  1018. // first digit
  1019. Val := Pos(S[I], OctDigits) - 1;
  1020. if I < Len then
  1021. begin
  1022. N := Pos(S[I + 1], OctDigits) - 1;
  1023. if N >= 0 then
  1024. begin
  1025. Inc(I);
  1026. Val := Val * 8 + N;
  1027. end;
  1028. if I < Len then
  1029. begin
  1030. N := Pos(S[I + 1], OctDigits) - 1;
  1031. if N >= 0 then
  1032. begin
  1033. Inc(I);
  1034. Val := Val * 8 + N;
  1035. end;
  1036. end;
  1037. end;
  1038. if Val > Ord(High(Char)) then
  1039. raise EJclStringError.CreateResFmt(@RsNumericConstantTooLarge, [Val, StartI]);
  1040. Dest := Dest + Char(Val);
  1041. end;
  1042. var
  1043. I, Len: SizeInt;
  1044. begin
  1045. Result := '';
  1046. I := 1;
  1047. Len := Length(S);
  1048. while I <= Len do
  1049. begin
  1050. if not ((S[I] = '\') and (I < Len)) then
  1051. Result := Result + S[I]
  1052. else
  1053. begin
  1054. Inc(I); // Jump over escape character
  1055. case S[I] of
  1056. 'a':
  1057. Result := Result + NativeBell;
  1058. 'b':
  1059. Result := Result + NativeBackspace;
  1060. 'f':
  1061. Result := Result + NativeFormFeed;
  1062. 'n':
  1063. Result := Result + NativeLineFeed;
  1064. 'r':
  1065. Result := Result + NativeCarriageReturn;
  1066. 't':
  1067. Result := Result + NativeTab;
  1068. 'v':
  1069. Result := Result + NativeVerticalTab;
  1070. '\':
  1071. Result := Result + '\';
  1072. '"':
  1073. Result := Result + '"';
  1074. '''':
  1075. Result := Result + ''''; // Optionally escaped
  1076. '?':
  1077. Result := Result + '?'; // Optionally escaped
  1078. 'x':
  1079. if I < Len then
  1080. // Start of hex escape sequence
  1081. HandleHexEscapeSeq(S, I, Len, Result)
  1082. else
  1083. // '\x' at end of string is not escape sequence
  1084. Result := Result + '\x';
  1085. '0'..'7':
  1086. // start of octal escape sequence
  1087. HandleOctEscapeSeq(S, I, Len, Result);
  1088. else
  1089. // no escape sequence
  1090. Result := Result + '\' + S[I];
  1091. end;
  1092. end;
  1093. Inc(I);
  1094. end;
  1095. end;
  1096. function StrLower(const S: string): string;
  1097. begin
  1098. Result := S;
  1099. StrLowerInPlace(Result);
  1100. end;
  1101. procedure StrLowerInPlace(var S: string);
  1102. {$IFDEF UNICODE_RTL_DATABASE}
  1103. var
  1104. P: PChar;
  1105. I, L: SizeInt;
  1106. begin
  1107. L := Length(S);
  1108. if L > 0 then
  1109. begin
  1110. UniqueString(S);
  1111. P := PChar(S);
  1112. for I := 1 to L do
  1113. begin
  1114. P^ := TCharacter.ToLower(P^);
  1115. Inc(P);
  1116. end;
  1117. end;
  1118. end;
  1119. {$ELSE ~UNICODE_RTL_DATABASE}
  1120. begin
  1121. StrCase(S, StrLoOffset);
  1122. end;
  1123. {$ENDIF ~UNICODE_RTL_DATABASE}
  1124. procedure StrLowerBuff(S: PChar);
  1125. begin
  1126. {$IFDEF UNICODE_RTL_DATABASE}
  1127. if S <> nil then
  1128. begin
  1129. repeat
  1130. S^ := TCharacter.ToLower(S^);
  1131. Inc(S);
  1132. until S^ = #0;
  1133. end;
  1134. {$ELSE ~UNICODE_RTL_DATABASE}
  1135. StrCaseBuff(S, StrLoOffset);
  1136. {$ENDIF ~UNICODE_RTL_DATABASE}
  1137. end;
  1138. procedure StrMove(var Dest: string; const Source: string;
  1139. const ToIndex, FromIndex, Count: SizeInt);
  1140. begin
  1141. // Check strings
  1142. if (Source = '') or (Length(Dest) = 0) then
  1143. Exit;
  1144. // Check FromIndex
  1145. if (FromIndex <= 0) or (FromIndex > Length(Source)) or
  1146. (ToIndex <= 0) or (ToIndex > Length(Dest)) or
  1147. ((FromIndex + Count - 1) > Length(Source)) or ((ToIndex + Count - 1) > Length(Dest)) then
  1148. { TODO : Is failure without notice the proper thing to do here? }
  1149. Exit;
  1150. // Move
  1151. Move(Source[FromIndex], Dest[ToIndex], Count * SizeOf(Char));
  1152. end;
  1153. function StrPadLeft(const S: string; Len: SizeInt; C: Char): string;
  1154. var
  1155. L: SizeInt;
  1156. begin
  1157. L := Length(S);
  1158. if L < Len then
  1159. Result := StringOfChar(C, Len - L) + S
  1160. else
  1161. Result := S;
  1162. end;
  1163. function StrPadRight(const S: string; Len: SizeInt; C: Char): string;
  1164. var
  1165. L: SizeInt;
  1166. begin
  1167. L := Length(S);
  1168. if L < Len then
  1169. Result := S + StringOfChar(C, Len - L)
  1170. else
  1171. Result := S;
  1172. end;
  1173. function StrProper(const S: string): string;
  1174. begin
  1175. Result := StrLower(S);
  1176. if Result <> '' then
  1177. Result[1] := UpCase(Result[1]);
  1178. end;
  1179. procedure StrProperBuff(S: PChar);
  1180. begin
  1181. if (S <> nil) and (S^ <> #0) then
  1182. begin
  1183. StrLowerBuff(S);
  1184. S^ := CharUpper(S^);
  1185. end;
  1186. end;
  1187. function StrQuote(const S: string; C: Char): string;
  1188. var
  1189. L: SizeInt;
  1190. begin
  1191. L := Length(S);
  1192. Result := S;
  1193. if L > 0 then
  1194. begin
  1195. if Result[1] <> C then
  1196. begin
  1197. Result := C + Result;
  1198. Inc(L);
  1199. end;
  1200. if Result[L] <> C then
  1201. Result := Result + C;
  1202. end;
  1203. end;
  1204. function StrRemoveChars(const S: string; const Chars: TCharValidator): string;
  1205. var
  1206. Source, Dest: PChar;
  1207. Len, Index: SizeInt;
  1208. begin
  1209. Len := Length(S);
  1210. SetLength(Result, Len);
  1211. UniqueString(Result);
  1212. Source := PChar(S);
  1213. Dest := PChar(Result);
  1214. for Index := 0 to Len - 1 do
  1215. begin
  1216. if not Chars(Source^) then
  1217. begin
  1218. Dest^ := Source^;
  1219. Inc(Dest);
  1220. end;
  1221. Inc(Source);
  1222. end;
  1223. SetLength(Result, Dest - PChar(Result));
  1224. end;
  1225. function StrRemoveChars(const S: string; const Chars: array of Char): string;
  1226. var
  1227. Source, Dest: PChar;
  1228. Len, Index: SizeInt;
  1229. begin
  1230. Len := Length(S);
  1231. SetLength(Result, Len);
  1232. UniqueString(Result);
  1233. Source := PChar(S);
  1234. Dest := PChar(Result);
  1235. for Index := 0 to Len - 1 do
  1236. begin
  1237. if not ArrayContainsChar(Chars, Source^) then
  1238. begin
  1239. Dest^ := Source^;
  1240. Inc(Dest);
  1241. end;
  1242. Inc(Source);
  1243. end;
  1244. SetLength(Result, Dest - PChar(Result));
  1245. end;
  1246. function StrRemoveLeadingChars(const S: string; const Chars: TCharValidator): string;
  1247. var
  1248. Len : SizeInt;
  1249. I: SizeInt;
  1250. begin
  1251. Len := Length(S);
  1252. I := 1;
  1253. while (I <= Len) and Chars(s[I]) do
  1254. Inc(I);
  1255. Result := Copy (s, I, Len-I+1);
  1256. end;
  1257. function StrRemoveLeadingChars(const S: string; const Chars: array of Char): string;
  1258. var
  1259. Len : SizeInt;
  1260. I: SizeInt;
  1261. begin
  1262. Len := Length(S);
  1263. I := 1;
  1264. while (I <= Len) and ArrayContainsChar(Chars, s[I]) do
  1265. Inc(I);
  1266. Result := Copy (s, I, Len-I+1);
  1267. end;
  1268. function StrRemoveEndChars(const S: string; const Chars: TCharValidator): string;
  1269. var
  1270. Len : SizeInt;
  1271. begin
  1272. Len := Length(S);
  1273. while (Len > 0) and Chars(s[Len]) do
  1274. Dec(Len);
  1275. Result := Copy (s, 1, Len);
  1276. end;
  1277. function StrRemoveEndChars(const S: string; const Chars: array of Char): string;
  1278. var
  1279. Len : SizeInt;
  1280. begin
  1281. Len := Length(S);
  1282. while (Len > 0) and ArrayContainsChar(Chars, s[Len]) do
  1283. Dec(Len);
  1284. Result := Copy (s, 1, Len);
  1285. end;
  1286. function StrKeepChars(const S: string; const Chars: TCharValidator): string;
  1287. var
  1288. Source, Dest: PChar;
  1289. Len, Index: SizeInt;
  1290. begin
  1291. Len := Length(S);
  1292. SetLength(Result, Len);
  1293. UniqueString(Result);
  1294. Source := PChar(S);
  1295. Dest := PChar(Result);
  1296. for Index := 0 to Len - 1 do
  1297. begin
  1298. if Chars(Source^) then
  1299. begin
  1300. Dest^ := Source^;
  1301. Inc(Dest);
  1302. end;
  1303. Inc(Source);
  1304. end;
  1305. SetLength(Result, Dest - PChar(Result));
  1306. end;
  1307. function StrKeepChars(const S: string; const Chars: array of Char): string;
  1308. var
  1309. Source, Dest: PChar;
  1310. Len, Index: SizeInt;
  1311. begin
  1312. Len := Length(S);
  1313. SetLength(Result, Len);
  1314. UniqueString(Result);
  1315. Source := PChar(S);
  1316. Dest := PChar(Result);
  1317. for Index := 0 to Len - 1 do
  1318. begin
  1319. if ArrayContainsChar(Chars, Source^) then
  1320. begin
  1321. Dest^ := Source^;
  1322. Inc(Dest);
  1323. end;
  1324. Inc(Source);
  1325. end;
  1326. SetLength(Result, Dest - PChar(Result));
  1327. end;
  1328. function StrRepeat(const S: string; Count: SizeInt): string;
  1329. var
  1330. Len, Index: SizeInt;
  1331. Dest, Source: PChar;
  1332. begin
  1333. Len := Length(S);
  1334. SetLength(Result, Count * Len);
  1335. Dest := PChar(Result);
  1336. Source := PChar(S);
  1337. if Dest <> nil then
  1338. for Index := 0 to Count - 1 do
  1339. begin
  1340. Move(Source^, Dest^, Len * SizeOf(Char));
  1341. Inc(Dest, Len);
  1342. end;
  1343. end;
  1344. function StrRepeatLength(const S: string; L: SizeInt): string;
  1345. var
  1346. Len: SizeInt;
  1347. Dest: PChar;
  1348. begin
  1349. Result := '';
  1350. Len := Length(S);
  1351. if (Len > 0) and (S <> '') then
  1352. begin
  1353. SetLength(Result, L);
  1354. Dest := PChar(Result);
  1355. while (L > 0) do
  1356. begin
  1357. Move(S[1], Dest^, Min(L, Len) * SizeOf(Char));
  1358. Inc(Dest, Len);
  1359. Dec(L, Len);
  1360. end;
  1361. end;
  1362. end;
  1363. procedure StrReplace(var S: string; const Search, Replace: string; Flags: TReplaceFlags);
  1364. var
  1365. SearchStr: string;
  1366. ResultStr: string; { result string }
  1367. SourcePtr: PChar; { pointer into S of character under examination }
  1368. SourceMatchPtr: PChar; { pointers into S and Search when first character has }
  1369. SearchMatchPtr: PChar; { been matched and we're probing for a complete match }
  1370. ResultPtr: PChar; { pointer into Result of character being written }
  1371. ResultIndex,
  1372. SearchLength, { length of search string }
  1373. ReplaceLength, { length of replace string }
  1374. BufferLength, { length of temporary result buffer }
  1375. ResultLength: SizeInt; { length of result string }
  1376. C: Char; { first character of search string }
  1377. IgnoreCase: Boolean;
  1378. begin
  1379. if Search = '' then
  1380. begin
  1381. if S = '' then
  1382. begin
  1383. S := Replace;
  1384. Exit;
  1385. end
  1386. else
  1387. raise EJclStringError.CreateRes(@RsBlankSearchString);
  1388. end;
  1389. if S <> '' then
  1390. begin
  1391. IgnoreCase := rfIgnoreCase in Flags;
  1392. if IgnoreCase then
  1393. SearchStr := StrUpper(Search)
  1394. else
  1395. SearchStr := Search;
  1396. { avoid having to call Length() within the loop }
  1397. SearchLength := Length(Search);
  1398. ReplaceLength := Length(Replace);
  1399. ResultLength := Length(S);
  1400. BufferLength := ResultLength;
  1401. SetLength(ResultStr, BufferLength);
  1402. { get pointers to begin of source and result }
  1403. ResultPtr := PChar(ResultStr);
  1404. SourcePtr := PChar(S);
  1405. C := SearchStr[1];
  1406. { while we haven't reached the end of the string }
  1407. while True do
  1408. begin
  1409. { copy characters until we find the first character of the search string }
  1410. if IgnoreCase then
  1411. while (CharUpper(SourcePtr^) <> C) and (SourcePtr^ <> #0) do
  1412. begin
  1413. ResultPtr^ := SourcePtr^;
  1414. Inc(ResultPtr);
  1415. Inc(SourcePtr);
  1416. end
  1417. else
  1418. while (SourcePtr^ <> C) and (SourcePtr^ <> #0) do
  1419. begin
  1420. ResultPtr^ := SourcePtr^;
  1421. Inc(ResultPtr);
  1422. Inc(SourcePtr);
  1423. end;
  1424. { did we find that first character or did we hit the end of the string? }
  1425. if SourcePtr^ = #0 then
  1426. Break
  1427. else
  1428. begin
  1429. { continue comparing, +1 because first character was matched already }
  1430. SourceMatchPtr := SourcePtr + 1;
  1431. SearchMatchPtr := PChar(SearchStr) + 1;
  1432. if IgnoreCase then
  1433. while (CharUpper(SourceMatchPtr^) = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
  1434. begin
  1435. Inc(SourceMatchPtr);
  1436. Inc(SearchMatchPtr);
  1437. end
  1438. else
  1439. while (SourceMatchPtr^ = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
  1440. begin
  1441. Inc(SourceMatchPtr);
  1442. Inc(SearchMatchPtr);
  1443. end;
  1444. { did we find a complete match? }
  1445. if SearchMatchPtr^ = #0 then
  1446. begin
  1447. // keep track of result length
  1448. Inc(ResultLength, ReplaceLength - SearchLength);
  1449. if ReplaceLength > 0 then
  1450. begin
  1451. // increase buffer size if required
  1452. if ResultLength > BufferLength then
  1453. begin
  1454. BufferLength := ResultLength * 2;
  1455. ResultIndex := ResultPtr - PChar(ResultStr) + 1;
  1456. SetLength(ResultStr, BufferLength);
  1457. ResultPtr := @ResultStr[ResultIndex];
  1458. end;
  1459. { append replace to result and move past the search string in source }
  1460. Move((@Replace[1])^, ResultPtr^, ReplaceLength * SizeOf(Char));
  1461. end;
  1462. Inc(SourcePtr, SearchLength);
  1463. Inc(ResultPtr, ReplaceLength);
  1464. { replace all instances or just one? }
  1465. if not (rfReplaceAll in Flags) then
  1466. begin
  1467. { just one, copy until end of source and break out of loop }
  1468. while SourcePtr^ <> #0 do
  1469. begin
  1470. ResultPtr^ := SourcePtr^;
  1471. Inc(ResultPtr);
  1472. Inc(SourcePtr);
  1473. end;
  1474. Break;
  1475. end;
  1476. end
  1477. else
  1478. begin
  1479. { copy current character and start over with the next }
  1480. ResultPtr^ := SourcePtr^;
  1481. Inc(ResultPtr);
  1482. Inc(SourcePtr);
  1483. end;
  1484. end;
  1485. end;
  1486. { set result length and copy result into S }
  1487. SetLength(ResultStr, ResultLength);
  1488. S := ResultStr;
  1489. end;
  1490. end;
  1491. function StrReplaceChar(const S: string; const Source, Replace: Char): string;
  1492. var
  1493. I: SizeInt;
  1494. begin
  1495. Result := S;
  1496. for I := 1 to Length(S) do
  1497. if Result[I] = Source then
  1498. Result[I] := Replace;
  1499. end;
  1500. function StrReplaceChars(const S: string; const Chars: TCharValidator; Replace: Char): string;
  1501. var
  1502. I: SizeInt;
  1503. begin
  1504. Result := S;
  1505. for I := 1 to Length(S) do
  1506. if Chars(Result[I]) then
  1507. Result[I] := Replace;
  1508. end;
  1509. function StrReplaceChars(const S: string; const Chars: array of Char; Replace: Char): string;
  1510. var
  1511. I: SizeInt;
  1512. begin
  1513. Result := S;
  1514. for I := 1 to Length(S) do
  1515. if ArrayContainsChar(Chars, Result[I]) then
  1516. Result[I] := Replace;
  1517. end;
  1518. function StrReplaceButChars(const S: string; const Chars: TCharValidator;
  1519. Replace: Char): string;
  1520. var
  1521. I: SizeInt;
  1522. begin
  1523. Result := S;
  1524. for I := 1 to Length(S) do
  1525. if not Chars(Result[I]) then
  1526. Result[I] := Replace;
  1527. end;
  1528. function StrReplaceButChars(const S: string; const Chars: array of Char; Replace: Char): string;
  1529. var
  1530. I: SizeInt;
  1531. begin
  1532. Result := S;
  1533. for I := 1 to Length(S) do
  1534. if not ArrayContainsChar(Chars, Result[I]) then
  1535. Result[I] := Replace;
  1536. end;
  1537. function StrReverse(const S: string): string;
  1538. begin
  1539. Result := S;
  1540. StrReverseInplace(Result);
  1541. end;
  1542. procedure StrReverseInPlace(var S: string);
  1543. { TODO -oahuser : Warning: This is dangerous for unicode surrogates }
  1544. var
  1545. P1, P2: PChar;
  1546. C: Char;
  1547. begin
  1548. UniqueString(S);
  1549. P1 := PChar(S);
  1550. P2 := P1 + (Length(S) - 1);
  1551. while P1 < P2 do
  1552. begin
  1553. C := P1^;
  1554. P1^ := P2^;
  1555. P2^ := C;
  1556. Inc(P1);
  1557. Dec(P2);
  1558. end;
  1559. end;
  1560. function StrSingleQuote(const S: string): string;
  1561. begin
  1562. Result := NativeSingleQuote + S + NativeSingleQuote;
  1563. end;
  1564. procedure StrSkipChars(var S: PChar; const Chars: TCharValidator);
  1565. begin
  1566. while Chars(S^) do
  1567. Inc(S);
  1568. end;
  1569. procedure StrSkipChars(var S: PChar; const Chars: array of Char);
  1570. begin
  1571. while ArrayContainsChar(Chars, S^) do
  1572. Inc(S);
  1573. end;
  1574. procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: TCharValidator);
  1575. begin
  1576. while Chars(S[Index]) do
  1577. Inc(Index);
  1578. end;
  1579. procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: array of Char);
  1580. begin
  1581. while ArrayContainsChar(Chars, S[Index]) do
  1582. Inc(Index);
  1583. end;
  1584. function StrSmartCase(const S: string; const Delimiters: TCharValidator): string;
  1585. var
  1586. Source, Dest: PChar;
  1587. Index, Len: SizeInt;
  1588. InternalDelimiters: TCharValidator;
  1589. begin
  1590. Result := '';
  1591. if Assigned(Delimiters) then
  1592. InternalDelimiters := Delimiters
  1593. else
  1594. InternalDelimiters := CharIsSpace;
  1595. if S <> '' then
  1596. begin
  1597. Result := S;
  1598. UniqueString(Result);
  1599. Len := Length(S);
  1600. Source := PChar(S);
  1601. Dest := PChar(Result);
  1602. Inc(Dest);
  1603. for Index := 2 to Len do
  1604. begin
  1605. if InternalDelimiters(Source^) and not InternalDelimiters(Dest^) then
  1606. Dest^ := CharUpper(Dest^);
  1607. Inc(Dest);
  1608. Inc(Source);
  1609. end;
  1610. Result[1] := CharUpper(Result[1]);
  1611. end;
  1612. end;
  1613. function StrSmartCase(const S: string; const Delimiters: array of Char): string;
  1614. var
  1615. Source, Dest: PChar;
  1616. Index, Len: SizeInt;
  1617. begin
  1618. Result := '';
  1619. if S <> '' then
  1620. begin
  1621. Result := S;
  1622. UniqueString(Result);
  1623. Len := Length(S);
  1624. Source := PChar(S);
  1625. Dest := PChar(Result);
  1626. Inc(Dest);
  1627. for Index := 2 to Len do
  1628. begin
  1629. if ArrayContainsChar(Delimiters, Source^) and not ArrayContainsChar(Delimiters, Dest^) then
  1630. Dest^ := CharUpper(Dest^);
  1631. Inc(Dest);
  1632. Inc(Source);
  1633. end;
  1634. Result[1] := CharUpper(Result[1]);
  1635. end;
  1636. end;
  1637. function StrStringToEscaped(const S: string): string;
  1638. var
  1639. I: SizeInt;
  1640. begin
  1641. Result := '';
  1642. for I := 1 to Length(S) do
  1643. begin
  1644. case S[I] of
  1645. NativeBackspace:
  1646. Result := Result + '\b';
  1647. NativeBell:
  1648. Result := Result + '\a';
  1649. NativeCarriageReturn:
  1650. Result := Result + '\r';
  1651. NAtiveFormFeed:
  1652. Result := Result + '\f';
  1653. NativeLineFeed:
  1654. Result := Result + '\n';
  1655. NativeTab:
  1656. Result := Result + '\t';
  1657. NativeVerticalTab:
  1658. Result := Result + '\v';
  1659. NativeBackSlash:
  1660. Result := Result + '\\';
  1661. NativeDoubleQuote:
  1662. Result := Result + '\"';
  1663. else
  1664. // Characters < ' ' are escaped with hex sequence
  1665. if S[I] < #32 then
  1666. Result := Result + Format('\x%.2x', [SizeInt(S[I])])
  1667. else
  1668. Result := Result + S[I];
  1669. end;
  1670. end;
  1671. end;
  1672. function StrStripNonNumberChars(const S: string): string;
  1673. var
  1674. I: SizeInt;
  1675. C: Char;
  1676. begin
  1677. Result := '';
  1678. for I := 1 to Length(S) do
  1679. begin
  1680. C := S[I];
  1681. if CharIsNumberChar(C) then
  1682. Result := Result + C;
  1683. end;
  1684. end;
  1685. function StrToHex(const Source: string): string;
  1686. var
  1687. Index: SizeInt;
  1688. C, L, N: SizeInt;
  1689. BL, BH: Byte;
  1690. S: string;
  1691. begin
  1692. Result := '';
  1693. if Source <> '' then
  1694. begin
  1695. S := Source;
  1696. L := Length(S);
  1697. if Odd(L) then
  1698. begin
  1699. S := '0' + S;
  1700. Inc(L);
  1701. end;
  1702. Index := 1;
  1703. SetLength(Result, L div 2);
  1704. C := 1;
  1705. N := 1;
  1706. while C <= L do
  1707. begin
  1708. BH := CharHex(S[Index]);
  1709. Inc(Index);
  1710. BL := CharHex(S[Index]);
  1711. Inc(Index);
  1712. Inc(C, 2);
  1713. if (BH = $FF) or (BL = $FF) then
  1714. begin
  1715. Result := '';
  1716. Exit;
  1717. end;
  1718. Result[N] := Char((BH shl 4) or BL);
  1719. Inc(N);
  1720. end;
  1721. end;
  1722. end;
  1723. function StrTrimCharLeft(const S: string; C: Char): string;
  1724. var
  1725. I, L: SizeInt;
  1726. begin
  1727. I := 1;
  1728. L := Length(S);
  1729. while (I <= L) and (S[I] = C) do
  1730. Inc(I);
  1731. Result := Copy(S, I, L - I + 1);
  1732. end;
  1733. function StrTrimCharsLeft(const S: string; const Chars: TCharValidator): string;
  1734. var
  1735. I, L: SizeInt;
  1736. begin
  1737. I := 1;
  1738. L := Length(S);
  1739. while (I <= L) and Chars(S[I]) do
  1740. Inc(I);
  1741. Result := Copy(S, I, L - I + 1);
  1742. end;
  1743. function StrTrimCharsLeft(const S: string; const Chars: array of Char): string;
  1744. var
  1745. I, L: SizeInt;
  1746. begin
  1747. I := 1;
  1748. L := Length(S);
  1749. while (I <= L) and ArrayContainsChar(Chars, S[I]) do
  1750. Inc(I);
  1751. Result := Copy(S, I, L - I + 1);
  1752. end;
  1753. function StrTrimCharRight(const S: string; C: Char): string;
  1754. var
  1755. I: SizeInt;
  1756. begin
  1757. I := Length(S);
  1758. while (I >= 1) and (S[I] = C) do
  1759. Dec(I);
  1760. Result := Copy(S, 1, I);
  1761. end;
  1762. function StrTrimCharsRight(const S: string; const Chars: TCharValidator): string;
  1763. var
  1764. I: SizeInt;
  1765. begin
  1766. I := Length(S);
  1767. while (I >= 1) and Chars(S[I]) do
  1768. Dec(I);
  1769. Result := Copy(S, 1, I);
  1770. end;
  1771. function StrTrimCharsRight(const S: string; const Chars: array of Char): string;
  1772. var
  1773. I: SizeInt;
  1774. begin
  1775. I := Length(S);
  1776. while (I >= 1) and ArrayContainsChar(Chars, S[I]) do
  1777. Dec(I);
  1778. Result := Copy(S, 1, I);
  1779. end;
  1780. function StrTrimQuotes(const S: string): string;
  1781. var
  1782. First, Last: Char;
  1783. L: SizeInt;
  1784. begin
  1785. L := Length(S);
  1786. if L > 1 then
  1787. begin
  1788. First := S[1];
  1789. Last := S[L];
  1790. if (First = Last) and ((First = NativeSingleQuote) or (First = NativeDoubleQuote)) then
  1791. Result := Copy(S, 2, L - 2)
  1792. else
  1793. Result := S;
  1794. end
  1795. else
  1796. Result := S;
  1797. end;
  1798. function StrUpper(const S: string): string;
  1799. begin
  1800. Result := S;
  1801. StrUpperInPlace(Result);
  1802. end;
  1803. procedure StrUpperInPlace(var S: string);
  1804. {$IFDEF UNICODE_RTL_DATABASE}
  1805. var
  1806. P: PChar;
  1807. I, L: SizeInt;
  1808. begin
  1809. L := Length(S);
  1810. if L > 0 then
  1811. begin
  1812. UniqueString(S);
  1813. P := PChar(S);
  1814. for I := 1 to L do
  1815. begin
  1816. P^ := TCharacter.ToUpper(P^);
  1817. Inc(P);
  1818. end;
  1819. end;
  1820. end;
  1821. {$ELSE ~UNICODE_RTL_DATABASE}
  1822. begin
  1823. StrCase(S, StrUpOffset);
  1824. end;
  1825. {$ENDIF ~UNICODE_RTL_DATABASE}
  1826. procedure StrUpperBuff(S: PChar);
  1827. begin
  1828. {$IFDEF UNICODE_RTL_DATABASE}
  1829. if S <> nil then
  1830. begin
  1831. repeat
  1832. S^ := TCharacter.ToUpper(S^);
  1833. Inc(S);
  1834. until S^ = #0;
  1835. end;
  1836. {$ELSE ~UNICODE_RTL_DATABASE}
  1837. StrCaseBuff(S, StrUpOffset);
  1838. {$ENDIF ~UNICODE_RTL_DATABASE}
  1839. end;
  1840. //=== String Management ======================================================
  1841. procedure StrAddRef(var S: string);
  1842. var
  1843. P: PStrRec;
  1844. begin
  1845. P := Pointer(S);
  1846. if P <> nil then
  1847. begin
  1848. Dec(P);
  1849. if P^.RefCount = -1 then
  1850. UniqueString(S)
  1851. else
  1852. LockedInc(P^.RefCount);
  1853. end;
  1854. end;
  1855. procedure StrDecRef(var S: string);
  1856. var
  1857. P: PStrRec;
  1858. begin
  1859. P := Pointer(S);
  1860. if P <> nil then
  1861. begin
  1862. Dec(P);
  1863. case P^.RefCount of
  1864. -1, 0: { nothing } ;
  1865. 1:
  1866. begin
  1867. Finalize(S);
  1868. Pointer(S) := nil;
  1869. end;
  1870. else
  1871. LockedDec(P^.RefCount);
  1872. end;
  1873. end;
  1874. end;
  1875. function StrLength(const S: string): SizeInt;
  1876. var
  1877. P: PStrRec;
  1878. begin
  1879. Result := 0;
  1880. P := Pointer(S);
  1881. if P <> nil then
  1882. begin
  1883. Dec(P);
  1884. Result := P^.Length and (not $80000000 shr 1);
  1885. end;
  1886. end;
  1887. function StrRefCount(const S: string): SizeInt;
  1888. var
  1889. P: PStrRec;
  1890. begin
  1891. Result := 0;
  1892. P := Pointer(S);
  1893. if P <> nil then
  1894. begin
  1895. Dec(P);
  1896. Result := P^.RefCount;
  1897. end;
  1898. end;
  1899. procedure StrResetLength(var S: WideString);
  1900. var
  1901. I: SizeInt;
  1902. begin
  1903. for I := 0 to Length(S) - 1 do
  1904. if S[I + 1] = #0 then
  1905. begin
  1906. SetLength(S, I);
  1907. Exit;
  1908. end;
  1909. end;
  1910. procedure StrResetLength(var S: AnsiString);
  1911. var
  1912. I: SizeInt;
  1913. begin
  1914. for I := 0 to Length(S) - 1 do
  1915. if S[I + 1] = #0 then
  1916. begin
  1917. SetLength(S, I);
  1918. Exit;
  1919. end;
  1920. end;
  1921. procedure StrResetLength(S: TJclStringBuilder);
  1922. var
  1923. I: SizeInt;
  1924. begin
  1925. if S <> nil then
  1926. for I := 0 to S.Length - 1 do
  1927. if S[I] = #0 then
  1928. begin
  1929. S.Length := I;
  1930. Exit;
  1931. end;
  1932. end;
  1933. {$IFDEF SUPPORTS_UNICODE_STRING}
  1934. procedure StrResetLength(var S: UnicodeString);
  1935. var
  1936. I: SizeInt;
  1937. begin
  1938. for I := 0 to Length(S) - 1 do
  1939. if S[I + 1] = #0 then
  1940. begin
  1941. SetLength(S, I);
  1942. Exit;
  1943. end;
  1944. end;
  1945. {$ENDIF SUPPORTS_UNICODE_STRING}
  1946. //=== String Search and Replace Routines =====================================
  1947. function StrCharCount(const S: string; C: Char): SizeInt;
  1948. var
  1949. I: SizeInt;
  1950. begin
  1951. Result := 0;
  1952. for I := 1 to Length(S) do
  1953. if S[I] = C then
  1954. Inc(Result);
  1955. end;
  1956. function StrCharsCount(const S: string; const Chars: TCharValidator): SizeInt;
  1957. var
  1958. I: SizeInt;
  1959. begin
  1960. Result := 0;
  1961. for I := 1 to Length(S) do
  1962. if Chars(S[I]) then
  1963. Inc(Result);
  1964. end;
  1965. function StrCharsCount(const S: string; const Chars: array of Char): SizeInt;
  1966. var
  1967. I: SizeInt;
  1968. begin
  1969. Result := 0;
  1970. for I := 1 to Length(S) do
  1971. if ArrayContainsChar(Chars, S[I]) then
  1972. Inc(Result);
  1973. end;
  1974. function StrStrCount(const S, SubS: string): SizeInt;
  1975. var
  1976. I: SizeInt;
  1977. begin
  1978. Result := 0;
  1979. if (Length(SubS) > Length(S)) or (Length(SubS) = 0) or (Length(S) = 0) then
  1980. Exit;
  1981. if Length(SubS) = 1 then
  1982. begin
  1983. Result := StrCharCount(S, SubS[1]);
  1984. Exit;
  1985. end;
  1986. I := StrSearch(SubS, S, 1);
  1987. if I > 0 then
  1988. Inc(Result);
  1989. while (I > 0) and (Length(S) > I + Length(SubS)) do
  1990. begin
  1991. I := StrSearch(SubS, S, I + 1);
  1992. if I > 0 then
  1993. Inc(Result);
  1994. end;
  1995. end;
  1996. (*
  1997. { 1} Test(StrCompareRange('', '', 1, 5), 0);
  1998. { 2} Test(StrCompareRange('A', '', 1, 5), -1);
  1999. { 3} Test(StrCompareRange('AB', '', 1, 5), -1);
  2000. { 4} Test(StrCompareRange('ABC', '', 1, 5), -1);
  2001. { 5} Test(StrCompareRange('', 'A', 1, 5), -1);
  2002. { 6} Test(StrCompareRange('', 'AB', 1, 5), -1);
  2003. { 7} Test(StrCompareRange('', 'ABC', 1, 5), -1);
  2004. { 8} Test(StrCompareRange('A', 'a', 1, 5), -2);
  2005. { 9} Test(StrCompareRange('A', 'a', 1, 1), -32);
  2006. {10} Test(StrCompareRange('aA', 'aB', 1, 1), 0);
  2007. {11} Test(StrCompareRange('aA', 'aB', 1, 2), -1);
  2008. {12} Test(StrCompareRange('aB', 'aA', 1, 2), 1);
  2009. {13} Test(StrCompareRange('aA', 'aa', 1, 2), -32);
  2010. {14} Test(StrCompareRange('aa', 'aA', 1, 2), 32);
  2011. {15} Test(StrCompareRange('', '', 1, 0), 0);
  2012. {16} Test(StrCompareRange('A', 'A', 1, 0), -2);
  2013. {17} Test(StrCompareRange('Aa', 'A', 1, 0), -2);
  2014. {18} Test(StrCompareRange('Aa', 'Aa', 1, 2), 0);
  2015. {19} Test(StrCompareRange('Aa', 'A', 1, 2), 0);
  2016. {20} Test(StrCompareRange('Ba', 'A', 1, 2), 1);
  2017. *)
  2018. function StrCompareRangeEx(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
  2019. var
  2020. Len1, Len2: SizeInt;
  2021. I: SizeInt;
  2022. C1, C2: Char;
  2023. begin
  2024. if Pointer(S1) = Pointer(S2) then
  2025. begin
  2026. if (Count <= 0) and (S1 <> '') then
  2027. Result := -2 // no work
  2028. else
  2029. Result := 0;
  2030. end
  2031. else
  2032. if (S1 = '') or (S2 = '') then
  2033. Result := -1 // null string
  2034. else
  2035. if Count <= 0 then
  2036. Result := -2 // no work
  2037. else
  2038. begin
  2039. Len1 := Length(S1);
  2040. Len2 := Length(S2);
  2041. if (Index - 1) + Count > Len1 then
  2042. Result := -2
  2043. else
  2044. begin
  2045. if (Index - 1) + Count > Len2 then // strange behaviour, but the assembler code does it
  2046. Count := Len2 - (Index - 1);
  2047. if CaseSensitive then
  2048. begin
  2049. for I := 0 to Count - 1 do
  2050. begin
  2051. C1 := S1[Index + I];
  2052. C2 := S2[Index + I];
  2053. if C1 <> C2 then
  2054. begin
  2055. Result := Ord(C1) - Ord(C2);
  2056. Exit;
  2057. end;
  2058. end;
  2059. end
  2060. else
  2061. begin
  2062. for I := 0 to Count - 1 do
  2063. begin
  2064. C1 := S1[Index + I];
  2065. C2 := S2[Index + I];
  2066. if C1 <> C2 then
  2067. begin
  2068. C1 := CharLower(C1);
  2069. C2 := CharLower(C2);
  2070. if C1 <> C2 then
  2071. begin
  2072. Result := Ord(C1) - Ord(C2);
  2073. Exit;
  2074. end;
  2075. end;
  2076. end;
  2077. end;
  2078. Result := 0;
  2079. end;
  2080. end;
  2081. end;
  2082. function StrCompare(const S1, S2: string; CaseSensitive: Boolean): SizeInt;
  2083. var
  2084. Len1, Len2: SizeInt;
  2085. begin
  2086. if Pointer(S1) = Pointer(S2) then
  2087. Result := 0
  2088. else
  2089. begin
  2090. Len1 := Length(S1);
  2091. Len2 := Length(S2);
  2092. Result := Len1 - Len2;
  2093. if Result = 0 then
  2094. Result := StrCompareRangeEx(S1, S2, 1, Len1, CaseSensitive);
  2095. end;
  2096. end;
  2097. function StrCompareRange(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
  2098. begin
  2099. Result := StrCompareRangeEx(S1, S2, Index, Count, CaseSensitive);
  2100. end;
  2101. procedure StrFillChar(var S; Count: SizeInt; C: Char);
  2102. {$IFDEF SUPPORTS_UNICODE}
  2103. asm
  2104. // 32 --> EAX S
  2105. // EDX Count
  2106. // ECX C
  2107. // 64 --> RCX S
  2108. // RDX Count
  2109. // R8W C
  2110. {$IFDEF CPU32}
  2111. DEC EDX
  2112. JS @@Leave
  2113. @@Loop:
  2114. MOV [EAX], CX
  2115. ADD EAX, 2
  2116. DEC EDX
  2117. JNS @@Loop
  2118. {$ENDIF CPU32}
  2119. {$IFDEF CPU64}
  2120. DEC RDX
  2121. JS @@Leave
  2122. @@Loop:
  2123. MOV WORD PTR [RCX], R8W
  2124. ADD RCX, 2
  2125. DEC RDX
  2126. JNS @@Loop
  2127. {$ENDIF CPU64}
  2128. @@Leave:
  2129. end;
  2130. {$ELSE ~SUPPORTS_UNICODE}
  2131. begin
  2132. if Count > 0 then
  2133. FillChar(S, Count, C);
  2134. end;
  2135. {$ENDIF ~SUPPORTS_UNICODE}
  2136. function StrRepeatChar(C: Char; Count: SizeInt): string;
  2137. begin
  2138. SetLength(Result, Count);
  2139. if Count > 0 then
  2140. StrFillChar(Result[1], Count, C);
  2141. end;
  2142. function StrFind(const Substr, S: string; const Index: SizeInt): SizeInt;
  2143. var
  2144. pos: SizeInt;
  2145. begin
  2146. if (SubStr <> '') and (S <> '') then
  2147. begin
  2148. pos := StrIPos(Substr, Copy(S, Index, Length(S) - Index + 1));
  2149. if pos = 0 then
  2150. Result := 0
  2151. else
  2152. Result := Index + Pos - 1;
  2153. end
  2154. else
  2155. Result := 0;
  2156. end;
  2157. function StrHasPrefix(const S: string; const Prefixes: array of string): Boolean;
  2158. begin
  2159. Result := StrPrefixIndex(S, Prefixes) > -1;
  2160. end;
  2161. function StrHasSuffix(const S: string; const Suffixes: array of string): Boolean;
  2162. begin
  2163. Result := StrSuffixIndex(S, Suffixes) > -1;
  2164. end;
  2165. function StrIndex(const S: string; const List: array of string; CaseSensitive: Boolean): SizeInt;
  2166. var
  2167. I: SizeInt;
  2168. begin
  2169. Result := -1;
  2170. for I := Low(List) to High(List) do
  2171. begin
  2172. if StrCompare(S, List[I], CaseSensitive) = 0 then
  2173. begin
  2174. Result := I;
  2175. Break;
  2176. end;
  2177. end;
  2178. end;
  2179. function StrIHasPrefix(const S: string; const Prefixes: array of string): Boolean;
  2180. begin
  2181. Result := StrIPrefixIndex(S, Prefixes) > -1;
  2182. end;
  2183. function StrIHasSuffix(const S: string; const Suffixes: array of string): Boolean;
  2184. begin
  2185. Result := StrISuffixIndex(S, Suffixes) > -1;
  2186. end;
  2187. function StrILastPos(const SubStr, S: string): SizeInt;
  2188. begin
  2189. Result := StrLastPos(StrUpper(SubStr), StrUpper(S));
  2190. end;
  2191. function StrIPos(const SubStr, S: string): SizeInt;
  2192. begin
  2193. Result := Pos(StrUpper(SubStr), StrUpper(S));
  2194. end;
  2195. function StrIPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
  2196. var
  2197. I: SizeInt;
  2198. Test: string;
  2199. begin
  2200. Result := -1;
  2201. for I := Low(Prefixes) to High(Prefixes) do
  2202. begin
  2203. Test := StrLeft(S, Length(Prefixes[I]));
  2204. if CompareText(Test, Prefixes[I]) = 0 then
  2205. begin
  2206. Result := I;
  2207. Break;
  2208. end;
  2209. end;
  2210. end;
  2211. function StrIsOneOf(const S: string; const List: array of string): Boolean;
  2212. begin
  2213. Result := StrIndex(S, List) > -1;
  2214. end;
  2215. function StrISuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
  2216. var
  2217. I: SizeInt;
  2218. Test: string;
  2219. begin
  2220. Result := -1;
  2221. for I := Low(Suffixes) to High(Suffixes) do
  2222. begin
  2223. Test := StrRight(S, Length(Suffixes[I]));
  2224. if CompareText(Test, Suffixes[I]) = 0 then
  2225. begin
  2226. Result := I;
  2227. Break;
  2228. end;
  2229. end;
  2230. end;
  2231. function StrLastPos(const SubStr, S: string): SizeInt;
  2232. var
  2233. Last, Current: PChar;
  2234. begin
  2235. Result := 0;
  2236. Last := nil;
  2237. Current := PChar(S);
  2238. while (Current <> nil) and (Current^ <> #0) do
  2239. begin
  2240. Current := StrPos(PChar(Current), PChar(SubStr));
  2241. if Current <> nil then
  2242. begin
  2243. Last := Current;
  2244. Inc(Current);
  2245. end;
  2246. end;
  2247. if Last <> nil then
  2248. Result := Abs(PChar(S) - Last) + 1;
  2249. end;
  2250. // IMPORTANT NOTE: The StrMatch function does currently not work with the Asterix (*)
  2251. // (*) acts like (?)
  2252. function StrMatch(const Substr, S: string; Index: SizeInt): SizeInt;
  2253. var
  2254. SI, SubI, SLen, SubLen: SizeInt;
  2255. SubC: Char;
  2256. begin
  2257. SLen := Length(S);
  2258. SubLen := Length(Substr);
  2259. Result := 0;
  2260. if (Index > SLen) or (SubLen = 0) then
  2261. Exit;
  2262. while Index <= SLen do
  2263. begin
  2264. SubI := 1;
  2265. SI := Index;
  2266. while (SI <= SLen) and (SubI <= SubLen) do
  2267. begin
  2268. SubC := Substr[SubI];
  2269. if (SubC = '*') or (SubC = '?') or (SubC = S[SI]) then
  2270. begin
  2271. Inc(SI);
  2272. Inc(SubI);
  2273. end
  2274. else
  2275. Break;
  2276. end;
  2277. if SubI > SubLen then
  2278. begin
  2279. Result := Index;
  2280. Break;
  2281. end;
  2282. Inc(Index);
  2283. end;
  2284. end;
  2285. // Derived from "Like" by Michael Winter
  2286. function StrMatches(const Substr, S: string; const Index: SizeInt): Boolean;
  2287. var
  2288. StringPtr: PChar;
  2289. PatternPtr: PChar;
  2290. StringRes: PChar;
  2291. PatternRes: PChar;
  2292. begin
  2293. if SubStr = '' then
  2294. raise EJclStringError.CreateRes(@RsBlankSearchString);
  2295. Result := SubStr = '*';
  2296. if Result or (S = '') then
  2297. Exit;
  2298. if (Index <= 0) or (Index > Length(S)) then
  2299. raise EJclStringError.CreateRes(@RsArgumentOutOfRange);
  2300. StringPtr := PChar(@S[Index]);
  2301. PatternPtr := PChar(SubStr);
  2302. StringRes := nil;
  2303. PatternRes := nil;
  2304. repeat
  2305. repeat
  2306. case PatternPtr^ of
  2307. #0:
  2308. begin
  2309. Result := StringPtr^ = #0;
  2310. if Result or (StringRes = nil) or (PatternRes = nil) then
  2311. Exit;
  2312. StringPtr := StringRes;
  2313. PatternPtr := PatternRes;
  2314. Break;
  2315. end;
  2316. '*':
  2317. begin
  2318. Inc(PatternPtr);
  2319. PatternRes := PatternPtr;
  2320. Break;
  2321. end;
  2322. '?':
  2323. begin
  2324. if StringPtr^ = #0 then
  2325. Exit;
  2326. Inc(StringPtr);
  2327. Inc(PatternPtr);
  2328. end;
  2329. else
  2330. begin
  2331. if StringPtr^ = #0 then
  2332. Exit;
  2333. if StringPtr^ <> PatternPtr^ then
  2334. begin
  2335. if (StringRes = nil) or (PatternRes = nil) then
  2336. Exit;
  2337. StringPtr := StringRes;
  2338. PatternPtr := PatternRes;
  2339. Break;
  2340. end
  2341. else
  2342. begin
  2343. Inc(StringPtr);
  2344. Inc(PatternPtr);
  2345. end;
  2346. end;
  2347. end;
  2348. until False;
  2349. repeat
  2350. case PatternPtr^ of
  2351. #0:
  2352. begin
  2353. Result := True;
  2354. Exit;
  2355. end;
  2356. '*':
  2357. begin
  2358. Inc(PatternPtr);
  2359. PatternRes := PatternPtr;
  2360. end;
  2361. '?':
  2362. begin
  2363. if StringPtr^ = #0 then
  2364. Exit;
  2365. Inc(StringPtr);
  2366. Inc(PatternPtr);
  2367. end;
  2368. else
  2369. begin
  2370. repeat
  2371. if StringPtr^ = #0 then
  2372. Exit;
  2373. if StringPtr^ = PatternPtr^ then
  2374. Break;
  2375. Inc(StringPtr);
  2376. until False;
  2377. Inc(StringPtr);
  2378. StringRes := StringPtr;
  2379. Inc(PatternPtr);
  2380. Break;
  2381. end;
  2382. end;
  2383. until False;
  2384. until False;
  2385. end;
  2386. function StrNPos(const S, SubStr: string; N: SizeInt): SizeInt;
  2387. var
  2388. I, P: SizeInt;
  2389. begin
  2390. if N < 1 then
  2391. begin
  2392. Result := 0;
  2393. Exit;
  2394. end;
  2395. Result := StrSearch(SubStr, S, 1);
  2396. I := 1;
  2397. while I < N do
  2398. begin
  2399. P := StrSearch(SubStr, S, Result + 1);
  2400. if P = 0 then
  2401. begin
  2402. Result := 0;
  2403. Break;
  2404. end
  2405. else
  2406. begin
  2407. Result := P;
  2408. Inc(I);
  2409. end;
  2410. end;
  2411. end;
  2412. function StrNIPos(const S, SubStr: string; N: SizeInt): SizeInt;
  2413. var
  2414. I, P: SizeInt;
  2415. begin
  2416. if N < 1 then
  2417. begin
  2418. Result := 0;
  2419. Exit;
  2420. end;
  2421. Result := StrFind(SubStr, S, 1);
  2422. I := 1;
  2423. while I < N do
  2424. begin
  2425. P := StrFind(SubStr, S, Result + 1);
  2426. if P = 0 then
  2427. begin
  2428. Result := 0;
  2429. Break;
  2430. end
  2431. else
  2432. begin
  2433. Result := P;
  2434. Inc(I);
  2435. end;
  2436. end;
  2437. end;
  2438. function StrPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
  2439. var
  2440. I: SizeInt;
  2441. Test: string;
  2442. begin
  2443. Result := -1;
  2444. for I := Low(Prefixes) to High(Prefixes) do
  2445. begin
  2446. Test := StrLeft(S, Length(Prefixes[I]));
  2447. if CompareStr(Test, Prefixes[I]) = 0 then
  2448. begin
  2449. Result := I;
  2450. Break;
  2451. end;
  2452. end;
  2453. end;
  2454. function StrSearch(const Substr, S: string; const Index: SizeInt): SizeInt;
  2455. var
  2456. SP, SPI, SubP: PChar;
  2457. SLen: SizeInt;
  2458. begin
  2459. SLen := Length(S);
  2460. if Index <= SLen then
  2461. begin
  2462. SP := PChar(S);
  2463. SubP := PChar(Substr);
  2464. SPI := SP;
  2465. Inc(SPI, Index);
  2466. Dec(SPI);
  2467. SPI := StrPos(SPI, SubP);
  2468. if SPI <> nil then
  2469. Result := SPI - SP + 1
  2470. else
  2471. Result := 0;
  2472. end
  2473. else
  2474. Result := 0;
  2475. end;
  2476. function StrSuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
  2477. var
  2478. I: SizeInt;
  2479. Test: string;
  2480. begin
  2481. Result := -1;
  2482. for I := Low(Suffixes) to High(Suffixes) do
  2483. begin
  2484. Test := StrRight(S, Length(Suffixes[I]));
  2485. if CompareStr(Test, Suffixes[I]) = 0 then
  2486. begin
  2487. Result := I;
  2488. Break;
  2489. end;
  2490. end;
  2491. end;
  2492. //=== String Extraction ======================================================
  2493. function StrAfter(const SubStr, S: string): string;
  2494. var
  2495. P: SizeInt;
  2496. begin
  2497. P := StrFind(SubStr, S, 1); // StrFind is case-insensitive pos
  2498. if P <= 0 then
  2499. Result := '' // substr not found -> nothing after it
  2500. else
  2501. Result := StrRestOf(S, P + Length(SubStr));
  2502. end;
  2503. function StrBefore(const SubStr, S: string): string;
  2504. var
  2505. P: SizeInt;
  2506. begin
  2507. P := StrFind(SubStr, S, 1);
  2508. if P <= 0 then
  2509. Result := S
  2510. else
  2511. Result := StrLeft(S, P - 1);
  2512. end;
  2513. function StrSplit(const SubStr, S: string;var Left, Right : string): boolean;
  2514. var
  2515. P: SizeInt;
  2516. begin
  2517. P := StrFind(SubStr, S, 1);
  2518. Result:= p > 0;
  2519. if Result then
  2520. begin
  2521. Left := StrLeft(S, P - 1);
  2522. Right := StrRestOf(S, P + Length(SubStr));
  2523. end
  2524. else
  2525. begin
  2526. Left := '';
  2527. Right := '';
  2528. end;
  2529. end;
  2530. function StrBetween(const S: string; const Start, Stop: Char): string;
  2531. var
  2532. PosStart, PosEnd: SizeInt;
  2533. L: SizeInt;
  2534. begin
  2535. PosStart := Pos(Start, S);
  2536. PosEnd := StrSearch(Stop, S, PosStart + 1); // PosEnd has to be after PosStart.
  2537. if (PosStart > 0) and (PosEnd > PosStart) then
  2538. begin
  2539. L := PosEnd - PosStart;
  2540. Result := Copy(S, PosStart + 1, L - 1);
  2541. end
  2542. else
  2543. Result := '';
  2544. end;
  2545. function StrChopRight(const S: string; N: SizeInt): string;
  2546. begin
  2547. Result := Copy(S, 1, Length(S) - N);
  2548. end;
  2549. function StrLeft(const S: string; Count: SizeInt): string;
  2550. begin
  2551. Result := Copy(S, 1, Count);
  2552. end;
  2553. function StrMid(const S: string; Start, Count: SizeInt): string;
  2554. begin
  2555. Result := Copy(S, Start, Count);
  2556. end;
  2557. function StrRestOf(const S: string; N: SizeInt): string;
  2558. begin
  2559. Result := Copy(S, N, (Length(S) - N + 1));
  2560. end;
  2561. function StrRight(const S: string; Count: SizeInt): string;
  2562. begin
  2563. Result := Copy(S, Length(S) - Count + 1, Count);
  2564. end;
  2565. //=== Character (do we have it ;) ============================================
  2566. function CharEqualNoCase(const C1, C2: Char): Boolean;
  2567. begin
  2568. //if they are not equal chars, may be same letter different case
  2569. Result := (C1 = C2) or
  2570. (CharIsAlpha(C1) and CharIsAlpha(C2) and (CharLower(C1) = CharLower(C2)));
  2571. end;
  2572. function CharIsAlpha(const C: Char): Boolean;
  2573. begin
  2574. {$IFDEF UNICODE_RTL_DATABASE}
  2575. Result := TCharacter.IsLetter(C);
  2576. {$ELSE ~UNICODE_RTL_DATABASE}
  2577. Result := (StrCharTypes[C] and C1_ALPHA) <> 0;
  2578. {$ENDIF ~UNICODE_RTL_DATABASE}
  2579. end;
  2580. function CharIsAlphaNum(const C: Char): Boolean;
  2581. begin
  2582. {$IFDEF UNICODE_RTL_DATABASE}
  2583. Result := TCharacter.IsLetterOrDigit(C);
  2584. {$ELSE ~UNICODE_RTL_DATABASE}
  2585. Result := ((StrCharTypes[C] and C1_ALPHA) <> 0) or ((StrCharTypes[C] and C1_DIGIT) <> 0);
  2586. {$ENDIF ~UNICODE_RTL_DATABASE}
  2587. end;
  2588. function CharIsBlank(const C: Char): Boolean;
  2589. begin
  2590. {$IFDEF UNICODE_RTL_DATABASE}
  2591. //http://blogs.msdn.com/b/michkap/archive/2007/06/11/3230072.aspx
  2592. Result := (C = ' ') or (C = #$0009) or (C = #$00A0) or (C = #$3000);
  2593. {$ELSE ~UNICODE_RTL_DATABASE}
  2594. Result := ((StrCharTypes[C] and C1_BLANK) <> 0);
  2595. {$ENDIF ~UNICODE_RTL_DATABASE}
  2596. end;
  2597. function CharIsControl(const C: Char): Boolean;
  2598. begin
  2599. {$IFDEF UNICODE_RTL_DATABASE}
  2600. Result := TCharacter.IsControl(C);
  2601. {$ELSE ~UNICODE_RTL_DATABASE}
  2602. Result := (StrCharTypes[C] and C1_CNTRL) <> 0;
  2603. {$ENDIF ~UNICODE_RTL_DATABASE}
  2604. end;
  2605. function CharIsDelete(const C: Char): Boolean;
  2606. begin
  2607. Result := (C = #8);
  2608. end;
  2609. function CharIsDigit(const C: Char): Boolean;
  2610. begin
  2611. {$IFDEF UNICODE_RTL_DATABASE}
  2612. Result := TCharacter.IsDigit(C);
  2613. {$ELSE ~UNICODE_RTL_DATABASE}
  2614. Result := (StrCharTypes[C] and C1_DIGIT) <> 0;
  2615. {$ENDIF ~UNICODE_RTL_DATABASE}
  2616. end;
  2617. function CharIsFracDigit(const C: Char): Boolean;
  2618. begin
  2619. Result := (C = '.') or CharIsDigit(C);
  2620. end;
  2621. function CharIsHexDigit(const C: Char): Boolean;
  2622. begin
  2623. case C of
  2624. 'A'..'F',
  2625. 'a'..'f':
  2626. Result := True;
  2627. else
  2628. Result := CharIsDigit(C);
  2629. end;
  2630. end;
  2631. function CharIsLower(const C: Char): Boolean;
  2632. begin
  2633. {$IFDEF UNICODE_RTL_DATABASE}
  2634. Result := TCharacter.IsLower(C);
  2635. {$ELSE ~UNICODE_RTL_DATABASE}
  2636. Result := (StrCharTypes[C] and C1_LOWER) <> 0;
  2637. {$ENDIF ~UNICODE_RTL_DATABASE}
  2638. end;
  2639. function CharIsNumberChar(const C: Char): Boolean;
  2640. begin
  2641. Result := CharIsDigit(C) or (C = '+') or (C = '-') or (C = JclFormatSettings.DecimalSeparator);
  2642. end;
  2643. function CharIsNumber(const C: Char): Boolean;
  2644. begin
  2645. Result := CharIsDigit(C) or (C = JclFormatSettings.DecimalSeparator);
  2646. end;
  2647. function CharIsPrintable(const C: Char): Boolean;
  2648. begin
  2649. Result := not CharIsControl(C);
  2650. end;
  2651. function CharIsPunctuation(const C: Char): Boolean;
  2652. begin
  2653. {$IFDEF UNICODE_RTL_DATABASE}
  2654. Result := TCharacter.IsPunctuation(C);
  2655. {$ELSE ~UNICODE_RTL_DATABASE}
  2656. Result := ((StrCharTypes[C] and C1_PUNCT) <> 0);
  2657. {$ENDIF ~UNICODE_RTL_DATABASE}
  2658. end;
  2659. function CharIsReturn(const C: Char): Boolean;
  2660. begin
  2661. Result := (C = NativeLineFeed) or (C = NativeCarriageReturn);
  2662. end;
  2663. function CharIsSpace(const C: Char): Boolean;
  2664. begin
  2665. {$IFDEF UNICODE_RTL_DATABASE}
  2666. Result := TCharacter.IsWhiteSpace(C);
  2667. {$ELSE ~UNICODE_RTL_DATABASE}
  2668. Result := (StrCharTypes[C] and C1_SPACE) <> 0;
  2669. {$ENDIF ~UNICODE_RTL_DATABASE}
  2670. end;
  2671. function CharIsUpper(const C: Char): Boolean;
  2672. begin
  2673. {$IFDEF UNICODE_RTL_DATABASE}
  2674. Result := TCharacter.IsUpper(C);
  2675. {$ELSE ~UNICODE_RTL_DATABASE}
  2676. Result := (StrCharTypes[C] and C1_UPPER) <> 0;
  2677. {$ENDIF ~UNICODE_RTL_DATABASE}
  2678. end;
  2679. function CharIsValidIdentifierLetter(const C: Char): Boolean;
  2680. begin
  2681. case C of
  2682. {$IFDEF SUPPORTS_UNICODE}
  2683. // from XML specifications
  2684. #$00C0..#$00D6, #$00D8..#$00F6, #$00F8..#$02FF, #$0370..#$037D,
  2685. #$037F..#$1FFF, #$200C..#$200D, #$2070..#$218F, #$2C00..#$2FEF,
  2686. #$3001..#$D7FF, #$F900..#$FDCF, #$FDF0..#$FFFD, // #$10000..#$EFFFF, howto match surrogate pairs?
  2687. #$00B7, #$0300..#$036F, #$203F..#$2040,
  2688. {$ENDIF SUPPORTS_UNICODE}
  2689. '0'..'9', 'A'..'Z', 'a'..'z', '_':
  2690. Result := True;
  2691. else
  2692. Result := False;
  2693. end;
  2694. end;
  2695. function CharIsWhiteSpace(const C: Char): Boolean;
  2696. begin
  2697. case C of
  2698. NativeTab,
  2699. NativeLineFeed,
  2700. NativeVerticalTab,
  2701. NativeFormFeed,
  2702. NativeCarriageReturn,
  2703. NativeSpace:
  2704. Result := True;
  2705. else
  2706. Result := False;
  2707. end;
  2708. end;
  2709. function CharIsWildcard(const C: Char): Boolean;
  2710. begin
  2711. case C of
  2712. '*', '?':
  2713. Result := True;
  2714. else
  2715. Result := False;
  2716. end;
  2717. end;
  2718. function CharType(const C: Char): Word;
  2719. begin
  2720. {$IFDEF UNICODE_RTL_DATABASE}
  2721. GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, @C, 1, Result);
  2722. {$ELSE ~UNICODE_RTL_DATABASE}
  2723. Result := StrCharTypes[C];
  2724. {$ENDIF ~UNICODE_RTL_DATABASE}
  2725. end;
  2726. //=== PCharVector ============================================================
  2727. function StringsToPCharVector(var Dest: PCharVector; const Source: TStrings): PCharVector;
  2728. var
  2729. I: SizeInt;
  2730. S: string;
  2731. List: array of PChar;
  2732. begin
  2733. Assert(Source <> nil);
  2734. Dest := AllocMem((Source.Count + SizeOf(Char)) * SizeOf(PChar));
  2735. SetLength(List, Source.Count + SizeOf(Char));
  2736. for I := 0 to Source.Count - 1 do
  2737. begin
  2738. S := Source[I];
  2739. List[I] := StrAlloc(Length(S) + SizeOf(Char));
  2740. StrPCopy(List[I], S);
  2741. end;
  2742. List[Source.Count] := nil;
  2743. Move(List[0], Dest^, (Source.Count + 1) * SizeOf(PChar));
  2744. Result := Dest;
  2745. end;
  2746. function PCharVectorCount(Source: PCharVector): SizeInt;
  2747. begin
  2748. Result := 0;
  2749. if Source <> nil then
  2750. begin
  2751. while Source^ <> nil do
  2752. begin
  2753. Inc(Source);
  2754. Inc(Result);
  2755. end;
  2756. end;
  2757. end;
  2758. procedure PCharVectorToStrings(const Dest: TStrings; Source: PCharVector);
  2759. var
  2760. I, Count: SizeInt;
  2761. List: array of PChar;
  2762. begin
  2763. Assert(Dest <> nil);
  2764. if Source <> nil then
  2765. begin
  2766. Count := PCharVectorCount(Source);
  2767. SetLength(List, Count);
  2768. Move(Source^, List[0], Count * SizeOf(PChar));
  2769. Dest.BeginUpdate;
  2770. try
  2771. Dest.Clear;
  2772. for I := 0 to Count - 1 do
  2773. Dest.Add(List[I]);
  2774. finally
  2775. Dest.EndUpdate;
  2776. end;
  2777. end;
  2778. end;
  2779. procedure FreePCharVector(var Dest: PCharVector);
  2780. var
  2781. I, Count: SizeInt;
  2782. List: array of PChar;
  2783. begin
  2784. if Dest <> nil then
  2785. begin
  2786. Count := PCharVectorCount(Dest);
  2787. SetLength(List, Count);
  2788. Move(Dest^, List[0], Count * SizeOf(PChar));
  2789. for I := 0 to Count - 1 do
  2790. StrDispose(List[I]);
  2791. FreeMem(Dest, (Count + 1) * SizeOf(PChar));
  2792. Dest := nil;
  2793. end;
  2794. end;
  2795. //=== Character Transformation Routines ======================================
  2796. function CharHex(const C: Char): Byte;
  2797. begin
  2798. case C of
  2799. '0'..'9':
  2800. Result := Ord(C) - Ord('0');
  2801. 'a'..'f':
  2802. Result := Ord(C) - Ord('a') + 10;
  2803. 'A'..'F':
  2804. Result := Ord(C) - Ord('A') + 10;
  2805. else
  2806. Result := $FF;
  2807. end;
  2808. end;
  2809. function CharLower(const C: Char): Char;
  2810. begin
  2811. {$IFDEF UNICODE_RTL_DATABASE}
  2812. Result := TCharacter.ToLower(C);
  2813. {$ELSE ~UNICODE_RTL_DATABASE}
  2814. Result := StrCaseMap[Ord(C) + StrLoOffset];
  2815. {$ENDIF ~UNICODE_RTL_DATABASE}
  2816. end;
  2817. function CharToggleCase(const C: Char): Char;
  2818. begin
  2819. {$IFDEF UNICODE_RTL_DATABASE}
  2820. if CharIsLower(C) then
  2821. Result := CharUpper(C)
  2822. else if CharIsUpper(C) then
  2823. Result := CharLower(C)
  2824. else
  2825. Result := C;
  2826. {$ELSE ~UNICODE_RTL_DATABASE}
  2827. Result := StrCaseMap[Ord(C) + StrReOffset];
  2828. {$ENDIF ~UNICODE_RTL_DATABASE}
  2829. end;
  2830. function CharUpper(const C: Char): Char;
  2831. begin
  2832. {$IFDEF UNICODE_RTL_DATABASE}
  2833. Result := TCharacter.ToUpper(C);
  2834. {$ELSE ~UNICODE_RTL_DATABASE}
  2835. Result := StrCaseMap[Ord(C) + StrUpOffset];
  2836. {$ENDIF ~UNICODE_RTL_DATABASE}
  2837. end;
  2838. //=== Character Search and Replace ===========================================
  2839. function CharLastPos(const S: string; const C: Char; const Index: SizeInt): SizeInt;
  2840. begin
  2841. if (Index > 0) and (Index <= Length(S)) then
  2842. begin
  2843. for Result := Length(S) downto Index do
  2844. if S[Result] = C then
  2845. Exit;
  2846. end;
  2847. Result := 0;
  2848. end;
  2849. function CharPos(const S: string; const C: Char; const Index: SizeInt): SizeInt;
  2850. begin
  2851. if (Index > 0) and (Index <= Length(S)) then
  2852. begin
  2853. for Result := Index to Length(S) do
  2854. if S[Result] = C then
  2855. Exit;
  2856. end;
  2857. Result := 0;
  2858. end;
  2859. function CharIPos(const S: string; C: Char; const Index: SizeInt): SizeInt;
  2860. begin
  2861. if (Index > 0) and (Index <= Length(S)) then
  2862. begin
  2863. C := CharUpper(C);
  2864. for Result := Index to Length(S) do
  2865. if CharUpper(S[Result]) = C then
  2866. Exit;
  2867. end;
  2868. Result := 0;
  2869. end;
  2870. function CharReplace(var S: string; const Search, Replace: Char): SizeInt;
  2871. var
  2872. P: PChar;
  2873. Index, Len: SizeInt;
  2874. begin
  2875. Result := 0;
  2876. if Search <> Replace then
  2877. begin
  2878. UniqueString(S);
  2879. P := PChar(S);
  2880. Len := Length(S);
  2881. for Index := 0 to Len - 1 do
  2882. begin
  2883. if P^ = Search then
  2884. begin
  2885. P^ := Replace;
  2886. Inc(Result);
  2887. end;
  2888. Inc(P);
  2889. end;
  2890. end;
  2891. end;
  2892. //=== MultiSz ================================================================
  2893. function StringsToMultiSz(var Dest: PMultiSz; const Source: TStrings): PMultiSz;
  2894. var
  2895. I, TotalLength: SizeInt;
  2896. P: PMultiSz;
  2897. begin
  2898. Assert(Source <> nil);
  2899. TotalLength := 1;
  2900. for I := 0 to Source.Count - 1 do
  2901. if Source[I] = '' then
  2902. raise EJclStringError.CreateRes(@RsInvalidEmptyStringItem)
  2903. else
  2904. Inc(TotalLength, StrLen(PChar(Source[I])) + 1);
  2905. AllocateMultiSz(Dest, TotalLength);
  2906. P := Dest;
  2907. for I := 0 to Source.Count - 1 do
  2908. begin
  2909. P := StrECopy(P, PChar(Source[I]));
  2910. Inc(P);
  2911. end;
  2912. P^ := #0;
  2913. Result := Dest;
  2914. end;
  2915. procedure MultiSzToStrings(const Dest: TStrings; const Source: PMultiSz);
  2916. var
  2917. P: PMultiSz;
  2918. begin
  2919. Assert(Dest <> nil);
  2920. Dest.BeginUpdate;
  2921. try
  2922. Dest.Clear;
  2923. if Source <> nil then
  2924. begin
  2925. P := Source;
  2926. while P^ <> #0 do
  2927. begin
  2928. Dest.Add(P);
  2929. P := StrEnd(P);
  2930. Inc(P);
  2931. end;
  2932. end;
  2933. finally
  2934. Dest.EndUpdate;
  2935. end;
  2936. end;
  2937. function MultiSzLength(const Source: PMultiSz): SizeInt;
  2938. var
  2939. P: PMultiSz;
  2940. begin
  2941. Result := 0;
  2942. if Source <> nil then
  2943. begin
  2944. P := Source;
  2945. repeat
  2946. Inc(Result, StrLen(P) + 1);
  2947. P := StrEnd(P);
  2948. Inc(P);
  2949. until P^ = #0;
  2950. Inc(Result);
  2951. end;
  2952. end;
  2953. procedure AllocateMultiSz(var Dest: PMultiSz; Len: SizeInt);
  2954. begin
  2955. if Len > 0 then
  2956. GetMem(Dest, Len * SizeOf(Char))
  2957. else
  2958. Dest := nil;
  2959. end;
  2960. procedure FreeMultiSz(var Dest: PMultiSz);
  2961. begin
  2962. if Dest <> nil then
  2963. FreeMem(Dest);
  2964. Dest := nil;
  2965. end;
  2966. function MultiSzDup(const Source: PMultiSz): PMultiSz;
  2967. var
  2968. Len: SizeInt;
  2969. begin
  2970. if Source <> nil then
  2971. begin
  2972. Len := MultiSzLength(Source);
  2973. Result := nil;
  2974. AllocateMultiSz(Result, Len);
  2975. Move(Source^, Result^, Len * SizeOf(Char));
  2976. end
  2977. else
  2978. Result := nil;
  2979. end;
  2980. function AnsiStringsToAnsiMultiSz(var Dest: PAnsiMultiSz; const Source: TAnsiStrings): PAnsiMultiSz;
  2981. begin
  2982. Result := JclAnsiStrings.StringsToMultiSz(Dest, Source);
  2983. end;
  2984. procedure AnsiMultiSzToAnsiStrings(const Dest: TAnsiStrings; const Source: PAnsiMultiSz);
  2985. begin
  2986. JclAnsiStrings.MultiSzToStrings(Dest, Source);
  2987. end;
  2988. function AnsiMultiSzLength(const Source: PAnsiMultiSz): SizeInt;
  2989. begin
  2990. Result := JclAnsiStrings.MultiSzLength(Source);
  2991. end;
  2992. procedure AllocateAnsiMultiSz(var Dest: PAnsiMultiSz; Len: SizeInt);
  2993. begin
  2994. JclAnsiStrings.AllocateMultiSz(Dest, Len);
  2995. end;
  2996. procedure FreeAnsiMultiSz(var Dest: PAnsiMultiSz);
  2997. begin
  2998. JclAnsiStrings.FreeMultiSz(Dest);
  2999. end;
  3000. function AnsiMultiSzDup(const Source: PAnsiMultiSz): PAnsiMultiSz;
  3001. begin
  3002. Result := JclAnsiStrings.MultiSzDup(Source);
  3003. end;
  3004. function WideStringsToWideMultiSz(var Dest: PWideMultiSz; const Source: TWideStrings): PWideMultiSz;
  3005. begin
  3006. Result := JclWideStrings.StringsToMultiSz(Dest, Source);
  3007. end;
  3008. procedure WideMultiSzToWideStrings(const Dest: TWideStrings; const Source: PWideMultiSz);
  3009. begin
  3010. JclWideStrings.MultiSzToStrings(Dest, Source);
  3011. end;
  3012. function WideMultiSzLength(const Source: PWideMultiSz): SizeInt;
  3013. begin
  3014. Result := JclWideStrings.MultiSzLength(Source);
  3015. end;
  3016. procedure AllocateWideMultiSz(var Dest: PWideMultiSz; Len: SizeInt);
  3017. begin
  3018. JclWideStrings.AllocateMultiSz(Dest, Len);
  3019. end;
  3020. procedure FreeWideMultiSz(var Dest: PWideMultiSz);
  3021. begin
  3022. JclWideStrings.FreeMultiSz(Dest);
  3023. end;
  3024. function WideMultiSzDup(const Source: PWideMultiSz): PWideMultiSz;
  3025. begin
  3026. Result := JclWideStrings.MultiSzDup(Source);
  3027. end;
  3028. //=== TStrings Manipulation ==================================================
  3029. procedure StrToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
  3030. var
  3031. I, L: SizeInt;
  3032. Left: string;
  3033. begin
  3034. Assert(List <> nil);
  3035. List.BeginUpdate;
  3036. try
  3037. List.Clear;
  3038. L := Length(Sep);
  3039. I := Pos(Sep, S);
  3040. while I > 0 do
  3041. begin
  3042. Left := StrLeft(S, I - 1);
  3043. if (Left <> '') or AllowEmptyString then
  3044. List.Add(Left);
  3045. Delete(S, 1, I + L - 1);
  3046. I := Pos(Sep, S);
  3047. end;
  3048. if (S <> '') or AllowEmptyString then
  3049. List.Add(S); // Ignore empty strings at the end (only if AllowEmptyString = False).
  3050. finally
  3051. List.EndUpdate;
  3052. end;
  3053. end;
  3054. procedure StrIToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
  3055. var
  3056. I, L: SizeInt;
  3057. LowerCaseStr: string;
  3058. Left: string;
  3059. begin
  3060. Assert(List <> nil);
  3061. LowerCaseStr := StrLower(S);
  3062. Sep := StrLower(Sep);
  3063. L := Length(Sep);
  3064. I := Pos(Sep, LowerCaseStr);
  3065. List.BeginUpdate;
  3066. try
  3067. List.Clear;
  3068. while I > 0 do
  3069. begin
  3070. Left := StrLeft(S, I - 1);
  3071. if (Left <> '') or AllowEmptyString then
  3072. List.Add(Left);
  3073. Delete(S, 1, I + L - 1);
  3074. Delete(LowerCaseStr, 1, I + L - 1);
  3075. I := Pos(Sep, LowerCaseStr);
  3076. end;
  3077. if (S <> '') or AllowEmptyString then
  3078. List.Add(S); // Ignore empty strings at the end (only if AllowEmptyString = False).
  3079. finally
  3080. List.EndUpdate;
  3081. end;
  3082. end;
  3083. function StringsToStr(const List: TStrings; const Sep: string; const AllowEmptyString: Boolean = True): string;
  3084. var
  3085. I, L: SizeInt;
  3086. begin
  3087. Result := '';
  3088. for I := 0 to List.Count - 1 do
  3089. begin
  3090. if (List[I] <> '') or AllowEmptyString then
  3091. begin
  3092. // don't combine these into one addition, somehow it hurts performance
  3093. Result := Result + List[I];
  3094. Result := Result + Sep;
  3095. end;
  3096. end;
  3097. // remove terminating separator
  3098. if List.Count > 0 then
  3099. begin
  3100. L := Length(Sep);
  3101. Delete(Result, Length(Result) - L + 1, L);
  3102. end;
  3103. end;
  3104. function StringsToStr(const List: TStrings; const Sep: string; const NumberOfItems: SizeInt; const AllowEmptyString:
  3105. Boolean = True): string;
  3106. var
  3107. I, L, N: SizeInt;
  3108. begin
  3109. Result := '';
  3110. if List.Count > NumberOfItems then
  3111. N := NumberOfItems
  3112. else
  3113. N := List.Count;
  3114. for I := 0 to N - 1 do
  3115. begin
  3116. if (List[I] <> '') or AllowEmptyString then
  3117. begin
  3118. // don't combine these into one addition, somehow it hurts performance
  3119. Result := Result + List[I];
  3120. Result := Result + Sep;
  3121. end;
  3122. end;
  3123. // remove terminating separator
  3124. if N > 0 then
  3125. begin
  3126. L := Length(Sep);
  3127. Delete(Result, Length(Result) - L + 1, L);
  3128. end;
  3129. end;
  3130. procedure TrimStrings(const List: TStrings; DeleteIfEmpty: Boolean);
  3131. var
  3132. I: SizeInt;
  3133. begin
  3134. Assert(List <> nil);
  3135. List.BeginUpdate;
  3136. try
  3137. for I := List.Count - 1 downto 0 do
  3138. begin
  3139. List[I] := Trim(List[I]);
  3140. if (List[I] = '') and DeleteIfEmpty then
  3141. List.Delete(I);
  3142. end;
  3143. finally
  3144. List.EndUpdate;
  3145. end;
  3146. end;
  3147. procedure TrimStringsRight(const List: TStrings; DeleteIfEmpty: Boolean);
  3148. var
  3149. I: SizeInt;
  3150. begin
  3151. Assert(List <> nil);
  3152. List.BeginUpdate;
  3153. try
  3154. for I := List.Count - 1 downto 0 do
  3155. begin
  3156. List[I] := TrimRight(List[I]);
  3157. if (List[I] = '') and DeleteIfEmpty then
  3158. List.Delete(I);
  3159. end;
  3160. finally
  3161. List.EndUpdate;
  3162. end;
  3163. end;
  3164. procedure TrimStringsLeft(const List: TStrings; DeleteIfEmpty: Boolean);
  3165. var
  3166. I: SizeInt;
  3167. begin
  3168. Assert(List <> nil);
  3169. List.BeginUpdate;
  3170. try
  3171. for I := List.Count - 1 downto 0 do
  3172. begin
  3173. List[I] := TrimLeft(List[I]);
  3174. if (List[I] = '') and DeleteIfEmpty then
  3175. List.Delete(I);
  3176. end;
  3177. finally
  3178. List.EndUpdate;
  3179. end;
  3180. end;
  3181. function AddStringToStrings(const S: string; Strings: TStrings; const Unique: Boolean): Boolean;
  3182. begin
  3183. Assert(Strings <> nil);
  3184. Result := Unique and (Strings.IndexOf(S) <> -1);
  3185. if not Result then
  3186. Result := Strings.Add(S) > -1;
  3187. end;
  3188. //=== Miscellaneous ==========================================================
  3189. function FileToString(const FileName: string): {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
  3190. var
  3191. fs: TFileStream;
  3192. Len: SizeInt;
  3193. begin
  3194. fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  3195. try
  3196. Len := fs.Size;
  3197. SetLength(Result, Len);
  3198. if Len > 0 then
  3199. fs.ReadBuffer(Result[1], Len);
  3200. finally
  3201. fs.Free;
  3202. end;
  3203. end;
  3204. procedure StringToFile(const FileName: string; const Contents: {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
  3205. Append: Boolean);
  3206. var
  3207. FS: TFileStream;
  3208. Len: SizeInt;
  3209. begin
  3210. if Append and FileExists(filename) then
  3211. FS := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyWrite)
  3212. else
  3213. FS := TFileStream.Create(FileName, fmCreate);
  3214. try
  3215. if Append then
  3216. FS.Seek(0, soEnd); // faster than .Position := .Size
  3217. Len := Length(Contents);
  3218. if Len > 0 then
  3219. FS.WriteBuffer(Contents[1], Len);
  3220. finally
  3221. FS.Free;
  3222. end;
  3223. end;
  3224. function StrToken(var S: string; Separator: Char): string;
  3225. var
  3226. I: SizeInt;
  3227. begin
  3228. I := Pos(Separator, S);
  3229. if I <> 0 then
  3230. begin
  3231. Result := Copy(S, 1, I - 1);
  3232. Delete(S, 1, I);
  3233. end
  3234. else
  3235. begin
  3236. Result := S;
  3237. S := '';
  3238. end;
  3239. end;
  3240. procedure StrTokens(const S: string; const List: TStrings);
  3241. var
  3242. Start: PChar;
  3243. Token: string;
  3244. Done: Boolean;
  3245. begin
  3246. Assert(List <> nil);
  3247. if List = nil then
  3248. Exit;
  3249. List.BeginUpdate;
  3250. try
  3251. List.Clear;
  3252. Start := Pointer(S);
  3253. repeat
  3254. Done := JclStrings.StrWord(Start, Token);
  3255. if Token <> '' then
  3256. List.Add(Token);
  3257. until Done;
  3258. finally
  3259. List.EndUpdate;
  3260. end;
  3261. end;
  3262. function StrWord(const S: string; var Index: SizeInt; out Word: string): Boolean;
  3263. var
  3264. Start: SizeInt;
  3265. C: Char;
  3266. begin
  3267. Word := '';
  3268. if (S = '') then
  3269. begin
  3270. Result := True;
  3271. Exit;
  3272. end;
  3273. Start := Index;
  3274. Result := False;
  3275. while True do
  3276. begin
  3277. C := S[Index];
  3278. case C of
  3279. #0:
  3280. begin
  3281. if Start <> 0 then
  3282. Word := Copy(S, Start, Index - Start);
  3283. Result := True;
  3284. Exit;
  3285. end;
  3286. NativeSpace, NativeLineFeed, NativeCarriageReturn:
  3287. begin
  3288. if Start <> 0 then
  3289. begin
  3290. Word := Copy(S, Start, Index - Start);
  3291. Exit;
  3292. end
  3293. else
  3294. begin
  3295. while CharIsWhiteSpace(C) do
  3296. begin
  3297. Inc(Index);
  3298. C := S[Index];
  3299. end;
  3300. end;
  3301. end;
  3302. else
  3303. if Start = 0 then
  3304. Start := Index;
  3305. Inc(Index);
  3306. end;
  3307. end;
  3308. end;
  3309. function StrWord(var S: PChar; out Word: string): Boolean;
  3310. var
  3311. Start: PChar;
  3312. begin
  3313. Word := '';
  3314. if S = nil then
  3315. begin
  3316. Result := True;
  3317. Exit;
  3318. end;
  3319. Start := nil;
  3320. Result := False;
  3321. while True do
  3322. begin
  3323. case S^ of
  3324. #0:
  3325. begin
  3326. if Start <> nil then
  3327. SetString(Word, Start, S - Start);
  3328. Result := True;
  3329. Exit;
  3330. end;
  3331. NativeSpace, NativeLineFeed, NativeCarriageReturn:
  3332. begin
  3333. if Start <> nil then
  3334. begin
  3335. SetString(Word, Start, S - Start);
  3336. Exit;
  3337. end
  3338. else
  3339. while CharIsWhiteSpace(S^) do
  3340. Inc(S);
  3341. end;
  3342. else
  3343. if Start = nil then
  3344. Start := S;
  3345. Inc(S);
  3346. end;
  3347. end;
  3348. end;
  3349. function StrIdent(const S: string; var Index: SizeInt; out Ident: string): Boolean;
  3350. var
  3351. Start: SizeInt;
  3352. C: Char;
  3353. begin
  3354. Ident := '';
  3355. if (S = '') then
  3356. begin
  3357. Result := True;
  3358. Exit;
  3359. end;
  3360. Start := Index;
  3361. Result := False;
  3362. while True do
  3363. begin
  3364. C := S[Index];
  3365. if CharIsValidIdentifierLetter(C) then
  3366. begin
  3367. if Start = 0 then
  3368. Start := Index;
  3369. end
  3370. else
  3371. if C = #0 then
  3372. begin
  3373. if Start <> 0 then
  3374. Ident := Copy(S, Start, Index - Start);
  3375. Result := True;
  3376. Exit;
  3377. end
  3378. else
  3379. begin
  3380. if Start <> 0 then
  3381. begin
  3382. Ident := Copy(S, Start, Index - Start);
  3383. Exit;
  3384. end;
  3385. end;
  3386. Inc(Index);
  3387. end;
  3388. end;
  3389. function StrIdent(var S: PChar; out Ident: string): Boolean;
  3390. var
  3391. Start: PChar;
  3392. C: Char;
  3393. begin
  3394. Ident := '';
  3395. if S = nil then
  3396. begin
  3397. Result := True;
  3398. Exit;
  3399. end;
  3400. Start := nil;
  3401. Result := False;
  3402. while True do
  3403. begin
  3404. C := S^;
  3405. if CharIsValidIdentifierLetter(C) then
  3406. begin
  3407. if Start = nil then
  3408. Start := S;
  3409. end
  3410. else
  3411. if C = #0 then
  3412. begin
  3413. if Start <> nil then
  3414. SetString(Ident, Start, S - Start);
  3415. Result := True;
  3416. Exit;
  3417. end
  3418. else
  3419. begin
  3420. if Start <> nil then
  3421. begin
  3422. SetString(Ident, Start, S - Start);
  3423. Exit;
  3424. end
  3425. end;
  3426. Inc(S);
  3427. end;
  3428. end;
  3429. procedure StrTokenToStrings(S: string; Separator: Char; const List: TStrings);
  3430. var
  3431. Token: string;
  3432. begin
  3433. Assert(List <> nil);
  3434. if List = nil then
  3435. Exit;
  3436. List.BeginUpdate;
  3437. try
  3438. List.Clear;
  3439. while S <> '' do
  3440. begin
  3441. Token := StrToken(S, Separator);
  3442. List.Add(Token);
  3443. end;
  3444. finally
  3445. List.EndUpdate;
  3446. end;
  3447. end;
  3448. function StrToFloatSafe(const S: string): Float;
  3449. var
  3450. Temp: string;
  3451. I, J, K: SizeInt;
  3452. SwapSeparators, IsNegative: Boolean;
  3453. DecSep, ThouSep, C: Char;
  3454. begin
  3455. DecSep := {$IFDEF RTL220_UP}FormatSettings.{$ENDIF}DecimalSeparator;
  3456. ThouSep := {$IFDEF RTL220_UP}FormatSettings.{$ENDIF}ThousandSeparator;
  3457. Temp := S;
  3458. SwapSeparators := False;
  3459. IsNegative := False;
  3460. J := 0;
  3461. for I := 1 to Length(Temp) do
  3462. begin
  3463. C := Temp[I];
  3464. if C = '-' then
  3465. IsNegative := not IsNegative
  3466. else
  3467. if (C <> ' ') and (C <> '(') and (C <> '+') then
  3468. begin
  3469. // if it appears prior to any digit, it has to be a decimal separator
  3470. SwapSeparators := Temp[I] = ThouSep;
  3471. J := I;
  3472. Break;
  3473. end;
  3474. end;
  3475. if not SwapSeparators then
  3476. begin
  3477. K := CharPos(Temp, DecSep);
  3478. SwapSeparators :=
  3479. // if it appears prior to any digit, it has to be a decimal separator
  3480. (K > J) and
  3481. // if it appears multiple times, it has to be a thousand separator
  3482. ((StrCharCount(Temp, DecSep) > 1) or
  3483. // we assume (consistent with Windows Platform SDK documentation),
  3484. // that thousand separators appear only to the left of the decimal
  3485. (K < CharPos(Temp, ThouSep)));
  3486. end;
  3487. if SwapSeparators then
  3488. begin
  3489. // assume a numerical string from a different locale,
  3490. // where DecimalSeparator and ThousandSeparator are exchanged
  3491. for I := 1 to Length(Temp) do
  3492. if Temp[I] = DecSep then
  3493. Temp[I] := ThouSep
  3494. else
  3495. if Temp[I] = ThouSep then
  3496. Temp[I] := DecSep;
  3497. end;
  3498. Temp := StrKeepChars(Temp, CharIsNumber);
  3499. if Length(Temp) > 0 then
  3500. begin
  3501. if Temp[1] = DecSep then
  3502. Temp := '0' + Temp;
  3503. if Temp[Length(Temp)] = DecSep then
  3504. Temp := Temp + '0';
  3505. Result := StrToFloat(Temp);
  3506. if IsNegative then
  3507. Result := -Result;
  3508. end
  3509. else
  3510. Result := 0.0;
  3511. end;
  3512. function StrToIntSafe(const S: string): Integer;
  3513. begin
  3514. Result := Trunc(StrToFloatSafe(S));
  3515. end;
  3516. procedure StrNormIndex(const StrLen: SizeInt; var Index: SizeInt; var Count: SizeInt); overload;
  3517. begin
  3518. Index := Max(1, Min(Index, StrLen + 1));
  3519. Count := Max(0, Min(Count, StrLen + 1 - Index));
  3520. end;
  3521. function ArrayOf(List: TStrings): TDynStringArray;
  3522. var
  3523. I: SizeInt;
  3524. begin
  3525. if List <> nil then
  3526. begin
  3527. SetLength(Result, List.Count);
  3528. for I := 0 to List.Count - 1 do
  3529. Result[I] := List[I];
  3530. end
  3531. else
  3532. Result := nil;
  3533. end;
  3534. const
  3535. BoolToStr: array [Boolean] of string = ('false', 'true');
  3536. type
  3537. TInterfacedObjectAccess = class(TInterfacedObject);
  3538. procedure MoveChar(const Source; var Dest; Count: SizeInt);
  3539. begin
  3540. if Count > 0 then
  3541. Move(Source, Dest, Count * SizeOf(Char));
  3542. end;
  3543. function DotNetFormat(const Fmt: string; const Arg0: Variant): string;
  3544. begin
  3545. Result := DotNetFormat(Fmt, [Arg0]);
  3546. end;
  3547. function DotNetFormat(const Fmt: string; const Arg0, Arg1: Variant): string;
  3548. begin
  3549. Result := DotNetFormat(Fmt, [Arg0, Arg1]);
  3550. end;
  3551. function DotNetFormat(const Fmt: string; const Arg0, Arg1, Arg2: Variant): string;
  3552. begin
  3553. Result := DotNetFormat(Fmt, [Arg0, Arg1, Arg2]);
  3554. end;
  3555. function DotNetFormat(const Fmt: string; const Args: array of const): string;
  3556. var
  3557. F, P: PChar;
  3558. Len, Capacity, Count: SizeInt;
  3559. Index: SizeInt;
  3560. ErrorCode: Integer;
  3561. S: string;
  3562. procedure Grow(Count: SizeInt);
  3563. begin
  3564. if Len + Count > Capacity then
  3565. begin
  3566. Capacity := Capacity * 5 div 3 + Count;
  3567. SetLength(Result, Capacity);
  3568. end;
  3569. end;
  3570. function InheritsFrom(AClass: TClass; const ClassName: string): Boolean;
  3571. begin
  3572. Result := True;
  3573. while AClass <> nil do
  3574. begin
  3575. if CompareText(AClass.ClassName, ClassName) = 0 then
  3576. Exit;
  3577. AClass := AClass.ClassParent;
  3578. end;
  3579. Result := False;
  3580. end;
  3581. function GetStringOf(const V: TVarData; Index: SizeInt): string; overload;
  3582. begin
  3583. case V.VType of
  3584. varEmpty, varNull:
  3585. raise ArgumentNullException.CreateRes(@RsArgumentIsNull);
  3586. varSmallInt:
  3587. Result := IntToStr(V.VSmallInt);
  3588. varInteger:
  3589. Result := IntToStr(V.VInteger);
  3590. varSingle:
  3591. Result := FloatToStr(V.VSingle);
  3592. varDouble:
  3593. Result := FloatToStr(V.VDouble);
  3594. varCurrency:
  3595. Result := CurrToStr(V.VCurrency);
  3596. varDate:
  3597. Result := DateTimeToStr(V.VDate);
  3598. varOleStr:
  3599. Result := V.VOleStr;
  3600. varBoolean:
  3601. Result := BoolToStr[V.VBoolean <> False];
  3602. varByte:
  3603. Result := IntToStr(V.VByte);
  3604. varWord:
  3605. Result := IntToStr(V.VWord);
  3606. varShortInt:
  3607. Result := IntToStr(V.VShortInt);
  3608. varLongWord:
  3609. Result := IntToStr(V.VLongWord);
  3610. varInt64:
  3611. Result := IntToStr(V.VInt64);
  3612. varString:
  3613. Result := string(V.VString);
  3614. {$IFDEF SUPPORTS_UNICODE_STRING}
  3615. varUString:
  3616. Result := string(V.VUString);
  3617. {$ENDIF SUPPORTS_UNICODE_STRING}
  3618. {varArray,
  3619. varDispatch,
  3620. varError,
  3621. varUnknown,
  3622. varAny,
  3623. varByRef:}
  3624. else
  3625. raise ArgumentNullException.CreateResFmt(@RsDotNetFormatArgumentNotSupported, [Index]);
  3626. end;
  3627. end;
  3628. function GetStringOf(Index: SizeInt): string; overload;
  3629. var
  3630. V: TVarRec;
  3631. Intf: IToString;
  3632. begin
  3633. V := Args[Index];
  3634. if (V.VInteger = 0) and
  3635. (V.VType in [vtExtended, vtString, vtObject, vtClass, vtCurrency,
  3636. vtInterface, vtInt64]) then
  3637. raise ArgumentNullException.CreateResFmt(@RsArgumentIsNull, [Index]);
  3638. case V.VType of
  3639. vtInteger:
  3640. Result := IntToStr(V.VInteger);
  3641. vtBoolean:
  3642. Result := BoolToStr[V.VBoolean];
  3643. vtChar:
  3644. Result := string(AnsiString(V.VChar));
  3645. vtExtended:
  3646. Result := FloatToStr(V.VExtended^);
  3647. vtString:
  3648. Result := string(V.VString^);
  3649. vtPointer:
  3650. Result := IntToHex(TJclAddr(V.VPointer), 8);
  3651. vtPChar:
  3652. Result := string(AnsiString(V.VPChar));
  3653. vtObject:
  3654. if (V.VObject is TInterfacedObject) and V.VObject.GetInterface(IToString, Intf) then
  3655. begin
  3656. Result := Intf.ToString;
  3657. Pointer(Intf) := nil; // do not release the object
  3658. // undo the RefCount change
  3659. Dec(TInterfacedObjectAccess(V.VObject).FRefCount);
  3660. end
  3661. else
  3662. if ((V.VObject is TComponent) or (V.VObject is TInterfacedPersistent)) and V.VObject.GetInterface(IToString, Intf) then
  3663. Result := Intf.ToString
  3664. {$IFDEF RTL200_UP}
  3665. else
  3666. Result := V.VObject.ToString;
  3667. {$ELSE}
  3668. else
  3669. raise ArgumentNullException.CreateResFmt(@RsDotNetFormatObjectArgumentNotSupported, [V.VObject.ClassName, Index]);
  3670. {$ENDIF RTL200_UP}
  3671. vtClass:
  3672. Result := V.VClass.ClassName;
  3673. vtWideChar:
  3674. Result := V.VWideChar;
  3675. vtPWideChar:
  3676. Result := V.VPWideChar;
  3677. vtAnsiString:
  3678. Result := string(V.VAnsiString);
  3679. vtCurrency:
  3680. Result := CurrToStr(V.VCurrency^);
  3681. vtVariant:
  3682. Result := GetStringOf(TVarData(V.VVariant^), Index);
  3683. vtInterface:
  3684. if IInterface(V.VInterface).QueryInterface(IToString, Intf) = 0 then
  3685. Result := IToString(Intf).ToString
  3686. else
  3687. raise ArgumentNullException.CreateResFmt(@RsDotNetFormatArgumentNotSupported, [Index]);
  3688. vtWideString:
  3689. Result := WideString(V.VWideString);
  3690. vtInt64:
  3691. Result := IntToStr(V.VInt64^);
  3692. {$IFDEF SUPPORTS_UNICODE_STRING}
  3693. vtUnicodeString:
  3694. Result := UnicodeString(V.VUnicodeString);
  3695. {$ENDIF SUPPORTS_UNICODE_STRING}
  3696. else
  3697. raise ArgumentNullException.CreateResFmt(@RsDotNetFormatArgumentNotSupported, [Index]);
  3698. end;
  3699. end;
  3700. begin
  3701. if Length(Args) = 0 then
  3702. begin
  3703. Result := Fmt;
  3704. Exit;
  3705. end;
  3706. Len := 0;
  3707. Capacity := Length(Fmt);
  3708. SetLength(Result, Capacity);
  3709. if Capacity = 0 then
  3710. raise ArgumentNullException.CreateRes(@RsDotNetFormatNullFormat);
  3711. P := Pointer(Fmt);
  3712. F := P;
  3713. while True do
  3714. begin
  3715. if (P[0] = #0) or (P[0] = '{') then
  3716. begin
  3717. Count := P - F;
  3718. Inc(P);
  3719. if (P[-1] <> #0) and (P[0] = '{') then
  3720. Inc(Count); // include '{'
  3721. if Count > 0 then
  3722. begin
  3723. Grow(Count);
  3724. MoveChar(F[0], Result[Len + 1], Count);
  3725. Inc(Len, Count);
  3726. end;
  3727. if P[-1] = #0 then
  3728. Break;
  3729. if P[0] <> '{' then
  3730. begin
  3731. F := P;
  3732. Inc(P);
  3733. while (P[0] <> #0) and (P[0] <> '}') do
  3734. Inc(P);
  3735. SetString(S, F, P - F);
  3736. Val(S, Index, ErrorCode);
  3737. if ErrorCode <> 0 then
  3738. raise FormatException.CreateRes(@RsFormatException);
  3739. if (Index < 0) or (Index > High(Args)) then
  3740. raise FormatException.CreateRes(@RsFormatException);
  3741. S := GetStringOf(Index);
  3742. if S <> '' then
  3743. begin
  3744. Grow(Length(S));
  3745. MoveChar(S[1], Result[Len + 1], Length(S));
  3746. Inc(Len, Length(S));
  3747. end;
  3748. if P[0] = #0 then
  3749. Break;
  3750. end;
  3751. F := P + 1;
  3752. end
  3753. else
  3754. if (P[0] = '}') and (P[1] = '}') then
  3755. begin
  3756. Count := P - F + 1;
  3757. Inc(P); // skip next '}'
  3758. Grow(Count);
  3759. MoveChar(F[0], Result[Len + 1], Count);
  3760. Inc(Len, Count);
  3761. F := P + 1;
  3762. end;
  3763. Inc(P);
  3764. end;
  3765. SetLength(Result, Len);
  3766. end;
  3767. //=== { TJclStringBuilder } =====================================================
  3768. constructor TJclStringBuilder.Create(Capacity: SizeInt; MaxCapacity: SizeInt);
  3769. begin
  3770. inherited Create;
  3771. SetLength(FChars, Capacity);
  3772. FMaxCapacity := MaxCapacity;
  3773. end;
  3774. constructor TJclStringBuilder.Create(const Value: string; Capacity: SizeInt);
  3775. begin
  3776. Create(Capacity);
  3777. Append(Value);
  3778. end;
  3779. constructor TJclStringBuilder.Create(const Value: string; StartIndex, Length, Capacity: SizeInt);
  3780. begin
  3781. Create(Capacity);
  3782. Append(Value, StartIndex + 1, Length);
  3783. end;
  3784. function TJclStringBuilder.ToString: string;
  3785. begin
  3786. if FLength > 0 then
  3787. SetString(Result, PChar(@FChars[0]), FLength)
  3788. else
  3789. Result := '';
  3790. end;
  3791. function TJclStringBuilder.EnsureCapacity(Capacity: SizeInt): SizeInt;
  3792. begin
  3793. if System.Length(FChars) < Capacity then
  3794. SetCapacity(Capacity);
  3795. Result := System.Length(FChars);
  3796. end;
  3797. procedure TJclStringBuilder.Clear;
  3798. begin
  3799. Length := 0;
  3800. end;
  3801. procedure TJclStringBuilder.SetCapacity(const Value: SizeInt);
  3802. begin
  3803. if Value <> System.Length(FChars) then
  3804. begin
  3805. SetLength(FChars, Value);
  3806. if Value < FLength then
  3807. FLength := Value;
  3808. end;
  3809. end;
  3810. function TJclStringBuilder.GetChars(Index: SizeInt): Char;
  3811. begin
  3812. Result := FChars[Index];
  3813. end;
  3814. procedure TJclStringBuilder.SetChars(Index: SizeInt; const Value: Char);
  3815. begin
  3816. FChars[Index] := Value;
  3817. end;
  3818. procedure TJclStringBuilder.Set_Length(const Value: SizeInt);
  3819. begin
  3820. FLength := Value;
  3821. end;
  3822. function TJclStringBuilder.GetCapacity: SizeInt;
  3823. begin
  3824. Result := System.Length(FChars);
  3825. end;
  3826. function TJclStringBuilder.AppendPChar(Value: PChar; Count: SizeInt; RepeatCount: SizeInt): TJclStringBuilder;
  3827. var
  3828. Capacity: SizeInt;
  3829. begin
  3830. if (Count > 0) and (RepeatCount > 0) then
  3831. begin
  3832. repeat
  3833. Capacity := System.Length(FChars);
  3834. if Capacity + Count > MaxCapacity then
  3835. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  3836. if Capacity < FLength + Count then
  3837. SetLength(FChars, Capacity * 5 div 3 + Count);
  3838. if Count = 1 then
  3839. FChars[FLength] := Value[0]
  3840. else
  3841. MoveChar(Value[0], FChars[FLength], Count);
  3842. Inc(FLength, Count);
  3843. Dec(RepeatCount);
  3844. until RepeatCount <= 0;
  3845. end;
  3846. Result := Self;
  3847. end;
  3848. function TJclStringBuilder.InsertPChar(Index: SizeInt; Value: PChar; Count,
  3849. RepeatCount: SizeInt): TJclStringBuilder;
  3850. var
  3851. Capacity: SizeInt;
  3852. begin
  3853. if (Index < 0) or (Index > FLength) then
  3854. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  3855. if Index = FLength then
  3856. AppendPChar(Value, Count, RepeatCount)
  3857. else
  3858. if (Count > 0) and (RepeatCount > 0) then
  3859. begin
  3860. repeat
  3861. Capacity := System.Length(FChars);
  3862. if Capacity + Count > MaxCapacity then
  3863. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  3864. if Capacity < FLength + Count then
  3865. SetLength(FChars, Capacity * 5 div 3 + Count);
  3866. MoveChar(FChars[Index], FChars[Index + Count], FLength - Index);
  3867. if Count = 1 then
  3868. FChars[Index] := Value[0]
  3869. else
  3870. MoveChar(Value[0], FChars[Index], Count);
  3871. Inc(FLength, Count);
  3872. Dec(RepeatCount);
  3873. Inc(Index, Count); // little optimization
  3874. until RepeatCount <= 0;
  3875. end;
  3876. Result := Self;
  3877. end;
  3878. function TJclStringBuilder.Append(const Value: array of Char): TJclStringBuilder;
  3879. var
  3880. Len: SizeInt;
  3881. begin
  3882. Len := System.Length(Value);
  3883. if Len > 0 then
  3884. AppendPChar(@Value[0], Len);
  3885. Result := Self;
  3886. end;
  3887. function TJclStringBuilder.Append(const Value: array of Char; StartIndex, Length: SizeInt): TJclStringBuilder;
  3888. var
  3889. Len: SizeInt;
  3890. begin
  3891. Len := System.Length(Value);
  3892. if (Length > 0) and (StartIndex < Len) then
  3893. begin
  3894. if StartIndex + Length > Len then
  3895. Length := Len - StartIndex;
  3896. AppendPChar(PChar(@Value[0]) + StartIndex, Length);
  3897. end;
  3898. Result := Self;
  3899. end;
  3900. function TJclStringBuilder.Append(Value: Char; RepeatCount: SizeInt = 1): TJclStringBuilder;
  3901. begin
  3902. Result := AppendPChar(@Value, 1, RepeatCount);
  3903. end;
  3904. function TJclStringBuilder.Append(const Value: string): TJclStringBuilder;
  3905. var
  3906. Len: SizeInt;
  3907. begin
  3908. Len := System.Length(Value);
  3909. if Len > 0 then
  3910. AppendPChar(Pointer(Value), Len);
  3911. Result := Self;
  3912. end;
  3913. function TJclStringBuilder.Append(const Value: string; StartIndex, Length: SizeInt): TJclStringBuilder;
  3914. var
  3915. Len: SizeInt;
  3916. begin
  3917. Len := System.Length(Value);
  3918. if (Length > 0) and (StartIndex < Len) then
  3919. begin
  3920. if StartIndex + Length > Len then
  3921. Length := Len - StartIndex;
  3922. AppendPChar(PChar(Pointer(Value)) + StartIndex, Length);
  3923. end;
  3924. Result := Self;
  3925. end;
  3926. function TJclStringBuilder.Append(Value: Boolean): TJclStringBuilder;
  3927. begin
  3928. Result := Append(BoolToStr[Value]);
  3929. end;
  3930. function TJclStringBuilder.Append(Value: Cardinal): TJclStringBuilder;
  3931. begin
  3932. Result := Append(IntToStr(Value));
  3933. end;
  3934. function TJclStringBuilder.Append(Value: Integer): TJclStringBuilder;
  3935. begin
  3936. Result := Append(IntToStr(Value));
  3937. end;
  3938. function TJclStringBuilder.Append(Value: Double): TJclStringBuilder;
  3939. begin
  3940. Result := Append(FloatToStr(Value));
  3941. end;
  3942. function TJclStringBuilder.Append(Value: Int64): TJclStringBuilder;
  3943. begin
  3944. Result := Append(IntToStr(Value));
  3945. end;
  3946. function TJclStringBuilder.Append(Obj: TObject): TJclStringBuilder;
  3947. begin
  3948. Result := Append(DotNetFormat('{0}', [Obj]));
  3949. end;
  3950. function TJclStringBuilder.AppendFormat(const Fmt: string; Arg0: Variant): TJclStringBuilder;
  3951. begin
  3952. Result := Append(DotNetFormat(Fmt, [Arg0]));
  3953. end;
  3954. function TJclStringBuilder.AppendFormat(const Fmt: string; Arg0, Arg1: Variant): TJclStringBuilder;
  3955. begin
  3956. Result := Append(DotNetFormat(Fmt, [Arg0, Arg1]));
  3957. end;
  3958. function TJclStringBuilder.AppendFormat(const Fmt: string; Arg0, Arg1, Arg2: Variant): TJclStringBuilder;
  3959. begin
  3960. Result := Append(DotNetFormat(Fmt, [Arg0, Arg1, Arg2]));
  3961. end;
  3962. function TJclStringBuilder.AppendFormat(const Fmt: string; const Args: array of const): TJclStringBuilder;
  3963. begin
  3964. Result := Append(DotNetFormat(Fmt, Args));
  3965. end;
  3966. function TJclStringBuilder.Insert(Index: SizeInt; const Value: array of Char): TJclStringBuilder;
  3967. var
  3968. Len: SizeInt;
  3969. begin
  3970. Len := System.Length(Value);
  3971. if Len > 0 then
  3972. InsertPChar(Index, @Value[0], Len);
  3973. Result := Self;
  3974. end;
  3975. function TJclStringBuilder.Insert(Index: SizeInt; const Value: string; Count: SizeInt): TJclStringBuilder;
  3976. var
  3977. Len: SizeInt;
  3978. begin
  3979. Len := System.Length(Value);
  3980. if Len > 0 then
  3981. InsertPChar(Index, Pointer(Value), Len, Count);
  3982. Result := Self;
  3983. end;
  3984. function TJclStringBuilder.Insert(Index: SizeInt; Value: Boolean): TJclStringBuilder;
  3985. begin
  3986. Result := Insert(Index, BoolToStr[Value]);
  3987. end;
  3988. function TJclStringBuilder.Insert(Index: SizeInt; const Value: array of Char;
  3989. StartIndex, Length: SizeInt): TJclStringBuilder;
  3990. var
  3991. Len: SizeInt;
  3992. begin
  3993. Len := System.Length(Value);
  3994. if (Length > 0) and (StartIndex < Len) then
  3995. begin
  3996. if StartIndex + Length > Len then
  3997. Length := Len - StartIndex;
  3998. InsertPChar(Index, PChar(@Value[0]) + StartIndex, Length);
  3999. end;
  4000. Result := Self;
  4001. end;
  4002. function TJclStringBuilder.Insert(Index: SizeInt; Value: Double): TJclStringBuilder;
  4003. begin
  4004. Result := Insert(Index, FloatToStr(Value));
  4005. end;
  4006. function TJclStringBuilder.Insert(Index: SizeInt; Value: Int64): TJclStringBuilder;
  4007. begin
  4008. Result := Insert(Index, IntToStr(Value));
  4009. end;
  4010. function TJclStringBuilder.Insert(Index: SizeInt; Value: Cardinal): TJclStringBuilder;
  4011. begin
  4012. Result := Insert(Index, IntToStr(Value));
  4013. end;
  4014. function TJclStringBuilder.Insert(Index: SizeInt; Value: Integer): TJclStringBuilder;
  4015. begin
  4016. Result := Insert(Index, IntToStr(Value));
  4017. end;
  4018. function TJclStringBuilder.Insert(Index: SizeInt; Obj: TObject): TJclStringBuilder;
  4019. begin
  4020. Result := Insert(Index, DotNetFormat('{0}', [Obj]));
  4021. end;
  4022. function TJclStringBuilder.Remove(StartIndex, Length: SizeInt): TJclStringBuilder;
  4023. begin
  4024. if (StartIndex < 0) or (Length < 0) or (StartIndex + Length >= FLength) then
  4025. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  4026. if Length > 0 then
  4027. begin
  4028. MoveChar(FChars[StartIndex + Length], FChars[StartIndex], FLength - (StartIndex + Length));
  4029. Dec(FLength, Length);
  4030. end;
  4031. Result := Self;
  4032. end;
  4033. function TJclStringBuilder.Replace(OldChar, NewChar: Char; StartIndex,
  4034. Count: SizeInt): TJclStringBuilder;
  4035. var
  4036. I: SizeInt;
  4037. begin
  4038. if Count = -1 then
  4039. Count := FLength;
  4040. if (StartIndex < 0) or (Count < 0) or (StartIndex + Count > FLength) then
  4041. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  4042. if (Count > 0) and (OldChar <> NewChar) then
  4043. begin
  4044. for I := StartIndex to StartIndex + Length - 1 do
  4045. if FChars[I] = OldChar then
  4046. FChars[I] := NewChar;
  4047. end;
  4048. Result := Self;
  4049. end;
  4050. function TJclStringBuilder.Replace(OldValue, NewValue: string; StartIndex, Count: SizeInt): TJclStringBuilder;
  4051. var
  4052. I: SizeInt;
  4053. Offset: SizeInt;
  4054. NewLen, OldLen, Capacity: SizeInt;
  4055. begin
  4056. if Count = -1 then
  4057. Count := FLength;
  4058. if (StartIndex < 0) or (Count < 0) or (StartIndex + Count > FLength) then
  4059. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  4060. if OldValue = '' then
  4061. raise ArgumentException.CreateResFmt(@RsArgumentIsNull, [0]);
  4062. if (Count > 0) and (OldValue <> NewValue) then
  4063. begin
  4064. OldLen := System.Length(OldValue);
  4065. NewLen := System.Length(NewValue);
  4066. Offset := NewLen - OldLen;
  4067. Capacity := System.Length(FChars);
  4068. for I := StartIndex to StartIndex + Length - 1 do
  4069. if FChars[I] = OldValue[1] then
  4070. begin
  4071. if OldLen > 1 then
  4072. if StrLComp(@FChars[I + 1], PChar(OldValue) + 1, OldLen - 1) <> 0 then
  4073. Continue;
  4074. if Offset <> 0 then
  4075. begin
  4076. if FLength - OldLen + NewLen > MaxCurrency then
  4077. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  4078. if Capacity < FLength + Offset then
  4079. begin
  4080. Capacity := Capacity * 5 div 3 + Offset;
  4081. SetLength(FChars, Capacity);
  4082. end;
  4083. if Offset < 0 then
  4084. MoveChar(FChars[I - Offset], FChars[I], FLength - I)
  4085. else
  4086. MoveChar(FChars[I + OldLen], FChars[I + OldLen + Offset], FLength - OldLen - I);
  4087. Inc(FLength, Offset);
  4088. end;
  4089. if NewLen > 0 then
  4090. begin
  4091. if (OldLen = 1) and (NewLen = 1) then
  4092. FChars[I] := NewValue[1]
  4093. else
  4094. MoveChar(NewValue[1], FChars[I], NewLen);
  4095. end;
  4096. end;
  4097. end;
  4098. Result := Self;
  4099. end;
  4100. function StrExpandTabs(S: string): string;
  4101. begin
  4102. // use an empty tab set, which will default to a tab width of 2
  4103. Result := TJclTabSet(nil).Expand(s);
  4104. end;
  4105. function StrExpandTabs(S: string; TabWidth: SizeInt): string;
  4106. var
  4107. TabSet: TJclTabSet;
  4108. begin
  4109. // create a tab set with no tab stops and the given tab width
  4110. TabSet := TJclTabSet.Create(TabWidth);
  4111. try
  4112. Result := TabSet.Expand(S);
  4113. finally
  4114. TabSet.Free;
  4115. end;
  4116. end;
  4117. function StrExpandTabs(S: string; TabSet: TJclTabSet): string;
  4118. begin
  4119. // use the provided tab set to perform the expansion
  4120. Result := TabSet.Expand(S);
  4121. end;
  4122. function StrOptimizeTabs(S: string): string;
  4123. begin
  4124. // use an empty tab set, which will default to a tab width of 2
  4125. Result := TJclTabSet(nil).Optimize(s);
  4126. end;
  4127. function StrOptimizeTabs(S: string; TabWidth: SizeInt): string;
  4128. var
  4129. TabSet: TJclTabSet;
  4130. begin
  4131. // create a tab set with no tab stops and the given tab width
  4132. TabSet := TJclTabSet.Create(TabWidth);
  4133. try
  4134. Result := TabSet.Optimize(S);
  4135. finally
  4136. TabSet.Free;
  4137. end;
  4138. end;
  4139. function StrOptimizeTabs(S: string; TabSet: TJclTabSet): string;
  4140. begin
  4141. // use the provided tab set to perform the optimization
  4142. Result := TabSet.Optimize(S);
  4143. end;
  4144. // === { TTabSetData } ===================================================
  4145. type
  4146. TTabSetData = class
  4147. public
  4148. FStops: TDynSizeIntArray;
  4149. FRealWidth: SizeInt;
  4150. FRefCount: SizeInt;
  4151. FWidth: SizeInt;
  4152. FZeroBased: Boolean;
  4153. constructor Create(TabStops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt);
  4154. function Add(Column: SizeInt): SizeInt;
  4155. function AddRef: SizeInt;
  4156. procedure CalcRealWidth;
  4157. function FindStop(Column: SizeInt): SizeInt;
  4158. function ReleaseRef: SizeInt;
  4159. procedure RemoveAt(Index: SizeInt);
  4160. procedure SetStops(Index, Value: SizeInt);
  4161. end;
  4162. constructor TTabSetData.Create(TabStops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt);
  4163. var
  4164. idx: SizeInt;
  4165. begin
  4166. inherited Create;
  4167. FRefCount := 1;
  4168. for idx := 0 to High(Tabstops) do
  4169. Add(Tabstops[idx]);
  4170. FWidth := TabWidth;
  4171. FZeroBased := ZeroBased;
  4172. CalcRealWidth;
  4173. end;
  4174. function TTabSetData.Add(Column: SizeInt): SizeInt;
  4175. var
  4176. I: SizeInt;
  4177. begin
  4178. if Column < Ord(FZeroBased) then
  4179. raise ArgumentOutOfRangeException.Create('Column');
  4180. Result := FindStop(Column);
  4181. if Result < 0 then
  4182. begin
  4183. // the column doesn't exist; invert the result of FindStop to get the correct index position
  4184. Result := not Result;
  4185. // increase the tab stop array
  4186. SetLength(FStops, Length(FStops) + 1);
  4187. // shift rooms after the insert position
  4188. for I := High(FStops) - 1 downto Result do
  4189. FStops[I + 1] := FStops[I];
  4190. // add the tab stop at the correct location
  4191. FStops[Result] := Column;
  4192. CalcRealWidth;
  4193. end
  4194. else
  4195. begin
  4196. raise EJclStringError.CreateRes(@RsTabs_DuplicatesNotAllowed);
  4197. end;
  4198. end;
  4199. function TTabSetData.AddRef: SizeInt;
  4200. begin
  4201. Result := LockedInc(FRefCount);
  4202. end;
  4203. procedure TTabSetData.CalcRealWidth;
  4204. begin
  4205. if FWidth < 1 then
  4206. begin
  4207. if Length(FStops) > 1 then
  4208. FRealWidth := FStops[High(FStops)] - FStops[Pred(High(FStops))]
  4209. else
  4210. if Length(FStops) = 1 then
  4211. FRealWidth := FStops[0]
  4212. else
  4213. FRealWidth := 2;
  4214. end
  4215. else
  4216. FRealWidth := FWidth;
  4217. end;
  4218. function TTabSetData.FindStop(Column: SizeInt): SizeInt;
  4219. begin
  4220. Result := High(FStops);
  4221. while (Result >= 0) and (FStops[Result] > Column) do
  4222. Dec(Result);
  4223. if (Result >= 0) and (FStops[Result] <> Column) then
  4224. Result := not Succ(Result);
  4225. end;
  4226. function TTabSetData.ReleaseRef: SizeInt;
  4227. begin
  4228. Result := LockedDec(FRefCount);
  4229. if Result <= 0 then
  4230. Destroy;
  4231. end;
  4232. procedure TTabSetData.RemoveAt(Index: SizeInt);
  4233. var
  4234. I: SizeInt;
  4235. begin
  4236. for I := Index to High(FStops) - 1 do
  4237. FStops[I] := FStops[I + 1];
  4238. SetLength(FStops, High(FStops));
  4239. CalcRealWidth;
  4240. end;
  4241. procedure TTabSetData.SetStops(Index, Value: SizeInt);
  4242. var
  4243. temp: SizeInt;
  4244. begin
  4245. if (Index < 0) or (Index >= Length(FStops)) then
  4246. begin
  4247. raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
  4248. end
  4249. else
  4250. begin
  4251. temp := FindStop(Value);
  4252. if temp < 0 then
  4253. begin
  4254. // remove existing tab stop...
  4255. RemoveAt(Index);
  4256. // now add the new tab stop
  4257. Add(Value);
  4258. end
  4259. else
  4260. if temp <> Index then
  4261. begin
  4262. // new tab stop already present at another index
  4263. raise EJclStringError.CreateRes(@RsTabs_DuplicatesNotAllowed);
  4264. end;
  4265. end;
  4266. end;
  4267. //=== { TJclTabSet } =====================================================
  4268. constructor TJclTabSet.Create;
  4269. begin
  4270. // no tab stops, tab width set to auto
  4271. Create([], True, 0);
  4272. end;
  4273. constructor TJclTabSet.Create(TabWidth: SizeInt);
  4274. begin
  4275. // no tab stops, specified tab width
  4276. Create([], True, TabWidth);
  4277. end;
  4278. constructor TJclTabSet.Create(const Tabstops: array of SizeInt; ZeroBased: Boolean);
  4279. begin
  4280. // specified tab stops, tab width equal to distance between last two tab stops
  4281. Create(Tabstops, ZeroBased, 0);
  4282. end;
  4283. constructor TJclTabSet.Create(const Tabstops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt);
  4284. begin
  4285. inherited Create;
  4286. FData := TTabSetData.Create(Tabstops, ZeroBased, TabWidth);
  4287. end;
  4288. constructor TJclTabSet.Create(Data: TObject);
  4289. begin
  4290. inherited Create;
  4291. // add a reference to the data
  4292. TTabSetData(Data).AddRef;
  4293. // assign the data to this instance
  4294. FData := TTabSetData(Data);
  4295. end;
  4296. destructor TJclTabSet.Destroy;
  4297. begin
  4298. // release the reference to the tab set data
  4299. TTabSetData(FData).ReleaseRef;
  4300. // make sure we won't accidentally refer to it later, just in case something goes wrong during destruction
  4301. FData := nil;
  4302. // really destroy the instance
  4303. inherited Destroy;
  4304. end;
  4305. function TJclTabSet.Add(Column: SizeInt): SizeInt;
  4306. begin
  4307. if Self = nil then
  4308. raise NullReferenceException.Create;
  4309. Result := TTabSetData(FData).Add(Column);
  4310. end;
  4311. function TJclTabSet.Clone: TJclTabSet;
  4312. begin
  4313. if Self <> nil then
  4314. Result := TJclTabSet.Create(TTabSetData(FData).FStops, TTabSetData(FData).FZeroBased, TTabSetData(FData).FWidth)
  4315. else
  4316. Result := nil;
  4317. end;
  4318. function TJclTabSet.Delete(Column: SizeInt): SizeInt;
  4319. begin
  4320. Result := TTabSetData(FData).FindStop(Column);
  4321. if Result >= 0 then
  4322. TTabSetData(FData).RemoveAt(Result);
  4323. end;
  4324. function TJclTabSet.Expand(const S: string): string;
  4325. begin
  4326. Result := Expand(s, StartColumn);
  4327. end;
  4328. function TJclTabSet.Expand(const S: string; Column: SizeInt): string;
  4329. var
  4330. sb: TJclStringBuilder;
  4331. head: PChar;
  4332. cur: PChar;
  4333. begin
  4334. if Column < StartColumn then
  4335. raise ArgumentOutOfRangeException.Create('Column');
  4336. sb := TJclStringBuilder.Create(Length(S));
  4337. try
  4338. cur := PChar(S);
  4339. while cur^ <> #0 do
  4340. begin
  4341. head := cur;
  4342. while (cur^ <> #0) and (cur^ <> #9) do
  4343. begin
  4344. if CharIsReturn(cur^) then
  4345. Column := StartColumn
  4346. else
  4347. Inc(Column);
  4348. Inc(cur);
  4349. end;
  4350. if cur > head then
  4351. sb.Append(head, 0, cur - head);
  4352. if cur^ = #9 then
  4353. begin
  4354. sb.Append(' ', TabFrom(Column) - Column);
  4355. Column := TabFrom(Column);
  4356. Inc(cur);
  4357. end;
  4358. end;
  4359. Result := sb.ToString;
  4360. finally
  4361. sb.Free;
  4362. end;
  4363. end;
  4364. function TJclTabSet.FindStop(Column: SizeInt): SizeInt;
  4365. begin
  4366. if Self <> nil then
  4367. Result := TTabSetData(FData).FindStop(Column)
  4368. else
  4369. Result := -1;
  4370. end;
  4371. class function TJclTabSet.FromString(const S: string): TJclTabSet;
  4372. var
  4373. cur: PChar;
  4374. function ParseNumber: Integer;
  4375. var
  4376. head: PChar;
  4377. begin
  4378. StrSkipChars(cur, CharIsWhiteSpace);
  4379. head := cur;
  4380. while CharIsDigit(cur^) do
  4381. Inc(cur);
  4382. Result := -1;
  4383. if (cur <= head) or not TryStrToInt(Copy(head, 1, cur - head), Result) then
  4384. Result := -1;
  4385. end;
  4386. procedure ParseStops;
  4387. var
  4388. openBracket, hadComma: Boolean;
  4389. num: SizeInt;
  4390. begin
  4391. StrSkipChars(cur, CharIsWhiteSpace);
  4392. openBracket := cur^ = '[';
  4393. hadComma := False;
  4394. if openBracket then
  4395. Inc(cur);
  4396. repeat
  4397. num := ParseNumber;
  4398. if (num < 0) and hadComma then
  4399. raise EJclStringError.CreateRes(@RsTabs_StopExpected)
  4400. else
  4401. if num >= 0 then
  4402. Result.Add(num);
  4403. StrSkipChars(cur, CharIsWhiteSpace);
  4404. hadComma := cur^ = ',';
  4405. if hadComma then
  4406. Inc(cur);
  4407. until (cur^ = #0) or (cur^ = '+') or (cur^ = ']');
  4408. if hadComma then
  4409. raise EJclStringError.CreateRes(@RsTabs_StopExpected)
  4410. else
  4411. if openBracket and (cur^ <> ']') then
  4412. raise EJclStringError.CreateRes(@RsTabs_CloseBracketExpected);
  4413. end;
  4414. procedure ParseTabWidth;
  4415. var
  4416. num: SizeInt;
  4417. begin
  4418. StrSkipChars(cur, CharIsWhiteSpace);
  4419. if cur^ = '+' then
  4420. begin
  4421. Inc(cur);
  4422. StrSkipChars(cur, CharIsWhiteSpace);
  4423. num := ParseNumber;
  4424. if (num < 0) then
  4425. raise EJclStringError.CreateRes(@RsTabs_TabWidthExpected)
  4426. else
  4427. Result.TabWidth := num;
  4428. end;
  4429. end;
  4430. procedure ParseZeroBasedFlag;
  4431. begin
  4432. StrSkipChars(cur, CharIsWhiteSpace);
  4433. if cur^ = '0' then
  4434. begin
  4435. Inc(cur);
  4436. if CharIsWhiteSpace(cur^) or (cur^ = #0) or (cur^ = '[') then
  4437. begin
  4438. Result.ZeroBased := True;
  4439. StrSkipChars(cur, CharIsWhiteSpace);
  4440. end
  4441. else
  4442. Dec(cur);
  4443. end;
  4444. end;
  4445. begin
  4446. Result := TJclTabSet.Create;
  4447. try
  4448. Result.ZeroBased := False;
  4449. cur := PChar(S);
  4450. ParseZeroBasedFlag;
  4451. ParseStops;
  4452. ParseTabWidth;
  4453. except
  4454. // clean up the partially complete instance (to avoid memory leaks)...
  4455. Result.Free;
  4456. // ... and re-raise the exception
  4457. raise;
  4458. end;
  4459. end;
  4460. function TJclTabSet.GetCount: SizeInt;
  4461. begin
  4462. if Self <> nil then
  4463. Result := Length(TTabSetData(FData).FStops)
  4464. else
  4465. Result := 0;
  4466. end;
  4467. function TJclTabSet.GetStops(Index: SizeInt): SizeInt;
  4468. begin
  4469. if Self <> nil then
  4470. begin
  4471. if (Index < 0) or (Index >= Length(TTabSetData(FData).FStops)) then
  4472. begin
  4473. raise EJclStringError.CreateRes(@RsArgumentOutOfRange);
  4474. end
  4475. else
  4476. Result := TTabSetData(FData).FStops[Index];
  4477. end
  4478. else
  4479. begin
  4480. raise EJclStringError.CreateRes(@RsArgumentOutOfRange);
  4481. end;
  4482. end;
  4483. function TJclTabSet.GetTabWidth: SizeInt;
  4484. begin
  4485. if Self <> nil then
  4486. Result := TTabSetData(FData).FWidth
  4487. else
  4488. Result := 0;
  4489. end;
  4490. function TJclTabSet.GetZeroBased: Boolean;
  4491. begin
  4492. Result := (Self = nil) or TTabSetData(FData).FZeroBased;
  4493. end;
  4494. procedure TJclTabSet.OptimalFillInfo(StartColumn, TargetColumn: SizeInt; out TabsNeeded, SpacesNeeded: SizeInt);
  4495. var
  4496. nextTab: SizeInt;
  4497. begin
  4498. if StartColumn < Self.StartColumn then // starting column less than 1 or 0 (depending on ZeroBased state)
  4499. raise ArgumentOutOfRangeException.Create('StartColumn');
  4500. if (TargetColumn < StartColumn) then // target lies before the starting column
  4501. raise ArgumentOutOfRangeException.Create('TargetColumn');
  4502. TabsNeeded := 0;
  4503. repeat
  4504. nextTab := TabFrom(StartColumn);
  4505. if nextTab <= TargetColumn then
  4506. begin
  4507. Inc(TabsNeeded);
  4508. StartColumn := nextTab;
  4509. end;
  4510. until nextTab > TargetColumn;
  4511. SpacesNeeded := TargetColumn - StartColumn;
  4512. end;
  4513. function TJclTabSet.Optimize(const S: string): string;
  4514. begin
  4515. Result := Optimize(S, StartColumn);
  4516. end;
  4517. function TJclTabSet.Optimize(const S: string; Column: SizeInt): string;
  4518. var
  4519. sb: TJclStringBuilder;
  4520. head: PChar;
  4521. cur: PChar;
  4522. tgt: SizeInt;
  4523. procedure AppendOptimalWhiteSpace(Target: SizeInt);
  4524. var
  4525. tabCount: SizeInt;
  4526. spaceCount: SizeInt;
  4527. begin
  4528. if cur > head then
  4529. begin
  4530. OptimalFillInfo(Column, Target, tabCount, spaceCount);
  4531. if tabCount > 0 then
  4532. sb.Append(#9, tabCount);
  4533. if spaceCount > 0 then
  4534. sb.Append(' ', spaceCount);
  4535. end;
  4536. end;
  4537. begin
  4538. if Column < StartColumn then
  4539. raise ArgumentOutOfRangeException.Create('Column');
  4540. sb := TJclStringBuilder.Create(Length(S));
  4541. try
  4542. cur := PChar(s);
  4543. while cur^ <> #0 do
  4544. begin
  4545. // locate first whitespace character
  4546. head := cur;
  4547. while (cur^ <> #0) and not CharIsWhiteSpace(cur^) do
  4548. Inc(cur);
  4549. // output non whitespace characters
  4550. if cur > head then
  4551. sb.Append(head, 0, cur - head);
  4552. // advance column
  4553. Inc(Column, cur - head);
  4554. // initialize target column indexer
  4555. tgt := Column;
  4556. // locate end of whitespace sequence
  4557. while CharIsWhiteSpace(cur^) do
  4558. begin
  4559. if CharIsReturn(cur^) then
  4560. begin
  4561. // append optimized whitespace sequence...
  4562. AppendOptimalWhiteSpace(tgt);
  4563. // ...set the column back to the start of the line...
  4564. Column := StartColumn;
  4565. // ...reset target column indexer...
  4566. tgt := Column;
  4567. // ...add the line break character...
  4568. sb.Append(cur^);
  4569. end
  4570. else
  4571. if cur^ = #9 then
  4572. tgt := TabFrom(tgt) // expand the tab
  4573. else
  4574. Inc(tgt); // a normal whitespace; taking up 1 column
  4575. Inc(cur);
  4576. end;
  4577. AppendOptimalWhiteSpace(tgt); // append optimized whitespace sequence...
  4578. Column := tgt; // ...and memorize the column for the next iteration
  4579. end;
  4580. Result := sb.ToString; // convert result to a string
  4581. finally
  4582. sb.Free;
  4583. end;
  4584. end;
  4585. procedure TJclTabSet.RemoveAt(Index: SizeInt);
  4586. begin
  4587. if Self <> nil then
  4588. TTabSetData(FData).RemoveAt(Index)
  4589. else
  4590. raise NullReferenceException.Create;
  4591. end;
  4592. procedure TJclTabSet.SetStops(Index, Value: SizeInt);
  4593. begin
  4594. if Self <> nil then
  4595. TTabSetData(FData).SetStops(Index, Value)
  4596. else
  4597. raise NullReferenceException.Create;
  4598. end;
  4599. procedure TJclTabSet.SetTabWidth(Value: SizeInt);
  4600. begin
  4601. if Self <> nil then
  4602. begin
  4603. TTabSetData(FData).FWidth := Value;
  4604. TTabSetData(FData).CalcRealWidth;
  4605. end
  4606. else
  4607. raise NullReferenceException.Create;
  4608. end;
  4609. procedure TJclTabSet.SetZeroBased(Value: Boolean);
  4610. var
  4611. shift: SizeInt;
  4612. idx: SizeInt;
  4613. begin
  4614. if Self <> nil then
  4615. begin
  4616. if Value <> TTabSetData(FData).FZeroBased then
  4617. begin
  4618. TTabSetData(FData).FZeroBased := Value;
  4619. if Value then
  4620. shift := -1
  4621. else
  4622. shift := 1;
  4623. for idx := 0 to High(TTabSetData(FData).FStops) do
  4624. TTabSetData(FData).FStops[idx] := TTabSetData(FData).FStops[idx] + shift;
  4625. end;
  4626. end
  4627. else
  4628. raise NullReferenceException.Create;
  4629. end;
  4630. function TJclTabSet.InternalTabStops: TDynSizeIntArray;
  4631. begin
  4632. if Self <> nil then
  4633. Result := TTabSetData(FData).FStops
  4634. else
  4635. Result := nil;
  4636. end;
  4637. function TJclTabSet.InternalTabWidth: SizeInt;
  4638. begin
  4639. if Self <> nil then
  4640. Result := TTabSetData(FData).FRealWidth
  4641. else
  4642. Result := 2;
  4643. end;
  4644. function TJclTabSet.NewReference: TJclTabSet;
  4645. begin
  4646. if Self <> nil then
  4647. Result := TJclTabSet.Create(FData)
  4648. else
  4649. Result := nil;
  4650. end;
  4651. function TJclTabSet.StartColumn: SizeInt;
  4652. begin
  4653. if GetZeroBased then
  4654. Result := 0
  4655. else
  4656. Result := 1;
  4657. end;
  4658. function TJclTabSet.TabFrom(Column: SizeInt): SizeInt;
  4659. begin
  4660. if Column < StartColumn then
  4661. raise ArgumentOutOfRangeException.Create('Column');
  4662. Result := FindStop(Column);
  4663. if Result < 0 then
  4664. Result := not Result
  4665. else
  4666. Inc(Result);
  4667. if Result >= GetCount then
  4668. begin
  4669. if GetCount > 0 then
  4670. Result := TTabSetData(FData).FStops[High(TTabSetData(FData).FStops)]
  4671. else
  4672. Result := StartColumn;
  4673. while Result <= Column do
  4674. Inc(Result, ActualTabWidth);
  4675. end
  4676. else
  4677. Result := TTabSetData(FData).FStops[Result];
  4678. end;
  4679. function TJclTabSet.ToString: string;
  4680. begin
  4681. Result := ToString(TabSetFormatting_Full);
  4682. end;
  4683. function TJclTabSet.ToString(FormattingOptions: SizeInt): string;
  4684. var
  4685. sb: TJclStringBuilder;
  4686. idx: SizeInt;
  4687. function WantBrackets: Boolean;
  4688. begin
  4689. Result := (TabSetFormatting_SurroundStopsWithBrackets and FormattingOptions) <> 0;
  4690. end;
  4691. function EmptyBrackets: Boolean;
  4692. begin
  4693. Result := (TabSetFormatting_EmptyBracketsIfNoStops and FormattingOptions) <> 0;
  4694. end;
  4695. function IncludeAutoWidth: Boolean;
  4696. begin
  4697. Result := (TabSetFormatting_AutoTabWidth and FormattingOptions) <> 0;
  4698. end;
  4699. function IncludeTabWidth: Boolean;
  4700. begin
  4701. Result := (TabSetFormatting_NoTabWidth and FormattingOptions) = 0;
  4702. end;
  4703. function IncludeStops: Boolean;
  4704. begin
  4705. Result := (TabSetFormatting_NoTabStops and FormattingOptions) = 0;
  4706. end;
  4707. begin
  4708. sb := TJclStringBuilder.Create;
  4709. try
  4710. // output the fixed tabulation positions if requested...
  4711. if IncludeStops then
  4712. begin
  4713. // output each individual tabulation position
  4714. for idx := 0 to GetCount - 1 do
  4715. begin
  4716. sb.Append(TabStops[idx]);
  4717. sb.Append(',');
  4718. end;
  4719. // remove the final comma if any tabulation positions where outputted
  4720. if sb.Length <> 0 then
  4721. sb.Remove(sb.Length - 1, 1);
  4722. // bracket the tabulation positions if requested
  4723. if WantBrackets and (EmptyBrackets or (sb.Length > 0)) then
  4724. begin
  4725. sb.Insert(0, '[');
  4726. sb.Append(']');
  4727. end;
  4728. end;
  4729. // output the tab width if requested....
  4730. if IncludeTabWidth and (IncludeAutoWidth or (TabWidth > 0)) then
  4731. begin
  4732. // separate the tab width from any outputted tabulation positions with a whitespace
  4733. if sb.Length > 0 then
  4734. sb.Append(' ');
  4735. // flag tab width
  4736. sb.Append('+');
  4737. // finally, output the tab width
  4738. sb.Append(ActualTabWidth);
  4739. end;
  4740. // flag zero-based tabset by outputting a 0 (zero) as the first character.
  4741. if ZeroBased then
  4742. sb.Insert(0, string('0 '));
  4743. Result := StrTrimCharRight(sb.ToString, ' ');
  4744. finally
  4745. sb.Free;
  4746. end;
  4747. end;
  4748. function TJclTabSet.UpdatePosition(const S: string): SizeInt;
  4749. var
  4750. Line: SizeInt;
  4751. begin
  4752. Result := StartColumn;
  4753. Line := -1;
  4754. UpdatePosition(S, Result, Line);
  4755. end;
  4756. function TJclTabSet.UpdatePosition(const S: string; Column: SizeInt): SizeInt;
  4757. var
  4758. Line: SizeInt;
  4759. begin
  4760. if Column < StartColumn then
  4761. raise ArgumentOutOfRangeException.Create('Column');
  4762. Result := Column;
  4763. Line := -1;
  4764. UpdatePosition(S, Result, Line);
  4765. end;
  4766. function TJclTabSet.UpdatePosition(const S: string; var Column, Line: SizeInt): SizeInt;
  4767. var
  4768. prevChar: Char;
  4769. cur: PChar;
  4770. begin
  4771. if Column < StartColumn then
  4772. raise ArgumentOutOfRangeException.Create('Column');
  4773. // initialize loop
  4774. cur := PChar(S);
  4775. // iterate until end of string (the Null-character)
  4776. while cur^ <> #0 do
  4777. begin
  4778. // check for line-breaking characters
  4779. if CharIsReturn(cur^) then
  4780. begin
  4781. // Column moves back all the way to the left
  4782. Column := StartColumn;
  4783. // If this is the first line-break character or the same line-break character, increment the Line parameter
  4784. Inc(Line);
  4785. // check if it's the first of a two-character line-break
  4786. prevChar := cur^;
  4787. Inc(cur);
  4788. // if it isn't a two-character line-break, undo the previous advancement
  4789. if (cur^ = prevChar) or not CharIsReturn(cur^) then
  4790. Dec(cur);
  4791. end
  4792. else // check for tab character and expand it
  4793. if cur^ = #9 then
  4794. Column := TabFrom(Column)
  4795. else // a normal character; increment column
  4796. Inc(Column);
  4797. // advance pointer
  4798. Inc(cur);
  4799. end;
  4800. // set the result to the newly calculated column
  4801. Result := Column;
  4802. end;
  4803. //=== { NullReferenceException } =============================================
  4804. constructor NullReferenceException.Create;
  4805. begin
  4806. CreateRes(@RsArg_NullReferenceException);
  4807. end;
  4808. function CompareNatural(const S1, S2: string; CaseInsensitive: Boolean): SizeInt;
  4809. var
  4810. Cur1, Len1,
  4811. Cur2, Len2: SizeInt;
  4812. function IsRealNumberChar(ch: Char): Boolean;
  4813. begin
  4814. Result := ((ch >= '0') and (ch <= '9')) or (ch = '-') or (ch = '+');
  4815. end;
  4816. procedure NumberCompare;
  4817. var
  4818. IsReallyNumber: Boolean;
  4819. FirstDiffBreaks: Boolean;
  4820. Val1, Val2: SizeInt;
  4821. begin
  4822. Result := 0;
  4823. IsReallyNumber := False;
  4824. // count leading spaces in S1
  4825. while (Cur1 <= Len1) and CharIsWhiteSpace(S1[Cur1]) do
  4826. begin
  4827. Dec(Result);
  4828. Inc(Cur1);
  4829. end;
  4830. // count leading spaces in S2 (canceling them out against the ones in S1)
  4831. while (Cur2 <= Len2) and CharIsWhiteSpace(S2[Cur2]) do
  4832. begin
  4833. Inc(Result);
  4834. Inc(Cur2);
  4835. end;
  4836. // if spaces match, or both strings are actually followed by a numeric character, continue the checks
  4837. if (Result = 0) or ((Cur1 <= Len1) and CharIsNumberChar(S1[Cur1]) and (Cur2 <= Len2) and CharIsNumberChar(S2[Cur2])) then
  4838. begin
  4839. // Check signed number
  4840. if (Cur1 <= Len1) and (S1[Cur1] = '-') and ((Cur2 > Len2) or (S2[Cur2] <> '-')) then
  4841. Result := 1
  4842. else
  4843. if (Cur2 <= Len2) and (S2[Cur2] = '-') and ((Cur1 > Len1) or (S1[Cur1] <> '-')) then
  4844. Result := -1
  4845. else
  4846. Result := 0;
  4847. if (Cur1 <= Len1) and ((S1[Cur1] = '-') or (S1[Cur1] = '+')) then
  4848. Inc(Cur1);
  4849. if (Cur2 <= Len2) and ((S2[Cur2] = '-') or (S2[Cur2] = '+')) then
  4850. Inc(Cur2);
  4851. FirstDiffBreaks := (Cur1 <= Len1) and (S1[Cur1] = '0') or (Cur2 <= Len2) and (S2[Cur2] = '0');
  4852. while (Cur1 <= Len1) and CharIsDigit(S1[Cur1]) and (Cur2 <= Len2) and CharIsDigit(S2[Cur2]) do
  4853. begin
  4854. IsReallyNumber := True;
  4855. Val1 := StrToInt(S1[Cur1]);
  4856. Val2 := StrToInt(S2[Cur2]);
  4857. if (Result = 0) and (Val1 < Val2) then
  4858. Result := -1
  4859. else
  4860. if (Result = 0) and (Val1 > Val2) then
  4861. Result := 1;
  4862. if FirstDiffBreaks and (Result <> 0) then
  4863. Break;
  4864. Inc(Cur1);
  4865. Inc(Cur2);
  4866. end;
  4867. if IsReallyNumber then
  4868. begin
  4869. if not FirstDiffBreaks then
  4870. begin
  4871. if (Cur1 <= Len1) and CharIsDigit(S1[Cur1]) then
  4872. Result := 1
  4873. else
  4874. if (Cur2 <= Len2) and CharIsDigit(S2[Cur2]) then
  4875. Result := -1;
  4876. end;
  4877. end;
  4878. end;
  4879. end;
  4880. procedure SetByCompareLength;
  4881. var
  4882. Remain1: SizeInt;
  4883. Remain2: SizeInt;
  4884. begin
  4885. // base result on relative compare length (spaces could be ignored, so even if S1 is longer than S2, they could be
  4886. // completely equal, or S2 could be longer)
  4887. Remain1 := Len1 - Cur1 + 1;
  4888. Remain2 := Len2 - Cur2 + 1;
  4889. if Remain1 < 0 then
  4890. Remain1 := 0;
  4891. if Remain2 < 0 then
  4892. Remain2 := 0;
  4893. if Remain1 < Remain2 then
  4894. Result := -1
  4895. else
  4896. if Remain1 > Remain2 then
  4897. Result := 1;
  4898. end;
  4899. begin
  4900. Cur1 := 1;
  4901. Len1 := Length(S1);
  4902. Cur2 := 1;
  4903. Len2 := Length(S2);
  4904. Result := 0;
  4905. while (Result = 0) do
  4906. begin
  4907. if (Cur1 > Len1) or (Cur2 > Len2) then
  4908. begin
  4909. SetByCompareLength;
  4910. Break;
  4911. end
  4912. else
  4913. if (Cur1 <= Len1) and (Cur2 > Len2) then
  4914. Result := 1
  4915. else
  4916. if (S1[Cur1] = '-') and IsRealNumberChar(S2[Cur2]) and (S2[Cur2] <> '-') then
  4917. Result := -1
  4918. else
  4919. if (S2[Cur2] = '-') and IsRealNumberChar(S1[Cur1]) and (S1[Cur1] <> '-') then
  4920. Result := 1
  4921. else
  4922. if (IsRealNumberChar(S1[Cur1]) or CharIsWhiteSpace(S1[Cur1])) and (IsRealNumberChar(S2[Cur2]) or CharIsWhiteSpace(S2[Cur2])) then
  4923. NumberCompare
  4924. else
  4925. begin
  4926. if CaseInsensitive then
  4927. Result := StrLIComp(PChar(@S1[Cur1]), PChar(@S2[Cur2]), 1)
  4928. else
  4929. Result := StrLComp(PChar(@S1[Cur1]), PChar(@S2[Cur2]), 1);
  4930. Inc(Cur1);
  4931. Inc(Cur2);
  4932. end;
  4933. end;
  4934. end;
  4935. function CompareNaturalStr(const S1, S2: string): SizeInt; overload;
  4936. begin
  4937. Result := CompareNatural(S1, S2, False);
  4938. end;
  4939. function CompareNaturalText(const S1, S2: string): SizeInt; overload;
  4940. begin
  4941. Result := CompareNatural(S1, S2, True);
  4942. end;
  4943. initialization
  4944. {$IFNDEF UNICODE_RTL_DATABASE}
  4945. LoadCharTypes; // this table first
  4946. LoadCaseMap; // or this function does not work
  4947. {$ENDIF ~UNICODE_RTL_DATABASE}
  4948. {$IFDEF UNITVERSIONING}
  4949. RegisterUnitVersion(HInstance, UnitVersioning);
  4950. {$ENDIF UNITVERSIONING}
  4951. {$IFDEF UNITVERSIONING}
  4952. finalization
  4953. UnregisterUnitVersion(HInstance);
  4954. {$ENDIF UNITVERSIONING}
  4955. end.