12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444 |
- {**************************************************************************************************}
- { }
- { Project JEDI Code Library (JCL) }
- { }
- { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
- { you may not use this file except in compliance with the License. You may obtain a copy of the }
- { License at http://www.mozilla.org/MPL/ }
- { }
- { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
- { ANY KIND, either express or implied. See the License for the specific language governing rights }
- { and limitations under the License. }
- { }
- { The Original Code is JclStrings.pas. }
- { }
- { The Initial Developer of the Original Code is Marcel van Brakel. }
- { Portions created by Marcel van Brakel are Copyright (C) Marcel van Brakel. All rights reserved. }
- { }
- { Contributor(s): }
- { Alexander Radchenko }
- { Andreas Hausladen (ahuser) }
- { Anthony Steele }
- { Azret Botash }
- { Barry Kelly }
- { Huanlin Tsai }
- { Jack N.A. Bakker }
- { Jean-Fabien Connault (cycocrew) }
- { John C Molyneux }
- { Kiriakos Vlahos }
- { Leonard Wennekers }
- { Marcel Bestebroer }
- { Martin Kimmings }
- { Martin Kubecka }
- { Massimo Maria Ghisalberti }
- { Matthias Thoma (mthoma) }
- { Michael Winter }
- { Nick Hodges }
- { Olivier Sannier (obones) }
- { Pelle F. S. Liljendal }
- { Petr Vones (pvones) }
- { Rik Barker (rikbarker) }
- { Robert Lee }
- { Robert Marquardt (marquardt) }
- { Robert Rossmair (rrossmair) }
- { Andreas Schmidt }
- { Sean Farrow (sfarrow) }
- { }
- {**************************************************************************************************}
- { }
- { Various character and string routines (searching, testing and transforming) }
- { }
- {**************************************************************************************************}
- { }
- { Last modified: $Date:: $ }
- { Revision: $Rev:: $ }
- { Author: $Author:: $ }
- { }
- {**************************************************************************************************}
- unit JclStrings;
- {$I jcl.inc}
- interface
- uses
- {$IFDEF UNITVERSIONING}
- JclUnitVersioning,
- {$ENDIF UNITVERSIONING}
- {$IFDEF HAS_UNITSCOPE}
- {$IFDEF MSWINDOWS}
- Winapi.Windows,
- {$ENDIF MSWINDOWS}
- {$IFDEF UNICODE_RTL_DATABASE}
- System.Character,
- {$ENDIF UNICODE_RTL_DATABASE}
- System.Classes, System.SysUtils,
- {$ELSE ~HAS_UNITSCOPE}
- {$IFDEF MSWINDOWS}
- Windows,
- {$ENDIF MSWINDOWS}
- {$IFDEF UNICODE_RTL_DATABASE}
- Character,
- {$ENDIF UNICODE_RTL_DATABASE}
- Classes, SysUtils,
- {$ENDIF ~HAS_UNITSCOPE}
- JclAnsiStrings,
- {$IFNDEF WINSCP}
- JclWideStrings,
- {$ENDIF ~WINSCP}
- JclBase;
- // Exceptions
- type
- EJclStringError = class(EJclError);
- // Character constants and sets
- const
- // Misc. often used character definitions
- NativeNull = Char(#0);
- NativeSoh = Char(#1);
- NativeStx = Char(#2);
- NativeEtx = Char(#3);
- NativeEot = Char(#4);
- NativeEnq = Char(#5);
- NativeAck = Char(#6);
- NativeBell = Char(#7);
- NativeBackspace = Char(#8);
- NativeTab = Char(#9);
- NativeLineFeed = JclBase.NativeLineFeed;
- NativeVerticalTab = Char(#11);
- NativeFormFeed = Char(#12);
- NativeCarriageReturn = JclBase.NativeCarriageReturn;
- NativeCrLf = JclBase.NativeCrLf;
- NativeSo = Char(#14);
- NativeSi = Char(#15);
- NativeDle = Char(#16);
- NativeDc1 = Char(#17);
- NativeDc2 = Char(#18);
- NativeDc3 = Char(#19);
- NativeDc4 = Char(#20);
- NativeNak = Char(#21);
- NativeSyn = Char(#22);
- NativeEtb = Char(#23);
- NativeCan = Char(#24);
- NativeEm = Char(#25);
- NativeEndOfFile = Char(#26);
- NativeEscape = Char(#27);
- NativeFs = Char(#28);
- NativeGs = Char(#29);
- NativeRs = Char(#30);
- NativeUs = Char(#31);
- NativeSpace = Char(' ');
- NativeComma = Char(',');
- NativeBackslash = Char('\');
- NativeForwardSlash = Char('/');
- NativeDoubleQuote = Char('"');
- NativeSingleQuote = Char('''');
- NativeLineBreak = JclBase.NativeLineBreak;
- const
- // CharType return values
- C1_UPPER = $0001; // Uppercase
- C1_LOWER = $0002; // Lowercase
- C1_DIGIT = $0004; // Decimal digits
- C1_SPACE = $0008; // Space characters
- C1_PUNCT = $0010; // Punctuation
- C1_CNTRL = $0020; // Control characters
- C1_BLANK = $0040; // Blank characters
- C1_XDIGIT = $0080; // Hexadecimal digits
- C1_ALPHA = $0100; // Any linguistic character: alphabetic, syllabary, or ideographic
- {$IFDEF MSWINDOWS}
- {$IFDEF SUPPORTS_EXTSYM}
- {$EXTERNALSYM C1_UPPER}
- {$EXTERNALSYM C1_LOWER}
- {$EXTERNALSYM C1_DIGIT}
- {$EXTERNALSYM C1_SPACE}
- {$EXTERNALSYM C1_PUNCT}
- {$EXTERNALSYM C1_CNTRL}
- {$EXTERNALSYM C1_BLANK}
- {$EXTERNALSYM C1_XDIGIT}
- {$EXTERNALSYM C1_ALPHA}
- {$ENDIF SUPPORTS_EXTSYM}
- {$ENDIF MSWINDOWS}
- type
- TCharValidator = function(const C: Char): Boolean;
- function ArrayContainsChar(const Chars: array of Char; const C: Char): Boolean; overload;
- function ArrayContainsChar(const Chars: array of Char; const C: Char; out Index: SizeInt): Boolean; overload;
- // String Test Routines
- function StrIsAlpha(const S: string): Boolean;
- function StrIsAlphaNum(const S: string): Boolean;
- function StrIsAlphaNumUnderscore(const S: string): Boolean;
- function StrContainsChars(const S: string; const Chars: TCharValidator; CheckAll: Boolean): Boolean; overload;
- function StrContainsChars(const S: string; const Chars: array of Char; CheckAll: Boolean): Boolean; overload;
- {$IFNDEF WINSCP}
- function StrConsistsOfNumberChars(const S: string): Boolean;
- {$ENDIF ~WINSCP}
- function StrIsDigit(const S: string): Boolean;
- function StrIsSubset(const S: string; const ValidChars: TCharValidator): Boolean; overload;
- function StrIsSubset(const S: string; const ValidChars: array of Char): Boolean; overload;
- function StrSame(const S1, S2: string; CaseSensitive: Boolean = False): Boolean;
- // String Transformation Routines
- function StrCenter(const S: string; L: SizeInt; C: Char = ' '): string;
- function StrCharPosLower(const S: string; CharPos: SizeInt): string;
- function StrCharPosUpper(const S: string; CharPos: SizeInt): string;
- function StrDoubleQuote(const S: string): string;
- function StrEnsureNoPrefix(const Prefix, Text: string): string;
- function StrEnsureNoSuffix(const Suffix, Text: string): string;
- function StrEnsurePrefix(const Prefix, Text: string): string;
- function StrEnsureSuffix(const Suffix, Text: string): string;
- function StrEscapedToString(const S: string): string;
- function StrLower(const S: string): string;
- procedure StrLowerInPlace(var S: string);
- procedure StrLowerBuff(S: PChar);
- procedure StrMove(var Dest: string; const Source: string; const ToIndex,
- FromIndex, Count: SizeInt);
- function StrPadLeft(const S: string; Len: SizeInt; C: Char = NativeSpace): string;
- function StrPadRight(const S: string; Len: SizeInt; C: Char = NativeSpace): string;
- function StrProper(const S: string): string;
- procedure StrProperBuff(S: PChar);
- function StrQuote(const S: string; C: Char): string;
- function StrRemoveChars(const S: string; const Chars: TCharValidator): string; overload;
- function StrRemoveChars(const S: string; const Chars: array of Char): string; overload;
- function StrRemoveLeadingChars(const S: string; const Chars: TCharValidator): string; overload;
- function StrRemoveLeadingChars(const S: string; const Chars: array of Char): string; overload;
- function StrRemoveEndChars(const S: string; const Chars: TCharValidator): string; overload;
- function StrRemoveEndChars(const S: string; const Chars: array of Char): string; overload;
- function StrKeepChars(const S: string; const Chars: TCharValidator): string; overload;
- function StrKeepChars(const S: string; const Chars: array of Char): string; overload;
- procedure StrReplace(var S: string; const Search, Replace: string; Flags: TReplaceFlags = []);
- function StrReplaceChar(const S: string; const Source, Replace: Char): string;
- function StrReplaceChars(const S: string; const Chars: TCharValidator; Replace: Char): string; overload;
- function StrReplaceChars(const S: string; const Chars: array of Char; Replace: Char): string; overload;
- function StrReplaceButChars(const S: string; const Chars: TCharValidator; Replace: Char): string; overload;
- function StrReplaceButChars(const S: string; const Chars: array of Char; Replace: Char): string; overload;
- function StrRepeat(const S: string; Count: SizeInt): string;
- function StrRepeatLength(const S: string; L: SizeInt): string;
- function StrReverse(const S: string): string;
- procedure StrReverseInPlace(var S: string);
- function StrSingleQuote(const S: string): string;
- procedure StrSkipChars(var S: PChar; const Chars: TCharValidator); overload;
- procedure StrSkipChars(var S: PChar; const Chars: array of Char); overload;
- procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: TCharValidator); overload;
- procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: array of Char); overload;
- function StrSmartCase(const S: string; const Delimiters: TCharValidator): string; overload;
- function StrSmartCase(const S: string; const Delimiters: array of Char): string; overload;
- function StrStringToEscaped(const S: string): string;
- {$IFNDEF WINSCP}
- function StrStripNonNumberChars(const S: string): string;
- {$ENDIF ~WINSCP}
- function StrToHex(const Source: string): string;
- function StrTrimCharLeft(const S: string; C: Char): string;
- function StrTrimCharsLeft(const S: string; const Chars: TCharValidator): string; overload;
- function StrTrimCharsLeft(const S: string; const Chars: array of Char): string; overload;
- function StrTrimCharRight(const S: string; C: Char): string;
- function StrTrimCharsRight(const S: string; const Chars: TCharValidator): string; overload;
- function StrTrimCharsRight(const S: string; const Chars: array of Char): string; overload;
- function StrTrimQuotes(const S: string): string;
- function StrUpper(const S: string): string;
- procedure StrUpperInPlace(var S: string);
- procedure StrUpperBuff(S: PChar);
- // String Management
- procedure StrAddRef(var S: string);
- procedure StrDecRef(var S: string);
- function StrLength(const S: string): SizeInt;
- function StrRefCount(const S: string): SizeInt;
- // String Search and Replace Routines
- function StrCharCount(const S: string; C: Char): SizeInt; overload;
- function StrCharsCount(const S: string; const Chars: TCharValidator): SizeInt; overload;
- function StrCharsCount(const S: string; const Chars: array of Char): SizeInt; overload;
- function StrStrCount(const S, SubS: string): SizeInt;
- function StrCompare(const S1, S2: string; CaseSensitive: Boolean = False): SizeInt;
- function StrCompareRange(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean = True): SizeInt;
- function StrCompareRangeEx(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
- procedure StrFillChar(var S; Count: SizeInt; C: Char);
- function StrRepeatChar(C: Char; Count: SizeInt): string;
- function StrFind(const Substr, S: string; const Index: SizeInt = 1): SizeInt;
- function StrHasPrefix(const S: string; const Prefixes: array of string): Boolean;
- function StrHasSuffix(const S: string; const Suffixes: array of string): Boolean;
- function StrIndex(const S: string; const List: array of string; CaseSensitive: Boolean = False): SizeInt;
- function StrIHasPrefix(const S: string; const Prefixes: array of string): Boolean;
- function StrIHasSuffix(const S: string; const Suffixes: array of string): Boolean;
- function StrILastPos(const SubStr, S: string): SizeInt;
- function StrIPos(const SubStr, S: string): SizeInt;
- function StrIPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
- function StrIsOneOf(const S: string; const List: array of string): Boolean;
- function StrISuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
- function StrLastPos(const SubStr, S: string): SizeInt;
- function StrMatch(const Substr, S: string; Index: SizeInt = 1): SizeInt;
- function StrMatches(const Substr, S: string; const Index: SizeInt = 1): Boolean;
- function StrNIPos(const S, SubStr: string; N: SizeInt): SizeInt;
- function StrNPos(const S, SubStr: string; N: SizeInt): SizeInt;
- function StrPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
- function StrSearch(const Substr, S: string; const Index: SizeInt = 1): SizeInt;
- function StrSuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
- // String Extraction
- /// Returns the string after SubStr
- function StrAfter(const SubStr, S: string): string;
- /// Returns the String before SubStr
- function StrBefore(const SubStr, S: string): string;
- /// Splits a string at SubStr, returns true when SubStr is found, Left contains the
- /// string before the SubStr and Right the string behind SubStr
- function StrSplit(const SubStr, S: string;var Left, Right : string): boolean;
- /// Returns the string between Start and Stop
- function StrBetween(const S: string; const Start, Stop: Char): string;
- /// Returns all but rightmost N characters of the string
- function StrChopRight(const S: string; N: SizeInt): string;{$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
- /// Returns the left Count characters of the string
- function StrLeft(const S: string; Count: SizeInt): string; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
- /// Returns the string starting from position Start for the Count Characters
- function StrMid(const S: string; Start, Count: SizeInt): string; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
- /// Returns the string starting from position N to the end
- function StrRestOf(const S: string; N: SizeInt): string;{$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
- /// Returns the right Count characters of the string
- function StrRight(const S: string; Count: SizeInt): string;{$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
- // Character Test Routines
- function CharEqualNoCase(const C1, C2: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsAlpha(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsAlphaNum(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsBlank(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsControl(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsDelete(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsDigit(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsFracDigit(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsHexDigit(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsLower(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- {$IFNDEF WINSCP}
- function CharIsNumberChar(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
- function CharIsNumber(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} {$IFDEF COMPILER16_UP} inline; {$ENDIF} {$ENDIF}
- {$ENDIF ~WINSCP}
- function CharIsPrintable(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsPunctuation(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsReturn(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsSpace(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsUpper(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsValidIdentifierLetter(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsWhiteSpace(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharIsWildcard(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharType(const C: Char): Word;
- // Character Transformation Routines
- function CharHex(const C: Char): Byte;
- function CharLower(const C: Char): Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharUpper(const C: Char): Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function CharToggleCase(const C: Char): Char;
- // Character Search and Replace
- function CharPos(const S: string; const C: Char; const Index: SizeInt = 1): SizeInt;
- function CharLastPos(const S: string; const C: Char; const Index: SizeInt = 1): SizeInt;
- function CharIPos(const S: string; C: Char; const Index: SizeInt = 1): SizeInt;
- function CharReplace(var S: string; const Search, Replace: Char): SizeInt;
- // PCharVector
- type
- PCharVector = ^PChar;
- function StringsToPCharVector(var Dest: PCharVector; const Source: TStrings): PCharVector;
- function PCharVectorCount(Source: PCharVector): SizeInt;
- procedure PCharVectorToStrings(const Dest: TStrings; Source: PCharVector);
- procedure FreePCharVector(var Dest: PCharVector);
- {$IFNDEF WINSCP}
- // MultiSz Routines
- type
- PMultiSz = PChar;
- PAnsiMultiSz = JclAnsiStrings.PAnsiMultiSz;
- PWideMultiSz = JclWideStrings.PWideMultiSz;
- TAnsiStrings = JclAnsiStrings.TJclAnsiStrings;
- TWideStrings = JclWideStrings.TJclWideStrings;
- TAnsiStringList = JclAnsiStrings.TJclAnsiStringList;
- TWideStringList = JclWideStrings.TJclWideStringList;
- function StringsToMultiSz(var Dest: PMultiSz; const Source: TStrings): PMultiSz;
- procedure MultiSzToStrings(const Dest: TStrings; const Source: PMultiSz);
- function MultiSzLength(const Source: PMultiSz): SizeInt;
- procedure AllocateMultiSz(var Dest: PMultiSz; Len: SizeInt);
- procedure FreeMultiSz(var Dest: PMultiSz);
- function MultiSzDup(const Source: PMultiSz): PMultiSz;
- function AnsiStringsToAnsiMultiSz(var Dest: PAnsiMultiSz; const Source: TAnsiStrings): PAnsiMultiSz;
- {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- procedure AnsiMultiSzToAnsiStrings(const Dest: TAnsiStrings; const Source: PAnsiMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function AnsiMultiSzLength(const Source: PAnsiMultiSz): SizeInt; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- procedure AllocateAnsiMultiSz(var Dest: PAnsiMultiSz; Len: SizeInt); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- procedure FreeAnsiMultiSz(var Dest: PAnsiMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function AnsiMultiSzDup(const Source: PAnsiMultiSz): PAnsiMultiSz; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function WideStringsToWideMultiSz(var Dest: PWideMultiSz; const Source: TWideStrings): PWideMultiSz;
- {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- procedure WideMultiSzToWideStrings(const Dest: TWideStrings; const Source: PWideMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function WideMultiSzLength(const Source: PWideMultiSz): SizeInt; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- procedure AllocateWideMultiSz(var Dest: PWideMultiSz; Len: SizeInt); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- procedure FreeWideMultiSz(var Dest: PWideMultiSz); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- function WideMultiSzDup(const Source: PWideMultiSz): PWideMultiSz; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- {$ENDIF ~WINSCP}
- // TStrings Manipulation
- procedure StrIToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
- procedure StrToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
- function StringsToStr(const List: TStrings; const Sep: string; const AllowEmptyString: Boolean = True): string; overload;
- function StringsToStr(const List: TStrings; const Sep: string; const NumberOfItems: SizeInt; const AllowEmptyString:
- Boolean = True): string; overload;
- procedure TrimStrings(const List: TStrings; DeleteIfEmpty: Boolean = True);
- procedure TrimStringsRight(const List: TStrings; DeleteIfEmpty: Boolean = True);
- procedure TrimStringsLeft(const List: TStrings; DeleteIfEmpty: Boolean = True);
- function AddStringToStrings(const S: string; Strings: TStrings; const Unique: Boolean): Boolean;
- // Miscellaneous
- // (OF) moved to JclSysUtils
- // function BooleanToStr(B: Boolean): string;
- // AnsiString here because it is binary data
- function FileToString(const FileName: string): {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
- procedure StringToFile(const FileName: string; const Contents: {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
- Append: Boolean = False);
- function StrToken(var S: string; Separator: Char): string;
- procedure StrTokens(const S: string; const List: TStrings);
- procedure StrTokenToStrings(S: string; Separator: Char; const List: TStrings);
- function StrWord(const S: string; var Index: SizeInt; out Word: string): Boolean; overload;
- function StrWord(var S: PChar; out Word: string): Boolean; overload;
- function StrIdent(const S: string; var Index: SizeInt; out Ident: string): Boolean; overload;
- function StrIdent(var S: PChar; out Ident: string): Boolean; overload;
- {$IFNDEF WINSCP}
- function StrToFloatSafe(const S: string): Float;
- function StrToIntSafe(const S: string): Integer;
- {$ENDIF ~WINSCP}
- procedure StrNormIndex(const StrLen: SizeInt; var Index: SizeInt; var Count: SizeInt); overload;
- function ArrayOf(List: TStrings): TDynStringArray; overload;
- type
- FormatException = class(EJclError);
- ArgumentException = class(EJclError);
- ArgumentNullException = class(EJclError);
- ArgumentOutOfRangeException = class(EJclError);
- IToString = interface
- ['{C4ABABB4-1029-46E7-B5FA-99800F130C05}']
- function ToString: string;
- end;
- TCharDynArray = array of Char;
- // The TStringBuilder class is a Delphi implementation of the .NET
- // System.Text.StringBuilder.
- // It is zero based and the methods that have a TObject argument (Append, Insert,
- // AppendFormat) are limited to IToString implementors or Delphi 2009+ RTL.
- // This class is not threadsafe. Any instance of TStringBuilder should not
- // be used in different threads at the same time.
- TJclStringBuilder = class(TInterfacedObject, IToString)
- private
- FChars: TCharDynArray;
- FLength: SizeInt;
- FMaxCapacity: SizeInt;
- function GetCapacity: SizeInt;
- procedure SetCapacity(const Value: SizeInt);
- function GetChars(Index: SizeInt): Char;
- procedure SetChars(Index: SizeInt; const Value: Char);
- procedure Set_Length(const Value: SizeInt);
- protected
- function AppendPChar(Value: PChar; Count: SizeInt; RepeatCount: SizeInt = 1): TJclStringBuilder;
- function InsertPChar(Index: SizeInt; Value: PChar; Count: SizeInt; RepeatCount: SizeInt = 1): TJclStringBuilder;
- public
- constructor Create(const Value: string; Capacity: SizeInt = 16); overload;
- constructor Create(Capacity: SizeInt = 16; MaxCapacity: SizeInt = MaxInt); overload;
- constructor Create(const Value: string; StartIndex, Length, Capacity: SizeInt); overload;
- function Append(const Value: string): TJclStringBuilder; overload;
- function Append(const Value: string; StartIndex, Length: SizeInt): TJclStringBuilder; overload;
- function Append(Value: Boolean): TJclStringBuilder; overload;
- function Append(Value: Char; RepeatCount: SizeInt = 1): TJclStringBuilder; overload;
- function Append(const Value: array of Char): TJclStringBuilder; overload;
- function Append(const Value: array of Char; StartIndex, Length: SizeInt): TJclStringBuilder; overload;
- function Append(Value: Cardinal): TJclStringBuilder; overload;
- function Append(Value: Integer): TJclStringBuilder; overload;
- function Append(Value: Double): TJclStringBuilder; overload;
- function Append(Value: Int64): TJclStringBuilder; overload;
- function Append(Obj: TObject): TJclStringBuilder; overload;
- function AppendFormat(const Fmt: string; const Args: array of const): TJclStringBuilder; overload;
- function AppendFormat(const Fmt: string; Arg0: Variant): TJclStringBuilder; overload;
- function AppendFormat(const Fmt: string; Arg0, Arg1: Variant): TJclStringBuilder; overload;
- function AppendFormat(const Fmt: string; Arg0, Arg1, Arg2: Variant): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; const Value: string; Count: SizeInt = 1): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; Value: Boolean): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; const Value: array of Char): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; const Value: array of Char; StartIndex, Length: SizeInt): TJclStringBuilder;
- overload;
- function Insert(Index: SizeInt; Value: Cardinal): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; Value: Integer): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; Value: Double): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; Value: Int64): TJclStringBuilder; overload;
- function Insert(Index: SizeInt; Obj: TObject): TJclStringBuilder; overload;
- function Replace(OldChar, NewChar: Char; StartIndex: SizeInt = 0; Count: SizeInt = -1): TJclStringBuilder;
- overload;
- function Replace(OldValue, NewValue: string; StartIndex: SizeInt = 0; Count: SizeInt = -1): TJclStringBuilder;
- overload;
- function Remove(StartIndex, Length: SizeInt): TJclStringBuilder;
- function EnsureCapacity(Capacity: SizeInt): SizeInt;
- procedure Clear;
- { IToString }
- function ToString: string; {$IFDEF RTL200_UP} override; {$ENDIF RTL200_UP}
- property __Chars__[Index: SizeInt]: Char read GetChars write SetChars; default;
- property Chars: TCharDynArray read FChars;
- property Length: SizeInt read FLength write Set_Length;
- property Capacity: SizeInt read GetCapacity write SetCapacity;
- property MaxCapacity: SizeInt read FMaxCapacity;
- end;
- {$IFDEF RTL200_UP}
- TStringBuilder = {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.TStringBuilder;
- {$ELSE ~RTL200_UP}
- TStringBuilder = TJclStringBuilder;
- {$ENDIF ~RTL200_UP}
- // DotNetFormat() uses the .NET format style: "{argX}"
- function DotNetFormat(const Fmt: string; const Args: array of const): string; overload;
- function DotNetFormat(const Fmt: string; const Arg0: Variant): string; overload;
- function DotNetFormat(const Fmt: string; const Arg0, Arg1: Variant): string; overload;
- function DotNetFormat(const Fmt: string; const Arg0, Arg1, Arg2: Variant): string; overload;
- // TJclTabSet
- type
- TJclTabSet = class (TInterfacedObject, IToString)
- private
- FData: TObject;
- function GetCount: SizeInt;
- function GetStops(Index: SizeInt): SizeInt;
- function GetTabWidth: SizeInt;
- function GetZeroBased: Boolean;
- procedure SetStops(Index, Value: SizeInt);
- procedure SetTabWidth(Value: SizeInt);
- procedure SetZeroBased(Value: Boolean);
- protected
- function FindStop(Column: SizeInt): SizeInt;
- function InternalTabStops: TDynSizeIntArray;
- function InternalTabWidth: SizeInt;
- procedure RemoveAt(Index: SizeInt);
- public
- constructor Create; overload;
- constructor Create(Data: TObject); overload;
- constructor Create(TabWidth: SizeInt); overload;
- constructor Create(const Tabstops: array of SizeInt; ZeroBased: Boolean); overload;
- constructor Create(const Tabstops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt); overload;
- destructor Destroy; override;
- // cloning and referencing
- function Clone: TJclTabSet;
- function NewReference: TJclTabSet;
- // Tab stops manipulation
- function Add(Column: SizeInt): SizeInt;
- function Delete(Column: SizeInt): SizeInt;
- // Usage
- function Expand(const S: string): string; overload;
- function Expand(const S: string; Column: SizeInt): string; overload;
- procedure OptimalFillInfo(StartColumn, TargetColumn: SizeInt; out TabsNeeded, SpacesNeeded: SizeInt);
- function Optimize(const S: string): string; overload;
- function Optimize(const S: string; Column: SizeInt): string; overload;
- function StartColumn: SizeInt;
- function TabFrom(Column: SizeInt): SizeInt;
- function UpdatePosition(const S: string): SizeInt; overload;
- function UpdatePosition(const S: string; Column: SizeInt): SizeInt; overload;
- function UpdatePosition(const S: string; var Column, Line: SizeInt): SizeInt; overload;
- { IToString }
- function ToString: string; overload; {$IFDEF RTL200_UP} override; {$ENDIF RTL200_UP}
- // Conversions
- function ToString(FormattingOptions: SizeInt): string; {$IFDEF RTL200_UP} reintroduce; {$ENDIF RTL200_UP} overload;
- class function FromString(const S: string): TJclTabSet; {$IFDEF SUPPORTS_STATIC} static; {$ENDIF SUPPORTS_STATIC}
- // Properties
- property ActualTabWidth: SizeInt read InternalTabWidth;
- property Count: SizeInt read GetCount;
- property TabStops[Index: SizeInt]: SizeInt read GetStops write SetStops; default;
- property TabWidth: SizeInt read GetTabWidth write SetTabWidth;
- property ZeroBased: Boolean read GetZeroBased write SetZeroBased;
- end;
- // Formatting constants
- const
- TabSetFormatting_SurroundStopsWithBrackets = 1;
- TabSetFormatting_EmptyBracketsIfNoStops = 2;
- TabSetFormatting_NoTabStops = 4;
- TabSetFormatting_NoTabWidth = 8;
- TabSetFormatting_AutoTabWidth = 16;
- // common combinations
- TabSetFormatting_Default = 0;
- TabSetFormatting_AlwaysUseBrackets = TabSetFormatting_SurroundStopsWithBrackets or
- TabSetFormatting_EmptyBracketsIfNoStops;
- TabSetFormatting_Full = TabSetFormatting_AlwaysUseBrackets or TabSetFormatting_AutoTabWidth;
- // aliases
- TabSetFormatting_StopsOnly = TabSetFormatting_NoTabWidth;
- TabSetFormatting_TabWidthOnly = TabSetFormatting_NoTabStops;
- TabSetFormatting_StopsWithoutBracketsAndTabWidth = TabSetFormatting_Default;
- // Tab expansion routines
- function StrExpandTabs(S: string): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
- function StrExpandTabs(S: string; TabWidth: SizeInt): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
- function StrExpandTabs(S: string; TabSet: TJclTabSet): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
- // Tab optimization routines
- function StrOptimizeTabs(S: string): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
- function StrOptimizeTabs(S: string; TabWidth: SizeInt): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
- function StrOptimizeTabs(S: string; TabSet: TJclTabSet): string; {$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} overload;
- // move to JclBase?
- type
- NullReferenceException = class(EJclError)
- public
- constructor Create; overload;
- end;
- procedure StrResetLength(var S: WideString); overload;
- procedure StrResetLength(var S: AnsiString); overload;
- procedure StrResetLength(S: TJclStringBuilder); overload;
- {$IFDEF SUPPORTS_UNICODE_STRING}
- procedure StrResetLength(var S: UnicodeString); overload;
- {$ENDIF SUPPORTS_UNICODE_STRING}
- // natural comparison functions
- {$IFNDEF WINSCP}
- function CompareNaturalStr(const S1, S2: string): SizeInt;
- function CompareNaturalText(const S1, S2: string): SizeInt;
- {$ENDIF ~WINSCP}
- {$IFNDEF UNICODE_RTL_DATABASE}
- // internal structures published to make function inlining working
- const
- MaxStrCharCount = Ord(High(Char)) + 1; // # of chars in one set
- StrLoOffset = MaxStrCharCount * 0; // offset to lower case chars
- StrUpOffset = MaxStrCharCount * 1; // offset to upper case chars
- StrReOffset = MaxStrCharCount * 2; // offset to reverse case chars
- StrCaseMapSize = MaxStrCharCount * 3; // # of chars is a table
- var
- StrCaseMap: array [0..StrCaseMapSize - 1] of Char; // case mappings
- StrCaseMapReady: Boolean = False; // true if case map exists
- StrCharTypes: array [Char] of Word;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- {$IFDEF UNITVERSIONING}
- const
- UnitVersioning: TUnitVersionInfo = (
- RCSfile: '$URL$';
- Revision: '$Revision$';
- Date: '$Date$';
- LogPath: 'JCL\source\common';
- Extra: '';
- Data: nil
- );
- {$ENDIF UNITVERSIONING}
- implementation
- uses
- {$IFDEF HAS_UNIT_LIBC}
- Libc,
- {$ENDIF HAS_UNIT_LIBC}
- {$IFDEF SUPPORTS_UNICODE}
- {$IFDEF HAS_UNITSCOPE}
- System.StrUtils,
- {$ELSE ~HAS_UNITSCOPE}
- StrUtils,
- {$ENDIF ~HAS_UNITSCOPE}
- {$ENDIF SUPPORTS_UNICODE}
- {$IFNDEF WINSCP}JclLogic,{$ELSE}Math,{$ENDIF ~WINSCP} JclResources, JclStreams, JclSynch{$IFNDEF WINSCP}, JclSysUtils{$ENDIF ~WINSCP};
- //=== Internal ===============================================================
- type
- TStrRec = packed record
- RefCount: Integer;
- Length: Integer;
- end;
- PStrRec = ^TStrRec;
- {$IFNDEF UNICODE_RTL_DATABASE}
- procedure LoadCharTypes;
- var
- CurrChar: Char;
- CurrType: Word;
- begin
- for CurrChar := Low(CurrChar) to High(CurrChar) do
- begin
- {$IFDEF MSWINDOWS}
- CurrType := 0;
- GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, @CurrChar, 1, CurrType);
- {$DEFINE CHAR_TYPES_INITIALIZED}
- {$ENDIF MSWINDOWS}
- {$IFDEF LINUX}
- CurrType := 0;
- if isupper(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_UPPER;
- if islower(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_LOWER;
- if isdigit(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_DIGIT;
- if isspace(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_SPACE;
- if ispunct(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_PUNCT;
- if iscntrl(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_CNTRL;
- if isblank(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_BLANK;
- if isxdigit(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_XDIGIT;
- if isalpha(Byte(CurrChar)) <> 0 then
- CurrType := CurrType or C1_ALPHA;
- {$DEFINE CHAR_TYPES_INITIALIZED}
- {$ENDIF LINUX}
- StrCharTypes[CurrChar] := CurrType;
- {$IFNDEF CHAR_TYPES_INITIALIZED}
- Implement case map initialization here
- {$ENDIF ~CHAR_TYPES_INITIALIZED}
- end;
- end;
- procedure LoadCaseMap;
- var
- CurrChar, UpCaseChar, LoCaseChar, ReCaseChar: Char;
- begin
- if not StrCaseMapReady then
- begin
- for CurrChar := Low(Char) to High(Char) do
- begin
- {$IFDEF MSWINDOWS}
- LoCaseChar := CurrChar;
- UpCaseChar := CurrChar;
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CharLowerBuff(@LoCaseChar, 1);
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CharUpperBuff(@UpCaseChar, 1);
- {$DEFINE CASE_MAP_INITIALIZED}
- {$ENDIF MSWINDOWS}
- {$IFDEF LINUX}
- LoCaseChar := Char(tolower(Byte(CurrChar)));
- UpCaseChar := Char(toupper(Byte(CurrChar)));
- {$DEFINE CASE_MAP_INITIALIZED}
- {$ENDIF LINUX}
- {$IFNDEF CASE_MAP_INITIALIZED}
- Implement case map initialization here
- {$ENDIF ~CASE_MAP_INITIALIZED}
- if CharIsUpper(CurrChar) then
- ReCaseChar := LoCaseChar
- else
- if CharIsLower(CurrChar) then
- ReCaseChar := UpCaseChar
- else
- ReCaseChar := CurrChar;
- StrCaseMap[Ord(CurrChar) + StrLoOffset] := LoCaseChar;
- StrCaseMap[Ord(CurrChar) + StrUpOffset] := UpCaseChar;
- StrCaseMap[Ord(CurrChar) + StrReOffset] := ReCaseChar;
- end;
- StrCaseMapReady := True;
- end;
- end;
- // Uppercases or Lowercases a give string depending on the
- // passed offset. (UpOffset or LoOffset)
- procedure StrCase(var Str: string; const Offset: SizeInt);
- var
- P: PChar;
- I, L: SizeInt;
- begin
- L := Length(Str);
- if L > 0 then
- begin
- UniqueString(Str);
- P := PChar(Str);
- for I := 1 to L do
- begin
- P^ := StrCaseMap[Offset + Ord(P^)];
- Inc(P);
- end;
- end;
- end;
- // Internal utility function
- // Uppercases or Lowercases a give null terminated string depending on the
- // passed offset. (UpOffset or LoOffset)
- procedure StrCaseBuff(S: PChar; const Offset: SizeInt);
- var
- C: Char;
- begin
- if S <> nil then
- begin
- repeat
- C := S^;
- S^ := StrCaseMap[Offset + Ord(C)];
- Inc(S);
- until C = #0;
- end;
- end;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- function StrEndW(Str: PWideChar): PWideChar;
- begin
- Result := Str;
- while Result^ <> #0 do
- Inc(Result);
- end;
- function ArrayContainsChar(const Chars: array of Char; const C: Char): Boolean;
- var
- idx: SizeInt;
- begin
- Result := ArrayContainsChar(Chars, C, idx);
- end;
- function ArrayContainsChar(const Chars: array of Char; const C: Char; out Index: SizeInt): Boolean;
- { optimized version for sorted arrays
- var
- I, L, H: SizeInt;
- begin
- L := Low(Chars);
- H := High(Chars);
- while L <= H do
- begin
- I := (L + H) div 2;
- if C = Chars[I] then
- begin
- Result := True;
- Exit;
- end
- else
- if C < Chars[I] then
- H := I - 1
- else
- // C > Chars[I]
- L := I + 1;
- end;
- Result := False;
- end;}
- begin
- Index := High(Chars);
- while (Index >= Low(Chars)) and (Chars[Index] <> C) do
- Dec(Index);
- Result := Index >= Low(Chars);
- end;
- // String Test Routines
- function StrIsAlpha(const S: string): Boolean;
- var
- I: SizeInt;
- begin
- Result := S <> '';
- for I := 1 to Length(S) do
- begin
- if not CharIsAlpha(S[I]) then
- begin
- Result := False;
- Exit;
- end;
- end;
- end;
- function StrIsAlphaNum(const S: string): Boolean;
- var
- I: SizeInt;
- begin
- Result := S <> '';
- for I := 1 to Length(S) do
- begin
- if not CharIsAlphaNum(S[I]) then
- begin
- Result := False;
- Exit;
- end;
- end;
- end;
- {$IFNDEF WINSCP}
- function StrConsistsofNumberChars(const S: string): Boolean;
- var
- I: SizeInt;
- begin
- Result := S <> '';
- for I := 1 to Length(S) do
- begin
- if not CharIsNumberChar(S[I]) then
- begin
- Result := False;
- Exit;
- end;
- end;
- end;
- {$ENDIF ~WINSCP}
- function StrContainsChars(const S: string; const Chars: TCharValidator; CheckAll: Boolean): Boolean;
- var
- I: SizeInt;
- begin
- Result := False;
- if CheckAll then
- begin
- // this will not work with the current definition of the validator. The validator would need to check each character
- // it requires against the string (which is currently not provided to the Validator). The current implementation of
- // CheckAll will check if all characters in S will be accepted by the provided Validator, which is wrong and incon-
- // sistent with the documentation and the array-based overload.
- for I := 1 to Length(S) do
- begin
- Result := Chars(S[I]);
- if not Result then
- Break;
- end;
- end
- else
- begin
- for I := 1 to Length(S) do
- begin
- Result := Chars(S[I]);
- if Result then
- Break;
- end;
- end;
- end;
- function StrContainsChars(const S: string; const Chars: array of Char; CheckAll: Boolean): Boolean;
- var
- I: SizeInt;
- begin
- if CheckAll then
- begin
- Result := True;
- I := High(Chars);
- while (I >= 0) and Result do
- begin
- Result := CharPos(S, Chars[I]) > 0;
- Dec(I);
- end;
- end
- else
- begin
- Result := False;
- for I := 1 to Length(S) do
- begin
- Result := ArrayContainsChar(Chars, S[I]);
- if Result then
- Break;
- end;
- end;
- end;
- function StrIsAlphaNumUnderscore(const S: string): Boolean;
- var
- I: SizeInt;
- C: Char;
- begin
- for I := 1 to Length(S) do
- begin
- C := S[I];
- if not (CharIsAlphaNum(C) or (C = '_')) then
- begin
- Result := False;
- Exit;
- end;
- end;
- Result := Length(S) > 0;
- end;
- function StrIsDigit(const S: string): Boolean;
- var
- I: SizeInt;
- begin
- Result := S <> '';
- for I := 1 to Length(S) do
- begin
- if not CharIsDigit(S[I]) then
- begin
- Result := False;
- Exit;
- end;
- end;
- end;
- function StrIsSubset(const S: string; const ValidChars: TCharValidator): Boolean;
- var
- I: SizeInt;
- begin
- for I := 1 to Length(S) do
- begin
- Result := ValidChars(S[I]);
- if not Result then
- Exit;
- end;
- Result := Length(S) > 0;
- end;
- function StrIsSubset(const S: string; const ValidChars: array of Char): Boolean;
- var
- I: SizeInt;
- begin
- for I := 1 to Length(S) do
- begin
- Result := ArrayContainsChar(ValidChars, S[I]);
- if not Result then
- Exit;
- end;
- Result := Length(S) > 0;
- end;
- function StrSame(const S1, S2: string; CaseSensitive: Boolean): Boolean;
- begin
- Result := StrCompare(S1, S2, CaseSensitive) = 0;
- end;
- //=== String Transformation Routines =========================================
- function StrCenter(const S: string; L: SizeInt; C: Char = ' '): string;
- begin
- if Length(S) < L then
- begin
- Result := StringOfChar(C, (L - Length(S)) div 2) + S;
- Result := Result + StringOfChar(C, L - Length(Result));
- end
- else
- Result := S;
- end;
- function StrCharPosLower(const S: string; CharPos: SizeInt): string;
- begin
- Result := S;
- if (CharPos > 0) and (CharPos <= Length(S)) then
- Result[CharPos] := CharLower(Result[CharPos]);
- end;
- function StrCharPosUpper(const S: string; CharPos: SizeInt): string;
- begin
- Result := S;
- if (CharPos > 0) and (CharPos <= Length(S)) then
- Result[CharPos] := CharUpper(Result[CharPos]);
- end;
- function StrDoubleQuote(const S: string): string;
- begin
- Result := NativeDoubleQuote + S + NativeDoubleQuote;
- end;
- function StrEnsureNoPrefix(const Prefix, Text: string): string;
- var
- PrefixLen: SizeInt;
- begin
- PrefixLen := Length(Prefix);
- if Copy(Text, 1, PrefixLen) = Prefix then
- Result := Copy(Text, PrefixLen + 1, Length(Text))
- else
- Result := Text;
- end;
- function StrEnsureNoSuffix(const Suffix, Text: string): string;
- var
- SuffixLen: SizeInt;
- StrLength: SizeInt;
- begin
- SuffixLen := Length(Suffix);
- StrLength := Length(Text);
- if Copy(Text, StrLength - SuffixLen + 1, SuffixLen) = Suffix then
- Result := Copy(Text, 1, StrLength - SuffixLen)
- else
- Result := Text;
- end;
- function StrEnsurePrefix(const Prefix, Text: string): string;
- var
- PrefixLen: SizeInt;
- begin
- PrefixLen := Length(Prefix);
- if Copy(Text, 1, PrefixLen) = Prefix then
- Result := Text
- else
- Result := Prefix + Text;
- end;
- function StrEnsureSuffix(const Suffix, Text: string): string;
- var
- SuffixLen: SizeInt;
- begin
- SuffixLen := Length(Suffix);
- if Copy(Text, Length(Text) - SuffixLen + 1, SuffixLen) = Suffix then
- Result := Text
- else
- Result := Text + Suffix;
- end;
- function StrEscapedToString(const S: string): string;
- procedure HandleHexEscapeSeq(const S: string; var I: SizeInt; Len: SizeInt; var Dest: string);
- const
- HexDigits = string('0123456789abcdefABCDEF');
- var
- StartI, Val, N: SizeInt;
- begin
- StartI := I;
- N := Pos(S[I + 1], HexDigits) - 1;
- if N < 0 then
- // '\x' without hex digit following is not escape sequence
- Dest := Dest + '\x'
- else
- begin
- Inc(I); // Jump over x
- if N >= 16 then
- N := N - 6;
- Val := N;
- // Same for second digit
- if I < Len then
- begin
- N := Pos(S[I + 1], HexDigits) - 1;
- if N >= 0 then
- begin
- Inc(I); // Jump over first digit
- if N >= 16 then
- N := N - 6;
- Val := Val * 16 + N;
- end;
- end;
- if Val > Ord(High(Char)) then
- raise EJclStringError.CreateResFmt(@RsNumericConstantTooLarge, [Val, StartI]);
- Dest := Dest + Char(Val);
- end;
- end;
- procedure HandleOctEscapeSeq(const S: string; var I: SizeInt; Len: SizeInt; var Dest: string);
- const
- OctDigits = string('01234567');
- var
- StartI, Val, N: SizeInt;
- begin
- StartI := I;
- // first digit
- Val := Pos(S[I], OctDigits) - 1;
- if I < Len then
- begin
- N := Pos(S[I + 1], OctDigits) - 1;
- if N >= 0 then
- begin
- Inc(I);
- Val := Val * 8 + N;
- end;
- if I < Len then
- begin
- N := Pos(S[I + 1], OctDigits) - 1;
- if N >= 0 then
- begin
- Inc(I);
- Val := Val * 8 + N;
- end;
- end;
- end;
- if Val > Ord(High(Char)) then
- raise EJclStringError.CreateResFmt(@RsNumericConstantTooLarge, [Val, StartI]);
- Dest := Dest + Char(Val);
- end;
- var
- I, Len: SizeInt;
- begin
- Result := '';
- I := 1;
- Len := Length(S);
- while I <= Len do
- begin
- if not ((S[I] = '\') and (I < Len)) then
- Result := Result + S[I]
- else
- begin
- Inc(I); // Jump over escape character
- case S[I] of
- 'a':
- Result := Result + NativeBell;
- 'b':
- Result := Result + NativeBackspace;
- 'f':
- Result := Result + NativeFormFeed;
- 'n':
- Result := Result + NativeLineFeed;
- 'r':
- Result := Result + NativeCarriageReturn;
- 't':
- Result := Result + NativeTab;
- 'v':
- Result := Result + NativeVerticalTab;
- '\':
- Result := Result + '\';
- '"':
- Result := Result + '"';
- '''':
- Result := Result + ''''; // Optionally escaped
- '?':
- Result := Result + '?'; // Optionally escaped
- 'x':
- if I < Len then
- // Start of hex escape sequence
- HandleHexEscapeSeq(S, I, Len, Result)
- else
- // '\x' at end of string is not escape sequence
- Result := Result + '\x';
- '0'..'7':
- // start of octal escape sequence
- HandleOctEscapeSeq(S, I, Len, Result);
- else
- // no escape sequence
- Result := Result + '\' + S[I];
- end;
- end;
- Inc(I);
- end;
- end;
- function StrLower(const S: string): string;
- begin
- Result := S;
- StrLowerInPlace(Result);
- end;
- procedure StrLowerInPlace(var S: string);
- {$IFDEF UNICODE_RTL_DATABASE}
- var
- P: PChar;
- I, L: SizeInt;
- begin
- L := Length(S);
- if L > 0 then
- begin
- UniqueString(S);
- P := PChar(S);
- for I := 1 to L do
- begin
- P^ := TCharacter.ToLower(P^);
- Inc(P);
- end;
- end;
- end;
- {$ELSE ~UNICODE_RTL_DATABASE}
- begin
- StrCase(S, StrLoOffset);
- end;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- procedure StrLowerBuff(S: PChar);
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- if S <> nil then
- begin
- repeat
- S^ := TCharacter.ToLower(S^);
- Inc(S);
- until S^ = #0;
- end;
- {$ELSE ~UNICODE_RTL_DATABASE}
- StrCaseBuff(S, StrLoOffset);
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- procedure StrMove(var Dest: string; const Source: string;
- const ToIndex, FromIndex, Count: SizeInt);
- begin
- // Check strings
- if (Source = '') or (Length(Dest) = 0) then
- Exit;
- // Check FromIndex
- if (FromIndex <= 0) or (FromIndex > Length(Source)) or
- (ToIndex <= 0) or (ToIndex > Length(Dest)) or
- ((FromIndex + Count - 1) > Length(Source)) or ((ToIndex + Count - 1) > Length(Dest)) then
- { TODO : Is failure without notice the proper thing to do here? }
- Exit;
- // Move
- Move(Source[FromIndex], Dest[ToIndex], Count * SizeOf(Char));
- end;
- function StrPadLeft(const S: string; Len: SizeInt; C: Char): string;
- var
- L: SizeInt;
- begin
- L := Length(S);
- if L < Len then
- Result := StringOfChar(C, Len - L) + S
- else
- Result := S;
- end;
- function StrPadRight(const S: string; Len: SizeInt; C: Char): string;
- var
- L: SizeInt;
- begin
- L := Length(S);
- if L < Len then
- Result := S + StringOfChar(C, Len - L)
- else
- Result := S;
- end;
- function StrProper(const S: string): string;
- begin
- Result := StrLower(S);
- if Result <> '' then
- Result[1] := UpCase(Result[1]);
- end;
- procedure StrProperBuff(S: PChar);
- begin
- if (S <> nil) and (S^ <> #0) then
- begin
- StrLowerBuff(S);
- S^ := CharUpper(S^);
- end;
- end;
- function StrQuote(const S: string; C: Char): string;
- var
- L: SizeInt;
- begin
- L := Length(S);
- Result := S;
- if L > 0 then
- begin
- if Result[1] <> C then
- begin
- Result := C + Result;
- Inc(L);
- end;
- if Result[L] <> C then
- Result := Result + C;
- end;
- end;
- function StrRemoveChars(const S: string; const Chars: TCharValidator): string;
- var
- Source, Dest: PChar;
- Len, Index: SizeInt;
- begin
- Len := Length(S);
- SetLength(Result, Len);
- UniqueString(Result);
- Source := PChar(S);
- Dest := PChar(Result);
- for Index := 0 to Len - 1 do
- begin
- if not Chars(Source^) then
- begin
- Dest^ := Source^;
- Inc(Dest);
- end;
- Inc(Source);
- end;
- SetLength(Result, Dest - PChar(Result));
- end;
- function StrRemoveChars(const S: string; const Chars: array of Char): string;
- var
- Source, Dest: PChar;
- Len, Index: SizeInt;
- begin
- Len := Length(S);
- SetLength(Result, Len);
- UniqueString(Result);
- Source := PChar(S);
- Dest := PChar(Result);
- for Index := 0 to Len - 1 do
- begin
- if not ArrayContainsChar(Chars, Source^) then
- begin
- Dest^ := Source^;
- Inc(Dest);
- end;
- Inc(Source);
- end;
- SetLength(Result, Dest - PChar(Result));
- end;
- function StrRemoveLeadingChars(const S: string; const Chars: TCharValidator): string;
- var
- Len : SizeInt;
- I: SizeInt;
- begin
- Len := Length(S);
- I := 1;
- while (I <= Len) and Chars(s[I]) do
- Inc(I);
- Result := Copy (s, I, Len-I+1);
- end;
- function StrRemoveLeadingChars(const S: string; const Chars: array of Char): string;
- var
- Len : SizeInt;
- I: SizeInt;
- begin
- Len := Length(S);
- I := 1;
- while (I <= Len) and ArrayContainsChar(Chars, s[I]) do
- Inc(I);
- Result := Copy (s, I, Len-I+1);
- end;
- function StrRemoveEndChars(const S: string; const Chars: TCharValidator): string;
- var
- Len : SizeInt;
- begin
- Len := Length(S);
- while (Len > 0) and Chars(s[Len]) do
- Dec(Len);
- Result := Copy (s, 1, Len);
- end;
- function StrRemoveEndChars(const S: string; const Chars: array of Char): string;
- var
- Len : SizeInt;
- begin
- Len := Length(S);
- while (Len > 0) and ArrayContainsChar(Chars, s[Len]) do
- Dec(Len);
- Result := Copy (s, 1, Len);
- end;
- function StrKeepChars(const S: string; const Chars: TCharValidator): string;
- var
- Source, Dest: PChar;
- Len, Index: SizeInt;
- begin
- Len := Length(S);
- SetLength(Result, Len);
- UniqueString(Result);
- Source := PChar(S);
- Dest := PChar(Result);
- for Index := 0 to Len - 1 do
- begin
- if Chars(Source^) then
- begin
- Dest^ := Source^;
- Inc(Dest);
- end;
- Inc(Source);
- end;
- SetLength(Result, Dest - PChar(Result));
- end;
- function StrKeepChars(const S: string; const Chars: array of Char): string;
- var
- Source, Dest: PChar;
- Len, Index: SizeInt;
- begin
- Len := Length(S);
- SetLength(Result, Len);
- UniqueString(Result);
- Source := PChar(S);
- Dest := PChar(Result);
- for Index := 0 to Len - 1 do
- begin
- if ArrayContainsChar(Chars, Source^) then
- begin
- Dest^ := Source^;
- Inc(Dest);
- end;
- Inc(Source);
- end;
- SetLength(Result, Dest - PChar(Result));
- end;
- function StrRepeat(const S: string; Count: SizeInt): string;
- var
- Len, Index: SizeInt;
- Dest, Source: PChar;
- begin
- Len := Length(S);
- SetLength(Result, Count * Len);
- Dest := PChar(Result);
- Source := PChar(S);
- if Dest <> nil then
- for Index := 0 to Count - 1 do
- begin
- Move(Source^, Dest^, Len * SizeOf(Char));
- Inc(Dest, Len);
- end;
- end;
- function StrRepeatLength(const S: string; L: SizeInt): string;
- var
- Len: SizeInt;
- Dest: PChar;
- begin
- Result := '';
- Len := Length(S);
- if (Len > 0) and (S <> '') then
- begin
- SetLength(Result, L);
- Dest := PChar(Result);
- while (L > 0) do
- begin
- Move(S[1], Dest^, Min(L, Len) * SizeOf(Char));
- Inc(Dest, Len);
- Dec(L, Len);
- end;
- end;
- end;
- procedure StrReplace(var S: string; const Search, Replace: string; Flags: TReplaceFlags);
- var
- SearchStr: string;
- ResultStr: string; { result string }
- SourcePtr: PChar; { pointer into S of character under examination }
- SourceMatchPtr: PChar; { pointers into S and Search when first character has }
- SearchMatchPtr: PChar; { been matched and we're probing for a complete match }
- ResultPtr: PChar; { pointer into Result of character being written }
- ResultIndex,
- SearchLength, { length of search string }
- ReplaceLength, { length of replace string }
- BufferLength, { length of temporary result buffer }
- ResultLength: SizeInt; { length of result string }
- C: Char; { first character of search string }
- IgnoreCase: Boolean;
- begin
- if Search = '' then
- begin
- if S = '' then
- begin
- S := Replace;
- Exit;
- end
- else
- raise EJclStringError.CreateRes(@RsBlankSearchString);
- end;
- if S <> '' then
- begin
- IgnoreCase := rfIgnoreCase in Flags;
- if IgnoreCase then
- SearchStr := StrUpper(Search)
- else
- SearchStr := Search;
- { avoid having to call Length() within the loop }
- SearchLength := Length(Search);
- ReplaceLength := Length(Replace);
- ResultLength := Length(S);
- BufferLength := ResultLength;
- SetLength(ResultStr, BufferLength);
- { get pointers to begin of source and result }
- ResultPtr := PChar(ResultStr);
- SourcePtr := PChar(S);
- C := SearchStr[1];
- { while we haven't reached the end of the string }
- while True do
- begin
- { copy characters until we find the first character of the search string }
- if IgnoreCase then
- while (CharUpper(SourcePtr^) <> C) and (SourcePtr^ <> #0) do
- begin
- ResultPtr^ := SourcePtr^;
- Inc(ResultPtr);
- Inc(SourcePtr);
- end
- else
- while (SourcePtr^ <> C) and (SourcePtr^ <> #0) do
- begin
- ResultPtr^ := SourcePtr^;
- Inc(ResultPtr);
- Inc(SourcePtr);
- end;
- { did we find that first character or did we hit the end of the string? }
- if SourcePtr^ = #0 then
- Break
- else
- begin
- { continue comparing, +1 because first character was matched already }
- SourceMatchPtr := SourcePtr + 1;
- SearchMatchPtr := PChar(SearchStr) + 1;
- if IgnoreCase then
- while (CharUpper(SourceMatchPtr^) = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
- begin
- Inc(SourceMatchPtr);
- Inc(SearchMatchPtr);
- end
- else
- while (SourceMatchPtr^ = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
- begin
- Inc(SourceMatchPtr);
- Inc(SearchMatchPtr);
- end;
- { did we find a complete match? }
- if SearchMatchPtr^ = #0 then
- begin
- // keep track of result length
- Inc(ResultLength, ReplaceLength - SearchLength);
- if ReplaceLength > 0 then
- begin
- // increase buffer size if required
- if ResultLength > BufferLength then
- begin
- BufferLength := ResultLength * 2;
- ResultIndex := ResultPtr - PChar(ResultStr) + 1;
- SetLength(ResultStr, BufferLength);
- ResultPtr := @ResultStr[ResultIndex];
- end;
- { append replace to result and move past the search string in source }
- Move((@Replace[1])^, ResultPtr^, ReplaceLength * SizeOf(Char));
- end;
- Inc(SourcePtr, SearchLength);
- Inc(ResultPtr, ReplaceLength);
- { replace all instances or just one? }
- if not (rfReplaceAll in Flags) then
- begin
- { just one, copy until end of source and break out of loop }
- while SourcePtr^ <> #0 do
- begin
- ResultPtr^ := SourcePtr^;
- Inc(ResultPtr);
- Inc(SourcePtr);
- end;
- Break;
- end;
- end
- else
- begin
- { copy current character and start over with the next }
- ResultPtr^ := SourcePtr^;
- Inc(ResultPtr);
- Inc(SourcePtr);
- end;
- end;
- end;
- { set result length and copy result into S }
- SetLength(ResultStr, ResultLength);
- S := ResultStr;
- end;
- end;
- function StrReplaceChar(const S: string; const Source, Replace: Char): string;
- var
- I: SizeInt;
- begin
- Result := S;
- for I := 1 to Length(S) do
- if Result[I] = Source then
- Result[I] := Replace;
- end;
- function StrReplaceChars(const S: string; const Chars: TCharValidator; Replace: Char): string;
- var
- I: SizeInt;
- begin
- Result := S;
- for I := 1 to Length(S) do
- if Chars(Result[I]) then
- Result[I] := Replace;
- end;
- function StrReplaceChars(const S: string; const Chars: array of Char; Replace: Char): string;
- var
- I: SizeInt;
- begin
- Result := S;
- for I := 1 to Length(S) do
- if ArrayContainsChar(Chars, Result[I]) then
- Result[I] := Replace;
- end;
- function StrReplaceButChars(const S: string; const Chars: TCharValidator;
- Replace: Char): string;
- var
- I: SizeInt;
- begin
- Result := S;
- for I := 1 to Length(S) do
- if not Chars(Result[I]) then
- Result[I] := Replace;
- end;
- function StrReplaceButChars(const S: string; const Chars: array of Char; Replace: Char): string;
- var
- I: SizeInt;
- begin
- Result := S;
- for I := 1 to Length(S) do
- if not ArrayContainsChar(Chars, Result[I]) then
- Result[I] := Replace;
- end;
- function StrReverse(const S: string): string;
- begin
- Result := S;
- StrReverseInplace(Result);
- end;
- procedure StrReverseInPlace(var S: string);
- { TODO -oahuser : Warning: This is dangerous for unicode surrogates }
- var
- P1, P2: PChar;
- C: Char;
- begin
- UniqueString(S);
- P1 := PChar(S);
- P2 := P1 + (Length(S) - 1);
- while P1 < P2 do
- begin
- C := P1^;
- P1^ := P2^;
- P2^ := C;
- Inc(P1);
- Dec(P2);
- end;
- end;
- function StrSingleQuote(const S: string): string;
- begin
- Result := NativeSingleQuote + S + NativeSingleQuote;
- end;
- procedure StrSkipChars(var S: PChar; const Chars: TCharValidator);
- begin
- while Chars(S^) do
- Inc(S);
- end;
- procedure StrSkipChars(var S: PChar; const Chars: array of Char);
- begin
- while ArrayContainsChar(Chars, S^) do
- Inc(S);
- end;
- procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: TCharValidator);
- begin
- while Chars(S[Index]) do
- Inc(Index);
- end;
- procedure StrSkipChars(const S: string; var Index: SizeInt; const Chars: array of Char);
- begin
- while ArrayContainsChar(Chars, S[Index]) do
- Inc(Index);
- end;
- function StrSmartCase(const S: string; const Delimiters: TCharValidator): string;
- var
- Source, Dest: PChar;
- Index, Len: SizeInt;
- InternalDelimiters: TCharValidator;
- begin
- Result := '';
- if Assigned(Delimiters) then
- InternalDelimiters := Delimiters
- else
- InternalDelimiters := CharIsSpace;
- if S <> '' then
- begin
- Result := S;
- UniqueString(Result);
- Len := Length(S);
- Source := PChar(S);
- Dest := PChar(Result);
- Inc(Dest);
- for Index := 2 to Len do
- begin
- if InternalDelimiters(Source^) and not InternalDelimiters(Dest^) then
- Dest^ := CharUpper(Dest^);
- Inc(Dest);
- Inc(Source);
- end;
- Result[1] := CharUpper(Result[1]);
- end;
- end;
- function StrSmartCase(const S: string; const Delimiters: array of Char): string;
- var
- Source, Dest: PChar;
- Index, Len: SizeInt;
- begin
- Result := '';
- if S <> '' then
- begin
- Result := S;
- UniqueString(Result);
- Len := Length(S);
- Source := PChar(S);
- Dest := PChar(Result);
- Inc(Dest);
- for Index := 2 to Len do
- begin
- if ArrayContainsChar(Delimiters, Source^) and not ArrayContainsChar(Delimiters, Dest^) then
- Dest^ := CharUpper(Dest^);
- Inc(Dest);
- Inc(Source);
- end;
- Result[1] := CharUpper(Result[1]);
- end;
- end;
- function StrStringToEscaped(const S: string): string;
- var
- I: SizeInt;
- begin
- Result := '';
- for I := 1 to Length(S) do
- begin
- case S[I] of
- NativeBackspace:
- Result := Result + '\b';
- NativeBell:
- Result := Result + '\a';
- NativeCarriageReturn:
- Result := Result + '\r';
- NAtiveFormFeed:
- Result := Result + '\f';
- NativeLineFeed:
- Result := Result + '\n';
- NativeTab:
- Result := Result + '\t';
- NativeVerticalTab:
- Result := Result + '\v';
- NativeBackSlash:
- Result := Result + '\\';
- NativeDoubleQuote:
- Result := Result + '\"';
- else
- // Characters < ' ' are escaped with hex sequence
- if S[I] < #32 then
- Result := Result + Format('\x%.2x', [SizeInt(S[I])])
- else
- Result := Result + S[I];
- end;
- end;
- end;
- {$IFNDEF WINSCP}
- function StrStripNonNumberChars(const S: string): string;
- var
- I: SizeInt;
- C: Char;
- begin
- Result := '';
- for I := 1 to Length(S) do
- begin
- C := S[I];
- if CharIsNumberChar(C) then
- Result := Result + C;
- end;
- end;
- {$ENDIF ~WINSCP}
- function StrToHex(const Source: string): string;
- var
- Index: SizeInt;
- C, L, N: SizeInt;
- BL, BH: Byte;
- S: string;
- begin
- Result := '';
- if Source <> '' then
- begin
- S := Source;
- L := Length(S);
- if Odd(L) then
- begin
- S := '0' + S;
- Inc(L);
- end;
- Index := 1;
- SetLength(Result, L div 2);
- C := 1;
- N := 1;
- while C <= L do
- begin
- BH := CharHex(S[Index]);
- Inc(Index);
- BL := CharHex(S[Index]);
- Inc(Index);
- Inc(C, 2);
- if (BH = $FF) or (BL = $FF) then
- begin
- Result := '';
- Exit;
- end;
- Result[N] := Char((BH shl 4) or BL);
- Inc(N);
- end;
- end;
- end;
- function StrTrimCharLeft(const S: string; C: Char): string;
- var
- I, L: SizeInt;
- begin
- I := 1;
- L := Length(S);
- while (I <= L) and (S[I] = C) do
- Inc(I);
- Result := Copy(S, I, L - I + 1);
- end;
- function StrTrimCharsLeft(const S: string; const Chars: TCharValidator): string;
- var
- I, L: SizeInt;
- begin
- I := 1;
- L := Length(S);
- while (I <= L) and Chars(S[I]) do
- Inc(I);
- Result := Copy(S, I, L - I + 1);
- end;
- function StrTrimCharsLeft(const S: string; const Chars: array of Char): string;
- var
- I, L: SizeInt;
- begin
- I := 1;
- L := Length(S);
- while (I <= L) and ArrayContainsChar(Chars, S[I]) do
- Inc(I);
- Result := Copy(S, I, L - I + 1);
- end;
- function StrTrimCharRight(const S: string; C: Char): string;
- var
- I: SizeInt;
- begin
- I := Length(S);
- while (I >= 1) and (S[I] = C) do
- Dec(I);
- Result := Copy(S, 1, I);
- end;
- function StrTrimCharsRight(const S: string; const Chars: TCharValidator): string;
- var
- I: SizeInt;
- begin
- I := Length(S);
- while (I >= 1) and Chars(S[I]) do
- Dec(I);
- Result := Copy(S, 1, I);
- end;
- function StrTrimCharsRight(const S: string; const Chars: array of Char): string;
- var
- I: SizeInt;
- begin
- I := Length(S);
- while (I >= 1) and ArrayContainsChar(Chars, S[I]) do
- Dec(I);
- Result := Copy(S, 1, I);
- end;
- function StrTrimQuotes(const S: string): string;
- var
- First, Last: Char;
- L: SizeInt;
- begin
- L := Length(S);
- if L > 1 then
- begin
- First := S[1];
- Last := S[L];
- if (First = Last) and ((First = NativeSingleQuote) or (First = NativeDoubleQuote)) then
- Result := Copy(S, 2, L - 2)
- else
- Result := S;
- end
- else
- Result := S;
- end;
- function StrUpper(const S: string): string;
- begin
- Result := S;
- StrUpperInPlace(Result);
- end;
- procedure StrUpperInPlace(var S: string);
- {$IFDEF UNICODE_RTL_DATABASE}
- var
- P: PChar;
- I, L: SizeInt;
- begin
- L := Length(S);
- if L > 0 then
- begin
- UniqueString(S);
- P := PChar(S);
- for I := 1 to L do
- begin
- P^ := TCharacter.ToUpper(P^);
- Inc(P);
- end;
- end;
- end;
- {$ELSE ~UNICODE_RTL_DATABASE}
- begin
- StrCase(S, StrUpOffset);
- end;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- procedure StrUpperBuff(S: PChar);
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- if S <> nil then
- begin
- repeat
- S^ := TCharacter.ToUpper(S^);
- Inc(S);
- until S^ = #0;
- end;
- {$ELSE ~UNICODE_RTL_DATABASE}
- StrCaseBuff(S, StrUpOffset);
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- //=== String Management ======================================================
- procedure StrAddRef(var S: string);
- var
- P: PStrRec;
- begin
- P := Pointer(S);
- if P <> nil then
- begin
- Dec(P);
- if P^.RefCount = -1 then
- UniqueString(S)
- else
- LockedInc(P^.RefCount);
- end;
- end;
- procedure StrDecRef(var S: string);
- var
- P: PStrRec;
- begin
- P := Pointer(S);
- if P <> nil then
- begin
- Dec(P);
- case P^.RefCount of
- -1, 0: { nothing } ;
- 1:
- begin
- Finalize(S);
- Pointer(S) := nil;
- end;
- else
- LockedDec(P^.RefCount);
- end;
- end;
- end;
- function StrLength(const S: string): SizeInt;
- var
- P: PStrRec;
- begin
- Result := 0;
- P := Pointer(S);
- if P <> nil then
- begin
- Dec(P);
- Result := P^.Length and (not $80000000 shr 1);
- end;
- end;
- function StrRefCount(const S: string): SizeInt;
- var
- P: PStrRec;
- begin
- Result := 0;
- P := Pointer(S);
- if P <> nil then
- begin
- Dec(P);
- Result := P^.RefCount;
- end;
- end;
- procedure StrResetLength(var S: WideString);
- var
- I: SizeInt;
- begin
- for I := 0 to Length(S) - 1 do
- if S[I + 1] = #0 then
- begin
- SetLength(S, I);
- Exit;
- end;
- end;
- procedure StrResetLength(var S: AnsiString);
- var
- I: SizeInt;
- begin
- for I := 0 to Length(S) - 1 do
- if S[I + 1] = #0 then
- begin
- SetLength(S, I);
- Exit;
- end;
- end;
- procedure StrResetLength(S: TJclStringBuilder);
- var
- I: SizeInt;
- begin
- if S <> nil then
- for I := 0 to S.Length - 1 do
- if S[I] = #0 then
- begin
- S.Length := I;
- Exit;
- end;
- end;
- {$IFDEF SUPPORTS_UNICODE_STRING}
- procedure StrResetLength(var S: UnicodeString);
- var
- I: SizeInt;
- begin
- for I := 0 to Length(S) - 1 do
- if S[I + 1] = #0 then
- begin
- SetLength(S, I);
- Exit;
- end;
- end;
- {$ENDIF SUPPORTS_UNICODE_STRING}
- //=== String Search and Replace Routines =====================================
- function StrCharCount(const S: string; C: Char): SizeInt;
- var
- I: SizeInt;
- begin
- Result := 0;
- for I := 1 to Length(S) do
- if S[I] = C then
- Inc(Result);
- end;
- function StrCharsCount(const S: string; const Chars: TCharValidator): SizeInt;
- var
- I: SizeInt;
- begin
- Result := 0;
- for I := 1 to Length(S) do
- if Chars(S[I]) then
- Inc(Result);
- end;
- function StrCharsCount(const S: string; const Chars: array of Char): SizeInt;
- var
- I: SizeInt;
- begin
- Result := 0;
- for I := 1 to Length(S) do
- if ArrayContainsChar(Chars, S[I]) then
- Inc(Result);
- end;
- function StrStrCount(const S, SubS: string): SizeInt;
- var
- I: SizeInt;
- begin
- Result := 0;
- if (Length(SubS) > Length(S)) or (Length(SubS) = 0) or (Length(S) = 0) then
- Exit;
- if Length(SubS) = 1 then
- begin
- Result := StrCharCount(S, SubS[1]);
- Exit;
- end;
- I := StrSearch(SubS, S, 1);
- if I > 0 then
- Inc(Result);
- while (I > 0) and (Length(S) > I + Length(SubS)) do
- begin
- I := StrSearch(SubS, S, I + 1);
- if I > 0 then
- Inc(Result);
- end;
- end;
- (*
- { 1} Test(StrCompareRange('', '', 1, 5), 0);
- { 2} Test(StrCompareRange('A', '', 1, 5), -1);
- { 3} Test(StrCompareRange('AB', '', 1, 5), -1);
- { 4} Test(StrCompareRange('ABC', '', 1, 5), -1);
- { 5} Test(StrCompareRange('', 'A', 1, 5), -1);
- { 6} Test(StrCompareRange('', 'AB', 1, 5), -1);
- { 7} Test(StrCompareRange('', 'ABC', 1, 5), -1);
- { 8} Test(StrCompareRange('A', 'a', 1, 5), -2);
- { 9} Test(StrCompareRange('A', 'a', 1, 1), -32);
- {10} Test(StrCompareRange('aA', 'aB', 1, 1), 0);
- {11} Test(StrCompareRange('aA', 'aB', 1, 2), -1);
- {12} Test(StrCompareRange('aB', 'aA', 1, 2), 1);
- {13} Test(StrCompareRange('aA', 'aa', 1, 2), -32);
- {14} Test(StrCompareRange('aa', 'aA', 1, 2), 32);
- {15} Test(StrCompareRange('', '', 1, 0), 0);
- {16} Test(StrCompareRange('A', 'A', 1, 0), -2);
- {17} Test(StrCompareRange('Aa', 'A', 1, 0), -2);
- {18} Test(StrCompareRange('Aa', 'Aa', 1, 2), 0);
- {19} Test(StrCompareRange('Aa', 'A', 1, 2), 0);
- {20} Test(StrCompareRange('Ba', 'A', 1, 2), 1);
- *)
- function StrCompareRangeEx(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
- var
- Len1, Len2: SizeInt;
- I: SizeInt;
- C1, C2: Char;
- begin
- if Pointer(S1) = Pointer(S2) then
- begin
- if (Count <= 0) and (S1 <> '') then
- Result := -2 // no work
- else
- Result := 0;
- end
- else
- if (S1 = '') or (S2 = '') then
- Result := -1 // null string
- else
- if Count <= 0 then
- Result := -2 // no work
- else
- begin
- Len1 := Length(S1);
- Len2 := Length(S2);
- if (Index - 1) + Count > Len1 then
- Result := -2
- else
- begin
- if (Index - 1) + Count > Len2 then // strange behaviour, but the assembler code does it
- Count := Len2 - (Index - 1);
- if CaseSensitive then
- begin
- for I := 0 to Count - 1 do
- begin
- C1 := S1[Index + I];
- C2 := S2[Index + I];
- if C1 <> C2 then
- begin
- Result := Ord(C1) - Ord(C2);
- Exit;
- end;
- end;
- end
- else
- begin
- for I := 0 to Count - 1 do
- begin
- C1 := S1[Index + I];
- C2 := S2[Index + I];
- if C1 <> C2 then
- begin
- C1 := CharLower(C1);
- C2 := CharLower(C2);
- if C1 <> C2 then
- begin
- Result := Ord(C1) - Ord(C2);
- Exit;
- end;
- end;
- end;
- end;
- Result := 0;
- end;
- end;
- end;
- function StrCompare(const S1, S2: string; CaseSensitive: Boolean): SizeInt;
- var
- Len1, Len2: SizeInt;
- begin
- if Pointer(S1) = Pointer(S2) then
- Result := 0
- else
- begin
- Len1 := Length(S1);
- Len2 := Length(S2);
- Result := Len1 - Len2;
- if Result = 0 then
- Result := StrCompareRangeEx(S1, S2, 1, Len1, CaseSensitive);
- end;
- end;
- function StrCompareRange(const S1, S2: string; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
- begin
- Result := StrCompareRangeEx(S1, S2, Index, Count, CaseSensitive);
- end;
- procedure StrFillChar(var S; Count: SizeInt; C: Char);
- {$IFDEF SUPPORTS_UNICODE}
- asm
- // 32 --> EAX S
- // EDX Count
- // ECX C
- // 64 --> RCX S
- // RDX Count
- // R8W C
- {$IFDEF CPU32}
- DEC EDX
- JS @@Leave
- @@Loop:
- MOV [EAX], CX
- ADD EAX, 2
- DEC EDX
- JNS @@Loop
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- DEC RDX
- JS @@Leave
- @@Loop:
- MOV WORD PTR [RCX], R8W
- ADD RCX, 2
- DEC RDX
- JNS @@Loop
- {$ENDIF CPU64}
- @@Leave:
- end;
- {$ELSE ~SUPPORTS_UNICODE}
- begin
- if Count > 0 then
- FillChar(S, Count, C);
- end;
- {$ENDIF ~SUPPORTS_UNICODE}
- function StrRepeatChar(C: Char; Count: SizeInt): string;
- begin
- SetLength(Result, Count);
- if Count > 0 then
- StrFillChar(Result[1], Count, C);
- end;
- function StrFind(const Substr, S: string; const Index: SizeInt): SizeInt;
- var
- pos: SizeInt;
- begin
- if (SubStr <> '') and (S <> '') then
- begin
- pos := StrIPos(Substr, Copy(S, Index, Length(S) - Index + 1));
- if pos = 0 then
- Result := 0
- else
- Result := Index + Pos - 1;
- end
- else
- Result := 0;
- end;
- function StrHasPrefix(const S: string; const Prefixes: array of string): Boolean;
- begin
- Result := StrPrefixIndex(S, Prefixes) > -1;
- end;
- function StrHasSuffix(const S: string; const Suffixes: array of string): Boolean;
- begin
- Result := StrSuffixIndex(S, Suffixes) > -1;
- end;
- function StrIndex(const S: string; const List: array of string; CaseSensitive: Boolean): SizeInt;
- var
- I: SizeInt;
- begin
- Result := -1;
- for I := Low(List) to High(List) do
- begin
- if StrCompare(S, List[I], CaseSensitive) = 0 then
- begin
- Result := I;
- Break;
- end;
- end;
- end;
- function StrIHasPrefix(const S: string; const Prefixes: array of string): Boolean;
- begin
- Result := StrIPrefixIndex(S, Prefixes) > -1;
- end;
- function StrIHasSuffix(const S: string; const Suffixes: array of string): Boolean;
- begin
- Result := StrISuffixIndex(S, Suffixes) > -1;
- end;
- function StrILastPos(const SubStr, S: string): SizeInt;
- begin
- Result := StrLastPos(StrUpper(SubStr), StrUpper(S));
- end;
- function StrIPos(const SubStr, S: string): SizeInt;
- begin
- Result := Pos(StrUpper(SubStr), StrUpper(S));
- end;
- function StrIPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
- var
- I: SizeInt;
- Test: string;
- begin
- Result := -1;
- for I := Low(Prefixes) to High(Prefixes) do
- begin
- Test := StrLeft(S, Length(Prefixes[I]));
- if CompareText(Test, Prefixes[I]) = 0 then
- begin
- Result := I;
- Break;
- end;
- end;
- end;
- function StrIsOneOf(const S: string; const List: array of string): Boolean;
- begin
- Result := StrIndex(S, List) > -1;
- end;
- function StrISuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
- var
- I: SizeInt;
- Test: string;
- begin
- Result := -1;
- for I := Low(Suffixes) to High(Suffixes) do
- begin
- Test := StrRight(S, Length(Suffixes[I]));
- if CompareText(Test, Suffixes[I]) = 0 then
- begin
- Result := I;
- Break;
- end;
- end;
- end;
- function StrLastPos(const SubStr, S: string): SizeInt;
- var
- Last, Current: PChar;
- begin
- Result := 0;
- Last := nil;
- Current := PChar(S);
- while (Current <> nil) and (Current^ <> #0) do
- begin
- Current := StrPos(PChar(Current), PChar(SubStr));
- if Current <> nil then
- begin
- Last := Current;
- Inc(Current);
- end;
- end;
- if Last <> nil then
- Result := Abs(PChar(S) - Last) + 1;
- end;
- // IMPORTANT NOTE: The StrMatch function does currently not work with the Asterix (*)
- // (*) acts like (?)
- function StrMatch(const Substr, S: string; Index: SizeInt): SizeInt;
- var
- SI, SubI, SLen, SubLen: SizeInt;
- SubC: Char;
- begin
- SLen := Length(S);
- SubLen := Length(Substr);
- Result := 0;
- if (Index > SLen) or (SubLen = 0) then
- Exit;
- while Index <= SLen do
- begin
- SubI := 1;
- SI := Index;
- while (SI <= SLen) and (SubI <= SubLen) do
- begin
- SubC := Substr[SubI];
- if (SubC = '*') or (SubC = '?') or (SubC = S[SI]) then
- begin
- Inc(SI);
- Inc(SubI);
- end
- else
- Break;
- end;
- if SubI > SubLen then
- begin
- Result := Index;
- Break;
- end;
- Inc(Index);
- end;
- end;
- // Derived from "Like" by Michael Winter
- function StrMatches(const Substr, S: string; const Index: SizeInt): Boolean;
- var
- StringPtr: PChar;
- PatternPtr: PChar;
- StringRes: PChar;
- PatternRes: PChar;
- begin
- if SubStr = '' then
- raise EJclStringError.CreateRes(@RsBlankSearchString);
- Result := SubStr = '*';
- if Result or (S = '') then
- Exit;
- if (Index <= 0) or (Index > Length(S)) then
- raise EJclStringError.CreateRes(@RsArgumentOutOfRange);
- StringPtr := PChar(@S[Index]);
- PatternPtr := PChar(SubStr);
- StringRes := nil;
- PatternRes := nil;
- repeat
- repeat
- case PatternPtr^ of
- #0:
- begin
- Result := StringPtr^ = #0;
- if Result or (StringRes = nil) or (PatternRes = nil) then
- Exit;
- StringPtr := StringRes;
- PatternPtr := PatternRes;
- Break;
- end;
- '*':
- begin
- Inc(PatternPtr);
- PatternRes := PatternPtr;
- Break;
- end;
- '?':
- begin
- if StringPtr^ = #0 then
- Exit;
- Inc(StringPtr);
- Inc(PatternPtr);
- end;
- else
- begin
- if StringPtr^ = #0 then
- Exit;
- if StringPtr^ <> PatternPtr^ then
- begin
- if (StringRes = nil) or (PatternRes = nil) then
- Exit;
- StringPtr := StringRes;
- PatternPtr := PatternRes;
- Break;
- end
- else
- begin
- Inc(StringPtr);
- Inc(PatternPtr);
- end;
- end;
- end;
- until False;
- repeat
- case PatternPtr^ of
- #0:
- begin
- Result := True;
- Exit;
- end;
- '*':
- begin
- Inc(PatternPtr);
- PatternRes := PatternPtr;
- end;
- '?':
- begin
- if StringPtr^ = #0 then
- Exit;
- Inc(StringPtr);
- Inc(PatternPtr);
- end;
- else
- begin
- repeat
- if StringPtr^ = #0 then
- Exit;
- if StringPtr^ = PatternPtr^ then
- Break;
- Inc(StringPtr);
- until False;
- Inc(StringPtr);
- StringRes := StringPtr;
- Inc(PatternPtr);
- Break;
- end;
- end;
- until False;
- until False;
- end;
- function StrNPos(const S, SubStr: string; N: SizeInt): SizeInt;
- var
- I, P: SizeInt;
- begin
- if N < 1 then
- begin
- Result := 0;
- Exit;
- end;
- Result := StrSearch(SubStr, S, 1);
- I := 1;
- while I < N do
- begin
- P := StrSearch(SubStr, S, Result + 1);
- if P = 0 then
- begin
- Result := 0;
- Break;
- end
- else
- begin
- Result := P;
- Inc(I);
- end;
- end;
- end;
- function StrNIPos(const S, SubStr: string; N: SizeInt): SizeInt;
- var
- I, P: SizeInt;
- begin
- if N < 1 then
- begin
- Result := 0;
- Exit;
- end;
- Result := StrFind(SubStr, S, 1);
- I := 1;
- while I < N do
- begin
- P := StrFind(SubStr, S, Result + 1);
- if P = 0 then
- begin
- Result := 0;
- Break;
- end
- else
- begin
- Result := P;
- Inc(I);
- end;
- end;
- end;
- function StrPrefixIndex(const S: string; const Prefixes: array of string): SizeInt;
- var
- I: SizeInt;
- Test: string;
- begin
- Result := -1;
- for I := Low(Prefixes) to High(Prefixes) do
- begin
- Test := StrLeft(S, Length(Prefixes[I]));
- if CompareStr(Test, Prefixes[I]) = 0 then
- begin
- Result := I;
- Break;
- end;
- end;
- end;
- function StrSearch(const Substr, S: string; const Index: SizeInt): SizeInt;
- var
- SP, SPI, SubP: PChar;
- SLen: SizeInt;
- begin
- SLen := Length(S);
- if Index <= SLen then
- begin
- SP := PChar(S);
- SubP := PChar(Substr);
- SPI := SP;
- Inc(SPI, Index);
- Dec(SPI);
- SPI := StrPos(SPI, SubP);
- if SPI <> nil then
- Result := SPI - SP + 1
- else
- Result := 0;
- end
- else
- Result := 0;
- end;
- function StrSuffixIndex(const S: string; const Suffixes: array of string): SizeInt;
- var
- I: SizeInt;
- Test: string;
- begin
- Result := -1;
- for I := Low(Suffixes) to High(Suffixes) do
- begin
- Test := StrRight(S, Length(Suffixes[I]));
- if CompareStr(Test, Suffixes[I]) = 0 then
- begin
- Result := I;
- Break;
- end;
- end;
- end;
- //=== String Extraction ======================================================
- function StrAfter(const SubStr, S: string): string;
- var
- P: SizeInt;
- begin
- P := StrFind(SubStr, S, 1); // StrFind is case-insensitive pos
- if P <= 0 then
- Result := '' // substr not found -> nothing after it
- else
- Result := StrRestOf(S, P + Length(SubStr));
- end;
- function StrBefore(const SubStr, S: string): string;
- var
- P: SizeInt;
- begin
- P := StrFind(SubStr, S, 1);
- if P <= 0 then
- Result := S
- else
- Result := StrLeft(S, P - 1);
- end;
- function StrSplit(const SubStr, S: string;var Left, Right : string): boolean;
- var
- P: SizeInt;
- begin
- P := StrFind(SubStr, S, 1);
- Result:= p > 0;
- if Result then
- begin
- Left := StrLeft(S, P - 1);
- Right := StrRestOf(S, P + Length(SubStr));
- end
- else
- begin
- Left := '';
- Right := '';
- end;
- end;
- function StrBetween(const S: string; const Start, Stop: Char): string;
- var
- PosStart, PosEnd: SizeInt;
- L: SizeInt;
- begin
- PosStart := Pos(Start, S);
- PosEnd := StrSearch(Stop, S, PosStart + 1); // PosEnd has to be after PosStart.
- if (PosStart > 0) and (PosEnd > PosStart) then
- begin
- L := PosEnd - PosStart;
- Result := Copy(S, PosStart + 1, L - 1);
- end
- else
- Result := '';
- end;
- function StrChopRight(const S: string; N: SizeInt): string;
- begin
- Result := Copy(S, 1, Length(S) - N);
- end;
- function StrLeft(const S: string; Count: SizeInt): string;
- begin
- Result := Copy(S, 1, Count);
- end;
- function StrMid(const S: string; Start, Count: SizeInt): string;
- begin
- Result := Copy(S, Start, Count);
- end;
- function StrRestOf(const S: string; N: SizeInt): string;
- begin
- Result := Copy(S, N, (Length(S) - N + 1));
- end;
- function StrRight(const S: string; Count: SizeInt): string;
- begin
- Result := Copy(S, Length(S) - Count + 1, Count);
- end;
- //=== Character (do we have it ;) ============================================
- function CharEqualNoCase(const C1, C2: Char): Boolean;
- begin
- //if they are not equal chars, may be same letter different case
- Result := (C1 = C2) or
- (CharIsAlpha(C1) and CharIsAlpha(C2) and (CharLower(C1) = CharLower(C2)));
- end;
- function CharIsAlpha(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsLetter(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := (StrCharTypes[C] and C1_ALPHA) <> 0;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsAlphaNum(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsLetterOrDigit(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := ((StrCharTypes[C] and C1_ALPHA) <> 0) or ((StrCharTypes[C] and C1_DIGIT) <> 0);
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsBlank(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- //http://blogs.msdn.com/b/michkap/archive/2007/06/11/3230072.aspx
- Result := (C = ' ') or (C = #$0009) or (C = #$00A0) or (C = #$3000);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := ((StrCharTypes[C] and C1_BLANK) <> 0);
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsControl(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsControl(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := (StrCharTypes[C] and C1_CNTRL) <> 0;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsDelete(const C: Char): Boolean;
- begin
- Result := (C = #8);
- end;
- function CharIsDigit(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsDigit(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := (StrCharTypes[C] and C1_DIGIT) <> 0;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsFracDigit(const C: Char): Boolean;
- begin
- Result := (C = '.') or CharIsDigit(C);
- end;
- function CharIsHexDigit(const C: Char): Boolean;
- begin
- case C of
- 'A'..'F',
- 'a'..'f':
- Result := True;
- else
- Result := CharIsDigit(C);
- end;
- end;
- function CharIsLower(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsLower(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := (StrCharTypes[C] and C1_LOWER) <> 0;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- {$IFNDEF WINSCP}
- function CharIsNumberChar(const C: Char): Boolean;
- begin
- Result := CharIsDigit(C) or (C = '+') or (C = '-') or (C = JclFormatSettings.DecimalSeparator);
- end;
- function CharIsNumber(const C: Char): Boolean;
- begin
- Result := CharIsDigit(C) or (C = JclFormatSettings.DecimalSeparator);
- end;
- {$ENDIF ~WINSCP}
- function CharIsPrintable(const C: Char): Boolean;
- begin
- Result := not CharIsControl(C);
- end;
- function CharIsPunctuation(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsPunctuation(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := ((StrCharTypes[C] and C1_PUNCT) <> 0);
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsReturn(const C: Char): Boolean;
- begin
- Result := (C = NativeLineFeed) or (C = NativeCarriageReturn);
- end;
- function CharIsSpace(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsWhiteSpace(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := (StrCharTypes[C] and C1_SPACE) <> 0;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsUpper(const C: Char): Boolean;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.IsUpper(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := (StrCharTypes[C] and C1_UPPER) <> 0;
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharIsValidIdentifierLetter(const C: Char): Boolean;
- begin
- case C of
- {$IFDEF SUPPORTS_UNICODE}
- // from XML specifications
- #$00C0..#$00D6, #$00D8..#$00F6, #$00F8..#$02FF, #$0370..#$037D,
- #$037F..#$1FFF, #$200C..#$200D, #$2070..#$218F, #$2C00..#$2FEF,
- #$3001..#$D7FF, #$F900..#$FDCF, #$FDF0..#$FFFD, // #$10000..#$EFFFF, howto match surrogate pairs?
- #$00B7, #$0300..#$036F, #$203F..#$2040,
- {$ENDIF SUPPORTS_UNICODE}
- '0'..'9', 'A'..'Z', 'a'..'z', '_':
- Result := True;
- else
- Result := False;
- end;
- end;
- function CharIsWhiteSpace(const C: Char): Boolean;
- begin
- case C of
- NativeTab,
- NativeLineFeed,
- NativeVerticalTab,
- NativeFormFeed,
- NativeCarriageReturn,
- NativeSpace:
- Result := True;
- else
- Result := False;
- end;
- end;
- function CharIsWildcard(const C: Char): Boolean;
- begin
- case C of
- '*', '?':
- Result := True;
- else
- Result := False;
- end;
- end;
- function CharType(const C: Char): Word;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, @C, 1, Result);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := StrCharTypes[C];
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- //=== PCharVector ============================================================
- function StringsToPCharVector(var Dest: PCharVector; const Source: TStrings): PCharVector;
- var
- I: SizeInt;
- S: string;
- List: array of PChar;
- begin
- Assert(Source <> nil);
- Dest := AllocMem((Source.Count + SizeOf(Char)) * SizeOf(PChar));
- SetLength(List, Source.Count + SizeOf(Char));
- for I := 0 to Source.Count - 1 do
- begin
- S := Source[I];
- List[I] := StrAlloc(Length(S) + SizeOf(Char));
- StrPCopy(List[I], S);
- end;
- List[Source.Count] := nil;
- Move(List[0], Dest^, (Source.Count + 1) * SizeOf(PChar));
- Result := Dest;
- end;
- function PCharVectorCount(Source: PCharVector): SizeInt;
- begin
- Result := 0;
- if Source <> nil then
- begin
- while Source^ <> nil do
- begin
- Inc(Source);
- Inc(Result);
- end;
- end;
- end;
- procedure PCharVectorToStrings(const Dest: TStrings; Source: PCharVector);
- var
- I, Count: SizeInt;
- List: array of PChar;
- begin
- Assert(Dest <> nil);
- if Source <> nil then
- begin
- Count := PCharVectorCount(Source);
- SetLength(List, Count);
- Move(Source^, List[0], Count * SizeOf(PChar));
- Dest.BeginUpdate;
- try
- Dest.Clear;
- for I := 0 to Count - 1 do
- Dest.Add(List[I]);
- finally
- Dest.EndUpdate;
- end;
- end;
- end;
- procedure FreePCharVector(var Dest: PCharVector);
- var
- I, Count: SizeInt;
- List: array of PChar;
- begin
- if Dest <> nil then
- begin
- Count := PCharVectorCount(Dest);
- SetLength(List, Count);
- Move(Dest^, List[0], Count * SizeOf(PChar));
- for I := 0 to Count - 1 do
- StrDispose(List[I]);
- FreeMem(Dest, (Count + 1) * SizeOf(PChar));
- Dest := nil;
- end;
- end;
- //=== Character Transformation Routines ======================================
- function CharHex(const C: Char): Byte;
- begin
- case C of
- '0'..'9':
- Result := Ord(C) - Ord('0');
- 'a'..'f':
- Result := Ord(C) - Ord('a') + 10;
- 'A'..'F':
- Result := Ord(C) - Ord('A') + 10;
- else
- Result := $FF;
- end;
- end;
- function CharLower(const C: Char): Char;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.ToLower(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := StrCaseMap[Ord(C) + StrLoOffset];
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharToggleCase(const C: Char): Char;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- if CharIsLower(C) then
- Result := CharUpper(C)
- else if CharIsUpper(C) then
- Result := CharLower(C)
- else
- Result := C;
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := StrCaseMap[Ord(C) + StrReOffset];
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- function CharUpper(const C: Char): Char;
- begin
- {$IFDEF UNICODE_RTL_DATABASE}
- Result := TCharacter.ToUpper(C);
- {$ELSE ~UNICODE_RTL_DATABASE}
- Result := StrCaseMap[Ord(C) + StrUpOffset];
- {$ENDIF ~UNICODE_RTL_DATABASE}
- end;
- //=== Character Search and Replace ===========================================
- function CharLastPos(const S: string; const C: Char; const Index: SizeInt): SizeInt;
- begin
- if (Index > 0) and (Index <= Length(S)) then
- begin
- for Result := Length(S) downto Index do
- if S[Result] = C then
- Exit;
- end;
- Result := 0;
- end;
- function CharPos(const S: string; const C: Char; const Index: SizeInt): SizeInt;
- begin
- if (Index > 0) and (Index <= Length(S)) then
- begin
- for Result := Index to Length(S) do
- if S[Result] = C then
- Exit;
- end;
- Result := 0;
- end;
- function CharIPos(const S: string; C: Char; const Index: SizeInt): SizeInt;
- begin
- if (Index > 0) and (Index <= Length(S)) then
- begin
- C := CharUpper(C);
- for Result := Index to Length(S) do
- if CharUpper(S[Result]) = C then
- Exit;
- end;
- Result := 0;
- end;
- function CharReplace(var S: string; const Search, Replace: Char): SizeInt;
- var
- P: PChar;
- Index, Len: SizeInt;
- begin
- Result := 0;
- if Search <> Replace then
- begin
- UniqueString(S);
- P := PChar(S);
- Len := Length(S);
- for Index := 0 to Len - 1 do
- begin
- if P^ = Search then
- begin
- P^ := Replace;
- Inc(Result);
- end;
- Inc(P);
- end;
- end;
- end;
- {$IFNDEF WINSCP}
- //=== MultiSz ================================================================
- function StringsToMultiSz(var Dest: PMultiSz; const Source: TStrings): PMultiSz;
- var
- I, TotalLength: SizeInt;
- P: PMultiSz;
- begin
- Assert(Source <> nil);
- TotalLength := 1;
- for I := 0 to Source.Count - 1 do
- if Source[I] = '' then
- raise EJclStringError.CreateRes(@RsInvalidEmptyStringItem)
- else
- Inc(TotalLength, StrLen(PChar(Source[I])) + 1);
- AllocateMultiSz(Dest, TotalLength);
- P := Dest;
- for I := 0 to Source.Count - 1 do
- begin
- P := StrECopy(P, PChar(Source[I]));
- Inc(P);
- end;
- P^ := #0;
- Result := Dest;
- end;
- procedure MultiSzToStrings(const Dest: TStrings; const Source: PMultiSz);
- var
- P: PMultiSz;
- begin
- Assert(Dest <> nil);
- Dest.BeginUpdate;
- try
- Dest.Clear;
- if Source <> nil then
- begin
- P := Source;
- while P^ <> #0 do
- begin
- Dest.Add(P);
- P := StrEnd(P);
- Inc(P);
- end;
- end;
- finally
- Dest.EndUpdate;
- end;
- end;
- function MultiSzLength(const Source: PMultiSz): SizeInt;
- var
- P: PMultiSz;
- begin
- Result := 0;
- if Source <> nil then
- begin
- P := Source;
- repeat
- Inc(Result, StrLen(P) + 1);
- P := StrEnd(P);
- Inc(P);
- until P^ = #0;
- Inc(Result);
- end;
- end;
- procedure AllocateMultiSz(var Dest: PMultiSz; Len: SizeInt);
- begin
- if Len > 0 then
- GetMem(Dest, Len * SizeOf(Char))
- else
- Dest := nil;
- end;
- procedure FreeMultiSz(var Dest: PMultiSz);
- begin
- if Dest <> nil then
- FreeMem(Dest);
- Dest := nil;
- end;
- function MultiSzDup(const Source: PMultiSz): PMultiSz;
- var
- Len: SizeInt;
- begin
- if Source <> nil then
- begin
- Len := MultiSzLength(Source);
- Result := nil;
- AllocateMultiSz(Result, Len);
- Move(Source^, Result^, Len * SizeOf(Char));
- end
- else
- Result := nil;
- end;
- function AnsiStringsToAnsiMultiSz(var Dest: PAnsiMultiSz; const Source: TAnsiStrings): PAnsiMultiSz;
- begin
- Result := JclAnsiStrings.StringsToMultiSz(Dest, Source);
- end;
- procedure AnsiMultiSzToAnsiStrings(const Dest: TAnsiStrings; const Source: PAnsiMultiSz);
- begin
- JclAnsiStrings.MultiSzToStrings(Dest, Source);
- end;
- function AnsiMultiSzLength(const Source: PAnsiMultiSz): SizeInt;
- begin
- Result := JclAnsiStrings.MultiSzLength(Source);
- end;
- procedure AllocateAnsiMultiSz(var Dest: PAnsiMultiSz; Len: SizeInt);
- begin
- JclAnsiStrings.AllocateMultiSz(Dest, Len);
- end;
- procedure FreeAnsiMultiSz(var Dest: PAnsiMultiSz);
- begin
- JclAnsiStrings.FreeMultiSz(Dest);
- end;
- function AnsiMultiSzDup(const Source: PAnsiMultiSz): PAnsiMultiSz;
- begin
- Result := JclAnsiStrings.MultiSzDup(Source);
- end;
- function WideStringsToWideMultiSz(var Dest: PWideMultiSz; const Source: TWideStrings): PWideMultiSz;
- begin
- Result := JclWideStrings.StringsToMultiSz(Dest, Source);
- end;
- procedure WideMultiSzToWideStrings(const Dest: TWideStrings; const Source: PWideMultiSz);
- begin
- JclWideStrings.MultiSzToStrings(Dest, Source);
- end;
- function WideMultiSzLength(const Source: PWideMultiSz): SizeInt;
- begin
- Result := JclWideStrings.MultiSzLength(Source);
- end;
- procedure AllocateWideMultiSz(var Dest: PWideMultiSz; Len: SizeInt);
- begin
- JclWideStrings.AllocateMultiSz(Dest, Len);
- end;
- procedure FreeWideMultiSz(var Dest: PWideMultiSz);
- begin
- JclWideStrings.FreeMultiSz(Dest);
- end;
- function WideMultiSzDup(const Source: PWideMultiSz): PWideMultiSz;
- begin
- Result := JclWideStrings.MultiSzDup(Source);
- end;
- {$ENDIF ~WINSCP}
- //=== TStrings Manipulation ==================================================
- procedure StrToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
- var
- I, L: SizeInt;
- Left: string;
- begin
- Assert(List <> nil);
- List.BeginUpdate;
- try
- List.Clear;
- L := Length(Sep);
- I := Pos(Sep, S);
- while I > 0 do
- begin
- Left := StrLeft(S, I - 1);
- if (Left <> '') or AllowEmptyString then
- List.Add(Left);
- Delete(S, 1, I + L - 1);
- I := Pos(Sep, S);
- end;
- if (S <> '') or AllowEmptyString then
- List.Add(S); // Ignore empty strings at the end (only if AllowEmptyString = False).
- finally
- List.EndUpdate;
- end;
- end;
- procedure StrIToStrings(S, Sep: string; const List: TStrings; const AllowEmptyString: Boolean = True);
- var
- I, L: SizeInt;
- LowerCaseStr: string;
- Left: string;
- begin
- Assert(List <> nil);
- LowerCaseStr := StrLower(S);
- Sep := StrLower(Sep);
- L := Length(Sep);
- I := Pos(Sep, LowerCaseStr);
- List.BeginUpdate;
- try
- List.Clear;
- while I > 0 do
- begin
- Left := StrLeft(S, I - 1);
- if (Left <> '') or AllowEmptyString then
- List.Add(Left);
- Delete(S, 1, I + L - 1);
- Delete(LowerCaseStr, 1, I + L - 1);
- I := Pos(Sep, LowerCaseStr);
- end;
- if (S <> '') or AllowEmptyString then
- List.Add(S); // Ignore empty strings at the end (only if AllowEmptyString = False).
- finally
- List.EndUpdate;
- end;
- end;
- function StringsToStr(const List: TStrings; const Sep: string; const AllowEmptyString: Boolean = True): string;
- var
- I, L: SizeInt;
- begin
- Result := '';
- for I := 0 to List.Count - 1 do
- begin
- if (List[I] <> '') or AllowEmptyString then
- begin
- // don't combine these into one addition, somehow it hurts performance
- Result := Result + List[I];
- Result := Result + Sep;
- end;
- end;
- // remove terminating separator
- if List.Count > 0 then
- begin
- L := Length(Sep);
- Delete(Result, Length(Result) - L + 1, L);
- end;
- end;
- function StringsToStr(const List: TStrings; const Sep: string; const NumberOfItems: SizeInt; const AllowEmptyString:
- Boolean = True): string;
- var
- I, L, N: SizeInt;
- begin
- Result := '';
- if List.Count > NumberOfItems then
- N := NumberOfItems
- else
- N := List.Count;
- for I := 0 to N - 1 do
- begin
- if (List[I] <> '') or AllowEmptyString then
- begin
- // don't combine these into one addition, somehow it hurts performance
- Result := Result + List[I];
- Result := Result + Sep;
- end;
- end;
- // remove terminating separator
- if N > 0 then
- begin
- L := Length(Sep);
- Delete(Result, Length(Result) - L + 1, L);
- end;
- end;
- procedure TrimStrings(const List: TStrings; DeleteIfEmpty: Boolean);
- var
- I: SizeInt;
- begin
- Assert(List <> nil);
- List.BeginUpdate;
- try
- for I := List.Count - 1 downto 0 do
- begin
- List[I] := Trim(List[I]);
- if (List[I] = '') and DeleteIfEmpty then
- List.Delete(I);
- end;
- finally
- List.EndUpdate;
- end;
- end;
- procedure TrimStringsRight(const List: TStrings; DeleteIfEmpty: Boolean);
- var
- I: SizeInt;
- begin
- Assert(List <> nil);
- List.BeginUpdate;
- try
- for I := List.Count - 1 downto 0 do
- begin
- List[I] := TrimRight(List[I]);
- if (List[I] = '') and DeleteIfEmpty then
- List.Delete(I);
- end;
- finally
- List.EndUpdate;
- end;
- end;
- procedure TrimStringsLeft(const List: TStrings; DeleteIfEmpty: Boolean);
- var
- I: SizeInt;
- begin
- Assert(List <> nil);
- List.BeginUpdate;
- try
- for I := List.Count - 1 downto 0 do
- begin
- List[I] := TrimLeft(List[I]);
- if (List[I] = '') and DeleteIfEmpty then
- List.Delete(I);
- end;
- finally
- List.EndUpdate;
- end;
- end;
- function AddStringToStrings(const S: string; Strings: TStrings; const Unique: Boolean): Boolean;
- begin
- Assert(Strings <> nil);
- Result := Unique and (Strings.IndexOf(S) <> -1);
- if not Result then
- Result := Strings.Add(S) > -1;
- end;
- //=== Miscellaneous ==========================================================
- function FileToString(const FileName: string): {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
- var
- fs: TFileStream;
- Len: SizeInt;
- begin
- fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
- try
- Len := fs.Size;
- SetLength(Result, Len);
- if Len > 0 then
- fs.ReadBuffer(Result[1], Len);
- finally
- fs.Free;
- end;
- end;
- procedure StringToFile(const FileName: string; const Contents: {$IFDEF COMPILER12_UP}RawByteString{$ELSE}AnsiString{$ENDIF};
- Append: Boolean);
- var
- FS: TFileStream;
- Len: SizeInt;
- begin
- if Append and FileExists(filename) then
- FS := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyWrite)
- else
- FS := TFileStream.Create(FileName, fmCreate);
- try
- if Append then
- FS.Seek(0, soEnd); // faster than .Position := .Size
- Len := Length(Contents);
- if Len > 0 then
- FS.WriteBuffer(Contents[1], Len);
- finally
- FS.Free;
- end;
- end;
- function StrToken(var S: string; Separator: Char): string;
- var
- I: SizeInt;
- begin
- I := Pos(Separator, S);
- if I <> 0 then
- begin
- Result := Copy(S, 1, I - 1);
- Delete(S, 1, I);
- end
- else
- begin
- Result := S;
- S := '';
- end;
- end;
- procedure StrTokens(const S: string; const List: TStrings);
- var
- Start: PChar;
- Token: string;
- Done: Boolean;
- begin
- Assert(List <> nil);
- if List = nil then
- Exit;
- List.BeginUpdate;
- try
- List.Clear;
- Start := Pointer(S);
- repeat
- Done := JclStrings.StrWord(Start, Token);
- if Token <> '' then
- List.Add(Token);
- until Done;
- finally
- List.EndUpdate;
- end;
- end;
- function StrWord(const S: string; var Index: SizeInt; out Word: string): Boolean;
- var
- Start: SizeInt;
- C: Char;
- begin
- Word := '';
- if (S = '') then
- begin
- Result := True;
- Exit;
- end;
- Start := Index;
- Result := False;
- while True do
- begin
- C := S[Index];
- case C of
- #0:
- begin
- if Start <> 0 then
- Word := Copy(S, Start, Index - Start);
- Result := True;
- Exit;
- end;
- NativeSpace, NativeLineFeed, NativeCarriageReturn:
- begin
- if Start <> 0 then
- begin
- Word := Copy(S, Start, Index - Start);
- Exit;
- end
- else
- begin
- while CharIsWhiteSpace(C) do
- begin
- Inc(Index);
- C := S[Index];
- end;
- end;
- end;
- else
- if Start = 0 then
- Start := Index;
- Inc(Index);
- end;
- end;
- end;
- function StrWord(var S: PChar; out Word: string): Boolean;
- var
- Start: PChar;
- begin
- Word := '';
- if S = nil then
- begin
- Result := True;
- Exit;
- end;
- Start := nil;
- Result := False;
- while True do
- begin
- case S^ of
- #0:
- begin
- if Start <> nil then
- SetString(Word, Start, S - Start);
- Result := True;
- Exit;
- end;
- NativeSpace, NativeLineFeed, NativeCarriageReturn:
- begin
- if Start <> nil then
- begin
- SetString(Word, Start, S - Start);
- Exit;
- end
- else
- while CharIsWhiteSpace(S^) do
- Inc(S);
- end;
- else
- if Start = nil then
- Start := S;
- Inc(S);
- end;
- end;
- end;
- function StrIdent(const S: string; var Index: SizeInt; out Ident: string): Boolean;
- var
- Start: SizeInt;
- C: Char;
- begin
- Ident := '';
- if (S = '') then
- begin
- Result := True;
- Exit;
- end;
- Start := Index;
- Result := False;
- while True do
- begin
- C := S[Index];
- if CharIsValidIdentifierLetter(C) then
- begin
- if Start = 0 then
- Start := Index;
- end
- else
- if C = #0 then
- begin
- if Start <> 0 then
- Ident := Copy(S, Start, Index - Start);
- Result := True;
- Exit;
- end
- else
- begin
- if Start <> 0 then
- begin
- Ident := Copy(S, Start, Index - Start);
- Exit;
- end;
- end;
- Inc(Index);
- end;
- end;
- function StrIdent(var S: PChar; out Ident: string): Boolean;
- var
- Start: PChar;
- C: Char;
- begin
- Ident := '';
- if S = nil then
- begin
- Result := True;
- Exit;
- end;
- Start := nil;
- Result := False;
- while True do
- begin
- C := S^;
- if CharIsValidIdentifierLetter(C) then
- begin
- if Start = nil then
- Start := S;
- end
- else
- if C = #0 then
- begin
- if Start <> nil then
- SetString(Ident, Start, S - Start);
- Result := True;
- Exit;
- end
- else
- begin
- if Start <> nil then
- begin
- SetString(Ident, Start, S - Start);
- Exit;
- end
- end;
- Inc(S);
- end;
- end;
- procedure StrTokenToStrings(S: string; Separator: Char; const List: TStrings);
- var
- Token: string;
- begin
- Assert(List <> nil);
- if List = nil then
- Exit;
- List.BeginUpdate;
- try
- List.Clear;
- while S <> '' do
- begin
- Token := StrToken(S, Separator);
- List.Add(Token);
- end;
- finally
- List.EndUpdate;
- end;
- end;
- {$IFNDEF WINSCP}
- function StrToFloatSafe(const S: string): Float;
- var
- Temp: string;
- I, J, K: SizeInt;
- SwapSeparators, IsNegative: Boolean;
- DecSep, ThouSep, C: Char;
- begin
- DecSep := {$IFDEF RTL220_UP}FormatSettings.{$ENDIF}DecimalSeparator;
- ThouSep := {$IFDEF RTL220_UP}FormatSettings.{$ENDIF}ThousandSeparator;
- Temp := S;
- SwapSeparators := False;
- IsNegative := False;
- J := 0;
- for I := 1 to Length(Temp) do
- begin
- C := Temp[I];
- if C = '-' then
- IsNegative := not IsNegative
- else
- if (C <> ' ') and (C <> '(') and (C <> '+') then
- begin
- // if it appears prior to any digit, it has to be a decimal separator
- SwapSeparators := Temp[I] = ThouSep;
- J := I;
- Break;
- end;
- end;
- if not SwapSeparators then
- begin
- K := CharPos(Temp, DecSep);
- SwapSeparators :=
- // if it appears prior to any digit, it has to be a decimal separator
- (K > J) and
- // if it appears multiple times, it has to be a thousand separator
- ((StrCharCount(Temp, DecSep) > 1) or
- // we assume (consistent with Windows Platform SDK documentation),
- // that thousand separators appear only to the left of the decimal
- (K < CharPos(Temp, ThouSep)));
- end;
- if SwapSeparators then
- begin
- // assume a numerical string from a different locale,
- // where DecimalSeparator and ThousandSeparator are exchanged
- for I := 1 to Length(Temp) do
- if Temp[I] = DecSep then
- Temp[I] := ThouSep
- else
- if Temp[I] = ThouSep then
- Temp[I] := DecSep;
- end;
- Temp := StrKeepChars(Temp, CharIsNumber);
- if Length(Temp) > 0 then
- begin
- if Temp[1] = DecSep then
- Temp := '0' + Temp;
- if Temp[Length(Temp)] = DecSep then
- Temp := Temp + '0';
- Result := StrToFloat(Temp);
- if IsNegative then
- Result := -Result;
- end
- else
- Result := 0.0;
- end;
- function StrToIntSafe(const S: string): Integer;
- begin
- Result := Trunc(StrToFloatSafe(S));
- end;
- {$ENDIF ~WINSCP}
- procedure StrNormIndex(const StrLen: SizeInt; var Index: SizeInt; var Count: SizeInt); overload;
- begin
- Index := Max(1, Min(Index, StrLen + 1));
- Count := Max(0, Min(Count, StrLen + 1 - Index));
- end;
- function ArrayOf(List: TStrings): TDynStringArray;
- var
- I: SizeInt;
- begin
- if List <> nil then
- begin
- SetLength(Result, List.Count);
- for I := 0 to List.Count - 1 do
- Result[I] := List[I];
- end
- else
- Result := nil;
- end;
- const
- BoolToStr: array [Boolean] of string = ('false', 'true');
- type
- TInterfacedObjectAccess = class(TInterfacedObject);
- procedure MoveChar(const Source; var Dest; Count: SizeInt);
- begin
- if Count > 0 then
- Move(Source, Dest, Count * SizeOf(Char));
- end;
- function DotNetFormat(const Fmt: string; const Arg0: Variant): string;
- begin
- Result := DotNetFormat(Fmt, [Arg0]);
- end;
- function DotNetFormat(const Fmt: string; const Arg0, Arg1: Variant): string;
- begin
- Result := DotNetFormat(Fmt, [Arg0, Arg1]);
- end;
- function DotNetFormat(const Fmt: string; const Arg0, Arg1, Arg2: Variant): string;
- begin
- Result := DotNetFormat(Fmt, [Arg0, Arg1, Arg2]);
- end;
- function DotNetFormat(const Fmt: string; const Args: array of const): string;
- var
- F, P: PChar;
- Len, Capacity, Count: SizeInt;
- Index: SizeInt;
- ErrorCode: Integer;
- S: string;
- procedure Grow(Count: SizeInt);
- begin
- if Len + Count > Capacity then
- begin
- Capacity := Capacity * 5 div 3 + Count;
- SetLength(Result, Capacity);
- end;
- end;
- function InheritsFrom(AClass: TClass; const ClassName: string): Boolean;
- begin
- Result := True;
- while AClass <> nil do
- begin
- if CompareText(AClass.ClassName, ClassName) = 0 then
- Exit;
- AClass := AClass.ClassParent;
- end;
- Result := False;
- end;
- function GetStringOf(const V: TVarData; Index: SizeInt): string; overload;
- begin
- case V.VType of
- varEmpty, varNull:
- raise ArgumentNullException.CreateRes(@RsArgumentIsNull);
- varSmallInt:
- Result := IntToStr(V.VSmallInt);
- varInteger:
- Result := IntToStr(V.VInteger);
- varSingle:
- Result := FloatToStr(V.VSingle);
- varDouble:
- Result := FloatToStr(V.VDouble);
- varCurrency:
- Result := CurrToStr(V.VCurrency);
- varDate:
- Result := DateTimeToStr(V.VDate);
- varOleStr:
- Result := V.VOleStr;
- varBoolean:
- Result := BoolToStr[V.VBoolean <> False];
- varByte:
- Result := IntToStr(V.VByte);
- varWord:
- Result := IntToStr(V.VWord);
- varShortInt:
- Result := IntToStr(V.VShortInt);
- varLongWord:
- Result := IntToStr(V.VLongWord);
- varInt64:
- Result := IntToStr(V.VInt64);
- varString:
- Result := string(V.VString);
- {$IFDEF SUPPORTS_UNICODE_STRING}
- varUString:
- Result := string(V.VUString);
- {$ENDIF SUPPORTS_UNICODE_STRING}
- {varArray,
- varDispatch,
- varError,
- varUnknown,
- varAny,
- varByRef:}
- else
- raise ArgumentNullException.CreateResFmt(@RsDotNetFormatArgumentNotSupported, [Index]);
- end;
- end;
- function GetStringOf(Index: SizeInt): string; overload;
- var
- V: TVarRec;
- Intf: IToString;
- begin
- V := Args[Index];
- if (V.VInteger = 0) and
- (V.VType in [vtExtended, vtString, vtObject, vtClass, vtCurrency,
- vtInterface, vtInt64]) then
- raise ArgumentNullException.CreateResFmt(@RsArgumentIsNull, [Index]);
- case V.VType of
- vtInteger:
- Result := IntToStr(V.VInteger);
- vtBoolean:
- Result := BoolToStr[V.VBoolean];
- vtChar:
- Result := string(AnsiString(V.VChar));
- vtExtended:
- Result := FloatToStr(V.VExtended^);
- vtString:
- Result := string(V.VString^);
- vtPointer:
- Result := IntToHex(TJclAddr(V.VPointer), 8);
- vtPChar:
- Result := string(AnsiString(V.VPChar));
- vtObject:
- if (V.VObject is TInterfacedObject) and V.VObject.GetInterface(IToString, Intf) then
- begin
- Result := Intf.ToString;
- Pointer(Intf) := nil; // do not release the object
- // undo the RefCount change
- Dec(TInterfacedObjectAccess(V.VObject).FRefCount);
- end
- else
- if ((V.VObject is TComponent) or (V.VObject is TInterfacedPersistent)) and V.VObject.GetInterface(IToString, Intf) then
- Result := Intf.ToString
- {$IFDEF RTL200_UP}
- else
- Result := V.VObject.ToString;
- {$ELSE}
- else
- raise ArgumentNullException.CreateResFmt(@RsDotNetFormatObjectArgumentNotSupported, [V.VObject.ClassName, Index]);
- {$ENDIF RTL200_UP}
- vtClass:
- Result := V.VClass.ClassName;
- vtWideChar:
- Result := V.VWideChar;
- vtPWideChar:
- Result := V.VPWideChar;
- vtAnsiString:
- Result := string(V.VAnsiString);
- vtCurrency:
- Result := CurrToStr(V.VCurrency^);
- vtVariant:
- Result := GetStringOf(TVarData(V.VVariant^), Index);
- vtInterface:
- if IInterface(V.VInterface).QueryInterface(IToString, Intf) = 0 then
- Result := IToString(Intf).ToString
- else
- raise ArgumentNullException.CreateResFmt(@RsDotNetFormatArgumentNotSupported, [Index]);
- vtWideString:
- Result := WideString(V.VWideString);
- vtInt64:
- Result := IntToStr(V.VInt64^);
- {$IFDEF SUPPORTS_UNICODE_STRING}
- vtUnicodeString:
- Result := UnicodeString(V.VUnicodeString);
- {$ENDIF SUPPORTS_UNICODE_STRING}
- else
- raise ArgumentNullException.CreateResFmt(@RsDotNetFormatArgumentNotSupported, [Index]);
- end;
- end;
- begin
- if Length(Args) = 0 then
- begin
- Result := Fmt;
- Exit;
- end;
- Len := 0;
- Capacity := Length(Fmt);
- SetLength(Result, Capacity);
- if Capacity = 0 then
- raise ArgumentNullException.CreateRes(@RsDotNetFormatNullFormat);
- P := Pointer(Fmt);
- F := P;
- while True do
- begin
- if (P[0] = #0) or (P[0] = '{') then
- begin
- Count := P - F;
- Inc(P);
- if (P[-1] <> #0) and (P[0] = '{') then
- Inc(Count); // include '{'
- if Count > 0 then
- begin
- Grow(Count);
- MoveChar(F[0], Result[Len + 1], Count);
- Inc(Len, Count);
- end;
- if P[-1] = #0 then
- Break;
- if P[0] <> '{' then
- begin
- F := P;
- Inc(P);
- while (P[0] <> #0) and (P[0] <> '}') do
- Inc(P);
- SetString(S, F, P - F);
- Val(S, Index, ErrorCode);
- if ErrorCode <> 0 then
- raise FormatException.CreateRes(@RsFormatException);
- if (Index < 0) or (Index > High(Args)) then
- raise FormatException.CreateRes(@RsFormatException);
- S := GetStringOf(Index);
- if S <> '' then
- begin
- Grow(Length(S));
- MoveChar(S[1], Result[Len + 1], Length(S));
- Inc(Len, Length(S));
- end;
- if P[0] = #0 then
- Break;
- end;
- F := P + 1;
- end
- else
- if (P[0] = '}') and (P[1] = '}') then
- begin
- Count := P - F + 1;
- Inc(P); // skip next '}'
- Grow(Count);
- MoveChar(F[0], Result[Len + 1], Count);
- Inc(Len, Count);
- F := P + 1;
- end;
- Inc(P);
- end;
- SetLength(Result, Len);
- end;
- //=== { TJclStringBuilder } =====================================================
- constructor TJclStringBuilder.Create(Capacity: SizeInt; MaxCapacity: SizeInt);
- begin
- inherited Create;
- SetLength(FChars, Capacity);
- FMaxCapacity := MaxCapacity;
- end;
- constructor TJclStringBuilder.Create(const Value: string; Capacity: SizeInt);
- begin
- Create(Capacity);
- Append(Value);
- end;
- constructor TJclStringBuilder.Create(const Value: string; StartIndex, Length, Capacity: SizeInt);
- begin
- Create(Capacity);
- Append(Value, StartIndex + 1, Length);
- end;
- function TJclStringBuilder.ToString: string;
- begin
- if FLength > 0 then
- SetString(Result, PChar(@FChars[0]), FLength)
- else
- Result := '';
- end;
- function TJclStringBuilder.EnsureCapacity(Capacity: SizeInt): SizeInt;
- begin
- if System.Length(FChars) < Capacity then
- SetCapacity(Capacity);
- Result := System.Length(FChars);
- end;
- procedure TJclStringBuilder.Clear;
- begin
- Length := 0;
- end;
- procedure TJclStringBuilder.SetCapacity(const Value: SizeInt);
- begin
- if Value <> System.Length(FChars) then
- begin
- SetLength(FChars, Value);
- if Value < FLength then
- FLength := Value;
- end;
- end;
- function TJclStringBuilder.GetChars(Index: SizeInt): Char;
- begin
- Result := FChars[Index];
- end;
- procedure TJclStringBuilder.SetChars(Index: SizeInt; const Value: Char);
- begin
- FChars[Index] := Value;
- end;
- procedure TJclStringBuilder.Set_Length(const Value: SizeInt);
- begin
- FLength := Value;
- end;
- function TJclStringBuilder.GetCapacity: SizeInt;
- begin
- Result := System.Length(FChars);
- end;
- function TJclStringBuilder.AppendPChar(Value: PChar; Count: SizeInt; RepeatCount: SizeInt): TJclStringBuilder;
- var
- Capacity: SizeInt;
- begin
- if (Count > 0) and (RepeatCount > 0) then
- begin
- repeat
- Capacity := System.Length(FChars);
- if Capacity + Count > MaxCapacity then
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- if Capacity < FLength + Count then
- SetLength(FChars, Capacity * 5 div 3 + Count);
- if Count = 1 then
- FChars[FLength] := Value[0]
- else
- MoveChar(Value[0], FChars[FLength], Count);
- Inc(FLength, Count);
- Dec(RepeatCount);
- until RepeatCount <= 0;
- end;
- Result := Self;
- end;
- function TJclStringBuilder.InsertPChar(Index: SizeInt; Value: PChar; Count,
- RepeatCount: SizeInt): TJclStringBuilder;
- var
- Capacity: SizeInt;
- begin
- if (Index < 0) or (Index > FLength) then
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- if Index = FLength then
- AppendPChar(Value, Count, RepeatCount)
- else
- if (Count > 0) and (RepeatCount > 0) then
- begin
- repeat
- Capacity := System.Length(FChars);
- if Capacity + Count > MaxCapacity then
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- if Capacity < FLength + Count then
- SetLength(FChars, Capacity * 5 div 3 + Count);
- MoveChar(FChars[Index], FChars[Index + Count], FLength - Index);
- if Count = 1 then
- FChars[Index] := Value[0]
- else
- MoveChar(Value[0], FChars[Index], Count);
- Inc(FLength, Count);
- Dec(RepeatCount);
- Inc(Index, Count); // little optimization
- until RepeatCount <= 0;
- end;
- Result := Self;
- end;
- function TJclStringBuilder.Append(const Value: array of Char): TJclStringBuilder;
- var
- Len: SizeInt;
- begin
- Len := System.Length(Value);
- if Len > 0 then
- AppendPChar(@Value[0], Len);
- Result := Self;
- end;
- function TJclStringBuilder.Append(const Value: array of Char; StartIndex, Length: SizeInt): TJclStringBuilder;
- var
- Len: SizeInt;
- begin
- Len := System.Length(Value);
- if (Length > 0) and (StartIndex < Len) then
- begin
- if StartIndex + Length > Len then
- Length := Len - StartIndex;
- AppendPChar(PChar(@Value[0]) + StartIndex, Length);
- end;
- Result := Self;
- end;
- function TJclStringBuilder.Append(Value: Char; RepeatCount: SizeInt = 1): TJclStringBuilder;
- begin
- Result := AppendPChar(@Value, 1, RepeatCount);
- end;
- function TJclStringBuilder.Append(const Value: string): TJclStringBuilder;
- var
- Len: SizeInt;
- begin
- Len := System.Length(Value);
- if Len > 0 then
- AppendPChar(Pointer(Value), Len);
- Result := Self;
- end;
- function TJclStringBuilder.Append(const Value: string; StartIndex, Length: SizeInt): TJclStringBuilder;
- var
- Len: SizeInt;
- begin
- Len := System.Length(Value);
- if (Length > 0) and (StartIndex < Len) then
- begin
- if StartIndex + Length > Len then
- Length := Len - StartIndex;
- AppendPChar(PChar(Pointer(Value)) + StartIndex, Length);
- end;
- Result := Self;
- end;
- function TJclStringBuilder.Append(Value: Boolean): TJclStringBuilder;
- begin
- Result := Append(BoolToStr[Value]);
- end;
- function TJclStringBuilder.Append(Value: Cardinal): TJclStringBuilder;
- begin
- Result := Append(IntToStr(Value));
- end;
- function TJclStringBuilder.Append(Value: Integer): TJclStringBuilder;
- begin
- Result := Append(IntToStr(Value));
- end;
- function TJclStringBuilder.Append(Value: Double): TJclStringBuilder;
- begin
- Result := Append(FloatToStr(Value));
- end;
- function TJclStringBuilder.Append(Value: Int64): TJclStringBuilder;
- begin
- Result := Append(IntToStr(Value));
- end;
- function TJclStringBuilder.Append(Obj: TObject): TJclStringBuilder;
- begin
- Result := Append(DotNetFormat('{0}', [Obj]));
- end;
- function TJclStringBuilder.AppendFormat(const Fmt: string; Arg0: Variant): TJclStringBuilder;
- begin
- Result := Append(DotNetFormat(Fmt, [Arg0]));
- end;
- function TJclStringBuilder.AppendFormat(const Fmt: string; Arg0, Arg1: Variant): TJclStringBuilder;
- begin
- Result := Append(DotNetFormat(Fmt, [Arg0, Arg1]));
- end;
- function TJclStringBuilder.AppendFormat(const Fmt: string; Arg0, Arg1, Arg2: Variant): TJclStringBuilder;
- begin
- Result := Append(DotNetFormat(Fmt, [Arg0, Arg1, Arg2]));
- end;
- function TJclStringBuilder.AppendFormat(const Fmt: string; const Args: array of const): TJclStringBuilder;
- begin
- Result := Append(DotNetFormat(Fmt, Args));
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; const Value: array of Char): TJclStringBuilder;
- var
- Len: SizeInt;
- begin
- Len := System.Length(Value);
- if Len > 0 then
- InsertPChar(Index, @Value[0], Len);
- Result := Self;
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; const Value: string; Count: SizeInt): TJclStringBuilder;
- var
- Len: SizeInt;
- begin
- Len := System.Length(Value);
- if Len > 0 then
- InsertPChar(Index, Pointer(Value), Len, Count);
- Result := Self;
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; Value: Boolean): TJclStringBuilder;
- begin
- Result := Insert(Index, BoolToStr[Value]);
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; const Value: array of Char;
- StartIndex, Length: SizeInt): TJclStringBuilder;
- var
- Len: SizeInt;
- begin
- Len := System.Length(Value);
- if (Length > 0) and (StartIndex < Len) then
- begin
- if StartIndex + Length > Len then
- Length := Len - StartIndex;
- InsertPChar(Index, PChar(@Value[0]) + StartIndex, Length);
- end;
- Result := Self;
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; Value: Double): TJclStringBuilder;
- begin
- Result := Insert(Index, FloatToStr(Value));
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; Value: Int64): TJclStringBuilder;
- begin
- Result := Insert(Index, IntToStr(Value));
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; Value: Cardinal): TJclStringBuilder;
- begin
- Result := Insert(Index, IntToStr(Value));
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; Value: Integer): TJclStringBuilder;
- begin
- Result := Insert(Index, IntToStr(Value));
- end;
- function TJclStringBuilder.Insert(Index: SizeInt; Obj: TObject): TJclStringBuilder;
- begin
- Result := Insert(Index, DotNetFormat('{0}', [Obj]));
- end;
- function TJclStringBuilder.Remove(StartIndex, Length: SizeInt): TJclStringBuilder;
- begin
- if (StartIndex < 0) or (Length < 0) or (StartIndex + Length >= FLength) then
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- if Length > 0 then
- begin
- MoveChar(FChars[StartIndex + Length], FChars[StartIndex], FLength - (StartIndex + Length));
- Dec(FLength, Length);
- end;
- Result := Self;
- end;
- function TJclStringBuilder.Replace(OldChar, NewChar: Char; StartIndex,
- Count: SizeInt): TJclStringBuilder;
- var
- I: SizeInt;
- begin
- if Count = -1 then
- Count := FLength;
- if (StartIndex < 0) or (Count < 0) or (StartIndex + Count > FLength) then
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- if (Count > 0) and (OldChar <> NewChar) then
- begin
- for I := StartIndex to StartIndex + Length - 1 do
- if FChars[I] = OldChar then
- FChars[I] := NewChar;
- end;
- Result := Self;
- end;
- function TJclStringBuilder.Replace(OldValue, NewValue: string; StartIndex, Count: SizeInt): TJclStringBuilder;
- var
- I: SizeInt;
- Offset: SizeInt;
- NewLen, OldLen, Capacity: SizeInt;
- begin
- if Count = -1 then
- Count := FLength;
- if (StartIndex < 0) or (Count < 0) or (StartIndex + Count > FLength) then
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- if OldValue = '' then
- raise ArgumentException.CreateResFmt(@RsArgumentIsNull, [0]);
- if (Count > 0) and (OldValue <> NewValue) then
- begin
- OldLen := System.Length(OldValue);
- NewLen := System.Length(NewValue);
- Offset := NewLen - OldLen;
- Capacity := System.Length(FChars);
- for I := StartIndex to StartIndex + Length - 1 do
- if FChars[I] = OldValue[1] then
- begin
- if OldLen > 1 then
- if StrLComp(@FChars[I + 1], PChar(OldValue) + 1, OldLen - 1) <> 0 then
- Continue;
- if Offset <> 0 then
- begin
- if FLength - OldLen + NewLen > MaxCurrency then
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- if Capacity < FLength + Offset then
- begin
- Capacity := Capacity * 5 div 3 + Offset;
- SetLength(FChars, Capacity);
- end;
- if Offset < 0 then
- MoveChar(FChars[I - Offset], FChars[I], FLength - I)
- else
- MoveChar(FChars[I + OldLen], FChars[I + OldLen + Offset], FLength - OldLen - I);
- Inc(FLength, Offset);
- end;
- if NewLen > 0 then
- begin
- if (OldLen = 1) and (NewLen = 1) then
- FChars[I] := NewValue[1]
- else
- MoveChar(NewValue[1], FChars[I], NewLen);
- end;
- end;
- end;
- Result := Self;
- end;
- function StrExpandTabs(S: string): string;
- begin
- // use an empty tab set, which will default to a tab width of 2
- Result := TJclTabSet(nil).Expand(s);
- end;
- function StrExpandTabs(S: string; TabWidth: SizeInt): string;
- var
- TabSet: TJclTabSet;
- begin
- // create a tab set with no tab stops and the given tab width
- TabSet := TJclTabSet.Create(TabWidth);
- try
- Result := TabSet.Expand(S);
- finally
- TabSet.Free;
- end;
- end;
- function StrExpandTabs(S: string; TabSet: TJclTabSet): string;
- begin
- // use the provided tab set to perform the expansion
- Result := TabSet.Expand(S);
- end;
- function StrOptimizeTabs(S: string): string;
- begin
- // use an empty tab set, which will default to a tab width of 2
- Result := TJclTabSet(nil).Optimize(s);
- end;
- function StrOptimizeTabs(S: string; TabWidth: SizeInt): string;
- var
- TabSet: TJclTabSet;
- begin
- // create a tab set with no tab stops and the given tab width
- TabSet := TJclTabSet.Create(TabWidth);
- try
- Result := TabSet.Optimize(S);
- finally
- TabSet.Free;
- end;
- end;
- function StrOptimizeTabs(S: string; TabSet: TJclTabSet): string;
- begin
- // use the provided tab set to perform the optimization
- Result := TabSet.Optimize(S);
- end;
- // === { TTabSetData } ===================================================
- type
- TTabSetData = class
- public
- FStops: TDynSizeIntArray;
- FRealWidth: SizeInt;
- FRefCount: SizeInt;
- FWidth: SizeInt;
- FZeroBased: Boolean;
- constructor Create(TabStops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt);
- function Add(Column: SizeInt): SizeInt;
- function AddRef: SizeInt;
- procedure CalcRealWidth;
- function FindStop(Column: SizeInt): SizeInt;
- function ReleaseRef: SizeInt;
- procedure RemoveAt(Index: SizeInt);
- procedure SetStops(Index, Value: SizeInt);
- end;
- constructor TTabSetData.Create(TabStops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt);
- var
- idx: SizeInt;
- begin
- inherited Create;
- FRefCount := 1;
- for idx := 0 to High(Tabstops) do
- Add(Tabstops[idx]);
- FWidth := TabWidth;
- FZeroBased := ZeroBased;
- CalcRealWidth;
- end;
- function TTabSetData.Add(Column: SizeInt): SizeInt;
- var
- I: SizeInt;
- begin
- if Column < Ord(FZeroBased) then
- raise ArgumentOutOfRangeException.Create('Column');
- Result := FindStop(Column);
- if Result < 0 then
- begin
- // the column doesn't exist; invert the result of FindStop to get the correct index position
- Result := not Result;
- // increase the tab stop array
- SetLength(FStops, Length(FStops) + 1);
- // shift rooms after the insert position
- for I := High(FStops) - 1 downto Result do
- FStops[I + 1] := FStops[I];
- // add the tab stop at the correct location
- FStops[Result] := Column;
- CalcRealWidth;
- end
- else
- begin
- raise EJclStringError.CreateRes(@RsTabs_DuplicatesNotAllowed);
- end;
- end;
- function TTabSetData.AddRef: SizeInt;
- begin
- Result := LockedInc(FRefCount);
- end;
- procedure TTabSetData.CalcRealWidth;
- begin
- if FWidth < 1 then
- begin
- if Length(FStops) > 1 then
- FRealWidth := FStops[High(FStops)] - FStops[Pred(High(FStops))]
- else
- if Length(FStops) = 1 then
- FRealWidth := FStops[0]
- else
- FRealWidth := 2;
- end
- else
- FRealWidth := FWidth;
- end;
- function TTabSetData.FindStop(Column: SizeInt): SizeInt;
- begin
- Result := High(FStops);
- while (Result >= 0) and (FStops[Result] > Column) do
- Dec(Result);
- if (Result >= 0) and (FStops[Result] <> Column) then
- Result := not Succ(Result);
- end;
- function TTabSetData.ReleaseRef: SizeInt;
- begin
- Result := LockedDec(FRefCount);
- if Result <= 0 then
- Destroy;
- end;
- procedure TTabSetData.RemoveAt(Index: SizeInt);
- var
- I: SizeInt;
- begin
- for I := Index to High(FStops) - 1 do
- FStops[I] := FStops[I + 1];
- SetLength(FStops, High(FStops));
- CalcRealWidth;
- end;
- procedure TTabSetData.SetStops(Index, Value: SizeInt);
- var
- temp: SizeInt;
- begin
- if (Index < 0) or (Index >= Length(FStops)) then
- begin
- raise ArgumentOutOfRangeException.CreateRes(@RsArgumentOutOfRange);
- end
- else
- begin
- temp := FindStop(Value);
- if temp < 0 then
- begin
- // remove existing tab stop...
- RemoveAt(Index);
- // now add the new tab stop
- Add(Value);
- end
- else
- if temp <> Index then
- begin
- // new tab stop already present at another index
- raise EJclStringError.CreateRes(@RsTabs_DuplicatesNotAllowed);
- end;
- end;
- end;
- //=== { TJclTabSet } =====================================================
- constructor TJclTabSet.Create;
- begin
- // no tab stops, tab width set to auto
- Create([], True, 0);
- end;
- constructor TJclTabSet.Create(TabWidth: SizeInt);
- begin
- // no tab stops, specified tab width
- Create([], True, TabWidth);
- end;
- constructor TJclTabSet.Create(const Tabstops: array of SizeInt; ZeroBased: Boolean);
- begin
- // specified tab stops, tab width equal to distance between last two tab stops
- Create(Tabstops, ZeroBased, 0);
- end;
- constructor TJclTabSet.Create(const Tabstops: array of SizeInt; ZeroBased: Boolean; TabWidth: SizeInt);
- begin
- inherited Create;
- FData := TTabSetData.Create(Tabstops, ZeroBased, TabWidth);
- end;
- constructor TJclTabSet.Create(Data: TObject);
- begin
- inherited Create;
- // add a reference to the data
- TTabSetData(Data).AddRef;
- // assign the data to this instance
- FData := TTabSetData(Data);
- end;
- destructor TJclTabSet.Destroy;
- begin
- // release the reference to the tab set data
- TTabSetData(FData).ReleaseRef;
- // make sure we won't accidentally refer to it later, just in case something goes wrong during destruction
- FData := nil;
- // really destroy the instance
- inherited Destroy;
- end;
- function TJclTabSet.Add(Column: SizeInt): SizeInt;
- begin
- if Self = nil then
- raise NullReferenceException.Create;
- Result := TTabSetData(FData).Add(Column);
- end;
- function TJclTabSet.Clone: TJclTabSet;
- begin
- if Self <> nil then
- Result := TJclTabSet.Create(TTabSetData(FData).FStops, TTabSetData(FData).FZeroBased, TTabSetData(FData).FWidth)
- else
- Result := nil;
- end;
- function TJclTabSet.Delete(Column: SizeInt): SizeInt;
- begin
- Result := TTabSetData(FData).FindStop(Column);
- if Result >= 0 then
- TTabSetData(FData).RemoveAt(Result);
- end;
- function TJclTabSet.Expand(const S: string): string;
- begin
- Result := Expand(s, StartColumn);
- end;
- function TJclTabSet.Expand(const S: string; Column: SizeInt): string;
- var
- sb: TJclStringBuilder;
- head: PChar;
- cur: PChar;
- begin
- if Column < StartColumn then
- raise ArgumentOutOfRangeException.Create('Column');
- sb := TJclStringBuilder.Create(Length(S));
- try
- cur := PChar(S);
- while cur^ <> #0 do
- begin
- head := cur;
- while (cur^ <> #0) and (cur^ <> #9) do
- begin
- if CharIsReturn(cur^) then
- Column := StartColumn
- else
- Inc(Column);
- Inc(cur);
- end;
- if cur > head then
- sb.Append(head, 0, cur - head);
- if cur^ = #9 then
- begin
- sb.Append(' ', TabFrom(Column) - Column);
- Column := TabFrom(Column);
- Inc(cur);
- end;
- end;
- Result := sb.ToString;
- finally
- sb.Free;
- end;
- end;
- function TJclTabSet.FindStop(Column: SizeInt): SizeInt;
- begin
- if Self <> nil then
- Result := TTabSetData(FData).FindStop(Column)
- else
- Result := -1;
- end;
- class function TJclTabSet.FromString(const S: string): TJclTabSet;
- var
- cur: PChar;
- function ParseNumber: Integer;
- var
- head: PChar;
- begin
- StrSkipChars(cur, CharIsWhiteSpace);
- head := cur;
- while CharIsDigit(cur^) do
- Inc(cur);
- Result := -1;
- if (cur <= head) or not TryStrToInt(Copy(head, 1, cur - head), Result) then
- Result := -1;
- end;
- procedure ParseStops;
- var
- openBracket, hadComma: Boolean;
- num: SizeInt;
- begin
- StrSkipChars(cur, CharIsWhiteSpace);
- openBracket := cur^ = '[';
- hadComma := False;
- if openBracket then
- Inc(cur);
- repeat
- num := ParseNumber;
- if (num < 0) and hadComma then
- raise EJclStringError.CreateRes(@RsTabs_StopExpected)
- else
- if num >= 0 then
- Result.Add(num);
- StrSkipChars(cur, CharIsWhiteSpace);
- hadComma := cur^ = ',';
- if hadComma then
- Inc(cur);
- until (cur^ = #0) or (cur^ = '+') or (cur^ = ']');
- if hadComma then
- raise EJclStringError.CreateRes(@RsTabs_StopExpected)
- else
- if openBracket and (cur^ <> ']') then
- raise EJclStringError.CreateRes(@RsTabs_CloseBracketExpected);
- end;
- procedure ParseTabWidth;
- var
- num: SizeInt;
- begin
- StrSkipChars(cur, CharIsWhiteSpace);
- if cur^ = '+' then
- begin
- Inc(cur);
- StrSkipChars(cur, CharIsWhiteSpace);
- num := ParseNumber;
- if (num < 0) then
- raise EJclStringError.CreateRes(@RsTabs_TabWidthExpected)
- else
- Result.TabWidth := num;
- end;
- end;
- procedure ParseZeroBasedFlag;
- begin
- StrSkipChars(cur, CharIsWhiteSpace);
- if cur^ = '0' then
- begin
- Inc(cur);
- if CharIsWhiteSpace(cur^) or (cur^ = #0) or (cur^ = '[') then
- begin
- Result.ZeroBased := True;
- StrSkipChars(cur, CharIsWhiteSpace);
- end
- else
- Dec(cur);
- end;
- end;
- begin
- Result := TJclTabSet.Create;
- try
- Result.ZeroBased := False;
- cur := PChar(S);
- ParseZeroBasedFlag;
- ParseStops;
- ParseTabWidth;
- except
- // clean up the partially complete instance (to avoid memory leaks)...
- Result.Free;
- // ... and re-raise the exception
- raise;
- end;
- end;
- function TJclTabSet.GetCount: SizeInt;
- begin
- if Self <> nil then
- Result := Length(TTabSetData(FData).FStops)
- else
- Result := 0;
- end;
- function TJclTabSet.GetStops(Index: SizeInt): SizeInt;
- begin
- if Self <> nil then
- begin
- if (Index < 0) or (Index >= Length(TTabSetData(FData).FStops)) then
- begin
- raise EJclStringError.CreateRes(@RsArgumentOutOfRange);
- end
- else
- Result := TTabSetData(FData).FStops[Index];
- end
- else
- begin
- raise EJclStringError.CreateRes(@RsArgumentOutOfRange);
- end;
- end;
- function TJclTabSet.GetTabWidth: SizeInt;
- begin
- if Self <> nil then
- Result := TTabSetData(FData).FWidth
- else
- Result := 0;
- end;
- function TJclTabSet.GetZeroBased: Boolean;
- begin
- Result := (Self = nil) or TTabSetData(FData).FZeroBased;
- end;
- procedure TJclTabSet.OptimalFillInfo(StartColumn, TargetColumn: SizeInt; out TabsNeeded, SpacesNeeded: SizeInt);
- var
- nextTab: SizeInt;
- begin
- if StartColumn < Self.StartColumn then // starting column less than 1 or 0 (depending on ZeroBased state)
- raise ArgumentOutOfRangeException.Create('StartColumn');
- if (TargetColumn < StartColumn) then // target lies before the starting column
- raise ArgumentOutOfRangeException.Create('TargetColumn');
- TabsNeeded := 0;
- repeat
- nextTab := TabFrom(StartColumn);
- if nextTab <= TargetColumn then
- begin
- Inc(TabsNeeded);
- StartColumn := nextTab;
- end;
- until nextTab > TargetColumn;
- SpacesNeeded := TargetColumn - StartColumn;
- end;
- function TJclTabSet.Optimize(const S: string): string;
- begin
- Result := Optimize(S, StartColumn);
- end;
- function TJclTabSet.Optimize(const S: string; Column: SizeInt): string;
- var
- sb: TJclStringBuilder;
- head: PChar;
- cur: PChar;
- tgt: SizeInt;
- procedure AppendOptimalWhiteSpace(Target: SizeInt);
- var
- tabCount: SizeInt;
- spaceCount: SizeInt;
- begin
- if cur > head then
- begin
- OptimalFillInfo(Column, Target, tabCount, spaceCount);
- if tabCount > 0 then
- sb.Append(#9, tabCount);
- if spaceCount > 0 then
- sb.Append(' ', spaceCount);
- end;
- end;
- begin
- if Column < StartColumn then
- raise ArgumentOutOfRangeException.Create('Column');
- sb := TJclStringBuilder.Create(Length(S));
- try
- cur := PChar(s);
- while cur^ <> #0 do
- begin
- // locate first whitespace character
- head := cur;
- while (cur^ <> #0) and not CharIsWhiteSpace(cur^) do
- Inc(cur);
- // output non whitespace characters
- if cur > head then
- sb.Append(head, 0, cur - head);
- // advance column
- Inc(Column, cur - head);
- // initialize target column indexer
- tgt := Column;
- // locate end of whitespace sequence
- while CharIsWhiteSpace(cur^) do
- begin
- if CharIsReturn(cur^) then
- begin
- // append optimized whitespace sequence...
- AppendOptimalWhiteSpace(tgt);
- // ...set the column back to the start of the line...
- Column := StartColumn;
- // ...reset target column indexer...
- tgt := Column;
- // ...add the line break character...
- sb.Append(cur^);
- end
- else
- if cur^ = #9 then
- tgt := TabFrom(tgt) // expand the tab
- else
- Inc(tgt); // a normal whitespace; taking up 1 column
- Inc(cur);
- end;
- AppendOptimalWhiteSpace(tgt); // append optimized whitespace sequence...
- Column := tgt; // ...and memorize the column for the next iteration
- end;
- Result := sb.ToString; // convert result to a string
- finally
- sb.Free;
- end;
- end;
- procedure TJclTabSet.RemoveAt(Index: SizeInt);
- begin
- if Self <> nil then
- TTabSetData(FData).RemoveAt(Index)
- else
- raise NullReferenceException.Create;
- end;
- procedure TJclTabSet.SetStops(Index, Value: SizeInt);
- begin
- if Self <> nil then
- TTabSetData(FData).SetStops(Index, Value)
- else
- raise NullReferenceException.Create;
- end;
- procedure TJclTabSet.SetTabWidth(Value: SizeInt);
- begin
- if Self <> nil then
- begin
- TTabSetData(FData).FWidth := Value;
- TTabSetData(FData).CalcRealWidth;
- end
- else
- raise NullReferenceException.Create;
- end;
- procedure TJclTabSet.SetZeroBased(Value: Boolean);
- var
- shift: SizeInt;
- idx: SizeInt;
- begin
- if Self <> nil then
- begin
- if Value <> TTabSetData(FData).FZeroBased then
- begin
- TTabSetData(FData).FZeroBased := Value;
- if Value then
- shift := -1
- else
- shift := 1;
- for idx := 0 to High(TTabSetData(FData).FStops) do
- TTabSetData(FData).FStops[idx] := TTabSetData(FData).FStops[idx] + shift;
- end;
- end
- else
- raise NullReferenceException.Create;
- end;
- function TJclTabSet.InternalTabStops: TDynSizeIntArray;
- begin
- if Self <> nil then
- Result := TTabSetData(FData).FStops
- else
- Result := nil;
- end;
- function TJclTabSet.InternalTabWidth: SizeInt;
- begin
- if Self <> nil then
- Result := TTabSetData(FData).FRealWidth
- else
- Result := 2;
- end;
- function TJclTabSet.NewReference: TJclTabSet;
- begin
- if Self <> nil then
- Result := TJclTabSet.Create(FData)
- else
- Result := nil;
- end;
- function TJclTabSet.StartColumn: SizeInt;
- begin
- if GetZeroBased then
- Result := 0
- else
- Result := 1;
- end;
- function TJclTabSet.TabFrom(Column: SizeInt): SizeInt;
- begin
- if Column < StartColumn then
- raise ArgumentOutOfRangeException.Create('Column');
- Result := FindStop(Column);
- if Result < 0 then
- Result := not Result
- else
- Inc(Result);
- if Result >= GetCount then
- begin
- if GetCount > 0 then
- Result := TTabSetData(FData).FStops[High(TTabSetData(FData).FStops)]
- else
- Result := StartColumn;
- while Result <= Column do
- Inc(Result, ActualTabWidth);
- end
- else
- Result := TTabSetData(FData).FStops[Result];
- end;
- function TJclTabSet.ToString: string;
- begin
- Result := ToString(TabSetFormatting_Full);
- end;
- function TJclTabSet.ToString(FormattingOptions: SizeInt): string;
- var
- sb: TJclStringBuilder;
- idx: SizeInt;
- function WantBrackets: Boolean;
- begin
- Result := (TabSetFormatting_SurroundStopsWithBrackets and FormattingOptions) <> 0;
- end;
- function EmptyBrackets: Boolean;
- begin
- Result := (TabSetFormatting_EmptyBracketsIfNoStops and FormattingOptions) <> 0;
- end;
- function IncludeAutoWidth: Boolean;
- begin
- Result := (TabSetFormatting_AutoTabWidth and FormattingOptions) <> 0;
- end;
- function IncludeTabWidth: Boolean;
- begin
- Result := (TabSetFormatting_NoTabWidth and FormattingOptions) = 0;
- end;
- function IncludeStops: Boolean;
- begin
- Result := (TabSetFormatting_NoTabStops and FormattingOptions) = 0;
- end;
- begin
- sb := TJclStringBuilder.Create;
- try
- // output the fixed tabulation positions if requested...
- if IncludeStops then
- begin
- // output each individual tabulation position
- for idx := 0 to GetCount - 1 do
- begin
- sb.Append(TabStops[idx]);
- sb.Append(',');
- end;
- // remove the final comma if any tabulation positions where outputted
- if sb.Length <> 0 then
- sb.Remove(sb.Length - 1, 1);
- // bracket the tabulation positions if requested
- if WantBrackets and (EmptyBrackets or (sb.Length > 0)) then
- begin
- sb.Insert(0, '[');
- sb.Append(']');
- end;
- end;
- // output the tab width if requested....
- if IncludeTabWidth and (IncludeAutoWidth or (TabWidth > 0)) then
- begin
- // separate the tab width from any outputted tabulation positions with a whitespace
- if sb.Length > 0 then
- sb.Append(' ');
- // flag tab width
- sb.Append('+');
- // finally, output the tab width
- sb.Append(ActualTabWidth);
- end;
- // flag zero-based tabset by outputting a 0 (zero) as the first character.
- if ZeroBased then
- sb.Insert(0, string('0 '));
- Result := StrTrimCharRight(sb.ToString, ' ');
- finally
- sb.Free;
- end;
- end;
- function TJclTabSet.UpdatePosition(const S: string): SizeInt;
- var
- Line: SizeInt;
- begin
- Result := StartColumn;
- Line := -1;
- UpdatePosition(S, Result, Line);
- end;
- function TJclTabSet.UpdatePosition(const S: string; Column: SizeInt): SizeInt;
- var
- Line: SizeInt;
- begin
- if Column < StartColumn then
- raise ArgumentOutOfRangeException.Create('Column');
- Result := Column;
- Line := -1;
- UpdatePosition(S, Result, Line);
- end;
- function TJclTabSet.UpdatePosition(const S: string; var Column, Line: SizeInt): SizeInt;
- var
- prevChar: Char;
- cur: PChar;
- begin
- if Column < StartColumn then
- raise ArgumentOutOfRangeException.Create('Column');
- // initialize loop
- cur := PChar(S);
- // iterate until end of string (the Null-character)
- while cur^ <> #0 do
- begin
- // check for line-breaking characters
- if CharIsReturn(cur^) then
- begin
- // Column moves back all the way to the left
- Column := StartColumn;
- // If this is the first line-break character or the same line-break character, increment the Line parameter
- Inc(Line);
- // check if it's the first of a two-character line-break
- prevChar := cur^;
- Inc(cur);
- // if it isn't a two-character line-break, undo the previous advancement
- if (cur^ = prevChar) or not CharIsReturn(cur^) then
- Dec(cur);
- end
- else // check for tab character and expand it
- if cur^ = #9 then
- Column := TabFrom(Column)
- else // a normal character; increment column
- Inc(Column);
- // advance pointer
- Inc(cur);
- end;
- // set the result to the newly calculated column
- Result := Column;
- end;
- //=== { NullReferenceException } =============================================
- constructor NullReferenceException.Create;
- begin
- CreateRes(@RsArg_NullReferenceException);
- end;
- {$IFNDEF WINSCP}
- function CompareNatural(const S1, S2: string; CaseInsensitive: Boolean): SizeInt;
- var
- Cur1, Len1,
- Cur2, Len2: SizeInt;
- function IsRealNumberChar(ch: Char): Boolean;
- begin
- Result := ((ch >= '0') and (ch <= '9')) or (ch = '-') or (ch = '+');
- end;
- procedure NumberCompare;
- var
- IsReallyNumber: Boolean;
- FirstDiffBreaks: Boolean;
- Val1, Val2: SizeInt;
- begin
- Result := 0;
- IsReallyNumber := False;
- // count leading spaces in S1
- while (Cur1 <= Len1) and CharIsWhiteSpace(S1[Cur1]) do
- begin
- Dec(Result);
- Inc(Cur1);
- end;
- // count leading spaces in S2 (canceling them out against the ones in S1)
- while (Cur2 <= Len2) and CharIsWhiteSpace(S2[Cur2]) do
- begin
- Inc(Result);
- Inc(Cur2);
- end;
- // if spaces match, or both strings are actually followed by a numeric character, continue the checks
- if (Result = 0) or ((Cur1 <= Len1) and CharIsNumberChar(S1[Cur1]) and (Cur2 <= Len2) and CharIsNumberChar(S2[Cur2])) then
- begin
- // Check signed number
- if (Cur1 <= Len1) and (S1[Cur1] = '-') and ((Cur2 > Len2) or (S2[Cur2] <> '-')) then
- Result := 1
- else
- if (Cur2 <= Len2) and (S2[Cur2] = '-') and ((Cur1 > Len1) or (S1[Cur1] <> '-')) then
- Result := -1
- else
- Result := 0;
- if (Cur1 <= Len1) and ((S1[Cur1] = '-') or (S1[Cur1] = '+')) then
- Inc(Cur1);
- if (Cur2 <= Len2) and ((S2[Cur2] = '-') or (S2[Cur2] = '+')) then
- Inc(Cur2);
- FirstDiffBreaks := (Cur1 <= Len1) and (S1[Cur1] = '0') or (Cur2 <= Len2) and (S2[Cur2] = '0');
- while (Cur1 <= Len1) and CharIsDigit(S1[Cur1]) and (Cur2 <= Len2) and CharIsDigit(S2[Cur2]) do
- begin
- IsReallyNumber := True;
- Val1 := StrToInt(S1[Cur1]);
- Val2 := StrToInt(S2[Cur2]);
- if (Result = 0) and (Val1 < Val2) then
- Result := -1
- else
- if (Result = 0) and (Val1 > Val2) then
- Result := 1;
- if FirstDiffBreaks and (Result <> 0) then
- Break;
- Inc(Cur1);
- Inc(Cur2);
- end;
- if IsReallyNumber then
- begin
- if not FirstDiffBreaks then
- begin
- if (Cur1 <= Len1) and CharIsDigit(S1[Cur1]) then
- Result := 1
- else
- if (Cur2 <= Len2) and CharIsDigit(S2[Cur2]) then
- Result := -1;
- end;
- end;
- end;
- end;
- procedure SetByCompareLength;
- var
- Remain1: SizeInt;
- Remain2: SizeInt;
- begin
- // base result on relative compare length (spaces could be ignored, so even if S1 is longer than S2, they could be
- // completely equal, or S2 could be longer)
- Remain1 := Len1 - Cur1 + 1;
- Remain2 := Len2 - Cur2 + 1;
- if Remain1 < 0 then
- Remain1 := 0;
- if Remain2 < 0 then
- Remain2 := 0;
- if Remain1 < Remain2 then
- Result := -1
- else
- if Remain1 > Remain2 then
- Result := 1;
- end;
- begin
- Cur1 := 1;
- Len1 := Length(S1);
- Cur2 := 1;
- Len2 := Length(S2);
- Result := 0;
- while (Result = 0) do
- begin
- if (Cur1 > Len1) or (Cur2 > Len2) then
- begin
- SetByCompareLength;
- Break;
- end
- else
- if (Cur1 <= Len1) and (Cur2 > Len2) then
- Result := 1
- else
- if (S1[Cur1] = '-') and IsRealNumberChar(S2[Cur2]) and (S2[Cur2] <> '-') then
- Result := -1
- else
- if (S2[Cur2] = '-') and IsRealNumberChar(S1[Cur1]) and (S1[Cur1] <> '-') then
- Result := 1
- else
- if (IsRealNumberChar(S1[Cur1]) or CharIsWhiteSpace(S1[Cur1])) and (IsRealNumberChar(S2[Cur2]) or CharIsWhiteSpace(S2[Cur2])) then
- NumberCompare
- else
- begin
- if CaseInsensitive then
- Result := StrLIComp(PChar(@S1[Cur1]), PChar(@S2[Cur2]), 1)
- else
- Result := StrLComp(PChar(@S1[Cur1]), PChar(@S2[Cur2]), 1);
- Inc(Cur1);
- Inc(Cur2);
- end;
- end;
- end;
- function CompareNaturalStr(const S1, S2: string): SizeInt; overload;
- begin
- Result := CompareNatural(S1, S2, False);
- end;
- function CompareNaturalText(const S1, S2: string): SizeInt; overload;
- begin
- Result := CompareNatural(S1, S2, True);
- end;
- {$ENDIF ~WINSCP}
- initialization
- {$IFNDEF UNICODE_RTL_DATABASE}
- LoadCharTypes; // this table first
- LoadCaseMap; // or this function does not work
- {$ENDIF ~UNICODE_RTL_DATABASE}
- {$IFDEF UNITVERSIONING}
- RegisterUnitVersion(HInstance, UnitVersioning);
- {$ENDIF UNITVERSIONING}
- {$IFDEF UNITVERSIONING}
- finalization
- UnregisterUnitVersion(HInstance);
- {$ENDIF UNITVERSIONING}
- end.
|