JclStrings.pas 150 KB

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