JclFileUtils.pas 218 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067
  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclFileUtils.pas. }
  14. { }
  15. { The Initial Developer of the Original Code is Marcel van Brakel. }
  16. { Portions created by Marcel van Brakel are Copyright (C) Marcel van Brakel. All rights reserved. }
  17. { }
  18. { Contributors: }
  19. { Andre Snepvangers (asnepvangers) }
  20. { Andreas Hausladen (ahuser) }
  21. { Anthony Steele }
  22. { Rik Barker (rikbarker) }
  23. { Azret Botash }
  24. { Charlie Calvert }
  25. { David Hervieux }
  26. { Florent Ouchet (outchy) }
  27. { Jean-Fabien Connault (cycocrew) }
  28. { Jens Fudickar (jfudickar) }
  29. { JohnML }
  30. { John Molyneux }
  31. { Marcel Bestebroer }
  32. { Marcel van Brakel }
  33. { Massimo Maria Ghisalberti }
  34. { Matthias Thoma (mthoma) }
  35. { Olivier Sannier (obones) }
  36. { Pelle F. S. Liljendal }
  37. { Robert Marquardt (marquardt) }
  38. { Robert Rossmair (rrossmair) }
  39. { Rudy Velthuis }
  40. { Scott Price }
  41. { Wim De Cleen }
  42. { }
  43. {**************************************************************************************************}
  44. { }
  45. { This unit contains routines and classes for working with files, directories and path strings. }
  46. { Additionally it contains wrapper classes for file mapping objects and version resources. }
  47. { Generically speaking, everything that has to do with files and directories. Note that filesystem }
  48. { specific functionality has been extracted into external units, for example JclNTFS which }
  49. { contains NTFS specific utility routines, and that the JclShell unit contains some file related }
  50. { routines as well but they are specific to the Windows shell. }
  51. { }
  52. {**************************************************************************************************}
  53. { }
  54. { Last modified: $Date:: $ }
  55. { Revision: $Rev:: $ }
  56. { Author: $Author:: $ }
  57. { }
  58. {**************************************************************************************************}
  59. unit JclFileUtils;
  60. {$I jcl.inc}
  61. {$I crossplatform.inc}
  62. interface
  63. uses
  64. {$IFDEF UNITVERSIONING}
  65. JclUnitVersioning,
  66. {$ENDIF UNITVERSIONING}
  67. {$IFDEF HAS_UNIT_LIBC}
  68. Libc,
  69. {$ENDIF HAS_UNIT_LIBC}
  70. {$IFDEF HAS_UNITSCOPE}
  71. {$IFDEF MSWINDOWS}
  72. Winapi.Windows, JclWin32,
  73. {$ENDIF MSWINDOWS}
  74. System.Classes, System.SysUtils,
  75. {$ELSE ~HAS_UNITSCOPE}
  76. {$IFDEF MSWINDOWS}
  77. Windows, JclWin32,
  78. {$ENDIF MSWINDOWS}
  79. Classes, SysUtils,
  80. {$ENDIF ~HAS_UNITSCOPE}
  81. JclBase, JclSysUtils;
  82. // Path Manipulation
  83. //
  84. // Various support routines for working with path strings. For example, building a path from
  85. // elements or extracting the elements from a path, interpretation of paths and transformations of
  86. // paths.
  87. const
  88. {$IFDEF UNIX}
  89. // renamed to DirDelimiter
  90. // PathSeparator = '/';
  91. DirDelimiter = '/';
  92. DirSeparator = ':';
  93. {$ENDIF UNIX}
  94. {$IFDEF MSWINDOWS}
  95. PathDevicePrefix = '\\.\';
  96. // renamed to DirDelimiter
  97. // PathSeparator = '\';
  98. DirDelimiter = '\';
  99. DirSeparator = ';';
  100. PathUncPrefix = '\\';
  101. {$ENDIF MSWINDOWS}
  102. faSymLink = $00000040 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF}; // defined since D7
  103. faNormalFile = $00000080;
  104. faTemporary = $00000100 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
  105. faSparseFile = $00000200 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
  106. faReparsePoint = $00000400 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
  107. faCompressed = $00000800 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
  108. faOffline = $00001000 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
  109. faNotContentIndexed = $00002000 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
  110. faEncrypted = $00004000 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
  111. // Note: faVolumeID is potentially dangerous and its usage has been discontinued
  112. // Please see QC report 6003 for details, available online at this URL:
  113. // http://qc.embarcadero.com/wc/qcmain.aspx?d=6003
  114. faRejectedByDefault = faHidden + faSysFile + faDirectory;
  115. faWindowsSpecific = faArchive + faTemporary + faSparseFile + faReparsePoint +
  116. faCompressed + faOffline + faNotContentIndexed + faEncrypted;
  117. faUnixSpecific = faSymLink;
  118. type
  119. TCompactPath = ({cpBegin, }cpCenter, cpEnd);
  120. function CharIsDriveLetter(const C: char): Boolean;
  121. function CharIsInvalidFileNameCharacter(const C: Char): Boolean;
  122. function CharIsInvalidPathCharacter(const C: Char): Boolean;
  123. function PathAddSeparator(const Path: string): string;
  124. function PathAddExtension(const Path, Extension: string): string;
  125. function PathAppend(const Path, Append: string): string;
  126. function PathBuildRoot(const Drive: Byte): string;
  127. function PathCanonicalize(const Path: string): string;
  128. function PathCommonPrefix(const Path1, Path2: string): Integer;
  129. {$IFDEF MSWINDOWS}
  130. function PathCompactPath(const DC: HDC; const Path: string; const Width: Integer;
  131. CmpFmt: TCompactPath): string;
  132. {$ENDIF MSWINDOWS}
  133. procedure PathExtractElements(const Source: string; var Drive, Path, FileName, Ext: string);
  134. function PathExtractFileDirFixed(const S: string): string;
  135. function PathExtractFileNameNoExt(const Path: string): string;
  136. function PathExtractPathDepth(const Path: string; Depth: Integer): string;
  137. function PathGetDepth(const Path: string): Integer;
  138. {$IFDEF MSWINDOWS}
  139. function PathGetLongName(const Path: string): string;
  140. function PathGetShortName(const Path: string): string;
  141. {$ENDIF MSWINDOWS}
  142. function PathGetRelativePath(Origin, Destination: string): string;
  143. function PathGetTempPath: string;
  144. function PathIsAbsolute(const Path: string): Boolean;
  145. function PathIsChild(const Path, Base: string): Boolean;
  146. function PathIsEqualOrChild(const Path, Base: string): Boolean;
  147. function PathIsDiskDevice(const Path: string): Boolean;
  148. function PathIsUNC(const Path: string): Boolean;
  149. function PathRemoveSeparator(const Path: string): string;
  150. function PathRemoveExtension(const Path: string): string;
  151. // Windows Vista uses localized path names in the Windows Explorer but these
  152. // folders do not really exist on disk. This causes all I/O operations to fail
  153. // if the user specifies such a localized directory like "C:\Benutzer\MyName\Bilder"
  154. // instead of the physical folder "C:\Users\MyName\Pictures".
  155. // These two functions allow to convert the user's input from localized to
  156. // physical paths and vice versa.
  157. function PathGetPhysicalPath(const LocalizedPath: string): string;
  158. function PathGetLocalizedPath(const PhysicalPath: string): string;
  159. // Files and Directories
  160. //
  161. // Routines for working with files and directories. Includes routines to extract various file
  162. // attributes or update them, volume locking and routines for creating temporary files.
  163. type
  164. TDelTreeProgress = function (const FileName: string; Attr: DWORD): Boolean;
  165. TFileListOption = (flFullNames, flRecursive, flMaskedSubfolders);
  166. TFileListOptions = set of TFileListOption;
  167. TJclAttributeMatch = (amAny, amExact, amSubSetOf, amSuperSetOf, amCustom);
  168. TFileMatchFunc = function(const Attr: Integer; const FileInfo: TSearchRec): Boolean;
  169. TFileHandler = procedure (const FileName: string) of object;
  170. TFileHandlerEx = procedure (const Directory: string; const FileInfo: TSearchRec) of object;
  171. TFileInfoHandlerEx = procedure (const FileInfo: TSearchRec) of object;
  172. function BuildFileList(const Path: string; const Attr: Integer; const List: TStrings; IncludeDirectoryName: Boolean =
  173. False): Boolean;
  174. function AdvBuildFileList(const Path: string; const Attr: Integer; const Files: TStrings;
  175. const AttributeMatch: TJclAttributeMatch = amSuperSetOf; const Options: TFileListOptions = [];
  176. const SubfoldersMask: string = ''; const FileMatchFunc: TFileMatchFunc = nil): Boolean;
  177. function VerifyFileAttributeMask(var RejectedAttributes, RequiredAttributes: Integer): Boolean;
  178. function IsFileAttributeMatch(FileAttributes, RejectedAttributes,
  179. RequiredAttributes: Integer): Boolean;
  180. function FileAttributesStr(const FileInfo: TSearchRec): string;
  181. function IsFileNameMatch(FileName: string; const Mask: string;
  182. const CaseSensitive: Boolean = {$IFDEF MSWINDOWS} False {$ELSE} True {$ENDIF}): Boolean;
  183. procedure EnumFiles(const Path: string; HandleFile: TFileHandlerEx;
  184. RejectedAttributes: Integer = faRejectedByDefault; RequiredAttributes: Integer = 0;
  185. Abort: PBoolean = nil); overload;
  186. procedure EnumFiles(const Path: string; HandleFile: TFileInfoHandlerEx;
  187. RejectedAttributes: Integer = faRejectedByDefault; RequiredAttributes: Integer = 0;
  188. Abort: PBoolean = nil); overload;
  189. procedure EnumDirectories(const Root: string; const HandleDirectory: TFileHandler;
  190. const IncludeHiddenDirectories: Boolean = False; const SubDirectoriesMask: string = '';
  191. Abort: PBoolean = nil {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF});
  192. {$IFDEF MSWINDOWS}
  193. procedure CreateEmptyFile(const FileName: string);
  194. function CloseVolume(var Volume: THandle): Boolean;
  195. {$IFNDEF FPC}
  196. function DeleteDirectory(const DirectoryName: string; MoveToRecycleBin: Boolean): Boolean;
  197. function CopyDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
  198. function MoveDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
  199. {$ENDIF ~FPC}
  200. function DelTree(const Path: string): Boolean;
  201. function DelTreeEx(const Path: string; AbortOnFailure: Boolean; Progress: TDelTreeProgress): Boolean;
  202. function DiskInDrive(Drive: Char): Boolean;
  203. {$ENDIF MSWINDOWS}
  204. function DirectoryExists(const Name: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): Boolean;
  205. function FileCreateTemp(var Prefix: string): THandle;
  206. function FileBackup(const FileName: string; Move: Boolean = False): Boolean;
  207. function FileCopy(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
  208. function FileDateTime(const FileName: string): TDateTime;
  209. function FileDelete(const FileName: string; MoveToRecycleBin: Boolean = False): Boolean;
  210. function FileExists(const FileName: string): Boolean;
  211. /// <summary>procedure FileHistory Creates a list of history files of a specified
  212. /// source file. Each version of the file get's an extention .~<Nr>~ The file with
  213. /// the lowest number is the youngest file.
  214. /// </summary>
  215. /// <param name="FileName"> (string) Name of the source file</param>
  216. /// <param name="HistoryPath"> (string) Folder where the history files should be
  217. /// created. If no folder is defined the folder of the source file is used.</param>
  218. /// <param name="MaxHistoryCount"> (Integer) Max number of files</param>
  219. /// <param name="MinFileDate"> (TDateTime) Timestamp how old the file has to be to
  220. /// create a new history version. For example: NOW-1/24 => Only once per hour a new
  221. /// history file is created. Default 0 means allways
  222. /// <param name="ReplaceExtention"> (boolean) Flag to define that the history file
  223. /// extention should replace the current extention or should be added at the
  224. /// end</param>
  225. /// </param>
  226. procedure FileHistory(const FileName: string; HistoryPath: string = ''; MaxHistoryCount: Integer = 100; MinFileDate:
  227. TDateTime = 0; ReplaceExtention: Boolean = true);
  228. function FileMove(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
  229. function FileRestore(const FileName: string): Boolean;
  230. function GetBackupFileName(const FileName: string): string;
  231. function IsBackupFileName(const FileName: string): Boolean;
  232. function FileGetDisplayName(const FileName: string): string;
  233. function FileGetGroupName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
  234. function FileGetOwnerName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
  235. function FileGetSize(const FileName: string): Int64;
  236. function FileGetTempName(const Prefix: string): string;
  237. {$IFDEF MSWINDOWS}
  238. function FileGetTypeName(const FileName: string): string;
  239. {$ENDIF MSWINDOWS}
  240. function FindUnusedFileName(FileName: string; const FileExt: string; NumberPrefix: string = ''): string;
  241. function ForceDirectories(Name: string): Boolean;
  242. function GetDirectorySize(const Path: string): Int64;
  243. {$IFDEF MSWINDOWS}
  244. function GetDriveTypeStr(const Drive: Char): string;
  245. function GetFileAgeCoherence(const FileName: string): Boolean;
  246. {$ENDIF MSWINDOWS}
  247. procedure GetFileAttributeList(const Items: TStrings; const Attr: Integer);
  248. {$IFDEF MSWINDOWS}
  249. procedure GetFileAttributeListEx(const Items: TStrings; const Attr: Integer);
  250. {$ENDIF MSWINDOWS}
  251. function GetFileInformation(const FileName: string; out FileInfo: TSearchRec): Boolean; overload;
  252. function GetFileInformation(const FileName: string): TSearchRec; overload;
  253. {$IFDEF UNIX}
  254. function GetFileStatus(const FileName: string; out StatBuf: TStatBuf64;
  255. const ResolveSymLinks: Boolean): Integer;
  256. {$ENDIF UNIX}
  257. {$IFDEF MSWINDOWS}
  258. function GetFileLastWrite(const FileName: string): TFileTime; overload;
  259. function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime): Boolean; overload;
  260. function GetFileLastAccess(const FileName: string): TFileTime; overload;
  261. function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime): Boolean; overload;
  262. function GetFileCreation(const FileName: string): TFileTime; overload;
  263. function GetFileCreation(const FileName: string; out LocalTime: TDateTime): Boolean; overload;
  264. {$ENDIF MSWINDOWS}
  265. {$IFDEF UNIX}
  266. function GetFileLastWrite(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean = True): Boolean; overload;
  267. function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean = True): Boolean; overload;
  268. function GetFileLastWrite(const FileName: string; ResolveSymLinks: Boolean = True): Integer; overload;
  269. function GetFileLastAccess(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean = True): Boolean; overload;
  270. function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean = True): Boolean; overload;
  271. function GetFileLastAccess(const FileName: string; ResolveSymLinks: Boolean = True): Integer; overload;
  272. function GetFileLastAttrChange(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean = True): Boolean; overload;
  273. function GetFileLastAttrChange(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean = True): Boolean; overload;
  274. function GetFileLastAttrChange(const FileName: string; ResolveSymLinks: Boolean = True): Integer; overload;
  275. {$ENDIF UNIX}
  276. function GetModulePath(const Module: HMODULE): string;
  277. function GetSizeOfFile(const FileName: string): Int64; overload;
  278. function GetSizeOfFile(const FileInfo: TSearchRec): Int64; overload;
  279. {$IFDEF MSWINDOWS}
  280. function GetSizeOfFile(Handle: THandle): Int64; overload;
  281. function GetStandardFileInfo(const FileName: string): TWin32FileAttributeData;
  282. {$ENDIF MSWINDOWS}
  283. function IsDirectory(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): Boolean;
  284. function IsRootDirectory(const CanonicFileName: string): Boolean;
  285. {$IFDEF MSWINDOWS}
  286. function LockVolume(const Volume: string; var Handle: THandle): Boolean;
  287. function OpenVolume(const Drive: Char): THandle;
  288. function SetDirLastWrite(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
  289. function SetDirLastAccess(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
  290. function SetDirCreation(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
  291. {$ENDIF MSWINDOWS}
  292. function SetFileLastWrite(const FileName: string; const DateTime: TDateTime): Boolean;
  293. function SetFileLastAccess(const FileName: string; const DateTime: TDateTime): Boolean;
  294. {$IFDEF MSWINDOWS}
  295. function SetFileCreation(const FileName: string; const DateTime: TDateTime): Boolean;
  296. procedure ShredFile(const FileName: string; Times: Integer = 1);
  297. function UnlockVolume(var Handle: THandle): Boolean;
  298. {$ENDIF MSWINDOWS}
  299. {$IFDEF UNIX}
  300. function CreateSymbolicLink(const Name, Target: string): Boolean;
  301. { This function gets the value of the symbolic link filename. }
  302. function SymbolicLinkTarget(const Name: string): string;
  303. {$ENDIF UNIX}
  304. // TJclFileAttributeMask
  305. //
  306. // File search helper class, allows to specify required/rejected attributes
  307. type
  308. TAttributeInterest = (aiIgnored, aiRejected, aiRequired);
  309. TJclCustomFileAttrMask = class(TPersistent)
  310. private
  311. FRequiredAttr: Integer;
  312. FRejectedAttr: Integer;
  313. function GetAttr(Index: Integer): TAttributeInterest;
  314. procedure SetAttr(Index: Integer; const Value: TAttributeInterest);
  315. procedure ReadRequiredAttributes(Reader: TReader);
  316. procedure ReadRejectedAttributes(Reader: TReader);
  317. procedure WriteRequiredAttributes(Writer: TWriter);
  318. procedure WriteRejectedAttributes(Writer: TWriter);
  319. protected
  320. procedure DefineProperties(Filer: TFiler); override;
  321. property ReadOnly: TAttributeInterest index faReadOnly
  322. read GetAttr write SetAttr stored False;
  323. property Hidden: TAttributeInterest index faHidden
  324. read GetAttr write SetAttr stored False;
  325. property System: TAttributeInterest index faSysFile
  326. read GetAttr write SetAttr stored False;
  327. property Directory: TAttributeInterest index faDirectory
  328. read GetAttr write SetAttr stored False;
  329. property SymLink: TAttributeInterest index faSymLink
  330. read GetAttr write SetAttr stored False;
  331. property Normal: TAttributeInterest index faNormalFile
  332. read GetAttr write SetAttr stored False;
  333. property Archive: TAttributeInterest index faArchive
  334. read GetAttr write SetAttr stored False;
  335. property Temporary: TAttributeInterest index faTemporary
  336. read GetAttr write SetAttr stored False;
  337. property SparseFile: TAttributeInterest index faSparseFile
  338. read GetAttr write SetAttr stored False;
  339. property ReparsePoint: TAttributeInterest index faReparsePoint
  340. read GetAttr write SetAttr stored False;
  341. property Compressed: TAttributeInterest index faCompressed
  342. read GetAttr write SetAttr stored False;
  343. property OffLine: TAttributeInterest index faOffline
  344. read GetAttr write SetAttr stored False;
  345. property NotContentIndexed: TAttributeInterest index faNotContentIndexed
  346. read GetAttr write SetAttr stored False;
  347. property Encrypted: TAttributeInterest index faEncrypted
  348. read GetAttr write SetAttr stored False;
  349. public
  350. constructor Create;
  351. procedure Assign(Source: TPersistent); override;
  352. procedure Clear;
  353. function Match(FileAttributes: Integer): Boolean; overload;
  354. function Match(const FileInfo: TSearchRec): Boolean; overload;
  355. property Required: Integer read FRequiredAttr write FRequiredAttr;
  356. property Rejected: Integer read FRejectedAttr write FRejectedAttr;
  357. property Attribute[Index: Integer]: TAttributeInterest read GetAttr write SetAttr; default;
  358. end;
  359. TJclFileAttributeMask = class(TJclCustomFileAttrMask)
  360. private
  361. procedure ReadVolumeID(Reader: TReader);
  362. protected
  363. procedure DefineProperties(Filer: TFiler); override;
  364. published
  365. property ReadOnly;
  366. property Hidden;
  367. property System;
  368. property Directory;
  369. property Normal;
  370. {$IFDEF UNIX}
  371. property SymLink;
  372. {$ENDIF UNIX}
  373. {$IFDEF MSWINDOWS}
  374. property Archive;
  375. property Temporary;
  376. property SparseFile;
  377. property ReparsePoint;
  378. property Compressed;
  379. property OffLine;
  380. property NotContentIndexed;
  381. property Encrypted;
  382. {$ENDIF MSWINDOWS}
  383. end;
  384. type
  385. TFileSearchOption = (fsIncludeSubDirectories, fsIncludeHiddenSubDirectories, fsLastChangeAfter,
  386. fsLastChangeBefore, fsMaxSize, fsMinSize);
  387. TFileSearchOptions = set of TFileSearchOption;
  388. TFileSearchTaskID = Integer;
  389. TFileSearchTerminationEvent = procedure (const ID: TFileSearchTaskID; const Aborted: Boolean) of object;
  390. TFileEnumeratorSyncMode = (smPerFile, smPerDirectory);
  391. // IJclFileSearchOptions
  392. //
  393. // Interface for file search options
  394. type
  395. IJclFileSearchOptions = interface
  396. ['{B73D9E3D-34C5-4DA9-88EF-4CA730328FC9}']
  397. function GetAttributeMask: TJclFileAttributeMask;
  398. function GetCaseSensitiveSearch: Boolean;
  399. function GetRootDirectories: TStrings;
  400. function GetRootDirectory: string;
  401. function GetFileMask: string;
  402. function GetFileMasks: TStrings;
  403. function GetFileSizeMax: Int64;
  404. function GetFileSizeMin: Int64;
  405. function GetIncludeSubDirectories: Boolean;
  406. function GetIncludeHiddenSubDirectories: Boolean;
  407. function GetLastChangeAfter: TDateTime;
  408. function GetLastChangeBefore: TDateTime;
  409. function GetLastChangeAfterStr: string;
  410. function GetLastChangeBeforeStr: string;
  411. function GetSubDirectoryMask: string;
  412. function GetOption(const Option: TFileSearchOption): Boolean;
  413. function GetOptions: TFileSearchoptions;
  414. procedure SetAttributeMask(const Value: TJclFileAttributeMask);
  415. procedure SetCaseSensitiveSearch(const Value: Boolean);
  416. procedure SetRootDirectories(const Value: TStrings);
  417. procedure SetRootDirectory(const Value: string);
  418. procedure SetFileMask(const Value: string);
  419. procedure SetFileMasks(const Value: TStrings);
  420. procedure SetFileSizeMax(const Value: Int64);
  421. procedure SetFileSizeMin(const Value: Int64);
  422. procedure SetIncludeSubDirectories(const Value: Boolean);
  423. procedure SetIncludeHiddenSubDirectories(const Value: Boolean);
  424. procedure SetLastChangeAfter(const Value: TDateTime);
  425. procedure SetLastChangeBefore(const Value: TDateTime);
  426. procedure SetLastChangeAfterStr(const Value: string);
  427. procedure SetLastChangeBeforeStr(const Value: string);
  428. procedure SetOption(const Option: TFileSearchOption; const Value: Boolean);
  429. procedure SetOptions(const Value: TFileSearchOptions);
  430. procedure SetSubDirectoryMask(const Value: string);
  431. // properties
  432. property CaseSensitiveSearch: Boolean read GetCaseSensitiveSearch write SetCaseSensitiveSearch;
  433. property RootDirectories: TStrings read GetRootDirectories write SetRootDirectories;
  434. property RootDirectory: string read GetRootDirectory write SetRootDirectory;
  435. property FileMask: string read GetFileMask write SetFileMask;
  436. property SubDirectoryMask: string read GetSubDirectoryMask write SetSubDirectoryMask;
  437. property AttributeMask: TJclFileAttributeMask read GetAttributeMask write SetAttributeMask;
  438. property FileSizeMin: Int64 read GetFileSizeMin write SetFileSizeMin;
  439. property FileSizeMax: Int64 read GetFileSizeMax write SetFileSizeMax; // default InvalidFileSize;
  440. property LastChangeAfter: TDateTime read GetLastChangeAfter write SetLastChangeAfter;
  441. property LastChangeBefore: TDateTime read GetLastChangeBefore write SetLastChangeBefore;
  442. property LastChangeAfterAsString: string read GetLastChangeAfterStr write SetLastChangeAfterStr;
  443. property LastChangeBeforeAsString: string read GetLastChangeBeforeStr write SetLastChangeBeforeStr;
  444. property IncludeSubDirectories: Boolean read GetIncludeSubDirectories
  445. write SetIncludeSubDirectories;
  446. property IncludeHiddenSubDirectories: Boolean read GetIncludeHiddenSubDirectories
  447. write SetIncludeHiddenSubDirectories;
  448. end;
  449. // IJclFileSearchOptions
  450. //
  451. // Interface for file search options
  452. type
  453. TJclFileSearchOptions = class(TJclInterfacedPersistent, IJclFileSearchOptions)
  454. protected
  455. FFileMasks: TStringList;
  456. FRootDirectories: TStringList;
  457. FSubDirectoryMask: string;
  458. FAttributeMask: TJclFileAttributeMask;
  459. FFileSizeMin: Int64;
  460. FFileSizeMax: Int64;
  461. FLastChangeBefore: TDateTime;
  462. FLastChangeAfter: TDateTime;
  463. FOptions: TFileSearchOptions;
  464. FCaseSensitiveSearch: Boolean;
  465. function IsLastChangeAfterStored: Boolean;
  466. function IsLastChangeBeforeStored: Boolean;
  467. public
  468. constructor Create;
  469. destructor Destroy; override;
  470. procedure Assign(Source: TPersistent); override;
  471. { IJclFileSearchOptions }
  472. function GetAttributeMask: TJclFileAttributeMask;
  473. function GetCaseSensitiveSearch: Boolean;
  474. function GetRootDirectories: TStrings;
  475. function GetRootDirectory: string;
  476. function GetFileMask: string;
  477. function GetFileMasks: TStrings;
  478. function GetFileSizeMax: Int64;
  479. function GetFileSizeMin: Int64;
  480. function GetIncludeSubDirectories: Boolean;
  481. function GetIncludeHiddenSubDirectories: Boolean;
  482. function GetLastChangeAfter: TDateTime;
  483. function GetLastChangeBefore: TDateTime;
  484. function GetLastChangeAfterStr: string;
  485. function GetLastChangeBeforeStr: string;
  486. function GetSubDirectoryMask: string;
  487. function GetOption(const Option: TFileSearchOption): Boolean;
  488. function GetOptions: TFileSearchoptions;
  489. procedure SetAttributeMask(const Value: TJclFileAttributeMask);
  490. procedure SetCaseSensitiveSearch(const Value: Boolean);
  491. procedure SetRootDirectories(const Value: TStrings);
  492. procedure SetRootDirectory(const Value: string);
  493. procedure SetFileMask(const Value: string);
  494. procedure SetFileMasks(const Value: TStrings);
  495. procedure SetFileSizeMax(const Value: Int64);
  496. procedure SetFileSizeMin(const Value: Int64);
  497. procedure SetIncludeSubDirectories(const Value: Boolean);
  498. procedure SetIncludeHiddenSubDirectories(const Value: Boolean);
  499. procedure SetLastChangeAfter(const Value: TDateTime);
  500. procedure SetLastChangeBefore(const Value: TDateTime);
  501. procedure SetLastChangeAfterStr(const Value: string);
  502. procedure SetLastChangeBeforeStr(const Value: string);
  503. procedure SetOption(const Option: TFileSearchOption; const Value: Boolean);
  504. procedure SetOptions(const Value: TFileSearchOptions);
  505. procedure SetSubDirectoryMask(const Value: string);
  506. published
  507. property CaseSensitiveSearch: Boolean read GetCaseSensitiveSearch write SetCaseSensitiveSearch
  508. default {$IFDEF MSWINDOWS} False {$ELSE} True {$ENDIF};
  509. property FileMasks: TStrings read GetFileMasks write SetFileMasks;
  510. property RootDirectories: TStrings read GetRootDirectories write SetRootDirectories;
  511. property RootDirectory: string read GetRootDirectory write SetRootDirectory;
  512. property SubDirectoryMask: string read FSubDirectoryMask write FSubDirectoryMask;
  513. property AttributeMask: TJclFileAttributeMask read FAttributeMask write SetAttributeMask;
  514. property FileSizeMin: Int64 read FFileSizeMin write FFileSizeMin;
  515. property FileSizeMax: Int64 read FFileSizeMax write FFileSizeMax;
  516. property LastChangeAfter: TDateTime read FLastChangeAfter write FLastChangeAfter
  517. stored IsLastChangeAfterStored;
  518. property LastChangeBefore: TDateTime read FLastChangeBefore write FLastChangeBefore
  519. stored IsLastChangeBeforeStored;
  520. property Options: TFileSearchOptions read FOptions write FOptions
  521. default [fsIncludeSubDirectories];
  522. end;
  523. // IJclFileEnumerator
  524. //
  525. // Interface for thread-based file search
  526. type
  527. IJclFileEnumerator = interface(IJclFileSearchOptions)
  528. ['{F7E747ED-1C41-441F-B25B-BB314E00C4E9}']
  529. // property access methods
  530. function GetRunningTasks: Integer;
  531. function GetSynchronizationMode: TFileEnumeratorSyncMode;
  532. function GetOnEnterDirectory: TFileHandler;
  533. function GetOnTerminateTask: TFileSearchTerminationEvent;
  534. procedure SetSynchronizationMode(const Value: TFileEnumeratorSyncMode);
  535. procedure SetOnEnterDirectory(const Value: TFileHandler);
  536. procedure SetOnTerminateTask(const Value: TFileSearchTerminationEvent);
  537. // other methods
  538. function FillList(List: TStrings): TFileSearchTaskID;
  539. function ForEach(Handler: TFileHandler): TFileSearchTaskID; overload;
  540. function ForEach(Handler: TFileHandlerEx): TFileSearchTaskID; overload;
  541. procedure StopTask(ID: TFileSearchTaskID);
  542. procedure StopAllTasks(Silently: Boolean = False); // Silently: Don't call OnTerminateTask
  543. // properties
  544. property RunningTasks: Integer read GetRunningTasks;
  545. property SynchronizationMode: TFileEnumeratorSyncMode read GetSynchronizationMode
  546. write SetSynchronizationMode;
  547. property OnEnterDirectory: TFileHandler read GetOnEnterDirectory write SetOnEnterDirectory;
  548. property OnTerminateTask: TFileSearchTerminationEvent read GetOnTerminateTask
  549. write SetOnTerminateTask;
  550. end;
  551. // TJclFileEnumerator
  552. //
  553. // Class for thread-based file search
  554. type
  555. TJclFileEnumerator = class(TJclFileSearchOptions, IInterface, IJclFileSearchOptions, IJclFileEnumerator)
  556. private
  557. FTasks: TList;
  558. FOnEnterDirectory: TFileHandler;
  559. FOnTerminateTask: TFileSearchTerminationEvent;
  560. FNextTaskID: TFileSearchTaskID;
  561. FSynchronizationMode: TFileEnumeratorSyncMode;
  562. function GetNextTaskID: TFileSearchTaskID;
  563. protected
  564. function CreateTask: TThread;
  565. procedure TaskTerminated(Sender: TObject);
  566. property NextTaskID: TFileSearchTaskID read GetNextTaskID;
  567. public
  568. constructor Create;
  569. destructor Destroy; override;
  570. { IJclFileEnumerator }
  571. function GetRunningTasks: Integer;
  572. function GetSynchronizationMode: TFileEnumeratorSyncMode;
  573. function GetOnEnterDirectory: TFileHandler;
  574. function GetOnTerminateTask: TFileSearchTerminationEvent;
  575. procedure SetSynchronizationMode(const Value: TFileEnumeratorSyncMode);
  576. procedure SetOnEnterDirectory(const Value: TFileHandler);
  577. procedure SetOnTerminateTask(const Value: TFileSearchTerminationEvent);
  578. procedure Assign(Source: TPersistent); override;
  579. function FillList(List: TStrings): TFileSearchTaskID;
  580. function ForEach(Handler: TFileHandler): TFileSearchTaskID; overload;
  581. function ForEach(Handler: TFileHandlerEx): TFileSearchTaskID; overload;
  582. procedure StopTask(ID: TFileSearchTaskID);
  583. procedure StopAllTasks(Silently: Boolean = False); // Silently: Don't call OnTerminateTask
  584. property FileMask: string read GetFileMask write SetFileMask;
  585. property IncludeSubDirectories: Boolean
  586. read GetIncludeSubDirectories write SetIncludeSubDirectories;
  587. property IncludeHiddenSubDirectories: Boolean
  588. read GetIncludeHiddenSubDirectories write SetIncludeHiddenSubDirectories;
  589. property SearchOption[const Option: TFileSearchOption]: Boolean read GetOption write SetOption;
  590. property LastChangeAfterAsString: string read GetLastChangeAfterStr write SetLastChangeAfterStr;
  591. property LastChangeBeforeAsString: string read GetLastChangeBeforeStr write SetLastChangeBeforeStr;
  592. published
  593. property RunningTasks: Integer read GetRunningTasks;
  594. property SynchronizationMode: TFileEnumeratorSyncMode read FSynchronizationMode write FSynchronizationMode
  595. default smPerDirectory;
  596. property OnEnterDirectory: TFileHandler read FOnEnterDirectory write FOnEnterDirectory;
  597. property OnTerminateTask: TFileSearchTerminationEvent read FOnTerminateTask write FOnTerminateTask;
  598. end;
  599. function FileSearch: IJclFileEnumerator;
  600. {$IFDEF MSWINDOWS}
  601. // TFileVersionInfo
  602. //
  603. // Class that enables reading the version information stored in a PE file.
  604. type
  605. TFileFlag = (ffDebug, ffInfoInferred, ffPatched, ffPreRelease, ffPrivateBuild, ffSpecialBuild);
  606. TFileFlags = set of TFileFlag;
  607. PLangIdRec = ^TLangIdRec;
  608. TLangIdRec = packed record
  609. case Integer of
  610. 0: (
  611. LangId: Word;
  612. CodePage: Word);
  613. 1: (
  614. Pair: DWORD);
  615. end;
  616. EJclFileVersionInfoError = class(EJclError);
  617. TJclFileVersionInfo = class(TObject)
  618. private
  619. FBuffer: AnsiString;
  620. FFixedInfo: PVSFixedFileInfo;
  621. FFileFlags: TFileFlags;
  622. FItemList: TStringList;
  623. FItems: TStringList;
  624. FLanguages: array of TLangIdRec;
  625. FLanguageIndex: Integer;
  626. FTranslations: array of TLangIdRec;
  627. function GetFixedInfo: TVSFixedFileInfo;
  628. function GetItems: TStrings;
  629. function GetLanguageCount: Integer;
  630. function GetLanguageIds(Index: Integer): string;
  631. function GetLanguageNames(Index: Integer): string;
  632. function GetLanguages(Index: Integer): TLangIdRec;
  633. function GetTranslationCount: Integer;
  634. function GetTranslations(Index: Integer): TLangIdRec;
  635. procedure SetLanguageIndex(const Value: Integer);
  636. protected
  637. procedure CreateItemsForLanguage;
  638. procedure CheckLanguageIndex(Value: Integer);
  639. procedure ExtractData;
  640. procedure ExtractFlags;
  641. function GetBinFileVersion: string;
  642. function GetBinProductVersion: string;
  643. function GetFileOS: DWORD;
  644. function GetFileSubType: DWORD;
  645. function GetFileType: DWORD;
  646. function GetFileVersionBuild: string;
  647. function GetFileVersionMajor: string;
  648. function GetFileVersionMinor: string;
  649. function GetFileVersionRelease: string;
  650. function GetProductVersionBuild: string;
  651. function GetProductVersionMajor: string;
  652. function GetProductVersionMinor: string;
  653. function GetProductVersionRelease: string;
  654. function GetVersionKeyValue(Index: Integer): string;
  655. public
  656. constructor Attach(VersionInfoData: Pointer; Size: Integer);
  657. constructor Create(const FileName: string); overload;
  658. {$IFDEF MSWINDOWS}
  659. {$IFDEF FPC}
  660. constructor Create(const Window: HWND; Dummy: Pointer = nil); overload;
  661. {$ELSE}
  662. constructor Create(const Window: HWND); overload;
  663. {$ENDIF}
  664. constructor Create(const Module: HMODULE); overload;
  665. {$ENDIF MSWINDOWS}
  666. destructor Destroy; override;
  667. function GetCustomFieldValue(const FieldName: string): string;
  668. class function VersionLanguageId(const LangIdRec: TLangIdRec): string;
  669. class function VersionLanguageName(const LangId: Word): string;
  670. class function FileHasVersionInfo(const FileName: string): boolean;
  671. function TranslationMatchesLanguages(Exact: Boolean = True): Boolean;
  672. property BinFileVersion: string read GetBinFileVersion;
  673. property BinProductVersion: string read GetBinProductVersion;
  674. property Comments: string index 1 read GetVersionKeyValue;
  675. property CompanyName: string index 2 read GetVersionKeyValue;
  676. property FileDescription: string index 3 read GetVersionKeyValue;
  677. property FixedInfo: TVSFixedFileInfo read GetFixedInfo;
  678. property FileFlags: TFileFlags read FFileFlags;
  679. property FileOS: DWORD read GetFileOS;
  680. property FileSubType: DWORD read GetFileSubType;
  681. property FileType: DWORD read GetFileType;
  682. property FileVersion: string index 4 read GetVersionKeyValue;
  683. property FileVersionBuild: string read GetFileVersionBuild;
  684. property FileVersionMajor: string read GetFileVersionMajor;
  685. property FileVersionMinor: string read GetFileVersionMinor;
  686. property FileVersionRelease: string read GetFileVersionRelease;
  687. property Items: TStrings read GetItems;
  688. property InternalName: string index 5 read GetVersionKeyValue;
  689. property LanguageCount: Integer read GetLanguageCount;
  690. property LanguageIds[Index: Integer]: string read GetLanguageIds;
  691. property LanguageIndex: Integer read FLanguageIndex write SetLanguageIndex;
  692. property Languages[Index: Integer]: TLangIdRec read GetLanguages;
  693. property LanguageNames[Index: Integer]: string read GetLanguageNames;
  694. property LegalCopyright: string index 6 read GetVersionKeyValue;
  695. property LegalTradeMarks: string index 7 read GetVersionKeyValue;
  696. property OriginalFilename: string index 8 read GetVersionKeyValue;
  697. property PrivateBuild: string index 12 read GetVersionKeyValue;
  698. property ProductName: string index 9 read GetVersionKeyValue;
  699. property ProductVersion: string index 10 read GetVersionKeyValue;
  700. property ProductVersionBuild: string read GetProductVersionBuild;
  701. property ProductVersionMajor: string read GetProductVersionMajor;
  702. property ProductVersionMinor: string read GetProductVersionMinor;
  703. property ProductVersionRelease: string read GetProductVersionRelease;
  704. property SpecialBuild: string index 11 read GetVersionKeyValue;
  705. property TranslationCount: Integer read GetTranslationCount;
  706. property Translations[Index: Integer]: TLangIdRec read GetTranslations;
  707. end;
  708. function OSIdentToString(const OSIdent: DWORD): string;
  709. function OSFileTypeToString(const OSFileType: DWORD; const OSFileSubType: DWORD = 0): string;
  710. function VersionResourceAvailable(const FileName: string): Boolean; overload;
  711. function VersionResourceAvailable(const Window: HWND): Boolean; overload;
  712. function VersionResourceAvailable(const Module: HMODULE): Boolean; overload;
  713. function WindowToModuleFileName(const Window: HWND): string;
  714. {$ENDIF MSWINDOWS}
  715. // Version Info formatting
  716. type
  717. TFileVersionFormat = (vfMajorMinor, vfFull);
  718. function FormatVersionString(const HiV, LoV: Word): string; overload;
  719. function FormatVersionString(const Major, Minor, Build, Revision: Word): string; overload;
  720. {$IFDEF MSWINDOWS}
  721. function FormatVersionString(const FixedInfo: TVSFixedFileInfo; VersionFormat: TFileVersionFormat = vfFull): string; overload;
  722. // Version Info extracting
  723. procedure VersionExtractFileInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
  724. procedure VersionExtractProductInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
  725. // Fixed Version Info routines
  726. function VersionFixedFileInfo(const FileName: string; var FixedInfo: TVSFixedFileInfo): Boolean;
  727. function VersionFixedFileInfoString(const FileName: string; VersionFormat: TFileVersionFormat = vfFull;
  728. const NotAvailableText: string = ''): string;
  729. {$ENDIF MSWINDOWS}
  730. // Streams
  731. //
  732. // TStream descendent classes for dealing with temporary files and for using file mapping objects.
  733. type
  734. TJclTempFileStream = class(THandleStream)
  735. private
  736. FFileName: string;
  737. public
  738. constructor Create(const Prefix: string);
  739. destructor Destroy; override;
  740. property FileName: string read FFileName;
  741. end;
  742. {$IFDEF MSWINDOWS}
  743. TJclCustomFileMapping = class;
  744. TJclFileMappingView = class(TCustomMemoryStream)
  745. private
  746. FFileMapping: TJclCustomFileMapping;
  747. FOffsetHigh: Cardinal;
  748. FOffsetLow: Cardinal;
  749. function GetIndex: Integer;
  750. function GetOffset: Int64;
  751. public
  752. constructor Create(const FileMap: TJclCustomFileMapping;
  753. Access, Size: Cardinal; ViewOffset: Int64);
  754. constructor CreateAt(FileMap: TJclCustomFileMapping; Access,
  755. Size: Cardinal; ViewOffset: Int64; Address: Pointer);
  756. destructor Destroy; override;
  757. function Flush(const Count: Cardinal): Boolean;
  758. procedure LoadFromStream(const Stream: TStream);
  759. procedure LoadFromFile(const FileName: string);
  760. function Write(const Buffer; Count: Longint): Longint; override;
  761. property Index: Integer read GetIndex;
  762. property FileMapping: TJclCustomFileMapping read FFileMapping;
  763. property Offset: Int64 read GetOffset;
  764. end;
  765. TJclFileMappingRoundOffset = (rvDown, rvUp);
  766. TJclCustomFileMapping = class(TObject)
  767. private
  768. FExisted: Boolean;
  769. FHandle: THandle;
  770. FName: string;
  771. FRoundViewOffset: TJclFileMappingRoundOffset;
  772. FViews: TList;
  773. function GetCount: Integer;
  774. function GetView(Index: Integer): TJclFileMappingView;
  775. protected
  776. procedure ClearViews;
  777. procedure InternalCreate(const FileHandle: THandle; const Name: string;
  778. const Protect: Cardinal; MaximumSize: Int64; SecAttr: PSecurityAttributes);
  779. procedure InternalOpen(const Name: string; const InheritHandle: Boolean;
  780. const DesiredAccess: Cardinal);
  781. public
  782. constructor Create;
  783. constructor Open(const Name: string; const InheritHandle: Boolean; const DesiredAccess: Cardinal);
  784. destructor Destroy; override;
  785. function Add(const Access, Count: Cardinal; const Offset: Int64): Integer;
  786. function AddAt(const Access, Count: Cardinal; const Offset: Int64; const Address: Pointer): Integer;
  787. procedure Delete(const Index: Integer);
  788. function IndexOf(const View: TJclFileMappingView): Integer;
  789. property Count: Integer read GetCount;
  790. property Existed: Boolean read FExisted;
  791. property Handle: THandle read FHandle;
  792. property Name: string read FName;
  793. property RoundViewOffset: TJclFileMappingRoundOffset read FRoundViewOffset write FRoundViewOffset;
  794. property Views[index: Integer]: TJclFileMappingView read GetView;
  795. end;
  796. TJclFileMapping = class(TJclCustomFileMapping)
  797. private
  798. FFileHandle: THandle;
  799. public
  800. constructor Create(const FileName: string; FileMode: Cardinal;
  801. const Name: string; Protect: Cardinal; const MaximumSize: Int64;
  802. SecAttr: PSecurityAttributes); overload;
  803. constructor Create(const FileHandle: THandle; const Name: string;
  804. Protect: Cardinal; const MaximumSize: Int64;
  805. SecAttr: PSecurityAttributes); overload;
  806. destructor Destroy; override;
  807. property FileHandle: THandle read FFileHandle;
  808. end;
  809. TJclSwapFileMapping = class(TJclCustomFileMapping)
  810. public
  811. constructor Create(const Name: string; Protect: Cardinal;
  812. const MaximumSize: Int64; SecAttr: PSecurityAttributes);
  813. end;
  814. TJclFileMappingStream = class(TCustomMemoryStream)
  815. private
  816. FFileHandle: THandle;
  817. FMapping: THandle;
  818. protected
  819. procedure Close;
  820. public
  821. constructor Create(const FileName: string; FileMode: Word = fmOpenRead or fmShareDenyWrite);
  822. destructor Destroy; override;
  823. function Write(const Buffer; Count: Longint): Longint; override;
  824. end;
  825. {$ENDIF MSWINDOWS}
  826. TJclMappedTextReaderIndex = (tiNoIndex, tiFull);
  827. PPAnsiCharArray = ^TPAnsiCharArray;
  828. TPAnsiCharArray = array [0..MaxInt div SizeOf(PAnsiChar) - 1] of PAnsiChar;
  829. TJclAnsiMappedTextReader = class(TPersistent)
  830. private
  831. FContent: PAnsiChar;
  832. FEnd: PAnsiChar;
  833. FIndex: PPAnsiCharArray;
  834. FIndexOption: TJclMappedTextReaderIndex;
  835. FFreeStream: Boolean;
  836. FLastLineNumber: Integer;
  837. FLastPosition: PAnsiChar;
  838. FLineCount: Integer;
  839. FMemoryStream: TCustomMemoryStream;
  840. FPosition: PAnsiChar;
  841. FSize: Integer;
  842. function GetAsString: AnsiString;
  843. function GetEof: Boolean;
  844. function GetChars(Index: Integer): AnsiChar;
  845. function GetLineCount: Integer;
  846. function GetLines(LineNumber: Integer): AnsiString;
  847. function GetPosition: Integer;
  848. function GetPositionFromLine(LineNumber: Integer): Integer;
  849. procedure SetPosition(const Value: Integer);
  850. protected
  851. procedure AssignTo(Dest: TPersistent); override;
  852. procedure CreateIndex;
  853. procedure Init;
  854. function PtrFromLine(LineNumber: Integer): PAnsiChar;
  855. function StringFromPosition(var StartPos: PAnsiChar): AnsiString;
  856. public
  857. constructor Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean = True;
  858. const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
  859. constructor Create(const FileName: TFileName;
  860. const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
  861. destructor Destroy; override;
  862. procedure GoBegin;
  863. function Read: AnsiChar;
  864. function ReadLn: AnsiString;
  865. property AsString: AnsiString read GetAsString;
  866. property Chars[Index: Integer]: AnsiChar read GetChars;
  867. property Content: PAnsiChar read FContent;
  868. property Eof: Boolean read GetEof;
  869. property IndexOption: TJclMappedTextReaderIndex read FIndexOption;
  870. property Lines[LineNumber: Integer]: AnsiString read GetLines;
  871. property LineCount: Integer read GetLineCount;
  872. property PositionFromLine[LineNumber: Integer]: Integer read GetPositionFromLine;
  873. property Position: Integer read GetPosition write SetPosition;
  874. property Size: Integer read FSize;
  875. end;
  876. PPWideCharArray = ^TPWideCharArray;
  877. TPWideCharArray = array [0..MaxInt div SizeOf(PWideChar) - 1] of PWideChar;
  878. TJclWideMappedTextReader = class(TPersistent)
  879. private
  880. FContent: PWideChar;
  881. FEnd: PWideChar;
  882. FIndex: PPWideCharArray;
  883. FIndexOption: TJclMappedTextReaderIndex;
  884. FFreeStream: Boolean;
  885. FLastLineNumber: Integer;
  886. FLastPosition: PWideChar;
  887. FLineCount: Integer;
  888. FMemoryStream: TCustomMemoryStream;
  889. FPosition: PWideChar;
  890. FSize: Integer;
  891. function GetAsString: WideString;
  892. function GetEof: Boolean;
  893. function GetChars(Index: Integer): WideChar;
  894. function GetLineCount: Integer;
  895. function GetLines(LineNumber: Integer): WideString;
  896. function GetPosition: Integer;
  897. function GetPositionFromLine(LineNumber: Integer): Integer;
  898. procedure SetPosition(const Value: Integer);
  899. protected
  900. procedure AssignTo(Dest: TPersistent); override;
  901. procedure CreateIndex;
  902. procedure Init;
  903. function PtrFromLine(LineNumber: Integer): PWideChar;
  904. function StringFromPosition(var StartPos: PWideChar): WideString;
  905. public
  906. constructor Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean = True;
  907. const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
  908. constructor Create(const FileName: TFileName;
  909. const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
  910. destructor Destroy; override;
  911. procedure GoBegin;
  912. function Read: WideChar;
  913. function ReadLn: WideString;
  914. property AsString: WideString read GetAsString;
  915. property Chars[Index: Integer]: WideChar read GetChars;
  916. property Content: PWideChar read FContent;
  917. property Eof: Boolean read GetEof;
  918. property IndexOption: TJclMappedTextReaderIndex read FIndexOption;
  919. property Lines[LineNumber: Integer]: WideString read GetLines;
  920. property LineCount: Integer read GetLineCount;
  921. property PositionFromLine[LineNumber: Integer]: Integer read GetPositionFromLine;
  922. property Position: Integer read GetPosition write SetPosition;
  923. property Size: Integer read FSize;
  924. end;
  925. { TODO : UNTESTED/UNDOCUMENTED }
  926. type
  927. TJclFileMaskComparator = class(TObject)
  928. private
  929. FFileMask: string;
  930. FExts: array of string;
  931. FNames: array of string;
  932. FWildChars: array of Byte;
  933. FSeparator: Char;
  934. procedure CreateMultiMasks;
  935. function GetCount: Integer;
  936. function GetExts(Index: Integer): string;
  937. function GetMasks(Index: Integer): string;
  938. function GetNames(Index: Integer): string;
  939. procedure SetFileMask(const Value: string);
  940. procedure SetSeparator(const Value: Char);
  941. public
  942. constructor Create;
  943. function Compare(const NameExt: string): Boolean;
  944. property Count: Integer read GetCount;
  945. property Exts[Index: Integer]: string read GetExts;
  946. property FileMask: string read FFileMask write SetFileMask;
  947. property Masks[Index: Integer]: string read GetMasks;
  948. property Names[Index: Integer]: string read GetNames;
  949. property Separator: Char read FSeparator write SetSeparator;
  950. end;
  951. EJclPathError = class(EJclError);
  952. EJclFileUtilsError = class(EJclError);
  953. {$IFDEF UNIX}
  954. EJclTempFileStreamError = class(EJclFileUtilsError);
  955. {$ENDIF UNIX}
  956. {$IFDEF MSWINDOWS}
  957. EJclTempFileStreamError = class(EJclWin32Error);
  958. EJclFileMappingError = class(EJclWin32Error);
  959. EJclFileMappingViewError = class(EJclWin32Error);
  960. {$ENDIF MSWINDOWS}
  961. function SamePath(const Path1, Path2: string): Boolean;
  962. // functions to add/delete paths from a separated list of paths
  963. // on windows the separator is a semi-colon ';'
  964. // on linux the separator is a colon ':'
  965. // add items at the end
  966. procedure PathListAddItems(var List: string; const Items: string);
  967. // add items at the end if they are not present
  968. procedure PathListIncludeItems(var List: string; const Items: string);
  969. // delete multiple items
  970. procedure PathListDelItems(var List: string; const Items: string);
  971. // delete one item
  972. procedure PathListDelItem(var List: string; const Index: Integer);
  973. // return the number of item
  974. function PathListItemCount(const List: string): Integer;
  975. // return the Nth item
  976. function PathListGetItem(const List: string; const Index: Integer): string;
  977. // set the Nth item
  978. procedure PathListSetItem(var List: string; const Index: Integer; const Value: string);
  979. // return the index of an item
  980. function PathListItemIndex(const List, Item: string): Integer;
  981. // additional functions to access the commandline parameters of an application
  982. // returns the name of the command line parameter at position index, which is
  983. // separated by the given separator, if the first character of the name part
  984. // is one of the AllowedPrefixCharacters, this character will be deleted.
  985. function ParamName(Index: Integer; const Separator: string = '=';
  986. const AllowedPrefixCharacters: string = '-/'; TrimName: Boolean = True): string;
  987. // returns the value of the command line parameter at position index, which is
  988. // separated by the given separator
  989. function ParamValue (Index: Integer; const Separator: string = '='; TrimValue: Boolean = True): string; overload;
  990. // seaches a command line parameter where the namepart is the searchname
  991. // and returns the value which is which by the given separator.
  992. // CaseSensitive defines the search type. if the first character of the name part
  993. // is one of the AllowedPrefixCharacters, this character will be deleted.
  994. function ParamValue (const SearchName: string; const Separator: string = '=';
  995. CaseSensitive: Boolean = False;
  996. const AllowedPrefixCharacters: string = '-/'; TrimValue: Boolean = True): string; overload;
  997. // seaches a command line parameter where the namepart is the searchname
  998. // and returns the position index. if no separator is defined, the full paramstr is compared.
  999. // CaseSensitive defines the search type. if the first character of the name part
  1000. // is one of the AllowedPrefixCharacters, this character will be deleted.
  1001. function ParamPos (const SearchName: string; const Separator: string = '=';
  1002. CaseSensitive: Boolean = False;
  1003. const AllowedPrefixCharacters: string = '-/'): Integer;
  1004. {$IFDEF UNITVERSIONING}
  1005. const
  1006. UnitVersioning: TUnitVersionInfo = (
  1007. RCSfile: '$URL$';
  1008. Revision: '$Revision$';
  1009. Date: '$Date$';
  1010. LogPath: 'JCL\source\common';
  1011. Extra: '';
  1012. Data: nil
  1013. );
  1014. {$ENDIF UNITVERSIONING}
  1015. implementation
  1016. uses
  1017. {$IFDEF HAS_UNITSCOPE}
  1018. System.Types, // inlining of TList.Remove
  1019. {$IFDEF HAS_UNIT_CHARACTER}
  1020. System.Character,
  1021. {$ENDIF HAS_UNIT_CHARACTER}
  1022. System.Math,
  1023. {$IFDEF MSWINDOWS}
  1024. Winapi.ShellApi, Winapi.ActiveX, System.Win.ComObj, Winapi.ShlObj,
  1025. JclShell, JclSysInfo, JclSecurity,
  1026. {$ENDIF MSWINDOWS}
  1027. {$ELSE ~HAS_UNITSCOPE}
  1028. {$IFDEF HAS_UNIT_CHARACTER}
  1029. Character,
  1030. {$ENDIF HAS_UNIT_CHARACTER}
  1031. Math,
  1032. {$IFDEF MSWINDOWS}
  1033. ShellApi, ActiveX, ComObj, ShlObj,
  1034. JclShell, JclSysInfo, JclSecurity,
  1035. {$ENDIF MSWINDOWS}
  1036. {$ENDIF ~HAS_UNITSCOPE}
  1037. JclDateTime, JclResources,
  1038. JclStrings;
  1039. { Some general notes:
  1040. This unit redeclares some functions from FileCtrl.pas to avoid a dependency on that unit in the
  1041. JCL. The problem is that FileCtrl.pas uses some units (eg Forms.pas) which have ridiculous
  1042. initialization requirements. They add 4KB (!) to the executable and roughly 1 second of startup.
  1043. That initialization is only necessary for GUI applications and is unacceptable for high
  1044. performance services or console apps.
  1045. The routines which query files or directories for their attributes deliberately use FindFirst
  1046. even though there may be easier ways to get at the required information. This is because FindFirst
  1047. is about the only routine which doesn't cause the file's last modification/accessed time to be
  1048. changed which is usually an undesired side-effect. }
  1049. {$IFDEF UNIX}
  1050. const
  1051. ERROR_NO_MORE_FILES = -1;
  1052. INVALID_HANDLE_VALUE = THandle(-1);
  1053. {$ENDIF UNIX}
  1054. //=== { TJclTempFileStream } =================================================
  1055. constructor TJclTempFileStream.Create(const Prefix: string);
  1056. var
  1057. FileHandle: THandle;
  1058. begin
  1059. FFileName := Prefix;
  1060. FileHandle := FileCreateTemp(FFileName);
  1061. // (rom) is it really wise to throw an exception before calling inherited?
  1062. if FileHandle = INVALID_HANDLE_VALUE then
  1063. raise EJclTempFileStreamError.CreateRes(@RsFileStreamCreate);
  1064. inherited Create(FileHandle);
  1065. end;
  1066. destructor TJclTempFileStream.Destroy;
  1067. begin
  1068. if THandle(Handle) <> INVALID_HANDLE_VALUE then
  1069. FileClose(Handle);
  1070. inherited Destroy;
  1071. end;
  1072. //=== { TJclFileMappingView } ================================================
  1073. {$IFDEF MSWINDOWS}
  1074. constructor TJclFileMappingView.Create(const FileMap: TJclCustomFileMapping;
  1075. Access, Size: Cardinal; ViewOffset: Int64);
  1076. var
  1077. BaseAddress: Pointer;
  1078. OffsetLow, OffsetHigh: Cardinal;
  1079. begin
  1080. inherited Create;
  1081. if FileMap = nil then
  1082. raise EJclFileMappingViewError.CreateRes(@RsViewNeedsMapping);
  1083. FFileMapping := FileMap;
  1084. // Offset must be a multiple of system memory allocation granularity
  1085. RoundToAllocGranularity64(ViewOffset, FFileMapping.RoundViewOffset = rvUp);
  1086. I64ToCardinals(ViewOffset, OffsetLow, OffsetHigh);
  1087. FOffsetHigh := OffsetHigh;
  1088. FOffsetLow := OffsetLow;
  1089. BaseAddress := MapViewOfFile(FFileMapping.Handle, Access, FOffsetHigh, FOffsetLow, Size);
  1090. if BaseAddress = nil then
  1091. raise EJclFileMappingViewError.CreateRes(@RsCreateFileMappingView);
  1092. // If we are mapping a file and size = 0 then MapViewOfFile has mapped the entire file. We must
  1093. // figure out the size ourselves before we can call SetPointer. Since in case of failure to
  1094. // retrieve the size we raise an exception, we also have to explicitly unmap the view which
  1095. // otherwise would have been done by the destructor.
  1096. if (Size = 0) and (FileMap is TJclFileMapping) then
  1097. begin
  1098. Size := GetFileSize(TJclFileMapping(FileMap).FFileHandle, nil);
  1099. if Size = DWORD(-1) then
  1100. begin
  1101. UnMapViewOfFile(BaseAddress);
  1102. raise EJclFileMappingViewError.CreateRes(@RsFailedToObtainSize);
  1103. end;
  1104. end;
  1105. SetPointer(BaseAddress, Size);
  1106. FFileMapping.FViews.Add(Self);
  1107. end;
  1108. constructor TJclFileMappingView.CreateAt(FileMap: TJclCustomFileMapping;
  1109. Access, Size: Cardinal; ViewOffset: Int64; Address: Pointer);
  1110. var
  1111. BaseAddress: Pointer;
  1112. OffsetLow, OffsetHigh: Cardinal;
  1113. begin
  1114. inherited Create;
  1115. if FileMap = nil then
  1116. raise EJclFileMappingViewError.CreateRes(@RsViewNeedsMapping);
  1117. FFileMapping := FileMap;
  1118. // Offset must be a multiple of system memory allocation granularity
  1119. RoundToAllocGranularity64(ViewOffset, FFileMapping.RoundViewOffset = rvUp);
  1120. RoundToAllocGranularityPtr(Address, FFileMapping.RoundViewOffset = rvUp);
  1121. I64ToCardinals(ViewOffset, OffsetLow, OffsetHigh);
  1122. FOffsetHigh := OffsetHigh;
  1123. FOffsetLow := OffsetLow;
  1124. BaseAddress := MapViewOfFileEx(FFileMapping.Handle, Access, FOffsetHigh,
  1125. FOffsetLow, Size, Address);
  1126. if BaseAddress = nil then
  1127. raise EJclFileMappingViewError.CreateRes(@RsCreateFileMappingView);
  1128. // If we are mapping a file and size = 0 then MapViewOfFile has mapped the entire file. We must
  1129. // figure out the size ourselves before we can call SetPointer. Since in case of failure to
  1130. // retrieve the size we raise an exception, we also have to explicitly unmap the view which
  1131. // otherwise would have been done by the destructor.
  1132. if (Size = 0) and (FileMap is TJclFileMapping) then
  1133. begin
  1134. Size := GetFileSize(TJclFileMapping(FileMap).FFileHandle, nil);
  1135. if Size = DWORD(-1) then
  1136. begin
  1137. UnMapViewOfFile(BaseAddress);
  1138. raise EJclFileMappingViewError.CreateRes(@RsFailedToObtainSize);
  1139. end;
  1140. end;
  1141. SetPointer(BaseAddress, Size);
  1142. FFileMapping.FViews.Add(Self);
  1143. end;
  1144. destructor TJclFileMappingView.Destroy;
  1145. var
  1146. IndexOfSelf: Integer;
  1147. begin
  1148. if Memory <> nil then
  1149. begin
  1150. UnMapViewOfFile(Memory);
  1151. SetPointer(nil, 0);
  1152. end;
  1153. if FFileMapping <> nil then
  1154. begin
  1155. IndexOfSelf := FFileMapping.IndexOf(Self);
  1156. if IndexOfSelf <> -1 then
  1157. FFileMapping.FViews.Delete(IndexOfSelf);
  1158. end;
  1159. inherited Destroy;
  1160. end;
  1161. function TJclFileMappingView.Flush(const Count: Cardinal): Boolean;
  1162. begin
  1163. Result := FlushViewOfFile(Memory, Count);
  1164. end;
  1165. function TJclFileMappingView.GetIndex: Integer;
  1166. begin
  1167. Result := FFileMapping.IndexOf(Self);
  1168. end;
  1169. function TJclFileMappingView.GetOffset: Int64;
  1170. begin
  1171. CardinalsToI64(Result, FOffsetLow, FOffsetHigh);
  1172. end;
  1173. procedure TJclFileMappingView.LoadFromFile(const FileName: string);
  1174. var
  1175. Stream: TFileStream;
  1176. begin
  1177. Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  1178. try
  1179. LoadFromStream(Stream);
  1180. finally
  1181. FreeAndNil(Stream);
  1182. end;
  1183. end;
  1184. procedure TJclFileMappingView.LoadFromStream(const Stream: TStream);
  1185. begin
  1186. if Stream.Size > Size then
  1187. raise EJclFileMappingViewError.CreateRes(@RsLoadFromStreamSize);
  1188. Stream.Position := 0;
  1189. Stream.ReadBuffer(Memory^, Stream.Size);
  1190. end;
  1191. function TJclFileMappingView.Write(const Buffer; Count: Integer): Longint;
  1192. begin
  1193. Result := 0;
  1194. if (Size - Position) >= Count then
  1195. begin
  1196. System.Move(Buffer, Pointer(TJclAddr(Memory) + TJclAddr(Position))^, Count);
  1197. Position := Position + Count;
  1198. Result := Count;
  1199. end;
  1200. end;
  1201. //=== { TJclCustomFileMapping } ==============================================
  1202. constructor TJclCustomFileMapping.Create;
  1203. begin
  1204. inherited Create;
  1205. FViews := TList.Create;
  1206. FRoundViewOffset := rvDown;
  1207. end;
  1208. constructor TJclCustomFileMapping.Open(const Name: string;
  1209. const InheritHandle: Boolean; const DesiredAccess: Cardinal);
  1210. begin
  1211. Create;
  1212. InternalOpen(Name, InheritHandle, DesiredAccess);
  1213. end;
  1214. destructor TJclCustomFileMapping.Destroy;
  1215. begin
  1216. ClearViews;
  1217. if FHandle <> 0 then
  1218. CloseHandle(FHandle);
  1219. FreeAndNil(FViews);
  1220. inherited Destroy;
  1221. end;
  1222. function TJclCustomFileMapping.Add(const Access, Count: Cardinal; const Offset: Int64): Integer;
  1223. var
  1224. View: TJclFileMappingView;
  1225. begin
  1226. // The view adds itself to the FViews list
  1227. View := TJclFileMappingView.Create(Self, Access, Count, Offset);
  1228. Result := View.Index;
  1229. end;
  1230. function TJclCustomFileMapping.AddAt(const Access, Count: Cardinal;
  1231. const Offset: Int64; const Address: Pointer): Integer;
  1232. var
  1233. View: TJclFileMappingView;
  1234. begin
  1235. // The view adds itself to the FViews list
  1236. View := TJclFileMappingView.CreateAt(Self, Access, Count, Offset, Address);
  1237. Result := View.Index;
  1238. end;
  1239. procedure TJclCustomFileMapping.ClearViews;
  1240. var
  1241. I: Integer;
  1242. begin
  1243. // Note that the view destructor removes the view object from the FViews list so we must loop
  1244. // downwards from count to 0
  1245. for I := FViews.Count - 1 downto 0 do
  1246. TJclFileMappingView(FViews[I]).Free;
  1247. end;
  1248. procedure TJclCustomFileMapping.Delete(const Index: Integer);
  1249. begin
  1250. // Note that the view destructor removes itself from FViews
  1251. TJclFileMappingView(FViews[Index]).Free;
  1252. end;
  1253. function TJclCustomFileMapping.GetCount: Integer;
  1254. begin
  1255. Result := FViews.Count;
  1256. end;
  1257. function TJclCustomFileMapping.GetView(Index: Integer): TJclFileMappingView;
  1258. begin
  1259. Result := TJclFileMappingView(FViews.Items[index]);
  1260. end;
  1261. function TJclCustomFileMapping.IndexOf(const View: TJclFileMappingView): Integer;
  1262. begin
  1263. Result := FViews.IndexOf(View);
  1264. end;
  1265. procedure TJclCustomFileMapping.InternalCreate(const FileHandle: THandle;
  1266. const Name: string; const Protect: Cardinal; MaximumSize: Int64;
  1267. SecAttr: PSecurityAttributes);
  1268. var
  1269. MaximumSizeLow, MaximumSizeHigh: Cardinal;
  1270. begin
  1271. FName := Name;
  1272. I64ToCardinals(MaximumSize, MaximumSizeLow, MaximumSizeHigh);
  1273. FHandle := CreateFileMapping(FileHandle, SecAttr, Protect, MaximumSizeHigh,
  1274. MaximumSizeLow, PChar(Name));
  1275. if FHandle = 0 then
  1276. raise EJclFileMappingError.CreateRes(@RsCreateFileMapping);
  1277. FExisted := GetLastError = ERROR_ALREADY_EXISTS;
  1278. end;
  1279. procedure TJclCustomFileMapping.InternalOpen(const Name: string;
  1280. const InheritHandle: Boolean; const DesiredAccess: Cardinal);
  1281. begin
  1282. FExisted := True;
  1283. FName := Name;
  1284. FHandle := OpenFileMapping(DesiredAccess, InheritHandle, PChar(Name));
  1285. if FHandle = 0 then
  1286. raise EJclFileMappingError.CreateRes(@RsCreateFileMapping);
  1287. end;
  1288. //=== { TJclFileMapping } ====================================================
  1289. constructor TJclFileMapping.Create(const FileName: string; FileMode: Cardinal;
  1290. const Name: string; Protect: Cardinal; const MaximumSize: Int64;
  1291. SecAttr: PSecurityAttributes);
  1292. begin
  1293. FFileHandle := INVALID_HANDLE_VALUE;
  1294. inherited Create;
  1295. FFileHandle := THandle(FileOpen(FileName, FileMode));
  1296. if FFileHandle = INVALID_HANDLE_VALUE then
  1297. raise EJclFileMappingError.CreateRes(@RsFileMappingOpenFile);
  1298. InternalCreate(FFileHandle, Name, Protect, MaximumSize, SecAttr);
  1299. end;
  1300. constructor TJclFileMapping.Create(const FileHandle: THandle; const Name: string;
  1301. Protect: Cardinal; const MaximumSize: Int64; SecAttr: PSecurityAttributes);
  1302. begin
  1303. FFileHandle := INVALID_HANDLE_VALUE;
  1304. inherited Create;
  1305. if FileHandle = INVALID_HANDLE_VALUE then
  1306. raise EJclFileMappingError.CreateRes(@RsFileMappingInvalidHandle);
  1307. InternalCreate(FileHandle, Name, Protect, MaximumSize, SecAttr);
  1308. // Duplicate the handle into FFileHandle as opposed to assigning it directly. This will cause
  1309. // FFileHandle to retrieve a unique copy which is independent of FileHandle. This makes the
  1310. // remainder of the class, especially the destructor, easier. The caller will have to close it's
  1311. // own copy of the handle explicitly.
  1312. DuplicateHandle(GetCurrentProcess, FileHandle, GetCurrentProcess,
  1313. @FFileHandle, 0, False, DUPLICATE_SAME_ACCESS);
  1314. end;
  1315. destructor TJclFileMapping.Destroy;
  1316. begin
  1317. if FFileHandle <> INVALID_HANDLE_VALUE then
  1318. CloseHandle(FFileHandle);
  1319. inherited Destroy;
  1320. end;
  1321. //=== { TJclSwapFileMapping } ================================================
  1322. constructor TJclSwapFileMapping.Create(const Name: string; Protect: Cardinal;
  1323. const MaximumSize: Int64; SecAttr: PSecurityAttributes);
  1324. begin
  1325. inherited Create;
  1326. InternalCreate(INVALID_HANDLE_VALUE, Name, Protect, MaximumSize, SecAttr);
  1327. end;
  1328. //=== { TJclFileMappingStream } ==============================================
  1329. constructor TJclFileMappingStream.Create(const FileName: string; FileMode: Word);
  1330. var
  1331. Protect, Access, Size: DWORD;
  1332. BaseAddress: Pointer;
  1333. begin
  1334. inherited Create;
  1335. FFileHandle := THandle(FileOpen(FileName, FileMode));
  1336. if FFileHandle = INVALID_HANDLE_VALUE then
  1337. RaiseLastOSError;
  1338. if (FileMode and $0F) = fmOpenReadWrite then
  1339. begin
  1340. Protect := PAGE_WRITECOPY;
  1341. Access := FILE_MAP_COPY;
  1342. end
  1343. else
  1344. begin
  1345. Protect := PAGE_READONLY;
  1346. Access := FILE_MAP_READ;
  1347. end;
  1348. FMapping := CreateFileMapping(FFileHandle, nil, Protect, 0, 0, nil);
  1349. if FMapping = 0 then
  1350. begin
  1351. Close;
  1352. raise EJclFileMappingError.CreateRes(@RsCreateFileMapping);
  1353. end;
  1354. BaseAddress := MapViewOfFile(FMapping, Access, 0, 0, 0);
  1355. if BaseAddress = nil then
  1356. begin
  1357. Close;
  1358. raise EJclFileMappingViewError.CreateRes(@RsCreateFileMappingView);
  1359. end;
  1360. Size := GetFileSize(FFileHandle, nil);
  1361. if Size = DWORD(-1) then
  1362. begin
  1363. UnMapViewOfFile(BaseAddress);
  1364. Close;
  1365. raise EJclFileMappingViewError.CreateRes(@RsFailedToObtainSize);
  1366. end;
  1367. SetPointer(BaseAddress, Size);
  1368. end;
  1369. destructor TJclFileMappingStream.Destroy;
  1370. begin
  1371. Close;
  1372. inherited Destroy;
  1373. end;
  1374. procedure TJclFileMappingStream.Close;
  1375. begin
  1376. if Memory <> nil then
  1377. begin
  1378. UnMapViewOfFile(Memory);
  1379. SetPointer(nil, 0);
  1380. end;
  1381. if FMapping <> 0 then
  1382. begin
  1383. CloseHandle(FMapping);
  1384. FMapping := 0;
  1385. end;
  1386. if FFileHandle <> INVALID_HANDLE_VALUE then
  1387. begin
  1388. FileClose(FFileHandle);
  1389. FFileHandle := INVALID_HANDLE_VALUE;
  1390. end;
  1391. end;
  1392. function TJclFileMappingStream.Write(const Buffer; Count: Integer): Longint;
  1393. begin
  1394. Result := 0;
  1395. if (Size - Position) >= Count then
  1396. begin
  1397. System.Move(Buffer, Pointer(TJclAddr(Memory) + TJclAddr(Position))^, Count);
  1398. Position := Position + Count;
  1399. Result := Count;
  1400. end;
  1401. end;
  1402. {$ENDIF MSWINDOWS}
  1403. //=== { TJclAnsiMappedTextReader } ===========================================
  1404. constructor TJclAnsiMappedTextReader.Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean;
  1405. const AIndexOption: TJclMappedTextReaderIndex);
  1406. begin
  1407. inherited Create;
  1408. FMemoryStream := MemoryStream;
  1409. FFreeStream := FreeStream;
  1410. FIndexOption := AIndexOption;
  1411. Init;
  1412. end;
  1413. constructor TJclAnsiMappedTextReader.Create(const FileName: TFileName;
  1414. const AIndexOption: TJclMappedTextReaderIndex);
  1415. begin
  1416. inherited Create;
  1417. {$IFDEF MSWINDOWS}
  1418. FMemoryStream := TJclFileMappingStream.Create(FileName);
  1419. {$ELSE ~ MSWINDOWS}
  1420. FMemoryStream := TMemoryStream.Create;
  1421. TMemoryStream(FMemoryStream).LoadFromFile(FileName);
  1422. {$ENDIF ~ MSWINDOWS}
  1423. FFreeStream := True;
  1424. FIndexOption := AIndexOption;
  1425. Init;
  1426. end;
  1427. destructor TJclAnsiMappedTextReader.Destroy;
  1428. begin
  1429. if FFreeStream then
  1430. FMemoryStream.Free;
  1431. FreeMem(FIndex);
  1432. inherited Destroy;
  1433. end;
  1434. procedure TJclAnsiMappedTextReader.AssignTo(Dest: TPersistent);
  1435. begin
  1436. if Dest is TStrings then
  1437. begin
  1438. GoBegin;
  1439. TStrings(Dest).BeginUpdate;
  1440. try
  1441. while not Eof do
  1442. TStrings(Dest).Add(string(ReadLn));
  1443. finally
  1444. TStrings(Dest).EndUpdate;
  1445. end;
  1446. end
  1447. else
  1448. inherited AssignTo(Dest);
  1449. end;
  1450. procedure TJclAnsiMappedTextReader.CreateIndex;
  1451. var
  1452. P, LastLineStart: PAnsiChar;
  1453. I: Integer;
  1454. begin
  1455. {$RANGECHECKS OFF}
  1456. P := FContent;
  1457. I := 0;
  1458. LastLineStart := P;
  1459. while P < FEnd do
  1460. begin
  1461. // CRLF, CR, LF and LFCR are seen as valid sets of chars for EOL marker
  1462. if CharIsReturn(Char(P^)) then
  1463. begin
  1464. if I and $FFFF = 0 then
  1465. ReallocMem(FIndex, (I + $10000) * SizeOf(Pointer));
  1466. FIndex[I] := LastLineStart;
  1467. Inc(I);
  1468. case P^ of
  1469. NativeLineFeed:
  1470. begin
  1471. Inc(P);
  1472. if (P < FEnd) and (P^ = NativeCarriageReturn) then
  1473. Inc(P);
  1474. end;
  1475. NativeCarriageReturn:
  1476. begin
  1477. Inc(P);
  1478. if (P < FEnd) and (P^ = NativeLineFeed) then
  1479. Inc(P);
  1480. end;
  1481. end;
  1482. LastLineStart := P;
  1483. end
  1484. else
  1485. Inc(P);
  1486. end;
  1487. if P > LastLineStart then
  1488. begin
  1489. ReallocMem(FIndex, (I + 1) * SizeOf(Pointer));
  1490. FIndex[I] := LastLineStart;
  1491. Inc(I);
  1492. end
  1493. else
  1494. ReallocMem(FIndex, I * SizeOf(Pointer));
  1495. FLineCount := I;
  1496. {$IFDEF RANGECHECKS_ON}
  1497. {$RANGECHECKS ON}
  1498. {$ENDIF RANGECHECKS_ON}
  1499. end;
  1500. function TJclAnsiMappedTextReader.GetEof: Boolean;
  1501. begin
  1502. Result := FPosition >= FEnd;
  1503. end;
  1504. function TJclAnsiMappedTextReader.GetAsString: AnsiString;
  1505. begin
  1506. SetString(Result, Content, Size);
  1507. end;
  1508. function TJclAnsiMappedTextReader.GetChars(Index: Integer): AnsiChar;
  1509. begin
  1510. if (Index < 0) or (Index >= Size) then
  1511. raise EJclError.CreateRes(@RsFileIndexOutOfRange);
  1512. Result := AnsiChar(PByte(FContent + Index)^);
  1513. end;
  1514. function TJclAnsiMappedTextReader.GetLineCount: Integer;
  1515. var
  1516. P: PAnsiChar;
  1517. begin
  1518. if FLineCount = -1 then
  1519. begin
  1520. FLineCount := 0;
  1521. if FContent < FEnd then
  1522. begin
  1523. P := FContent;
  1524. while P < FEnd do
  1525. begin
  1526. case P^ of
  1527. NativeLineFeed:
  1528. begin
  1529. Inc(FLineCount);
  1530. Inc(P);
  1531. if (P < FEnd) and (P^ = NativeCarriageReturn) then
  1532. Inc(P);
  1533. end;
  1534. NativeCarriageReturn:
  1535. begin
  1536. Inc(FLineCount);
  1537. Inc(P);
  1538. if (P < FEnd) and (P^ = NativeLineFeed) then
  1539. Inc(P);
  1540. end;
  1541. else
  1542. Inc(P);
  1543. end;
  1544. end;
  1545. if (P = FEnd) and (P > FContent) and not CharIsReturn(Char((P-1)^)) then
  1546. Inc(FLineCount);
  1547. end;
  1548. end;
  1549. Result := FLineCount;
  1550. end;
  1551. function TJclAnsiMappedTextReader.GetLines(LineNumber: Integer): AnsiString;
  1552. var
  1553. P: PAnsiChar;
  1554. begin
  1555. P := PtrFromLine(LineNumber);
  1556. Result := StringFromPosition(P);
  1557. end;
  1558. function TJclAnsiMappedTextReader.GetPosition: Integer;
  1559. begin
  1560. Result := FPosition - FContent;
  1561. end;
  1562. procedure TJclAnsiMappedTextReader.GoBegin;
  1563. begin
  1564. Position := 0;
  1565. end;
  1566. procedure TJclAnsiMappedTextReader.Init;
  1567. begin
  1568. FContent := FMemoryStream.Memory;
  1569. FSize := FMemoryStream.Size;
  1570. FEnd := FContent + FSize;
  1571. FPosition := FContent;
  1572. FLineCount := -1;
  1573. FLastLineNumber := 0;
  1574. FLastPosition := FContent;
  1575. if IndexOption = tiFull then
  1576. CreateIndex;
  1577. end;
  1578. function TJclAnsiMappedTextReader.GetPositionFromLine(LineNumber: Integer): Integer;
  1579. var
  1580. P: PAnsiChar;
  1581. begin
  1582. P := PtrFromLine(LineNumber);
  1583. if P = nil then
  1584. Result := -1
  1585. else
  1586. Result := P - FContent;
  1587. end;
  1588. function TJclAnsiMappedTextReader.PtrFromLine(LineNumber: Integer): PAnsiChar;
  1589. var
  1590. LineOffset: Integer;
  1591. begin
  1592. Result := nil;
  1593. {$RANGECHECKS OFF}
  1594. if (IndexOption <> tiNoIndex) and (LineNumber < FLineCount) and (FIndex[LineNumber] <> nil) then
  1595. Result := FIndex[LineNumber]
  1596. {$IFDEF RANGECHECKS_ON}
  1597. {$RANGECHECKS ON}
  1598. {$ENDIF RANGECHECKS_ON}
  1599. else
  1600. begin
  1601. LineOffset := LineNumber - FLastLineNumber;
  1602. if (FLineCount <> -1) and (LineNumber > 0) then
  1603. begin
  1604. if -LineOffset > LineNumber then
  1605. begin
  1606. FLastLineNumber := 0;
  1607. FLastPosition := FContent;
  1608. LineOffset := LineNumber;
  1609. end
  1610. else
  1611. if LineOffset > FLineCount - LineNumber then
  1612. begin
  1613. FLastLineNumber := FLineCount;
  1614. FLastPosition := FEnd;
  1615. LineOffset := LineNumber - FLineCount;
  1616. end;
  1617. end;
  1618. if LineNumber <= 0 then
  1619. Result := FContent
  1620. else
  1621. if LineOffset = 0 then
  1622. Result := FLastPosition
  1623. else
  1624. if LineOffset > 0 then
  1625. begin
  1626. Result := FLastPosition;
  1627. while (Result < FEnd) and (LineOffset > 0) do
  1628. begin
  1629. case Result^ of
  1630. NativeLineFeed:
  1631. begin
  1632. Dec(LineOffset);
  1633. Inc(Result);
  1634. if (Result < FEnd) and (Result^ = NativeCarriageReturn) then
  1635. Inc(Result);
  1636. end;
  1637. NativeCarriageReturn:
  1638. begin
  1639. Dec(LineOffset);
  1640. Inc(Result);
  1641. if (Result < FEnd) and (Result^ = NativeLineFeed) then
  1642. Inc(Result);
  1643. end;
  1644. else
  1645. Inc(Result);
  1646. end;
  1647. end;
  1648. end
  1649. else
  1650. if LineOffset < 0 then
  1651. begin
  1652. Result := FLastPosition;
  1653. while (Result > FContent) and (LineOffset < 1) do
  1654. begin
  1655. Dec(Result);
  1656. case Result^ of
  1657. NativeLineFeed:
  1658. begin
  1659. Inc(LineOffset);
  1660. if LineOffset >= 1 then
  1661. Inc(Result)
  1662. else
  1663. if (Result > FContent) and ((Result-1)^ = NativeCarriageReturn) then
  1664. Dec(Result);
  1665. end;
  1666. NativeCarriageReturn:
  1667. begin
  1668. Inc(LineOffset);
  1669. if LineOffset >= 1 then
  1670. Inc(Result)
  1671. else
  1672. if (Result > FContent) and ((Result-1)^ = NativeLineFeed) then
  1673. Dec(Result);
  1674. end;
  1675. end;
  1676. end;
  1677. end;
  1678. FLastLineNumber := LineNumber;
  1679. FLastPosition := Result;
  1680. end;
  1681. end;
  1682. function TJclAnsiMappedTextReader.Read: AnsiChar;
  1683. begin
  1684. if FPosition >= FEnd then
  1685. Result := #0
  1686. else
  1687. begin
  1688. Result := FPosition^;
  1689. Inc(FPosition);
  1690. end;
  1691. end;
  1692. function TJclAnsiMappedTextReader.ReadLn: AnsiString;
  1693. begin
  1694. Result := StringFromPosition(FPosition);
  1695. end;
  1696. procedure TJclAnsiMappedTextReader.SetPosition(const Value: Integer);
  1697. begin
  1698. FPosition := FContent + Value;
  1699. end;
  1700. function TJclAnsiMappedTextReader.StringFromPosition(var StartPos: PAnsiChar): AnsiString;
  1701. var
  1702. P: PAnsiChar;
  1703. begin
  1704. if (StartPos = nil) or (StartPos >= FEnd) then
  1705. Result := ''
  1706. else
  1707. begin
  1708. P := StartPos;
  1709. while (P < FEnd) and (not CharIsReturn(Char(P^))) do
  1710. Inc(P);
  1711. SetString(Result, StartPos, P - StartPos);
  1712. if P < FEnd then
  1713. begin
  1714. case P^ of
  1715. NativeLineFeed:
  1716. begin
  1717. Inc(P);
  1718. if (P < FEnd) and (P^ = NativeCarriageReturn) then
  1719. Inc(P);
  1720. end;
  1721. NativeCarriageReturn:
  1722. begin
  1723. Inc(P);
  1724. if (P < FEnd) and (P^ = NativeLineFeed) then
  1725. Inc(P);
  1726. end;
  1727. end;
  1728. end;
  1729. StartPos := P;
  1730. end;
  1731. end;
  1732. //=== { TJclWideMappedTextReader } ===========================================
  1733. constructor TJclWideMappedTextReader.Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean;
  1734. const AIndexOption: TJclMappedTextReaderIndex);
  1735. begin
  1736. inherited Create;
  1737. FMemoryStream := MemoryStream;
  1738. FFreeStream := FreeStream;
  1739. FIndexOption := AIndexOption;
  1740. Init;
  1741. end;
  1742. constructor TJclWideMappedTextReader.Create(const FileName: TFileName;
  1743. const AIndexOption: TJclMappedTextReaderIndex);
  1744. begin
  1745. inherited Create;
  1746. {$IFDEF MSWINDOWS}
  1747. FMemoryStream := TJclFileMappingStream.Create(FileName);
  1748. {$ELSE ~ MSWINDOWS}
  1749. FMemoryStream := TMemoryStream.Create;
  1750. TMemoryStream(FMemoryStream).LoadFromFile(FileName);
  1751. {$ENDIF ~ MSWINDOWS}
  1752. FFreeStream := True;
  1753. FIndexOption := AIndexOption;
  1754. Init;
  1755. end;
  1756. destructor TJclWideMappedTextReader.Destroy;
  1757. begin
  1758. if FFreeStream then
  1759. FMemoryStream.Free;
  1760. FreeMem(FIndex);
  1761. inherited Destroy;
  1762. end;
  1763. procedure TJclWideMappedTextReader.AssignTo(Dest: TPersistent);
  1764. begin
  1765. if Dest is TStrings then
  1766. begin
  1767. GoBegin;
  1768. TStrings(Dest).BeginUpdate;
  1769. try
  1770. while not Eof do
  1771. TStrings(Dest).Add(string(ReadLn));
  1772. finally
  1773. TStrings(Dest).EndUpdate;
  1774. end;
  1775. end
  1776. else
  1777. inherited AssignTo(Dest);
  1778. end;
  1779. procedure TJclWideMappedTextReader.CreateIndex;
  1780. var
  1781. P, LastLineStart: PWideChar;
  1782. I: Integer;
  1783. begin
  1784. {$RANGECHECKS OFF}
  1785. P := FContent;
  1786. I := 0;
  1787. LastLineStart := P;
  1788. while P < FEnd do
  1789. begin
  1790. // CRLF, CR, LF and LFCR are seen as valid sets of chars for EOL marker
  1791. if CharIsReturn(Char(P^)) then
  1792. begin
  1793. if I and $FFFF = 0 then
  1794. ReallocMem(FIndex, (I + $10000) * SizeOf(Pointer));
  1795. FIndex[I] := LastLineStart;
  1796. Inc(I);
  1797. case P^ of
  1798. NativeLineFeed:
  1799. begin
  1800. Inc(P);
  1801. if (P < FEnd) and (P^ = NativeCarriageReturn) then
  1802. Inc(P);
  1803. end;
  1804. NativeCarriageReturn:
  1805. begin
  1806. Inc(P);
  1807. if (P < FEnd) and (P^ = NativeLineFeed) then
  1808. Inc(P);
  1809. end;
  1810. end;
  1811. LastLineStart := P;
  1812. end
  1813. else
  1814. Inc(P);
  1815. end;
  1816. if P > LastLineStart then
  1817. begin
  1818. ReallocMem(FIndex, (I + 1) * SizeOf(Pointer));
  1819. FIndex[I] := LastLineStart;
  1820. Inc(I);
  1821. end
  1822. else
  1823. ReallocMem(FIndex, I * SizeOf(Pointer));
  1824. FLineCount := I;
  1825. {$IFDEF RANGECHECKS_ON}
  1826. {$RANGECHECKS ON}
  1827. {$ENDIF RANGECHECKS_ON}
  1828. end;
  1829. function TJclWideMappedTextReader.GetEof: Boolean;
  1830. begin
  1831. Result := FPosition >= FEnd;
  1832. end;
  1833. function TJclWideMappedTextReader.GetAsString: WideString;
  1834. begin
  1835. SetString(Result, Content, Size);
  1836. end;
  1837. function TJclWideMappedTextReader.GetChars(Index: Integer): WideChar;
  1838. begin
  1839. if (Index < 0) or (Index >= Size) then
  1840. raise EJclError.CreateRes(@RsFileIndexOutOfRange);
  1841. Result := WideChar(PByte(FContent + Index)^);
  1842. end;
  1843. function TJclWideMappedTextReader.GetLineCount: Integer;
  1844. var
  1845. P: PWideChar;
  1846. begin
  1847. if FLineCount = -1 then
  1848. begin
  1849. FLineCount := 0;
  1850. if FContent < FEnd then
  1851. begin
  1852. P := FContent;
  1853. while P < FEnd do
  1854. begin
  1855. case P^ of
  1856. NativeLineFeed:
  1857. begin
  1858. Inc(FLineCount);
  1859. Inc(P);
  1860. if (P < FEnd) and (P^ = NativeCarriageReturn) then
  1861. Inc(P);
  1862. end;
  1863. NativeCarriageReturn:
  1864. begin
  1865. Inc(FLineCount);
  1866. Inc(P);
  1867. if (P < FEnd) and (P^ = NativeLineFeed) then
  1868. Inc(P);
  1869. end;
  1870. else
  1871. Inc(P);
  1872. end;
  1873. end;
  1874. if (P = FEnd) and (P > FContent) and not CharIsReturn(Char((P-1)^)) then
  1875. Inc(FLineCount);
  1876. end;
  1877. end;
  1878. Result := FLineCount;
  1879. end;
  1880. function TJclWideMappedTextReader.GetLines(LineNumber: Integer): WideString;
  1881. var
  1882. P: PWideChar;
  1883. begin
  1884. P := PtrFromLine(LineNumber);
  1885. Result := StringFromPosition(P);
  1886. end;
  1887. function TJclWideMappedTextReader.GetPosition: Integer;
  1888. begin
  1889. Result := FPosition - FContent;
  1890. end;
  1891. procedure TJclWideMappedTextReader.GoBegin;
  1892. begin
  1893. Position := 0;
  1894. end;
  1895. procedure TJclWideMappedTextReader.Init;
  1896. begin
  1897. FContent := FMemoryStream.Memory;
  1898. FSize := FMemoryStream.Size;
  1899. FEnd := FContent + FSize;
  1900. FPosition := FContent;
  1901. FLineCount := -1;
  1902. FLastLineNumber := 0;
  1903. FLastPosition := FContent;
  1904. if IndexOption = tiFull then
  1905. CreateIndex;
  1906. end;
  1907. function TJclWideMappedTextReader.GetPositionFromLine(LineNumber: Integer): Integer;
  1908. var
  1909. P: PWideChar;
  1910. begin
  1911. P := PtrFromLine(LineNumber);
  1912. if P = nil then
  1913. Result := -1
  1914. else
  1915. Result := P - FContent;
  1916. end;
  1917. function TJclWideMappedTextReader.PtrFromLine(LineNumber: Integer): PWideChar;
  1918. var
  1919. LineOffset: Integer;
  1920. begin
  1921. Result := nil;
  1922. {$RANGECHECKS OFF}
  1923. if (IndexOption <> tiNoIndex) and (LineNumber < FLineCount) and (FIndex[LineNumber] <> nil) then
  1924. Result := FIndex[LineNumber]
  1925. {$IFDEF RANGECHECKS_ON}
  1926. {$RANGECHECKS ON}
  1927. {$ENDIF RANGECHECKS_ON}
  1928. else
  1929. begin
  1930. LineOffset := LineNumber - FLastLineNumber;
  1931. if (FLineCount <> -1) and (LineNumber > 0) then
  1932. begin
  1933. if -LineOffset > LineNumber then
  1934. begin
  1935. FLastLineNumber := 0;
  1936. FLastPosition := FContent;
  1937. LineOffset := LineNumber;
  1938. end
  1939. else
  1940. if LineOffset > FLineCount - LineNumber then
  1941. begin
  1942. FLastLineNumber := FLineCount;
  1943. FLastPosition := FEnd;
  1944. LineOffset := LineNumber - FLineCount;
  1945. end;
  1946. end;
  1947. if LineNumber <= 0 then
  1948. Result := FContent
  1949. else
  1950. if LineOffset = 0 then
  1951. Result := FLastPosition
  1952. else
  1953. if LineOffset > 0 then
  1954. begin
  1955. Result := FLastPosition;
  1956. while (Result < FEnd) and (LineOffset > 0) do
  1957. begin
  1958. case Result^ of
  1959. NativeLineFeed:
  1960. begin
  1961. Dec(LineOffset);
  1962. Inc(Result);
  1963. if (Result < FEnd) and (Result^ = NativeCarriageReturn) then
  1964. Inc(Result);
  1965. end;
  1966. NativeCarriageReturn:
  1967. begin
  1968. Dec(LineOffset);
  1969. Inc(Result);
  1970. if (Result < FEnd) and (Result^ = NativeLineFeed) then
  1971. Inc(Result);
  1972. end;
  1973. else
  1974. Inc(Result);
  1975. end;
  1976. end;
  1977. end
  1978. else
  1979. if LineOffset < 0 then
  1980. begin
  1981. Result := FLastPosition;
  1982. while (Result > FContent) and (LineOffset < 1) do
  1983. begin
  1984. Dec(Result);
  1985. case Result^ of
  1986. NativeLineFeed:
  1987. begin
  1988. Inc(LineOffset);
  1989. if LineOffset >= 1 then
  1990. Inc(Result)
  1991. else
  1992. if (Result > FContent) and ((Result-1)^ = NativeCarriageReturn) then
  1993. Dec(Result);
  1994. end;
  1995. NativeCarriageReturn:
  1996. begin
  1997. Inc(LineOffset);
  1998. if LineOffset >= 1 then
  1999. Inc(Result)
  2000. else
  2001. if (Result > FContent) and ((Result-1)^ = NativeLineFeed) then
  2002. Dec(Result);
  2003. end;
  2004. end;
  2005. end;
  2006. end;
  2007. FLastLineNumber := LineNumber;
  2008. FLastPosition := Result;
  2009. end;
  2010. end;
  2011. function TJclWideMappedTextReader.Read: WideChar;
  2012. begin
  2013. if FPosition >= FEnd then
  2014. Result := #0
  2015. else
  2016. begin
  2017. Result := FPosition^;
  2018. Inc(FPosition);
  2019. end;
  2020. end;
  2021. function TJclWideMappedTextReader.ReadLn: WideString;
  2022. begin
  2023. Result := StringFromPosition(FPosition);
  2024. end;
  2025. procedure TJclWideMappedTextReader.SetPosition(const Value: Integer);
  2026. begin
  2027. FPosition := FContent + Value;
  2028. end;
  2029. function TJclWideMappedTextReader.StringFromPosition(var StartPos: PWideChar): WideString;
  2030. var
  2031. P: PWideChar;
  2032. begin
  2033. if (StartPos = nil) or (StartPos >= FEnd) then
  2034. Result := ''
  2035. else
  2036. begin
  2037. P := StartPos;
  2038. while (P < FEnd) and (not CharIsReturn(Char(P^))) do
  2039. Inc(P);
  2040. SetString(Result, StartPos, P - StartPos);
  2041. if P < FEnd then
  2042. begin
  2043. case P^ of
  2044. NativeLineFeed:
  2045. begin
  2046. Inc(P);
  2047. if (P < FEnd) and (P^ = NativeCarriageReturn) then
  2048. Inc(P);
  2049. end;
  2050. NativeCarriageReturn:
  2051. begin
  2052. Inc(P);
  2053. if (P < FEnd) and (P^ = NativeLineFeed) then
  2054. Inc(P);
  2055. end;
  2056. end;
  2057. end;
  2058. StartPos := P;
  2059. end;
  2060. end;
  2061. function CharIsDriveLetter(const C: Char): Boolean;
  2062. begin
  2063. case C of
  2064. 'a'..'z',
  2065. 'A'..'Z':
  2066. Result := True;
  2067. else
  2068. Result := False;
  2069. end;
  2070. end;
  2071. //=== Path manipulation ======================================================
  2072. function PathAddSeparator(const Path: string): string;
  2073. begin
  2074. Result := Path;
  2075. if (Path = '') or (Path[Length(Path)] <> DirDelimiter) then
  2076. Result := Path + DirDelimiter;
  2077. end;
  2078. function PathAddExtension(const Path, Extension: string): string;
  2079. begin
  2080. Result := Path;
  2081. // (obones) Extension may not contain the leading dot while ExtractFileExt
  2082. // always returns it. Hence the need to use StrEnsurePrefix for the SameText
  2083. // test to return an accurate value.
  2084. if (Path <> '') and (Extension <> '') and
  2085. not SameText(ExtractFileExt(Path), StrEnsurePrefix('.', Extension)) then
  2086. begin
  2087. if Path[Length(Path)] = '.' then
  2088. Delete(Result, Length(Path), 1);
  2089. if Extension[1] = '.' then
  2090. Result := Result + Extension
  2091. else
  2092. Result := Result + '.' + Extension;
  2093. end;
  2094. end;
  2095. function PathAppend(const Path, Append: string): string;
  2096. var
  2097. PathLength: Integer;
  2098. B1, B2: Boolean;
  2099. begin
  2100. if Append = '' then
  2101. Result := Path
  2102. else
  2103. begin
  2104. PathLength := Length(Path);
  2105. if PathLength = 0 then
  2106. Result := Append
  2107. else
  2108. begin
  2109. // The following code may look a bit complex but all it does is add Append to Path ensuring
  2110. // that there is one and only one path separator character between them
  2111. B1 := Path[PathLength] = DirDelimiter;
  2112. B2 := Append[1] = DirDelimiter;
  2113. if B1 and B2 then
  2114. Result := Copy(Path, 1, PathLength - 1) + Append
  2115. else
  2116. begin
  2117. if not (B1 or B2) then
  2118. Result := Path + DirDelimiter + Append
  2119. else
  2120. Result := Path + Append;
  2121. end;
  2122. end;
  2123. end;
  2124. end;
  2125. function PathBuildRoot(const Drive: Byte): string;
  2126. begin
  2127. {$IFDEF UNIX}
  2128. Result := DirDelimiter;
  2129. {$ENDIF UNIX}
  2130. {$IFDEF MSWINDOWS}
  2131. // Remember, Win32 only allows 'a' to 'z' as drive letters (mapped to 0..25)
  2132. if Drive < 26 then
  2133. Result := Char(Drive + 65) + ':\'
  2134. else
  2135. raise EJclPathError.CreateResFmt(@RsPathInvalidDrive, [IntToStr(Drive)]);
  2136. {$ENDIF MSWINDOWS}
  2137. end;
  2138. function PathCanonicalize(const Path: string): string;
  2139. var
  2140. List: TStringList;
  2141. S: string;
  2142. I, K: Integer;
  2143. IsAbsolute: Boolean;
  2144. begin
  2145. I := Pos(':', Path); // for Windows' sake
  2146. K := Pos(DirDelimiter, Path);
  2147. IsAbsolute := K - I = 1;
  2148. if IsAbsolute then begin
  2149. if Copy(Path, 1, Length(PathUncPrefix)) = PathUncPrefix then // UNC path
  2150. K := 2;
  2151. end else
  2152. K := I;
  2153. if K = 0 then
  2154. S := Path
  2155. else
  2156. S := Copy(Path, K + 1, Length(Path));
  2157. List := TStringList.Create;
  2158. try
  2159. StrIToStrings(S, DirDelimiter, List, True);
  2160. I := 0;
  2161. while I < List.Count do
  2162. begin
  2163. if List[I] = '.' then
  2164. List.Delete(I)
  2165. else
  2166. if (IsAbsolute or (I > 0) and not (List[I-1] = '..')) and (List[I] = '..') then
  2167. begin
  2168. List.Delete(I);
  2169. if I > 0 then
  2170. begin
  2171. Dec(I);
  2172. List.Delete(I);
  2173. end;
  2174. end
  2175. else Inc(I);
  2176. end;
  2177. Result := StringsToStr(List, DirDelimiter, True);
  2178. finally
  2179. List.Free;
  2180. end;
  2181. if K > 0 then
  2182. Result := Copy(Path, 1, K) + Result
  2183. else
  2184. if Result = '' then
  2185. Result := '.';
  2186. end;
  2187. function PathCommonPrefix(const Path1, Path2: string): Integer;
  2188. var
  2189. Index1, Index2: Integer;
  2190. LastSeparator, LenS1: Integer;
  2191. S1, S2: string;
  2192. begin
  2193. Result := 0;
  2194. if (Path1 <> '') and (Path2 <> '') then
  2195. begin
  2196. // Initialize P1 to the shortest of the two paths so that the actual comparison loop below can
  2197. // use the terminating #0 of that string to terminate the loop.
  2198. if Length(Path1) <= Length(Path2) then
  2199. begin
  2200. S1 := Path1;
  2201. S2 := Path2;
  2202. end
  2203. else
  2204. begin
  2205. S1 := Path2;
  2206. S2 := Path1;
  2207. end;
  2208. Index1 := 1;
  2209. Index2 := 1;
  2210. LenS1 := Length(S1);
  2211. LastSeparator := 0;
  2212. while (S1[Index1] = S2[Index2]) and (Index1 <= LenS1) do
  2213. begin
  2214. Inc(Result);
  2215. if (S1[Index1] = DirDelimiter) or (S1[Index1] = ':') then
  2216. LastSeparator := Result;
  2217. Inc(Index1);
  2218. Inc(Index2);
  2219. end;
  2220. if (LastSeparator < Result) and (Index1 <= LenS1) then
  2221. Result := LastSeparator;
  2222. end;
  2223. end;
  2224. {$IFDEF MSWINDOWS}
  2225. function PathCompactPath(const DC: HDC; const Path: string;
  2226. const Width: Integer; CmpFmt: TCompactPath): string;
  2227. const
  2228. Compacts: array [TCompactPath] of Cardinal = (DT_PATH_ELLIPSIS, DT_END_ELLIPSIS);
  2229. var
  2230. TextRect: TRect;
  2231. Fmt: Cardinal;
  2232. begin
  2233. Result := '';
  2234. if (DC <> 0) and (Path <> '') and (Width > 0) then
  2235. begin
  2236. { Here's a note from the Platform SDK to explain the + 5 in the call below:
  2237. "If dwDTFormat includes DT_MODIFYSTRING, the function could add up to four additional characters
  2238. to this string. The buffer containing the string should be large enough to accommodate these
  2239. extra characters." }
  2240. SetString(Result, PChar(Path), Length(Path) + 4);
  2241. TextRect := Rect(0, 0, Width, 255);
  2242. Fmt := DT_MODIFYSTRING or DT_CALCRECT or Compacts[CmpFmt];
  2243. if DrawTextEx(DC, PChar(Result), -1, TextRect, Fmt, nil) <> 0 then
  2244. StrResetLength(Result)
  2245. else
  2246. Result := ''; // in case of error
  2247. end;
  2248. end;
  2249. {$ENDIF MSWINDOWS}
  2250. procedure PathExtractElements(const Source: string; var Drive, Path, FileName, Ext: string);
  2251. begin
  2252. Drive := ExtractFileDrive(Source);
  2253. Path := ExtractFilePath(Source);
  2254. // Path includes drive so remove that
  2255. if Drive <> '' then
  2256. Delete(Path, 1, Length(Drive));
  2257. // add/remove separators
  2258. Drive := PathAddSeparator(Drive);
  2259. Path := PathRemoveSeparator(Path);
  2260. if (Path <> '') and (Path[1] = DirDelimiter) then
  2261. Delete(Path, 1, 1);
  2262. // and extract the remaining elements
  2263. FileName := PathExtractFileNameNoExt(Source);
  2264. Ext := ExtractFileExt(Source);
  2265. end;
  2266. function PathExtractFileDirFixed(const S: string): string;
  2267. begin
  2268. Result := PathAddSeparator(ExtractFileDir(S));
  2269. end;
  2270. function PathExtractFileNameNoExt(const Path: string): string;
  2271. begin
  2272. Result := PathRemoveExtension(ExtractFileName(Path));
  2273. end;
  2274. function PathExtractPathDepth(const Path: string; Depth: Integer): string;
  2275. var
  2276. List: TStringList;
  2277. LocalPath: string;
  2278. I: Integer;
  2279. begin
  2280. List := TStringList.Create;
  2281. try
  2282. if IsDirectory(Path) then
  2283. LocalPath := Path
  2284. else
  2285. LocalPath := ExtractFilePath(Path);
  2286. StrIToStrings(LocalPath, DirDelimiter, List, True);
  2287. I := Depth + 1;
  2288. if PathIsUNC(LocalPath) then
  2289. I := I + 2;
  2290. while I < List.Count do
  2291. List.Delete(I);
  2292. Result := PathAddSeparator(StringsToStr(List, DirDelimiter, True));
  2293. finally
  2294. List.Free;
  2295. end;
  2296. end;
  2297. // Notes: maybe this function should first apply PathCanonicalize() ?
  2298. function PathGetDepth(const Path: string): Integer;
  2299. var
  2300. List: TStringList;
  2301. LocalPath: string;
  2302. I, Start: Integer;
  2303. begin
  2304. Result := 0;
  2305. List := TStringList.Create;
  2306. try
  2307. if IsDirectory(Path) then
  2308. LocalPath := Path
  2309. else
  2310. LocalPath := ExtractFilePath(Path);
  2311. StrIToStrings(LocalPath, DirDelimiter, List, False);
  2312. if PathIsUNC(LocalPath) then
  2313. Start := 1
  2314. else
  2315. Start := 0;
  2316. for I := Start to List.Count - 1 do
  2317. begin
  2318. if Pos(':', List[I]) = 0 then
  2319. Inc(Result);
  2320. end;
  2321. finally
  2322. List.Free;
  2323. end;
  2324. end;
  2325. {$IFDEF MSWINDOWS}
  2326. function ShellGetLongPathName(const Path: string): string;
  2327. {$IFDEF FPC}
  2328. // As of 2004-10-17, FPC's ShlObj unit is just a dummy
  2329. begin
  2330. Result := Path;
  2331. end;
  2332. {$ElSE ~FPC}
  2333. var
  2334. PIDL: PItemIDList;
  2335. Desktop: IShellFolder;
  2336. {$IFNDEF SUPPORTS_UNICODE}
  2337. AnsiName: string;
  2338. WideName: array [0..MAX_PATH] of WideChar;
  2339. {$ENDIF ~SUPPORTS_UNICODE}
  2340. Eaten, Attr: ULONG; // both unused but API requires them (incorrect translation)
  2341. begin
  2342. Result := Path;
  2343. if Path <> '' then
  2344. begin
  2345. if Succeeded(SHGetDesktopFolder(Desktop)) then
  2346. begin
  2347. {$IFDEF SUPPORTS_UNICODE}
  2348. if Succeeded(Desktop.ParseDisplayName(0, nil, PChar(Path), Eaten, PIDL, Attr)) then
  2349. try
  2350. SetLength(Result, MAX_PATH);
  2351. if SHGetPathFromIDList(PIDL, PChar(Result)) then
  2352. StrResetLength(Result);
  2353. finally
  2354. CoTaskMemFree(PIDL);
  2355. end;
  2356. {$ELSE ~SUPPORTS_UNICODE}
  2357. MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PAnsiChar(Path), -1, WideName, MAX_PATH);
  2358. if Succeeded(Desktop.ParseDisplayName(0, nil, WideName, Eaten, PIDL, Attr)) then
  2359. try
  2360. SetLength(AnsiName, MAX_PATH);
  2361. if SHGetPathFromIDList(PIDL, PChar(AnsiName)) then
  2362. StrResetLength(AnsiName);
  2363. Result := AnsiName;
  2364. finally
  2365. CoTaskMemFree(PIDL);
  2366. end;
  2367. {$ENDIF ~SUPPORTS_UNICODE}
  2368. end;
  2369. end;
  2370. end;
  2371. {$ENDIF ~FPC}
  2372. { TODO : Move RTDL code over to JclWin32 when JclWin32 gets overhauled. }
  2373. var
  2374. _Kernel32Handle: TModuleHandle = INVALID_MODULEHANDLE_VALUE;
  2375. _GetLongPathName: function (lpszShortPath: PChar; lpszLongPath: PChar;
  2376. cchBuffer: DWORD): DWORD; stdcall;
  2377. function Kernel32Handle: HMODULE;
  2378. begin
  2379. JclSysUtils.LoadModule(_Kernel32Handle, kernel32);
  2380. Result := _Kernel32Handle;
  2381. end;
  2382. function RtdlGetLongPathName(const Path: string): string;
  2383. begin
  2384. Result := Path;
  2385. if not Assigned(_GetLongPathName) then
  2386. _GetLongPathName := GetModuleSymbol(Kernel32Handle, 'GetLongPathName' + AWSuffix);
  2387. if not Assigned(_GetLongPathName) then
  2388. Result := ShellGetLongPathName(Path)
  2389. else
  2390. begin
  2391. SetLength(Result, MAX_PATH);
  2392. SetLength(Result, _GetLongPathName(PChar(Path), PChar(Result), MAX_PATH));
  2393. end;
  2394. end;
  2395. function PathGetLongName(const Path: string): string;
  2396. begin
  2397. if Pos('::', Path) > 0 then // Path contains '::{<GUID>}'
  2398. Result := ShellGetLongPathName(Path)
  2399. else
  2400. Result := RtdlGetLongPathName(Path);
  2401. if Result = '' then
  2402. Result := Path;
  2403. end;
  2404. function PathGetShortName(const Path: string): string;
  2405. var
  2406. Required: Integer;
  2407. begin
  2408. Result := Path;
  2409. Required := GetShortPathName(PChar(Path), nil, 0);
  2410. if Required <> 0 then
  2411. begin
  2412. SetLength(Result, Required);
  2413. Required := GetShortPathName(PChar(Path), PChar(Result), Required);
  2414. if (Required <> 0) and (Required = Length(Result) - 1) then
  2415. SetLength(Result, Required)
  2416. else
  2417. Result := Path;
  2418. end;
  2419. end;
  2420. {$ENDIF MSWINDOWS}
  2421. function PathGetRelativePath(Origin, Destination: string): string;
  2422. var
  2423. {$IFDEF MSWINDOWS}
  2424. OrigDrive: string;
  2425. DestDrive: string;
  2426. {$ENDIF MSWINDOWS}
  2427. OrigList: TStringList;
  2428. DestList: TStringList;
  2429. DiffIndex: Integer;
  2430. I: Integer;
  2431. function StartsFromRoot(const Path: string): Boolean;
  2432. {$IFDEF MSWINDOWS}
  2433. var
  2434. I: Integer;
  2435. begin
  2436. I := Length(ExtractFileDrive(Path));
  2437. Result := (Length(Path) > I) and (Path[I + 1] = DirDelimiter);
  2438. end;
  2439. {$ELSE ~MSWINDOWS}
  2440. begin
  2441. Result := Pos(DirDelimiter, Path) = 1;
  2442. end;
  2443. {$ENDIF ~MSWINDOWS}
  2444. function Equal(const Path1, Path2: string): Boolean;
  2445. begin
  2446. {$IFDEF MSWINDOWS} // case insensitive
  2447. Result := StrSame(Path1, Path2);
  2448. {$ELSE ~MSWINDOWS} // case sensitive
  2449. Result := Path1 = Path2;
  2450. {$ENDIF ~MSWINDOWS}
  2451. end;
  2452. begin
  2453. Origin := PathCanonicalize(Origin);
  2454. Destination := PathCanonicalize(Destination);
  2455. {$IFDEF MSWINDOWS}
  2456. OrigDrive := ExtractFileDrive(Origin);
  2457. DestDrive := ExtractFileDrive(Destination);
  2458. {$ENDIF MSWINDOWS}
  2459. if Equal(Origin, Destination) or (Destination = '') then
  2460. Result := '.'
  2461. else
  2462. if Origin = '' then
  2463. Result := Destination
  2464. else
  2465. {$IFDEF MSWINDOWS}
  2466. if (DestDrive <> '') and ((OrigDrive = '') or ((OrigDrive <> '') and not Equal(OrigDrive, DestDrive))) then
  2467. Result := Destination
  2468. else
  2469. if (OrigDrive <> '') and (Pos(DirDelimiter, Destination) = 1)
  2470. and not Equal(PathUncPrefix,Copy(Destination,1,Length(PathUncPrefix))) then
  2471. Result := OrigDrive + Destination // prepend drive part from Origin
  2472. else
  2473. {$ENDIF MSWINDOWS}
  2474. if StartsFromRoot(Origin) and not StartsFromRoot(Destination) then
  2475. Result := StrEnsureSuffix(DirDelimiter, Origin) +
  2476. StrEnsureNoPrefix(DirDelimiter, Destination)
  2477. else
  2478. begin
  2479. // create a list of paths as separate strings
  2480. OrigList := TStringList.Create;
  2481. DestList := TStringList.Create;
  2482. try
  2483. // NOTE: DO NOT USE DELIMITER AND DELIMITEDTEXT FROM
  2484. // TSTRINGS, THEY WILL SPLIT PATHS WITH SPACES !!!!
  2485. StrToStrings(Origin, DirDelimiter, OrigList, False);
  2486. StrToStrings(Destination, DirDelimiter, DestList, False);
  2487. begin
  2488. // find the first directory that is not the same
  2489. DiffIndex := OrigList.Count;
  2490. if DestList.Count < DiffIndex then
  2491. DiffIndex := DestList.Count;
  2492. for I := 0 to DiffIndex - 1 do
  2493. if not Equal(OrigList[I], DestList[I]) then
  2494. begin
  2495. DiffIndex := I;
  2496. Break;
  2497. end;
  2498. Result := StrRepeat('..' + DirDelimiter, OrigList.Count - DiffIndex);
  2499. Result := PathRemoveSeparator(Result);
  2500. for I := DiffIndex to DestList.Count - 1 do
  2501. begin
  2502. if Result <> '' then
  2503. Result := Result + DirDelimiter;
  2504. Result := Result + DestList[i];
  2505. end;
  2506. end;
  2507. finally
  2508. DestList.Free;
  2509. OrigList.Free;
  2510. end;
  2511. end;
  2512. end;
  2513. function PathGetTempPath: string;
  2514. {$IFDEF MSWINDOWS}
  2515. var
  2516. BufSize: Cardinal;
  2517. begin
  2518. BufSize := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetTempPath(0, nil);
  2519. SetLength(Result, BufSize);
  2520. { TODO : Check length (-1 or not) }
  2521. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetTempPath(BufSize, PChar(Result));
  2522. StrResetLength(Result);
  2523. end;
  2524. {$ENDIF MSWINDOWS}
  2525. {$IFDEF UNIX}
  2526. begin
  2527. Result := GetEnvironmentVariable('TMPDIR');
  2528. end;
  2529. {$ENDIF UNIX}
  2530. function PathIsAbsolute(const Path: string): Boolean;
  2531. {$IFDEF MSWINDOWS}
  2532. var
  2533. I: Integer;
  2534. {$ENDIF MSWINDOWS}
  2535. begin
  2536. Result := False;
  2537. if Path <> '' then
  2538. begin
  2539. {$IFDEF UNIX}
  2540. Result := (Path[1] = DirDelimiter);
  2541. {$ENDIF UNIX}
  2542. {$IFDEF MSWINDOWS}
  2543. if not PathIsUnc(Path) then
  2544. begin
  2545. I := 0;
  2546. if PathIsDiskDevice(Path) then
  2547. I := Length(PathDevicePrefix);
  2548. Result := (Length(Path) > I + 2) and CharIsDriveLetter(Path[I + 1]) and
  2549. (Path[I + 2] = ':') and (Path[I + 3] = DirDelimiter);
  2550. end
  2551. else
  2552. Result := True;
  2553. {$ENDIF MSWINDOWS}
  2554. end;
  2555. end;
  2556. function PathIsChild(const Path, Base: string): Boolean;
  2557. var
  2558. L: Integer;
  2559. B, P: string;
  2560. begin
  2561. Result := False;
  2562. B := PathRemoveSeparator(Base);
  2563. P := PathRemoveSeparator(Path);
  2564. // an empty path or one that's not longer than base cannot be a subdirectory
  2565. L := Length(B);
  2566. if (P = '') or (L >= Length(P)) then
  2567. Exit;
  2568. {$IFDEF MSWINDOWS}
  2569. Result := AnsiSameText(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
  2570. {$ENDIF MSWINDOWS}
  2571. {$IFDEF UNIX}
  2572. Result := AnsiSameStr(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
  2573. {$ENDIF UNIX}
  2574. end;
  2575. function PathIsEqualOrChild(const Path, Base: string): Boolean;
  2576. var
  2577. L: Integer;
  2578. B, P: string;
  2579. begin
  2580. B := PathRemoveSeparator(Base);
  2581. P := PathRemoveSeparator(Path);
  2582. // an empty path or one that's not longer than base cannot be a subdirectory
  2583. L := Length(B);
  2584. {$IFDEF MSWINDOWS}
  2585. Result := AnsiSameText(P, B);
  2586. {$ENDIF MSWINDOWS}
  2587. {$IFDEF UNIX}
  2588. Result := AnsiSameStr(P, B);
  2589. {$ENDIF UNIX}
  2590. if Result or (P = '') or (L >= Length(P)) then
  2591. Exit;
  2592. {$IFDEF MSWINDOWS}
  2593. Result := AnsiSameText(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
  2594. {$ENDIF MSWINDOWS}
  2595. {$IFDEF UNIX}
  2596. Result := AnsiSameStr(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
  2597. {$ENDIF UNIX}
  2598. end;
  2599. function PathIsDiskDevice(const Path: string): Boolean;
  2600. {$IFDEF UNIX}
  2601. var
  2602. FullPath: string;
  2603. F: PIOFile;
  2604. Buffer: array [0..255] of AnsiChar;
  2605. MountEntry: TMountEntry;
  2606. FsTypes: TStringList;
  2607. procedure GetAvailableFileSystems(const List: TStrings);
  2608. var
  2609. F: TextFile;
  2610. S: string;
  2611. begin
  2612. AssignFile(F, '/proc/filesystems');
  2613. Reset(F);
  2614. repeat
  2615. Readln(F, S);
  2616. if Pos('nodev', S) = 0 then // how portable is this ?
  2617. List.Add(Trim(S));
  2618. until Eof(F);
  2619. List.Add('supermount');
  2620. CloseFile(F);
  2621. end;
  2622. begin
  2623. Result := False;
  2624. SetLength(FullPath, _POSIX_PATH_MAX);
  2625. if realpath(PChar(Path), PChar(FullPath)) = nil then
  2626. RaiseLastOSError;
  2627. StrResetLength(FullPath);
  2628. FsTypes := TStringList.Create;
  2629. try
  2630. GetAvailableFileSystems(FsTypes);
  2631. F := setmntent(_PATH_MOUNTED, 'r'); // PATH_MOUNTED is deprecated,
  2632. // but PATH_MNTTAB is defective in Libc.pas
  2633. try
  2634. // get drives from mtab
  2635. while not Result and (getmntent_r(F, MountEntry, Buffer, SizeOf(Buffer)) <> nil) do
  2636. if FsTypes.IndexOf(MountEntry.mnt_type) <> -1 then
  2637. Result := MountEntry.mnt_dir = FullPath;
  2638. finally
  2639. endmntent(F);
  2640. end;
  2641. finally
  2642. FsTypes.Free;
  2643. end;
  2644. end;
  2645. {$ENDIF UNIX}
  2646. {$IFDEF MSWINDOWS}
  2647. begin
  2648. Result := Copy(Path, 1, Length(PathDevicePrefix)) = PathDevicePrefix;
  2649. end;
  2650. {$ENDIF MSWINDOWS}
  2651. function CharIsMachineName(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  2652. begin
  2653. case C of
  2654. 'a'..'z',
  2655. 'A'..'Z',
  2656. '-', '_', '.':
  2657. Result := True;
  2658. else
  2659. Result := False;
  2660. end;
  2661. end;
  2662. function CharIsInvalidFileNameCharacter(const C: Char): Boolean;
  2663. begin
  2664. case C of
  2665. '<', '>', '?', '/', '\', ',', '*', '+', '=', '[', ']', '|', ':', ';', '"', '''':
  2666. Result := True;
  2667. else
  2668. Result := False;
  2669. end;
  2670. end;
  2671. function CharIsInvalidPathCharacter(const C: Char): Boolean;
  2672. begin
  2673. case C of
  2674. '<', '>', '?',
  2675. {$IFDEF UNIX}
  2676. '/',
  2677. {$ELSE}
  2678. '\',
  2679. {$ENDIF}
  2680. ',', '*', '+', '=', '[', ']', '|', ':', ';', '"', '''':
  2681. Result := True;
  2682. else
  2683. Result := False;
  2684. end;
  2685. end;
  2686. function PathIsUNC(const Path: string): Boolean;
  2687. {$IFDEF MSWINDOWS}
  2688. const
  2689. cUNCSuffix = '?\UNC';
  2690. var
  2691. P: PChar;
  2692. function AbsorbSeparator: Boolean;
  2693. begin
  2694. Result := (P <> nil) and (P^ = DirDelimiter);
  2695. if Result then
  2696. Inc(P);
  2697. end;
  2698. function AbsorbMachineName: Boolean;
  2699. var
  2700. NonDigitFound: Boolean;
  2701. begin
  2702. // a valid machine name is a string composed of the set [a-z, A-Z, 0-9, -, _] but it may not
  2703. // consist entirely out of numbers
  2704. Result := True;
  2705. NonDigitFound := False;
  2706. while (P^ <> #0) and (P^ <> DirDelimiter) do
  2707. begin
  2708. if CharIsMachineName(P^) then
  2709. begin
  2710. NonDigitFound := True;
  2711. Inc(P);
  2712. end
  2713. else
  2714. if CharIsDigit(P^) then
  2715. Inc(P)
  2716. else
  2717. begin
  2718. Result := False;
  2719. Break;
  2720. end;
  2721. end;
  2722. Result := Result and NonDigitFound;
  2723. end;
  2724. function AbsorbShareName: Boolean;
  2725. begin
  2726. // a valid share name is a string composed of a set the set !InvalidCharacters note that a
  2727. // leading '$' is valid (indicates a hidden share)
  2728. Result := True;
  2729. while (P^ <> #0) and (P^ <> DirDelimiter) do
  2730. begin
  2731. if CharIsInvalidPathCharacter(P^) then
  2732. begin
  2733. Result := False;
  2734. Break;
  2735. end;
  2736. Inc(P);
  2737. end;
  2738. end;
  2739. begin
  2740. Result := Copy(Path, 1, Length(PathUncPrefix)) = PathUncPrefix;
  2741. if Result then
  2742. begin
  2743. if Copy(Path, 1, Length(PathUncPrefix + cUNCSuffix)) = PathUncPrefix + cUNCSuffix then
  2744. P := @Path[Length(PathUncPrefix + cUNCSuffix)]
  2745. else
  2746. begin
  2747. P := @Path[Length(PathUncPrefix)];
  2748. Result := AbsorbSeparator and AbsorbMachineName;
  2749. end;
  2750. Result := Result and AbsorbSeparator;
  2751. if Result then
  2752. begin
  2753. Result := AbsorbShareName;
  2754. // remaining, if anything, is path and or filename (optional) check those?
  2755. end;
  2756. end;
  2757. end;
  2758. {$ENDIF MSWINDOWS}
  2759. {$IFDEF UNIX}
  2760. begin
  2761. Result := False;
  2762. end;
  2763. {$ENDIF UNIX}
  2764. function PathRemoveSeparator(const Path: string): string;
  2765. var
  2766. L: Integer;
  2767. begin
  2768. L := Length(Path);
  2769. if (L <> 0) and (Path[L] = DirDelimiter) then
  2770. Result := Copy(Path, 1, L - 1)
  2771. else
  2772. Result := Path;
  2773. end;
  2774. function PathRemoveExtension(const Path: string): string;
  2775. var
  2776. I: Integer;
  2777. begin
  2778. I := LastDelimiter(':.' + DirDelimiter, Path);
  2779. if (I > 0) and (Path[I] = '.') then
  2780. Result := Copy(Path, 1, I - 1)
  2781. else
  2782. Result := Path;
  2783. end;
  2784. {$IFDEF MSWINDOWS}
  2785. function SHGetDisplayName(ShellFolder: IShellFolder; PIDL: PItemIDList; ForParsing: Boolean): string;
  2786. const
  2787. Flags: array[Boolean] of DWORD = (SHGDN_NORMAL, SHGDN_FORPARSING);
  2788. var
  2789. StrRet: TStrRet;
  2790. P: PChar;
  2791. begin
  2792. Result := '';
  2793. StrRet.utype := 0;
  2794. ShellFolder.GetDisplayNameOf(PIDL, Flags[ForParsing], StrRet);
  2795. case StrRet.uType of
  2796. STRRET_CSTR:
  2797. SetString(Result, StrRet.cStr, lstrlenA(StrRet.cStr));
  2798. STRRET_OFFSET:
  2799. begin
  2800. P := @PIDL.mkid.abID[StrRet.uOffset - SizeOf(PIDL.mkid.cb)];
  2801. SetString(Result, P, PIDL.mkid.cb - StrRet.uOffset);
  2802. end;
  2803. STRRET_WSTR:
  2804. Result := StrRet.pOleStr;
  2805. end;
  2806. Result := Copy(Result, 1, lstrlen(PChar(Result)));
  2807. end;
  2808. function CutFirstDirectory(var Path: string): string;
  2809. var
  2810. ps: Integer;
  2811. begin
  2812. ps := AnsiPos(DirDelimiter, Path);
  2813. if ps > 0 then
  2814. begin
  2815. Result := Copy(Path, 1, ps - 1);
  2816. Path := Copy(Path, ps + 1, Length(Path));
  2817. end
  2818. else
  2819. begin
  2820. Result := Path;
  2821. Path := '';
  2822. end;
  2823. end;
  2824. function PathGetPhysicalPath(const LocalizedPath: string): string;
  2825. var
  2826. Malloc: IMalloc;
  2827. DesktopFolder: IShellFolder;
  2828. RootFolder: IShellFolder;
  2829. Eaten: Cardinal;
  2830. Attributes: Cardinal;
  2831. pidl: PItemIDList;
  2832. EnumIDL: IEnumIDList;
  2833. Drive: WideString;
  2834. Featched: Cardinal;
  2835. ParsePath: WideString;
  2836. Path, Name: string;
  2837. Found: Boolean;
  2838. begin
  2839. if StrCompareRange('\\', LocalizedPath, 1, 2) = 0 then
  2840. begin
  2841. Result := LocalizedPath;
  2842. Exit;
  2843. end;
  2844. Drive := ExtractFileDrive(LocalizedPath);
  2845. if Drive = '' then
  2846. begin
  2847. Result := LocalizedPath;
  2848. Exit;
  2849. end;
  2850. Path := Copy(LocalizedPath, Length(Drive) + 2, Length(LocalizedPath));
  2851. ParsePath := Drive;
  2852. OLECheck( SHGetMalloc(Malloc) );
  2853. OleCheck( SHGetDesktopFolder(DesktopFolder) );
  2854. while Path <> '' do
  2855. begin
  2856. Name := CutFirstDirectory(Path);
  2857. Found := False;
  2858. pidl := nil;
  2859. Attributes := 0;
  2860. if Succeeded( DesktopFolder.ParseDisplayName(0, nil, PWideChar(ParsePath), Eaten, pidl, Attributes) ) then
  2861. begin
  2862. OleCheck( DesktopFolder.BindToObject(pidl, nil, IShellFolder, RootFolder) );
  2863. Malloc.Free(pidl);
  2864. OleCheck( RootFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, EnumIDL) );
  2865. Featched := 0;
  2866. while EnumIDL.Next(1, pidl, Featched) = NOERROR do
  2867. begin
  2868. if AnsiCompareText(Name, SHGetDisplayName(RootFolder, pidl, False)) = 0 then
  2869. begin
  2870. ParsePath := SHGetDisplayName(RootFolder, pidl, True);
  2871. Malloc.Free(pidl);
  2872. Found := True;
  2873. Break;
  2874. end;
  2875. Malloc.Free(pidl);
  2876. end;
  2877. EnumIDL := nil;
  2878. RootFolder := nil;
  2879. end;
  2880. if not Found then
  2881. ParsePath := ParsePath + DirDelimiter + Name;
  2882. end;
  2883. Result := ParsePath;
  2884. end;
  2885. function PathGetLocalizedPath(const PhysicalPath: string): string;
  2886. var
  2887. Malloc: IMalloc;
  2888. DesktopFolder: IShellFolder;
  2889. RootFolder: IShellFolder;
  2890. Eaten: Cardinal;
  2891. Attributes: Cardinal;
  2892. pidl: PItemIDList;
  2893. EnumIDL: IEnumIDList;
  2894. Drive: WideString;
  2895. Featched: Cardinal;
  2896. ParsePath: WideString;
  2897. Path, Name, ParseName, DisplayName: string;
  2898. Found: Boolean;
  2899. begin
  2900. if StrCompareRange('\\', PhysicalPath, 1, 2) = 0 then
  2901. begin
  2902. Result := PhysicalPath;
  2903. Exit;
  2904. end;
  2905. Drive := ExtractFileDrive(PhysicalPath);
  2906. if Drive = '' then
  2907. begin
  2908. Result := PhysicalPath;
  2909. Exit;
  2910. end;
  2911. Path := Copy(PhysicalPath, Length(Drive) + 2, Length(PhysicalPath));
  2912. ParsePath := Drive;
  2913. Result := Drive;
  2914. OLECheck( SHGetMalloc(Malloc) );
  2915. OleCheck( SHGetDesktopFolder(DesktopFolder) );
  2916. while Path <> '' do
  2917. begin
  2918. Name := CutFirstDirectory(Path);
  2919. Found := False;
  2920. pidl := nil;
  2921. Attributes := 0;
  2922. if Succeeded( DesktopFolder.ParseDisplayName(0, nil, PWideChar(ParsePath), Eaten, pidl, Attributes) ) then
  2923. begin
  2924. OleCheck( DesktopFolder.BindToObject(pidl, nil, IShellFolder, RootFolder) );
  2925. Malloc.Free(pidl);
  2926. OleCheck( RootFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, EnumIDL) );
  2927. Featched := 0;
  2928. while EnumIDL.Next(1, pidl, Featched) = NOERROR do
  2929. begin
  2930. ParseName := SHGetDisplayName(RootFolder, pidl, True);
  2931. DisplayName := SHGetDisplayName(RootFolder, pidl, False);
  2932. Malloc.Free(pidl);
  2933. if (AnsiCompareText(Name, ExtractFileName(ParseName)) = 0) or
  2934. (AnsiCompareText(Name, DisplayName) = 0) then
  2935. begin
  2936. Name := DisplayName;
  2937. ParsePath := ParseName;
  2938. Found := True;
  2939. Break;
  2940. end;
  2941. end;
  2942. EnumIDL := nil;
  2943. RootFolder := nil;
  2944. end;
  2945. Result := Result + DirDelimiter + Name;
  2946. if not Found then
  2947. ParsePath := ParsePath + DirDelimiter + Name;
  2948. end;
  2949. end;
  2950. {$ELSE ~MSWINDOWS}
  2951. function PathGetPhysicalPath(const LocalizedPath: string): string;
  2952. begin
  2953. Result := LocalizedPath;
  2954. end;
  2955. function PathGetLocalizedPath(const PhysicalPath: string): string;
  2956. begin
  2957. Result := PhysicalPath;
  2958. end;
  2959. {$ENDIF ~MSWINDOWS}
  2960. //=== Files and Directories ==================================================
  2961. {* Extended version of JclFileUtils.BuildFileList:
  2962. function parameter Path can include multiple FileMasks as:
  2963. c:\aaa\*.pas; pro*.dpr; *.d??
  2964. FileMask Seperator = ';'
  2965. *}
  2966. function BuildFileList(const Path: string; const Attr: Integer; const List: TStrings; IncludeDirectoryName: Boolean =
  2967. False): Boolean;
  2968. var
  2969. SearchRec: TSearchRec;
  2970. IndexMask: Integer;
  2971. MaskList: TStringList;
  2972. Masks, Directory: string;
  2973. begin
  2974. Assert(List <> nil);
  2975. MaskList := TStringList.Create;
  2976. try
  2977. {* extract the Directory *}
  2978. Directory := ExtractFileDir(Path);
  2979. {* files can be searched in the current directory *}
  2980. if Directory <> '' then
  2981. begin
  2982. Directory := PathAddSeparator(Directory);
  2983. {* extract the FileMasks portion out of Path *}
  2984. Masks := StrAfter(Directory, Path);
  2985. end
  2986. else
  2987. Masks := Path;
  2988. {* put the Masks into TStringlist *}
  2989. StrTokenToStrings(Masks, DirSeparator, MaskList);
  2990. {* search all files in the directory *}
  2991. Result := FindFirst(Directory + '*', faAnyFile, SearchRec) = 0;
  2992. List.BeginUpdate;
  2993. try
  2994. while Result do
  2995. begin
  2996. {* if the filename matches any mask then it is added to the list *}
  2997. for IndexMask := 0 to MaskList.Count - 1 do
  2998. if (SearchRec.Name <> '.') and (SearchRec.Name <> '..')
  2999. and ((SearchRec.Attr and Attr) = (SearchRec.Attr and faAnyFile))
  3000. and IsFileNameMatch(SearchRec.Name, MaskList.Strings[IndexMask]) then
  3001. begin
  3002. if IncludeDirectoryName then
  3003. List.Add(Directory+SearchRec.Name)
  3004. else
  3005. List.Add(SearchRec.Name);
  3006. Break;
  3007. end;
  3008. case FindNext(SearchRec) of
  3009. 0:
  3010. ;
  3011. ERROR_NO_MORE_FILES:
  3012. Break;
  3013. else
  3014. Result := False;
  3015. end;
  3016. end;
  3017. finally
  3018. {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindClose(SearchRec);
  3019. List.EndUpdate;
  3020. end;
  3021. finally
  3022. MaskList.Free;
  3023. end;
  3024. end;
  3025. {$IFDEF MSWINDOWS}
  3026. procedure CreateEmptyFile(const FileName: string);
  3027. var
  3028. Handle: THandle;
  3029. begin
  3030. Handle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
  3031. if Handle <> INVALID_HANDLE_VALUE then
  3032. CloseHandle(Handle)
  3033. else
  3034. RaiseLastOSError;
  3035. end;
  3036. {$ENDIF MSWINDOWS}
  3037. {$IFDEF MSWINDOWS}
  3038. function CloseVolume(var Volume: THandle): Boolean;
  3039. begin
  3040. Result := False;
  3041. if Volume <> INVALID_HANDLE_VALUE then
  3042. begin
  3043. Result := CloseHandle(Volume);
  3044. if Result then
  3045. Volume := INVALID_HANDLE_VALUE;
  3046. end;
  3047. end;
  3048. {$IFNDEF FPC} // needs JclShell
  3049. function DeleteDirectory(const DirectoryName: string; MoveToRecycleBin: Boolean): Boolean;
  3050. begin
  3051. if MoveToRecycleBin then
  3052. Result := SHDeleteFolder(0, DirectoryName, [doSilent, doAllowUndo])
  3053. else
  3054. Result := DelTree(DirectoryName);
  3055. end;
  3056. function CopyDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
  3057. var
  3058. SH: SHFILEOPSTRUCT;
  3059. begin
  3060. ResetMemory(SH, SizeOf(SH));
  3061. SH.Wnd := 0;
  3062. SH.wFunc := FO_COPY;
  3063. SH.pFrom := PChar(PathRemoveSeparator(ExistingDirectoryName) + #0);
  3064. SH.pTo := PChar(PathRemoveSeparator(NewDirectoryName) + #0);
  3065. SH.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_SILENT;
  3066. Result := SHFileOperation(SH) = 0;
  3067. end;
  3068. function MoveDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
  3069. var
  3070. SH: SHFILEOPSTRUCT;
  3071. begin
  3072. ResetMemory(SH, SizeOf(SH));
  3073. SH.Wnd := 0;
  3074. SH.wFunc := FO_MOVE;
  3075. SH.pFrom := PChar(PathRemoveSeparator(ExistingDirectoryName) + #0);
  3076. SH.pTo := PChar(PathRemoveSeparator(NewDirectoryName) + #0);
  3077. SH.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_SILENT;
  3078. Result := SHFileOperation(SH) = 0;
  3079. end;
  3080. {$ENDIF ~FPC}
  3081. function DelTree(const Path: string): Boolean;
  3082. begin
  3083. Result := DelTreeEx(Path, False, nil);
  3084. end;
  3085. function DelTreeEx(const Path: string; AbortOnFailure: Boolean; Progress: TDelTreeProgress): Boolean;
  3086. var
  3087. Files: TStringList;
  3088. LPath: string; // writable copy of Path
  3089. FileName: string;
  3090. I: Integer;
  3091. PartialResult: Boolean;
  3092. Attr: DWORD;
  3093. begin
  3094. Assert(Path <> '', LoadResString(@RsDelTreePathIsEmpty));
  3095. {$IFNDEF ASSERTIONS_ON}
  3096. if Path = '' then
  3097. begin
  3098. Result := False;
  3099. Exit;
  3100. end;
  3101. {$ENDIF ~ASSERTIONS_ON}
  3102. Result := True;
  3103. Files := TStringList.Create;
  3104. try
  3105. LPath := PathRemoveSeparator(Path);
  3106. BuildFileList(LPath + '\*.*', faAnyFile, Files);
  3107. for I := 0 to Files.Count - 1 do
  3108. begin
  3109. FileName := LPath + DirDelimiter + Files[I];
  3110. PartialResult := True;
  3111. // If the current file is itself a directory then recursively delete it
  3112. Attr := GetFileAttributes(PChar(FileName));
  3113. if (Attr <> DWORD(-1)) and ((Attr and FILE_ATTRIBUTE_DIRECTORY) <> 0) then
  3114. PartialResult := DelTreeEx(FileName, AbortOnFailure, Progress)
  3115. else
  3116. begin
  3117. if Assigned(Progress) then
  3118. PartialResult := Progress(FileName, Attr);
  3119. if PartialResult then
  3120. begin
  3121. // Set attributes to normal in case it's a readonly file
  3122. PartialResult := SetFileAttributes(PChar(FileName), FILE_ATTRIBUTE_NORMAL);
  3123. if PartialResult then
  3124. PartialResult := DeleteFile(FileName);
  3125. end;
  3126. end;
  3127. if not PartialResult then
  3128. begin
  3129. Result := False;
  3130. if AbortOnFailure then
  3131. Break;
  3132. end;
  3133. end;
  3134. finally
  3135. FreeAndNil(Files);
  3136. end;
  3137. if Result then
  3138. begin
  3139. // Finally remove the directory itself
  3140. Result := SetFileAttributes(PChar(LPath), FILE_ATTRIBUTE_NORMAL);
  3141. if Result then
  3142. begin
  3143. {$IOCHECKS OFF}
  3144. RmDir(LPath);
  3145. {$IFDEF IOCHECKS_ON}
  3146. {$IOCHECKS ON}
  3147. {$ENDIF IOCHECKS_ON}
  3148. Result := IOResult = 0;
  3149. end;
  3150. end;
  3151. end;
  3152. {$ENDIF MSWINDOWS}
  3153. {$IFDEF MSWINDOWS}
  3154. function DirectoryExists(const Name: string): Boolean;
  3155. var
  3156. R: DWORD;
  3157. begin
  3158. R := GetFileAttributes(PChar(Name));
  3159. Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) <> 0);
  3160. end;
  3161. {$ENDIF MSWINDOWS}
  3162. {$IFDEF UNIX}
  3163. function DirectoryExists(const Name: string; ResolveSymLinks: Boolean): Boolean;
  3164. begin
  3165. Result := IsDirectory(Name, ResolveSymLinks);
  3166. end;
  3167. {$ENDIF UNIX}
  3168. {$IFDEF MSWINDOWS}
  3169. function DiskInDrive(Drive: Char): Boolean;
  3170. var
  3171. ErrorMode: Cardinal;
  3172. begin
  3173. Result := False;
  3174. Assert(CharIsDriveLetter(Drive));
  3175. if CharIsDriveLetter(Drive) then
  3176. begin
  3177. Drive := CharUpper(Drive);
  3178. { try to access the drive, it doesn't really matter how we access the drive and as such calling
  3179. DiskSize is more or less a random choice. The call to SetErrorMode supresses the system provided
  3180. error dialog if there is no disk in the drive and causes the to DiskSize to fail. }
  3181. ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  3182. try
  3183. Result := DiskSize(Ord(Drive) - $40) <> -1;
  3184. finally
  3185. SetErrorMode(ErrorMode);
  3186. end;
  3187. end;
  3188. end;
  3189. {$ENDIF MSWINDOWS}
  3190. function FileCreateTemp(var Prefix: string): THandle;
  3191. {$IFDEF MSWINDOWS}
  3192. var
  3193. TempName: string;
  3194. begin
  3195. Result := INVALID_HANDLE_VALUE;
  3196. TempName := FileGetTempName(Prefix);
  3197. if TempName <> '' then
  3198. begin
  3199. Result := CreateFile(PChar(TempName), GENERIC_READ or GENERIC_WRITE, 0, nil,
  3200. OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
  3201. // In certain situations it's possible that CreateFile fails yet the file is actually created,
  3202. // therefore explicitly delete it upon failure.
  3203. if Result = INVALID_HANDLE_VALUE then
  3204. DeleteFile(TempName);
  3205. Prefix := TempName;
  3206. end;
  3207. end;
  3208. {$ENDIF MSWINDOWS}
  3209. {$IFDEF UNIX}
  3210. var
  3211. Template: string;
  3212. begin
  3213. // The mkstemp function generates a unique file name just as mktemp does, but
  3214. // it also opens the file for you with open. If successful, it modifies
  3215. // template in place and returns a file descriptor for that file open for
  3216. // reading and writing. If mkstemp cannot create a uniquely-named file, it
  3217. // returns -1. If template does not end with `XXXXXX', mkstemp returns -1 and
  3218. // does not modify template.
  3219. // The file is opened using mode 0600. If the file is meant to be used by
  3220. // other users this mode must be changed explicitly.
  3221. // Unlike mktemp, mkstemp is actually guaranteed to create a unique file that
  3222. // cannot possibly clash with any other program trying to create a temporary
  3223. // file. This is because it works by calling open with the O_EXCL flag, which
  3224. // says you want to create a new file and get an error if the file already
  3225. // exists.
  3226. Template := Prefix + 'XXXXXX';
  3227. Result := mkstemp(PChar(Template));
  3228. Prefix := Template;
  3229. end;
  3230. {$ENDIF UNIX}
  3231. function FileBackup(const FileName: string; Move: Boolean = False): Boolean;
  3232. begin
  3233. if Move then
  3234. Result := FileMove(FileName, GetBackupFileName(FileName), True)
  3235. else
  3236. Result := FileCopy(FileName, GetBackupFileName(FileName), True);
  3237. end;
  3238. function FileCopy(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
  3239. var
  3240. {$IFDEF UNIX}
  3241. SrcFile, DstFile: file;
  3242. Buf: array[0..511] of Byte;
  3243. BytesRead: Integer;
  3244. {$ENDIF UNIX}
  3245. DestFileName: string;
  3246. begin
  3247. if IsDirectory(NewFileName) then
  3248. DestFileName := PathAddSeparator(NewFileName) + ExtractFileName(ExistingFileName)
  3249. else
  3250. DestFileName := NewFileName;
  3251. {$IFDEF MSWINDOWS}
  3252. { TODO : Use CopyFileEx where available? }
  3253. Result := CopyFile(PChar(ExistingFileName), PChar(DestFileName), not ReplaceExisting);
  3254. {$ENDIF MSWINDOWS}
  3255. {$IFDEF UNIX}
  3256. Result := False;
  3257. if not FileExists(DestFileName) or ReplaceExisting then
  3258. begin
  3259. AssignFile(SrcFile, ExistingFileName);
  3260. Reset(SrcFile, 1);
  3261. AssignFile(DstFile, DestFileName);
  3262. Rewrite(DstFile, 1);
  3263. while not Eof(SrcFile) do
  3264. begin
  3265. BlockRead(SrcFile, Buf, SizeOf(Buf), BytesRead);
  3266. BlockWrite(DstFile, Buf, BytesRead);
  3267. end;
  3268. CloseFile(DstFile);
  3269. CloseFile(SrcFile);
  3270. Result := True;
  3271. end;
  3272. {$ENDIF UNIX}
  3273. end;
  3274. function FileDateTime(const FileName: string): TDateTime;
  3275. {$IFNDEF COMPILER10_UP}
  3276. var
  3277. Age: Longint;
  3278. {$ENDIF !COMPILER10_UP}
  3279. begin
  3280. {$IFDEF COMPILER10_UP}
  3281. if not FileAge(Filename, Result) then
  3282. Result := 0;
  3283. {$ELSE}
  3284. Age := FileAge(FileName);
  3285. {$IFDEF MSWINDOWS}
  3286. // [roko] -1 is valid FileAge value on Linux
  3287. if Age = -1 then
  3288. Result := 0
  3289. else
  3290. {$ENDIF MSWINDOWS}
  3291. Result := FileDateToDateTime(Age);
  3292. {$ENDIF COMPILER10_UP}
  3293. end;
  3294. function FileDelete(const FileName: string; MoveToRecycleBin: Boolean = False): Boolean;
  3295. {$IFDEF MSWINDOWS}
  3296. begin
  3297. if MoveToRecycleBin then
  3298. Result := SHDeleteFiles(0, FileName, [doSilent, doAllowUndo, doFilesOnly])
  3299. else
  3300. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.DeleteFile(PChar(FileName));
  3301. end;
  3302. {$ENDIF MSWINDOWS}
  3303. {$IFDEF UNIX}
  3304. { TODO : implement MoveToRecycleBin for appropriate Desktops (e.g. KDE) }
  3305. begin
  3306. Result := remove(PChar(FileName)) <> -1;
  3307. end;
  3308. {$ENDIF UNIX}
  3309. function FileExists(const FileName: string): Boolean;
  3310. {$IFDEF MSWINDOWS}
  3311. var
  3312. Attr: Cardinal;
  3313. {$ENDIF MSWINDOWS}
  3314. begin
  3315. if FileName <> '' then
  3316. begin
  3317. {$IFDEF MSWINDOWS}
  3318. // FileGetSize is very slow, GetFileAttributes is much faster
  3319. Attr := GetFileAttributes(Pointer(Filename));
  3320. Result := (Attr <> $FFFFFFFF) and (Attr and FILE_ATTRIBUTE_DIRECTORY = 0);
  3321. {$ELSE ~MSWINDOWS}
  3322. // Attempt to access the file, doesn't matter how, using FileGetSize is as good as anything else.
  3323. Result := FileGetSize(FileName) <> -1;
  3324. {$ENDIF ~MSWINDOWS}
  3325. end
  3326. else
  3327. Result := False;
  3328. end;
  3329. procedure FileHistory(const FileName: string; HistoryPath: string = ''; MaxHistoryCount: Integer = 100; MinFileDate:
  3330. TDateTime = 0; ReplaceExtention: Boolean = true);
  3331. Function Extention (Number : Integer) : String;
  3332. begin
  3333. Result := inttostr(Number);
  3334. while Length(Result) < 3 do
  3335. Result := '0' + Result;
  3336. Result := '.~'+Result+'~';
  3337. end;
  3338. procedure RenameToNumber(const RenameFileName: string; Number: Integer);
  3339. var
  3340. f1: string;
  3341. f2: string;
  3342. begin
  3343. f1 := ChangeFileExt(RenameFileName,Extention(Number-1));
  3344. f2 := ChangeFileExt(RenameFileName,Extention(Number));
  3345. if FileExists(f2) then
  3346. if Number >= MaxHistoryCount then
  3347. if not FileDelete(f2) then
  3348. Exception.Create('Unable to delete file "' + f2 + '".')
  3349. else
  3350. else
  3351. RenameToNumber(RenameFileName, Number + 1);
  3352. if FileExists(f1) then
  3353. if not FileMove(f1, f2, true) then
  3354. Exception.Create('Unable to rename file "' + f1 + '" to "' + f2 + '".')
  3355. end;
  3356. Var FirstFile : string;
  3357. begin
  3358. // TODO -cMM: FileHistory default body inserted
  3359. if not FileExists(FileName) or (MaxHistoryCount <= 0) then
  3360. Exit;
  3361. if HistoryPath = '' then
  3362. HistoryPath := ExtractFilePath(FileName);
  3363. FirstFile := PathAppend(HistoryPath, ExtractFileName(FileName));
  3364. if ReplaceExtention then
  3365. FirstFile := ChangeFileExt(FirstFile, Extention(1))
  3366. else
  3367. FirstFile := FirstFile+Extention(1);
  3368. if (FileDateTime(FirstFile) > MinFileDate) and (MinFileDate <> 0) then
  3369. Exit;
  3370. RenameToNumber(FirstFile, 2);
  3371. FileCopy(FileName, FirstFile, True);
  3372. end;
  3373. function FileMove(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
  3374. {$IFDEF MSWINDOWS}
  3375. const
  3376. Flag: array[Boolean] of Cardinal = (0, MOVEFILE_REPLACE_EXISTING);
  3377. {$ENDIF MSWINDOWS}
  3378. begin
  3379. {$IFDEF MSWINDOWS}
  3380. Result := MoveFileEx(PChar(ExistingFileName), PChar(NewFileName), Flag[ReplaceExisting]);
  3381. {$ENDIF MSWINDOWS}
  3382. {$IFDEF UNIX}
  3383. Result := __rename(PChar(ExistingFileName), PChar(NewFileName)) = 0;
  3384. {$ENDIF UNIX}
  3385. if not Result then
  3386. begin
  3387. Result := FileCopy(ExistingFileName, NewFileName, ReplaceExisting);
  3388. if Result then
  3389. FileDelete(ExistingFileName);
  3390. end;
  3391. end;
  3392. function FileRestore(const FileName: string): Boolean;
  3393. var
  3394. TempFileName: string;
  3395. begin
  3396. Result := False;
  3397. TempFileName := FileGetTempName('');
  3398. if FileMove(GetBackupFileName(FileName), TempFileName, True) then
  3399. if FileBackup(FileName, False) then
  3400. Result := FileMove(TempFileName, FileName, True);
  3401. end;
  3402. function GetBackupFileName(const FileName: string): string;
  3403. var
  3404. NewExt: string;
  3405. begin
  3406. NewExt := ExtractFileExt(FileName);
  3407. if Length(NewExt) > 0 then
  3408. begin
  3409. NewExt[1] := '~';
  3410. NewExt := '.' + NewExt
  3411. end
  3412. else
  3413. NewExt := '.~';
  3414. Result := ChangeFileExt(FileName, NewExt);
  3415. end;
  3416. function IsBackupFileName(const FileName: string): Boolean;
  3417. begin
  3418. Result := (pos('.~', ExtractFileExt(FileName)) = 1);
  3419. end;
  3420. function FileGetDisplayName(const FileName: string): string;
  3421. {$IFDEF MSWINDOWS}
  3422. var
  3423. FileInfo: TSHFileInfo;
  3424. begin
  3425. ResetMemory(FileInfo, SizeOf(FileInfo));
  3426. if SHGetFileInfo(PChar(FileName), 0, FileInfo, SizeOf(FileInfo), SHGFI_DISPLAYNAME) <> 0 then
  3427. Result := FileInfo.szDisplayName
  3428. else
  3429. Result := FileName;
  3430. end;
  3431. {$ELSE ~MSWINDOWS}
  3432. begin
  3433. { TODO -cHelp : mention this reduced solution }
  3434. Result := FileName;
  3435. end;
  3436. {$ENDIF ~MSWINDOWS}
  3437. function FileGetGroupName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
  3438. {$IFDEF MSWINDOWS}
  3439. var
  3440. DomainName: WideString;
  3441. TmpResult: WideString;
  3442. pSD: PSecurityDescriptor;
  3443. BufSize: DWORD;
  3444. begin
  3445. if IsWinNT then
  3446. begin
  3447. BufSize := 0;
  3448. GetFileSecurity(PChar(FileName), GROUP_SECURITY_INFORMATION, nil, 0, BufSize);
  3449. if BufSize > 0 then
  3450. begin
  3451. GetMem(pSD, BufSize);
  3452. GetFileSecurity(PChar(FileName), GROUP_SECURITY_INFORMATION,
  3453. pSD, BufSize, BufSize);
  3454. LookupAccountBySid(Pointer(TJclAddr(pSD) + TJclAddr(pSD^.Group)), TmpResult, DomainName, True);
  3455. FreeMem(pSD);
  3456. Result := Trim(TmpResult);
  3457. end;
  3458. end;
  3459. end;
  3460. {$ENDIF ~MSWINDOWS}
  3461. {$IFDEF UNIX}
  3462. var
  3463. Buf: TStatBuf64;
  3464. ResultBuf: TGroup;
  3465. ResultBufPtr: PGroup;
  3466. Buffer: array of Char;
  3467. begin
  3468. if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
  3469. begin
  3470. SetLength(Buffer, 128);
  3471. while getgrgid_r(Buf.st_gid, ResultBuf, @Buffer[0], Length(Buffer), ResultBufPtr) = ERANGE do
  3472. SetLength(Buffer, Length(Buffer) * 2);
  3473. Result := ResultBuf.gr_name;
  3474. end;
  3475. end;
  3476. {$ENDIF ~UNIX}
  3477. function FileGetOwnerName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
  3478. {$IFDEF MSWINDOWS}
  3479. var
  3480. DomainName: WideString;
  3481. TmpResult: WideString;
  3482. pSD: PSecurityDescriptor;
  3483. BufSize: DWORD;
  3484. begin
  3485. if IsWinNT then
  3486. begin
  3487. BufSize := 0;
  3488. GetFileSecurity(PChar(FileName), OWNER_SECURITY_INFORMATION, nil, 0, BufSize);
  3489. if BufSize > 0 then
  3490. begin
  3491. GetMem(pSD, BufSize);
  3492. try
  3493. GetFileSecurity(PChar(FileName), OWNER_SECURITY_INFORMATION,
  3494. pSD, BufSize, BufSize);
  3495. LookupAccountBySid(Pointer(TJclAddr(pSD) + TJclAddr(pSD^.Owner)), TmpResult, DomainName, True);
  3496. finally
  3497. FreeMem(pSD);
  3498. end;
  3499. Result := Trim(TmpResult);
  3500. end;
  3501. end;
  3502. end;
  3503. {$ENDIF ~MSWINDOWS}
  3504. {$IFDEF UNIX}
  3505. var
  3506. Buf: TStatBuf64;
  3507. ResultBuf: TPasswordRecord;
  3508. ResultBufPtr: PPasswordRecord;
  3509. Buffer: array of Char;
  3510. begin
  3511. if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
  3512. begin
  3513. SetLength(Buffer, 128);
  3514. while getpwuid_r(Buf.st_uid, ResultBuf, @Buffer[0], Length(Buffer), ResultBufPtr) = ERANGE do
  3515. SetLength(Buffer, Length(Buffer) * 2);
  3516. Result := ResultBuf.pw_name;
  3517. end;
  3518. end;
  3519. {$ENDIF ~UNIX}
  3520. function FileGetSize(const FileName: string): Int64;
  3521. {$IFDEF MSWINDOWS}
  3522. var
  3523. FileAttributesEx: WIN32_FILE_ATTRIBUTE_DATA;
  3524. OldMode: Cardinal;
  3525. Size: TJclULargeInteger;
  3526. begin
  3527. Result := -1;
  3528. OldMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  3529. try
  3530. if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @FileAttributesEx) then
  3531. begin
  3532. Size.LowPart := FileAttributesEx.nFileSizeLow;
  3533. Size.HighPart := FileAttributesEx.nFileSizeHigh;
  3534. Result := Size.QuadPart;
  3535. end;
  3536. finally
  3537. SetErrorMode(OldMode);
  3538. end;
  3539. end;
  3540. {$ENDIF MSWINDOWS}
  3541. {$IFDEF UNIX}
  3542. var
  3543. Buf: TStatBuf64;
  3544. begin
  3545. Result := -1;
  3546. if GetFileStatus(FileName, Buf, False) = 0 then
  3547. Result := Buf.st_size;
  3548. end;
  3549. {$ENDIF UNIX}
  3550. {$IFDEF MSWINDOWS}
  3551. {$IFDEF FPC}
  3552. { TODO : Move this over to JclWin32 when JclWin32 gets overhauled. }
  3553. function GetTempFileName(lpPathName, lpPrefixString: PChar;
  3554. uUnique: UINT; lpTempFileName: PChar): UINT; stdcall;
  3555. external kernel32 name 'GetTempFileNameA';
  3556. {$ENDIF FPC}
  3557. {$ENDIF MSWINDOWS}
  3558. function FileGetTempName(const Prefix: string): string;
  3559. {$IFDEF MSWINDOWS}
  3560. var
  3561. TempPath, TempFile: string;
  3562. R: Cardinal;
  3563. begin
  3564. Result := '';
  3565. TempPath := PathGetTempPath;
  3566. if TempPath <> '' then
  3567. begin
  3568. SetLength(TempFile, MAX_PATH);
  3569. R := GetTempFileName(PChar(TempPath), PChar(Prefix), 0, PChar(TempFile));
  3570. if R <> 0 then
  3571. begin
  3572. StrResetLength(TempFile);
  3573. Result := TempFile;
  3574. end;
  3575. end;
  3576. end;
  3577. {$ENDIF MSWINDOWS}
  3578. {$IFDEF UNIX}
  3579. // Warning: Between the time the pathname is constructed and the file is created
  3580. // another process might have created a file with the same name using tmpnam,
  3581. // leading to a possible security hole. The implementation generates names which
  3582. // can hardly be predicted, but when opening the file you should use the O_EXCL
  3583. // flag. Using tmpfile or mkstemp is a safe way to avoid this problem.
  3584. var
  3585. P: PChar;
  3586. begin
  3587. P := tempnam(PChar(PathGetTempPath), PChar(Prefix));
  3588. Result := P;
  3589. Libc.free(P);
  3590. end;
  3591. {$ENDIF UNIX}
  3592. {$IFDEF MSWINDOWS}
  3593. function FileGetTypeName(const FileName: string): string;
  3594. var
  3595. FileInfo: TSHFileInfo;
  3596. RetVal: DWORD;
  3597. begin
  3598. ResetMemory(FileInfo, SizeOf(FileInfo));
  3599. RetVal := SHGetFileInfo(PChar(FileName), 0, FileInfo, SizeOf(FileInfo),
  3600. SHGFI_TYPENAME or SHGFI_USEFILEATTRIBUTES);
  3601. if RetVal <> 0 then
  3602. Result := FileInfo.szTypeName;
  3603. if (RetVal = 0) or (Trim(Result) = '') then
  3604. begin
  3605. // Lookup failed so mimic explorer behaviour by returning "XYZ File"
  3606. Result := ExtractFileExt(FileName);
  3607. Delete(Result, 1, 1);
  3608. Result := TrimLeft(UpperCase(Result) + LoadResString(@RsDefaultFileTypeName));
  3609. end;
  3610. end;
  3611. {$ENDIF MSWINDOWS}
  3612. function FindUnusedFileName(FileName: string; const FileExt: string; NumberPrefix: string = ''): string;
  3613. var
  3614. I: Integer;
  3615. begin
  3616. Result := PathAddExtension(FileName, FileExt);
  3617. if not FileExists(Result) then
  3618. Exit;
  3619. if SameText(Result, FileName) then
  3620. Delete(FileName, Length(FileName) - Length(FileExt) + 1, Length(FileExt));
  3621. I := 0;
  3622. repeat
  3623. Inc(I);
  3624. Result := PathAddExtension(FileName + NumberPrefix + IntToStr(I), FileExt);
  3625. until not FileExists(Result);
  3626. end;
  3627. // This routine is copied from FileCtrl.pas to avoid dependency on that unit.
  3628. // See the remark at the top of this section
  3629. function ForceDirectories(Name: string): Boolean;
  3630. var
  3631. ExtractPath: string;
  3632. begin
  3633. Result := True;
  3634. if Length(Name) = 0 then
  3635. raise EJclFileUtilsError.CreateRes(@RsCannotCreateDir);
  3636. Name := PathRemoveSeparator(Name);
  3637. {$IFDEF MSWINDOWS}
  3638. ExtractPath := ExtractFilePath(Name);
  3639. if ((Length(Name) = 2) and (Copy(Name, 2,1) = ':')) or DirectoryExists(Name) or (ExtractPath = Name) then
  3640. Exit;
  3641. {$ENDIF MSWINDOWS}
  3642. {$IFDEF UNIX}
  3643. if (Length(Name) = 0) or DirectoryExists(Name) then
  3644. Exit;
  3645. ExtractPath := ExtractFilePath(Name);
  3646. {$ENDIF UNIX}
  3647. Result := (ExtractPath = '') or ForceDirectories(ExtractPath);
  3648. if Result then
  3649. begin
  3650. {$IFDEF MSWINDOWS}
  3651. SetLastError(ERROR_SUCCESS);
  3652. {$ENDIF MSWINDOWS}
  3653. Result := Result and CreateDir(Name);
  3654. {$IFDEF MSWINDOWS}
  3655. Result := Result or (GetLastError = ERROR_ALREADY_EXISTS);
  3656. {$ENDIF MSWINDOWS}
  3657. end;
  3658. end;
  3659. function GetDirectorySize(const Path: string): Int64;
  3660. function RecurseFolder(const Path: string): Int64;
  3661. var
  3662. F: TSearchRec;
  3663. R: Integer;
  3664. {$IFDEF MSWINDOWS}
  3665. TempSize: TJclULargeInteger;
  3666. {$ENDIF MSWINDOWS}
  3667. begin
  3668. Result := 0;
  3669. R := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Path + '*.*', faAnyFile, F);
  3670. if R = 0 then
  3671. try
  3672. while R = 0 do
  3673. begin
  3674. if (F.Name <> '.') and (F.Name <> '..') then
  3675. begin
  3676. if (F.Attr and faDirectory) = faDirectory then
  3677. Inc(Result, RecurseFolder(Path + F.Name + DirDelimiter))
  3678. else
  3679. {$IFDEF MSWINDOWS}
  3680. begin
  3681. TempSize.LowPart := F.FindData.nFileSizeLow;
  3682. TempSize.HighPart := F.FindData.nFileSizeHigh;
  3683. Inc(Result, TempSize.QuadPart);
  3684. end;
  3685. {$ENDIF MSWINDOWS}
  3686. {$IFDEF UNIX}
  3687. // SysUtils.Find* don't perceive files >= 2 GB anyway
  3688. Inc(Result, Int64(F.Size));
  3689. {$ENDIF UNIX}
  3690. end;
  3691. R := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindNext(F);
  3692. end;
  3693. if R <> ERROR_NO_MORE_FILES then
  3694. Abort;
  3695. finally
  3696. {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindClose(F);
  3697. end;
  3698. end;
  3699. begin
  3700. if not DirectoryExists(PathRemoveSeparator(Path)) then
  3701. Result := -1
  3702. else
  3703. try
  3704. Result := RecurseFolder(PathAddSeparator(Path))
  3705. except
  3706. Result := -1;
  3707. end;
  3708. end;
  3709. {$IFDEF MSWINDOWS}
  3710. function GetDriveTypeStr(const Drive: Char): string;
  3711. var
  3712. DriveType: Integer;
  3713. DriveStr: string;
  3714. begin
  3715. if not CharIsDriveLetter(Drive) then
  3716. raise EJclPathError.CreateResFmt(@RsPathInvalidDrive, [Drive]);
  3717. DriveStr := Drive + ':\';
  3718. DriveType := GetDriveType(PChar(DriveStr));
  3719. case DriveType of
  3720. DRIVE_REMOVABLE:
  3721. Result := LoadResString(@RsRemovableDrive);
  3722. DRIVE_FIXED:
  3723. Result := LoadResString(@RsHardDisk);
  3724. DRIVE_REMOTE:
  3725. Result := LoadResString(@RsRemoteDrive);
  3726. DRIVE_CDROM:
  3727. Result := LoadResString(@RsCDRomDrive);
  3728. DRIVE_RAMDISK:
  3729. Result := LoadResString(@RsRamDisk);
  3730. else
  3731. Result := LoadResString(@RsUnknownDrive);
  3732. end;
  3733. end;
  3734. function GetFileAgeCoherence(const FileName: string): Boolean;
  3735. var
  3736. FileAttributesEx: WIN32_FILE_ATTRIBUTE_DATA;
  3737. begin
  3738. Result := False;
  3739. if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @FileAttributesEx) then
  3740. {$IFDEF FPC}
  3741. Result := CompareFileTime(@FileAttributesEx.ftCreationTime, @FileAttributesEx.ftLastWriteTime) <= 0;
  3742. {$ELSE ~FPC}
  3743. Result := CompareFileTime(FileAttributesEx.ftCreationTime, FileAttributesEx.ftLastWriteTime) <= 0;
  3744. {$ENDIF ~FPC}
  3745. end;
  3746. {$ENDIF MSWINDOWS}
  3747. procedure GetFileAttributeList(const Items: TStrings; const Attr: Integer);
  3748. begin
  3749. { TODO : clear list? }
  3750. Assert(Assigned(Items));
  3751. if not Assigned(Items) then
  3752. Exit;
  3753. Items.BeginUpdate;
  3754. try
  3755. { TODO : differentiate Windows/UNIX idents }
  3756. if Attr and faDirectory = faDirectory then
  3757. Items.Add(LoadResString(@RsAttrDirectory));
  3758. if Attr and faReadOnly = faReadOnly then
  3759. Items.Add(LoadResString(@RsAttrReadOnly));
  3760. if Attr and faSysFile = faSysFile then
  3761. Items.Add(LoadResString(@RsAttrSystemFile));
  3762. if Attr and faArchive = faArchive then
  3763. Items.Add(LoadResString(@RsAttrArchive));
  3764. if Attr and faAnyFile = faAnyFile then
  3765. Items.Add(LoadResString(@RsAttrAnyFile));
  3766. if Attr and faHidden = faHidden then
  3767. Items.Add(LoadResString(@RsAttrHidden));
  3768. finally
  3769. Items.EndUpdate;
  3770. end;
  3771. end;
  3772. {$IFDEF MSWINDOWS}
  3773. { TODO : GetFileAttributeListEx - Unix version }
  3774. procedure GetFileAttributeListEx(const Items: TStrings; const Attr: Integer);
  3775. begin
  3776. { TODO : clear list? }
  3777. Assert(Assigned(Items));
  3778. if not Assigned(Items) then
  3779. Exit;
  3780. Items.BeginUpdate;
  3781. try
  3782. if Attr and FILE_ATTRIBUTE_READONLY = FILE_ATTRIBUTE_READONLY then
  3783. Items.Add(LoadResString(@RsAttrReadOnly));
  3784. if Attr and FILE_ATTRIBUTE_HIDDEN = FILE_ATTRIBUTE_HIDDEN then
  3785. Items.Add(LoadResString(@RsAttrHidden));
  3786. if Attr and FILE_ATTRIBUTE_SYSTEM = FILE_ATTRIBUTE_SYSTEM then
  3787. Items.Add(LoadResString(@RsAttrSystemFile));
  3788. if Attr and FILE_ATTRIBUTE_DIRECTORY = FILE_ATTRIBUTE_DIRECTORY then
  3789. Items.Add(LoadResString(@RsAttrDirectory));
  3790. if Attr and FILE_ATTRIBUTE_ARCHIVE = FILE_ATTRIBUTE_ARCHIVE then
  3791. Items.Add(LoadResString(@RsAttrArchive));
  3792. if Attr and FILE_ATTRIBUTE_NORMAL = FILE_ATTRIBUTE_NORMAL then
  3793. Items.Add(LoadResString(@RsAttrNormal));
  3794. if Attr and FILE_ATTRIBUTE_TEMPORARY = FILE_ATTRIBUTE_TEMPORARY then
  3795. Items.Add(LoadResString(@RsAttrTemporary));
  3796. if Attr and FILE_ATTRIBUTE_COMPRESSED = FILE_ATTRIBUTE_COMPRESSED then
  3797. Items.Add(LoadResString(@RsAttrCompressed));
  3798. if Attr and FILE_ATTRIBUTE_OFFLINE = FILE_ATTRIBUTE_OFFLINE then
  3799. Items.Add(LoadResString(@RsAttrOffline));
  3800. if Attr and FILE_ATTRIBUTE_ENCRYPTED = FILE_ATTRIBUTE_ENCRYPTED then
  3801. Items.Add(LoadResString(@RsAttrEncrypted));
  3802. if Attr and FILE_ATTRIBUTE_REPARSE_POINT = FILE_ATTRIBUTE_REPARSE_POINT then
  3803. Items.Add(LoadResString(@RsAttrReparsePoint));
  3804. if Attr and FILE_ATTRIBUTE_SPARSE_FILE = FILE_ATTRIBUTE_SPARSE_FILE then
  3805. Items.Add(LoadResString(@RsAttrSparseFile));
  3806. finally
  3807. Items.EndUpdate;
  3808. end;
  3809. end;
  3810. {$ENDIF MSWINDOWS}
  3811. function GetFileInformation(const FileName: string; out FileInfo: TSearchRec): Boolean;
  3812. begin
  3813. Result := FindFirst(FileName, faAnyFile, FileInfo) = 0;
  3814. if Result then
  3815. {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindClose(FileInfo);
  3816. end;
  3817. function GetFileInformation(const FileName: string): TSearchRec;
  3818. begin
  3819. if not GetFileInformation(FileName, Result) then
  3820. RaiseLastOSError;
  3821. end;
  3822. {$IFDEF UNIX}
  3823. { TODO -cHelp : Author: Robert Rossmair }
  3824. function GetFileStatus(const FileName: string; out StatBuf: TStatBuf64;
  3825. const ResolveSymLinks: Boolean): Integer;
  3826. begin
  3827. if ResolveSymLinks then
  3828. Result := stat64(PChar(FileName), StatBuf)
  3829. else
  3830. Result := lstat64(PChar(FileName), StatBuf);
  3831. end;
  3832. {$ENDIF UNIX}
  3833. {$IFDEF MSWINDOWS}
  3834. function GetFileLastWrite(const FileName: string): TFileTime;
  3835. begin
  3836. Result := GetFileInformation(FileName).FindData.ftLastWriteTime;
  3837. end;
  3838. function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime): Boolean;
  3839. var
  3840. FileInfo: TSearchRec;
  3841. begin
  3842. Result := GetFileInformation(FileName, FileInfo);
  3843. if Result then
  3844. LocalTime := FileTimeToLocalDateTime(FileInfo.FindData.ftLastWriteTime);
  3845. end;
  3846. {$ENDIF MSWINDOWS}
  3847. {$IFDEF UNIX}
  3848. function GetFileLastWrite(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean): Boolean;
  3849. var
  3850. Buf: TStatBuf64;
  3851. begin
  3852. Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
  3853. if Result then
  3854. TimeStamp := Buf.st_mtime
  3855. end;
  3856. function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean): Boolean;
  3857. var
  3858. Buf: TStatBuf64;
  3859. begin
  3860. Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
  3861. if Result then
  3862. LocalTime := FileDateToDateTime(Buf.st_mtime);
  3863. end;
  3864. function GetFileLastWrite(const FileName: string; ResolveSymLinks: Boolean): Integer;
  3865. var
  3866. Buf: TStatBuf64;
  3867. begin
  3868. if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
  3869. Result := Buf.st_mtime
  3870. else
  3871. Result := -1;
  3872. end;
  3873. {$ENDIF UNIX}
  3874. {$IFDEF MSWINDOWS}
  3875. function GetFileLastAccess(const FileName: string): TFileTime;
  3876. begin
  3877. Result := GetFileInformation(FileName).FindData.ftLastAccessTime;
  3878. end;
  3879. function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime): Boolean;
  3880. var
  3881. FileInfo: TSearchRec;
  3882. begin
  3883. Result := GetFileInformation(FileName, FileInfo);
  3884. if Result then
  3885. LocalTime := FileTimeToLocalDateTime(GetFileInformation(FileName).FindData.ftLastAccessTime);
  3886. end;
  3887. {$ENDIF MSWINDOWS}
  3888. {$IFDEF UNIX}
  3889. function GetFileLastAccess(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean): Boolean;
  3890. var
  3891. Buf: TStatBuf64;
  3892. begin
  3893. Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
  3894. if Result then
  3895. TimeStamp := Buf.st_atime
  3896. end;
  3897. function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean): Boolean;
  3898. var
  3899. Buf: TStatBuf64;
  3900. begin
  3901. Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
  3902. if Result then
  3903. LocalTime := FileDateToDateTime(Buf.st_atime);
  3904. end;
  3905. function GetFileLastAccess(const FileName: string; ResolveSymLinks: Boolean): Integer;
  3906. var
  3907. Buf: TStatBuf64;
  3908. begin
  3909. if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
  3910. Result := Buf.st_atime
  3911. else
  3912. Result := -1;
  3913. end;
  3914. {$ENDIF UNIX}
  3915. {$IFDEF MSWINDOWS}
  3916. function GetFileCreation(const FileName: string): TFileTime;
  3917. begin
  3918. Result := GetFileInformation(FileName).FindData.ftCreationTime;
  3919. end;
  3920. function GetFileCreation(const FileName: string; out LocalTime: TDateTime): Boolean;
  3921. var
  3922. FileInfo: TSearchRec;
  3923. begin
  3924. Result := GetFileInformation(FileName, FileInfo);
  3925. if Result then
  3926. LocalTime := FileTimeToLocalDateTime(GetFileInformation(FileName).FindData.ftCreationTime);
  3927. end;
  3928. {$ENDIF MSWINDOWS}
  3929. {$IFDEF UNIX}
  3930. function GetFileLastAttrChange(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean): Boolean;
  3931. var
  3932. Buf: TStatBuf64;
  3933. begin
  3934. Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
  3935. if Result then
  3936. TimeStamp := Buf.st_ctime
  3937. end;
  3938. function GetFileLastAttrChange(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean): Boolean;
  3939. var
  3940. Buf: TStatBuf64;
  3941. begin
  3942. Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
  3943. if Result then
  3944. LocalTime := FileDateToDateTime(Buf.st_ctime);
  3945. end;
  3946. function GetFileLastAttrChange(const FileName: string; ResolveSymLinks: Boolean): Integer;
  3947. var
  3948. Buf: TStatBuf64;
  3949. begin
  3950. if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
  3951. Result := Buf.st_ctime
  3952. else
  3953. Result := -1;
  3954. end;
  3955. {$ENDIF UNIX}
  3956. function GetModulePath(const Module: HMODULE): string;
  3957. var
  3958. L: Integer;
  3959. begin
  3960. L := MAX_PATH + 1;
  3961. SetLength(Result, L);
  3962. {$IFDEF MSWINDOWS}
  3963. L := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetModuleFileName(Module, Pointer(Result), L);
  3964. {$ENDIF MSWINDOWS}
  3965. {$IFDEF UNIX}
  3966. {$IFDEF FPC}
  3967. L := 0; // FIXME
  3968. {$ELSE ~FPC}
  3969. L := GetModuleFileName(Module, Pointer(Result), L);
  3970. {$ENDIF ~FPC}
  3971. {$ENDIF UNIX}
  3972. SetLength(Result, L);
  3973. end;
  3974. function GetSizeOfFile(const FileName: string): Int64;
  3975. {$IFDEF MSWINDOWS}
  3976. var
  3977. FileAttributesEx: WIN32_FILE_ATTRIBUTE_DATA;
  3978. Size: TJclULargeInteger;
  3979. begin
  3980. Result := 0;
  3981. if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @FileAttributesEx) then
  3982. begin
  3983. Size.LowPart := FileAttributesEx.nFileSizeLow;
  3984. Size.HighPart := FileAttributesEx.nFileSizeHigh;
  3985. Result := Size.QuadPart;
  3986. end
  3987. else
  3988. RaiseLastOSError;
  3989. end;
  3990. {$ENDIF MSWINDOWS}
  3991. {$IFDEF UNIX}
  3992. var
  3993. Buf: TStatBuf64;
  3994. begin
  3995. if GetFileStatus(FileName, Buf, False) <> 0 then
  3996. RaiseLastOSError;
  3997. Result := Buf.st_size;
  3998. end;
  3999. {$ENDIF UNIX}
  4000. {$IFDEF MSWINDOWS}
  4001. function GetSizeOfFile(Handle: THandle): Int64; overload;
  4002. var
  4003. Size: TJclULargeInteger;
  4004. begin
  4005. Size.LowPart := GetFileSize(Handle, @Size.HighPart);
  4006. Result := Size.QuadPart;
  4007. end;
  4008. {$ENDIF MSWINDOWS}
  4009. function GetSizeOfFile(const FileInfo: TSearchRec): Int64;
  4010. {$IFDEF MSWINDOWS}
  4011. begin
  4012. Int64Rec(Result).Lo := FileInfo.FindData.nFileSizeLow;
  4013. Int64Rec(Result).Hi := FileInfo.FindData.nFileSizeHigh;
  4014. end;
  4015. {$ENDIF MSWINDOWS}
  4016. {$IFDEF UNIX}
  4017. var
  4018. Buf: TStatBuf64;
  4019. begin
  4020. // rr: Note that SysUtils.FindFirst/Next ignore files >= 2 GB under Linux,
  4021. // thus the following code is rather pointless at the moment of this writing.
  4022. // We apparently need to write our own set of Findxxx functions to overcome this limitation.
  4023. if GetFileStatus(FileInfo.PathOnly + FileInfo.Name, Buf, True) <> 0 then
  4024. Result := -1
  4025. else
  4026. Result := Buf.st_size
  4027. end;
  4028. {$ENDIF UNIX}
  4029. {$IFDEF MSWINDOWS}
  4030. {$IFDEF FPC}
  4031. { TODO : Move this over to JclWin32 when JclWin32 gets overhauled. }
  4032. function GetFileAttributesEx(lpFileName: PChar;
  4033. fInfoLevelId: TGetFileExInfoLevels; lpFileInformation: Pointer): BOOL; stdcall;
  4034. external kernel32 name 'GetFileAttributesExA';
  4035. {$ENDIF FPC}
  4036. function GetStandardFileInfo(const FileName: string): TWin32FileAttributeData;
  4037. var
  4038. Handle: THandle;
  4039. FileInfo: TByHandleFileInformation;
  4040. begin
  4041. Assert(FileName <> '');
  4042. { TODO : Use RTDL-Version of GetFileAttributesEx }
  4043. if IsWin95 or IsWin95OSR2 or IsWinNT3 then
  4044. begin
  4045. Handle := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  4046. if Handle <> INVALID_HANDLE_VALUE then
  4047. try
  4048. FileInfo.dwFileAttributes := 0;
  4049. if not GetFileInformationByHandle(Handle, FileInfo) then
  4050. raise EJclFileUtilsError.CreateResFmt(@RsFileUtilsAttrUnavailable, [FileName]);
  4051. Result.dwFileAttributes := FileInfo.dwFileAttributes;
  4052. Result.ftCreationTime := FileInfo.ftCreationTime;
  4053. Result.ftLastAccessTime := FileInfo.ftLastAccessTime;
  4054. Result.ftLastWriteTime := FileInfo.ftLastWriteTime;
  4055. Result.nFileSizeHigh := FileInfo.nFileSizeHigh;
  4056. Result.nFileSizeLow := FileInfo.nFileSizeLow;
  4057. finally
  4058. CloseHandle(Handle);
  4059. end
  4060. else
  4061. raise EJclFileUtilsError.CreateResFmt(@RsFileUtilsAttrUnavailable, [FileName]);
  4062. end
  4063. else
  4064. begin
  4065. if not GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @Result) then
  4066. raise EJclFileUtilsError.CreateResFmt(@RsFileUtilsAttrUnavailable, [FileName]);
  4067. end;
  4068. end;
  4069. {$ENDIF MSWINDOWS}
  4070. {$IFDEF MSWINDOWS}
  4071. function IsDirectory(const FileName: string): Boolean;
  4072. var
  4073. R: DWORD;
  4074. begin
  4075. R := GetFileAttributes(PChar(FileName));
  4076. Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) <> 0);
  4077. end;
  4078. {$ENDIF MSWINDOWS}
  4079. {$IFDEF UNIX}
  4080. function IsDirectory(const FileName: string; ResolveSymLinks: Boolean): Boolean;
  4081. var
  4082. Buf: TStatBuf64;
  4083. begin
  4084. Result := False;
  4085. if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
  4086. Result := S_ISDIR(Buf.st_mode);
  4087. end;
  4088. {$ENDIF UNIX}
  4089. function IsRootDirectory(const CanonicFileName: string): Boolean;
  4090. {$IFDEF MSWINDOWS}
  4091. var
  4092. I: Integer;
  4093. begin
  4094. I := Pos(':\', CanonicFileName);
  4095. Result := (I > 0) and (I + 1 = Length(CanonicFileName));
  4096. end;
  4097. {$ENDIF MSWINDOWS}
  4098. {$IFDEF UNIX}
  4099. begin
  4100. Result := CanonicFileName = DirDelimiter;
  4101. end;
  4102. {$ENDIF UNIX}
  4103. {$IFDEF MSWINDOWS}
  4104. function LockVolume(const Volume: string; var Handle: THandle): Boolean;
  4105. var
  4106. BytesReturned: DWORD;
  4107. begin
  4108. Result := False;
  4109. Handle := CreateFile(PChar('\\.\' + Volume), GENERIC_READ or GENERIC_WRITE,
  4110. FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,
  4111. FILE_FLAG_NO_BUFFERING, 0);
  4112. if Handle <> INVALID_HANDLE_VALUE then
  4113. begin
  4114. BytesReturned := 0;
  4115. Result := DeviceIoControl(Handle, FSCTL_LOCK_VOLUME, nil, 0, nil, 0,
  4116. BytesReturned, nil);
  4117. if not Result then
  4118. begin
  4119. CloseHandle(Handle);
  4120. Handle := INVALID_HANDLE_VALUE;
  4121. end;
  4122. end;
  4123. end;
  4124. function OpenVolume(const Drive: Char): THandle;
  4125. var
  4126. VolumeName: array [0..6] of Char;
  4127. begin
  4128. VolumeName := '\\.\A:';
  4129. VolumeName[4] := Drive;
  4130. Result := CreateFile(VolumeName, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE,
  4131. nil, OPEN_EXISTING, 0, 0);
  4132. end;
  4133. {$ENDIF MSWINDOWS}
  4134. type
  4135. // indicates the file time to set, used by SetFileTimesHelper and SetDirTimesHelper
  4136. TFileTimes = (ftLastAccess, ftLastWrite {$IFDEF MSWINDOWS}, ftCreation {$ENDIF});
  4137. {$IFDEF MSWINDOWS}
  4138. function SetFileTimesHelper(const FileName: string; const DateTime: TDateTime; Times: TFileTimes): Boolean;
  4139. var
  4140. Handle: THandle;
  4141. FileTime: TFileTime;
  4142. SystemTime: TSystemTime;
  4143. begin
  4144. Result := False;
  4145. Handle := CreateFile(PChar(FileName), GENERIC_WRITE, FILE_SHARE_READ, nil,
  4146. OPEN_EXISTING, 0, 0);
  4147. if Handle <> INVALID_HANDLE_VALUE then
  4148. try
  4149. //SysUtils.DateTimeToSystemTime(DateTimeToLocalDateTime(DateTime), SystemTime);
  4150. {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.DateTimeToSystemTime(DateTime, SystemTime);
  4151. FileTime.dwLowDateTime := 0;
  4152. FileTime.dwHighDateTime := 0;
  4153. if {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SystemTimeToFileTime(SystemTime, FileTime) then
  4154. begin
  4155. case Times of
  4156. ftLastAccess:
  4157. Result := SetFileTime(Handle, nil, @FileTime, nil);
  4158. ftLastWrite:
  4159. Result := SetFileTime(Handle, nil, nil, @FileTime);
  4160. ftCreation:
  4161. Result := SetFileTime(Handle, @FileTime, nil, nil);
  4162. end;
  4163. end;
  4164. finally
  4165. CloseHandle(Handle);
  4166. end;
  4167. end;
  4168. {$ENDIF MSWINDOWS}
  4169. {$IFDEF UNIX}
  4170. function SetFileTimesHelper(const FileName: string; const DateTime: TDateTime; Times: TFileTimes): Boolean;
  4171. var
  4172. FileTime: Integer;
  4173. StatBuf: TStatBuf64;
  4174. TimeBuf: utimbuf;
  4175. begin
  4176. Result := False;
  4177. FileTime := DateTimeToFileDate(DateTime);
  4178. if GetFileStatus(FileName, StatBuf, False) = 0 then
  4179. begin
  4180. TimeBuf.actime := StatBuf.st_atime;
  4181. TimeBuf.modtime := StatBuf.st_mtime;
  4182. case Times of
  4183. ftLastAccess:
  4184. TimeBuf.actime := FileTime;
  4185. ftLastWrite:
  4186. TimeBuf.modtime := FileTime;
  4187. end;
  4188. Result := utime(PChar(FileName), @TimeBuf) = 0;
  4189. end;
  4190. end;
  4191. {$ENDIF UNIX}
  4192. function SetFileLastAccess(const FileName: string; const DateTime: TDateTime): Boolean;
  4193. begin
  4194. Result := SetFileTimesHelper(FileName, DateTime, ftLastAccess);
  4195. end;
  4196. function SetFileLastWrite(const FileName: string; const DateTime: TDateTime): Boolean;
  4197. begin
  4198. Result := SetFileTimesHelper(FileName, DateTime, ftLastWrite);
  4199. end;
  4200. {$IFDEF MSWINDOWS}
  4201. function SetFileCreation(const FileName: string; const DateTime: TDateTime): Boolean;
  4202. begin
  4203. Result := SetFileTimesHelper(FileName, DateTime, ftCreation);
  4204. end;
  4205. // utility function for SetDirTimesHelper
  4206. function BackupPrivilegesEnabled: Boolean;
  4207. begin
  4208. Result := IsPrivilegeEnabled(SE_BACKUP_NAME) and IsPrivilegeEnabled(SE_RESTORE_NAME);
  4209. end;
  4210. function SetDirTimesHelper(const DirName: string; const DateTime: TDateTime;
  4211. Times: TFileTimes; RequireBackupRestorePrivileges: Boolean): Boolean;
  4212. var
  4213. Handle: THandle;
  4214. FileTime: TFileTime;
  4215. SystemTime: TSystemTime;
  4216. begin
  4217. Result := False;
  4218. if IsDirectory(DirName) and (not RequireBackupRestorePrivileges or BackupPrivilegesEnabled) then
  4219. begin
  4220. Handle := CreateFile(PChar(DirName), GENERIC_WRITE, FILE_SHARE_READ, nil,
  4221. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
  4222. if Handle <> INVALID_HANDLE_VALUE then
  4223. try
  4224. {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.DateTimeToSystemTime(DateTime, SystemTime);
  4225. FileTime.dwLowDateTime := 0;
  4226. FileTime.dwHighDateTime := 0;
  4227. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SystemTimeToFileTime(SystemTime, FileTime);
  4228. case Times of
  4229. ftLastAccess:
  4230. Result := SetFileTime(Handle, nil, @FileTime, nil);
  4231. ftLastWrite:
  4232. Result := SetFileTime(Handle, nil, nil, @FileTime);
  4233. ftCreation:
  4234. Result := SetFileTime(Handle, @FileTime, nil, nil);
  4235. end;
  4236. finally
  4237. CloseHandle(Handle);
  4238. end;
  4239. end;
  4240. end;
  4241. function SetDirLastWrite(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
  4242. begin
  4243. Result := SetDirTimesHelper(DirName, DateTime, ftLastWrite, RequireBackupRestorePrivileges);
  4244. end;
  4245. function SetDirLastAccess(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
  4246. begin
  4247. Result := SetDirTimesHelper(DirName, DateTime, ftLastAccess, RequireBackupRestorePrivileges);
  4248. end;
  4249. function SetDirCreation(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
  4250. begin
  4251. Result := SetDirTimesHelper(DirName, DateTime, ftCreation, RequireBackupRestorePrivileges);
  4252. end;
  4253. procedure FillByteArray(var Bytes: array of Byte; Count: Cardinal; B: Byte);
  4254. begin
  4255. FillMemory(@Bytes[0], Count, B);
  4256. end;
  4257. procedure ShredFile(const FileName: string; Times: Integer);
  4258. const
  4259. BUFSIZE = 4096;
  4260. ODD_FILL = $C1;
  4261. EVEN_FILL = $3E;
  4262. var
  4263. Fs: TFileStream;
  4264. Size: Integer;
  4265. N: Integer;
  4266. ContentPtr: array of Byte;
  4267. begin
  4268. Size := FileGetSize(FileName);
  4269. if Size > 0 then
  4270. begin
  4271. if Times < 0 then
  4272. Times := 2
  4273. else
  4274. Times := Times * 2;
  4275. ContentPtr := nil;
  4276. Fs := TFileStream.Create(FileName, fmOpenReadWrite);
  4277. try
  4278. SetLength(ContentPtr, BUFSIZE);
  4279. while Times > 0 do
  4280. begin
  4281. if Times mod 2 = 0 then
  4282. FillByteArray(ContentPtr, BUFSIZE, EVEN_FILL)
  4283. else
  4284. FillByteArray(ContentPtr, BUFSIZE, ODD_FILL);
  4285. Fs.Seek(0, soBeginning);
  4286. N := Size div BUFSIZE;
  4287. while N > 0 do
  4288. begin
  4289. Fs.Write(ContentPtr[0], BUFSIZE);
  4290. Dec(N);
  4291. end;
  4292. N := Size mod BUFSIZE;
  4293. if N > 0 then
  4294. Fs.Write(ContentPtr[0], N);
  4295. FlushFileBuffers(Fs.Handle);
  4296. Dec(Times);
  4297. end;
  4298. finally
  4299. ContentPtr := nil;
  4300. Fs.Free;
  4301. DeleteFile(FileName);
  4302. end;
  4303. end
  4304. else
  4305. DeleteFile(FileName);
  4306. end;
  4307. function UnlockVolume(var Handle: THandle): Boolean;
  4308. var
  4309. BytesReturned: DWORD;
  4310. begin
  4311. Result := False;
  4312. if Handle <> INVALID_HANDLE_VALUE then
  4313. begin
  4314. BytesReturned := 0;
  4315. Result := DeviceIoControl(Handle, FSCTL_UNLOCK_VOLUME, nil, 0, nil, 0,
  4316. BytesReturned, nil);
  4317. if Result then
  4318. begin
  4319. CloseHandle(Handle);
  4320. Handle := INVALID_HANDLE_VALUE;
  4321. end;
  4322. end;
  4323. end;
  4324. {$ENDIF MSWINDOWS}
  4325. {$IFDEF UNIX}
  4326. function CreateSymbolicLink(const Name, Target: string): Boolean;
  4327. begin
  4328. Result := symlink(PChar(Target), PChar(Name)) = 0;
  4329. end;
  4330. function SymbolicLinkTarget(const Name: string): string;
  4331. var
  4332. N, BufLen: Integer;
  4333. begin
  4334. BufLen := 128;
  4335. repeat
  4336. Inc(BufLen, BufLen);
  4337. SetLength(Result, BufLen);
  4338. N := readlink(PChar(Name), PChar(Result), BufLen);
  4339. if N < 0 then // Error
  4340. begin
  4341. Result := '';
  4342. Exit;
  4343. end;
  4344. until N < BufLen;
  4345. SetLength(Result, N);
  4346. end;
  4347. {$ENDIF UNIX}
  4348. //=== File Version info routines =============================================
  4349. {$IFDEF MSWINDOWS}
  4350. const
  4351. VerKeyNames: array [1..12] of string =
  4352. ('Comments',
  4353. 'CompanyName',
  4354. 'FileDescription',
  4355. 'FileVersion',
  4356. 'InternalName',
  4357. 'LegalCopyright',
  4358. 'LegalTradeMarks',
  4359. 'OriginalFilename',
  4360. 'ProductName',
  4361. 'ProductVersion',
  4362. 'SpecialBuild',
  4363. 'PrivateBuild');
  4364. function OSIdentToString(const OSIdent: DWORD): string;
  4365. begin
  4366. case OSIdent of
  4367. VOS_UNKNOWN:
  4368. Result := LoadResString(@RsVosUnknown);
  4369. VOS_DOS:
  4370. Result := LoadResString(@RsVosDos);
  4371. VOS_OS216:
  4372. Result := LoadResString(@RsVosOS216);
  4373. VOS_OS232:
  4374. Result := LoadResString(@RsVosOS232);
  4375. VOS_NT:
  4376. Result := LoadResString(@RsVosNT);
  4377. VOS__WINDOWS16:
  4378. Result := LoadResString(@RsVosWindows16);
  4379. VOS__PM16:
  4380. Result := LoadResString(@RsVosPM16);
  4381. VOS__PM32:
  4382. Result := LoadResString(@RsVosPM32);
  4383. VOS__WINDOWS32:
  4384. Result := LoadResString(@RsVosWindows32);
  4385. VOS_DOS_WINDOWS16:
  4386. Result := LoadResString(@RsVosDosWindows16);
  4387. VOS_DOS_WINDOWS32:
  4388. Result := LoadResString(@RsVosDosWindows32);
  4389. VOS_OS216_PM16:
  4390. Result := LoadResString(@RsVosOS216PM16);
  4391. VOS_OS232_PM32:
  4392. Result := LoadResString(@RsVosOS232PM32);
  4393. VOS_NT_WINDOWS32:
  4394. Result := LoadResString(@RsVosNTWindows32);
  4395. else
  4396. Result := '';
  4397. end;
  4398. if Result = '' then
  4399. Result := LoadResString(@RsVosUnknown)
  4400. else
  4401. Result := Format(LoadResString(@RsVosDesignedFor), [Result]);
  4402. end;
  4403. function OSFileTypeToString(const OSFileType: DWORD; const OSFileSubType: DWORD): string;
  4404. begin
  4405. case OSFileType of
  4406. VFT_UNKNOWN:
  4407. Result := LoadResString(@RsVftUnknown);
  4408. VFT_APP:
  4409. Result := LoadResString(@RsVftApp);
  4410. VFT_DLL:
  4411. Result := LoadResString(@RsVftDll);
  4412. VFT_DRV:
  4413. begin
  4414. case OSFileSubType of
  4415. VFT2_DRV_PRINTER:
  4416. Result := LoadResString(@RsVft2DrvPRINTER);
  4417. VFT2_DRV_KEYBOARD:
  4418. Result := LoadResString(@RsVft2DrvKEYBOARD);
  4419. VFT2_DRV_LANGUAGE:
  4420. Result := LoadResString(@RsVft2DrvLANGUAGE);
  4421. VFT2_DRV_DISPLAY:
  4422. Result := LoadResString(@RsVft2DrvDISPLAY);
  4423. VFT2_DRV_MOUSE:
  4424. Result := LoadResString(@RsVft2DrvMOUSE);
  4425. VFT2_DRV_NETWORK:
  4426. Result := LoadResString(@RsVft2DrvNETWORK);
  4427. VFT2_DRV_SYSTEM:
  4428. Result := LoadResString(@RsVft2DrvSYSTEM);
  4429. VFT2_DRV_INSTALLABLE:
  4430. Result := LoadResString(@RsVft2DrvINSTALLABLE);
  4431. VFT2_DRV_SOUND:
  4432. Result := LoadResString(@RsVft2DrvSOUND);
  4433. VFT2_DRV_COMM:
  4434. Result := LoadResString(@RsVft2DrvCOMM);
  4435. else
  4436. Result := '';
  4437. end;
  4438. Result := Result + ' ' + LoadResString(@RsVftDrv);
  4439. end;
  4440. VFT_FONT:
  4441. begin
  4442. case OSFileSubType of
  4443. VFT2_FONT_RASTER:
  4444. Result := LoadResString(@RsVft2FontRASTER);
  4445. VFT2_FONT_VECTOR:
  4446. Result := LoadResString(@RsVft2FontVECTOR);
  4447. VFT2_FONT_TRUETYPE:
  4448. Result := LoadResString(@RsVft2FontTRUETYPE);
  4449. else
  4450. Result := '';
  4451. end;
  4452. Result := Result + ' ' + LoadResString(@RsVftFont);
  4453. end;
  4454. VFT_VXD:
  4455. Result := LoadResString(@RsVftVxd);
  4456. VFT_STATIC_LIB:
  4457. Result := LoadResString(@RsVftStaticLib);
  4458. else
  4459. Result := '';
  4460. end;
  4461. Result := TrimLeft(Result);
  4462. end;
  4463. function VersionResourceAvailable(const FileName: string): Boolean;
  4464. var
  4465. Size: DWORD;
  4466. Handle: DWORD;
  4467. Buffer: string;
  4468. begin
  4469. Result := False;
  4470. Handle := 0;
  4471. Size := GetFileVersionInfoSize(PChar(FileName), Handle);
  4472. if Size > 0 then
  4473. begin
  4474. SetLength(Buffer, Size);
  4475. Result := GetFileVersionInfo(PChar(FileName), Handle, Size, PChar(Buffer));
  4476. end;
  4477. end;
  4478. function VersionResourceAvailable(const Window: HWND): Boolean;
  4479. begin
  4480. Result := VersionResourceAvailable(WindowToModuleFileName(Window));
  4481. end;
  4482. function VersionResourceAvailable(const Module: HMODULE): Boolean;
  4483. begin
  4484. if Module <> 0 then
  4485. Result :=VersionResourceAvailable(GetModulePath(Module))
  4486. else
  4487. raise EJclError.CreateResFmt(@RsEModuleNotValid, [Module]);
  4488. end;
  4489. function WindowToModuleFileName(const Window: HWND): string;
  4490. type
  4491. {$IFDEF SUPPORTS_UNICODE}
  4492. TGetModuleFileNameEx = function(hProcess: THandle; hModule: HMODULE; FileName: PWideChar; nSize: DWORD): DWORD; stdcall;
  4493. TQueryFullProcessImageName = function(HProcess: THandle; dwFlags: DWORD; lpExeName: PWideChar; lpdwSize: PDWORD): BOOL; stdcall;
  4494. {$ELSE ~SUPPORTS_UNICODE}
  4495. TGetModuleFileNameEx = function(hProcess: THandle; hModule: HMODULE; FileName: PAnsiChar; nSize: DWORD): DWORD; stdcall;
  4496. TQueryFullProcessImageName = function(HProcess: THandle; dwFlags: DWORD; lpExeName: PAnsiChar; lpdwSize: PDWORD): BOOL; stdcall;
  4497. {$ENDIF ~SUPPORTS_UNICODE}
  4498. var
  4499. FileName: array[0..300] of Char;
  4500. DllHinst: HMODULE;
  4501. ProcessID: DWORD;
  4502. HProcess: THandle;
  4503. GetModuleFileNameExAddress: TGetModuleFileNameEx;
  4504. QueryFullProcessImageNameAddress: TQueryFullProcessImageName;
  4505. Len: DWORD;
  4506. begin
  4507. Result := '';
  4508. if Window <> 0 then
  4509. begin
  4510. if not JclCheckWinVersion(5, 0) then // Win2k or newer required
  4511. raise EJclWin32Error.CreateRes(@RsEWindowsVersionNotSupported);
  4512. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetWindowThreadProcessId(Window, @ProcessID);
  4513. hProcess := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, ProcessID);
  4514. if hProcess <> 0 then
  4515. begin
  4516. try
  4517. if JclCheckWinVersion(6, 0) then // WinVista or newer
  4518. begin
  4519. DllHinst := LoadLibrary('Kernel32.dll');
  4520. if DllHinst <> 0 then
  4521. begin
  4522. try
  4523. {$IFDEF SUPPORTS_UNICODE}
  4524. QueryFullProcessImageNameAddress := GetProcAddress(DllHinst, 'QueryFullProcessImageNameW');
  4525. {$ELSE ~SUPPORTS_UNICODE}
  4526. QueryFullProcessImageNameAddress := GetProcAddress(DllHinst, 'QueryFullProcessImageNameA');
  4527. {$ENDIF ~SUPPORTS_UNICODE}
  4528. if Assigned(QueryFullProcessImageNameAddress) then
  4529. begin
  4530. Len := Length(FileName);
  4531. if QueryFullProcessImageNameAddress(hProcess, 0, FileName, PDWORD(@Len)) then
  4532. Result := FileName;
  4533. //else
  4534. // RaiseLastOSError would be nice, but it didn't raise an exception before the return value was checked
  4535. end
  4536. else
  4537. raise EJclError.CreateResFmt(@RsEFunctionNotFound, ['Kernel32.dll', 'QueryFullProcessImageName']);
  4538. finally
  4539. FreeLibrary(DllHinst);
  4540. end;
  4541. end
  4542. else
  4543. raise EJclError.CreateResFmt(@RsELibraryNotFound, ['Kernel32.dll']);
  4544. end
  4545. else
  4546. begin
  4547. DllHinst := LoadLibrary('Psapi.dll');
  4548. if DllHinst <> 0 then
  4549. begin
  4550. try
  4551. {$IFDEF SUPPORTS_UNICODE}
  4552. GetModuleFileNameExAddress := GetProcAddress(DllHinst, 'GetModuleFileNameExW');
  4553. {$ELSE ~SUPPORTS_UNICODE}
  4554. GetModuleFileNameExAddress := GetProcAddress(DllHinst, 'GetModuleFileNameExA');
  4555. {$ENDIF ~SUPPORTS_UNICODE}
  4556. if Assigned(GetModuleFileNameExAddress) then
  4557. begin
  4558. Len := GetModuleFileNameExAddress(hProcess, 0, FileName, Length(FileName));
  4559. if Len > 0 then
  4560. Result := FileName;
  4561. //else
  4562. // RaiseLastOSError; would be nice, but it didn't raise an exception before the return value was checked
  4563. end
  4564. else
  4565. raise EJclError.CreateResFmt(@RsEFunctionNotFound, ['Psapi.dll', 'GetModuleFileNameEx']);
  4566. finally
  4567. FreeLibrary(DllHinst);
  4568. end;
  4569. end
  4570. else
  4571. raise EJclError.CreateResFmt(@RsELibraryNotFound, ['Psapi.dll']);
  4572. end;
  4573. finally
  4574. CloseHandle(hProcess);
  4575. end;
  4576. end
  4577. else
  4578. raise EJclError.CreateResFmt(@RsEProcessNotValid, [ProcessID]);
  4579. end
  4580. else
  4581. raise EJclError.CreateResFmt(@RsEWindowNotValid, [Window]);
  4582. end;
  4583. {$ENDIF MSWINDOWS}
  4584. // Version Info formatting
  4585. function FormatVersionString(const HiV, LoV: Word): string;
  4586. begin
  4587. Result := Format('%u.%.2u', [HiV, LoV]);
  4588. end;
  4589. function FormatVersionString(const Major, Minor, Build, Revision: Word): string;
  4590. begin
  4591. Result := Format('%u.%u.%u.%u', [Major, Minor, Build, Revision]);
  4592. end;
  4593. {$IFDEF MSWINDOWS}
  4594. function FormatVersionString(const FixedInfo: TVSFixedFileInfo; VersionFormat: TFileVersionFormat): string;
  4595. begin
  4596. case VersionFormat of
  4597. vfMajorMinor:
  4598. Result := Format('%u.%u', [HiWord(FixedInfo.dwFileVersionMS), LoWord(FixedInfo.dwFileVersionMS)]);
  4599. vfFull:
  4600. Result := Format('%u.%u.%u.%u', [HiWord(FixedInfo.dwFileVersionMS), LoWord(FixedInfo.dwFileVersionMS),
  4601. HiWord(FixedInfo.dwFileVersionLS), LoWord(FixedInfo.dwFileVersionLS)]);
  4602. end;
  4603. end;
  4604. // Version Info extracting
  4605. procedure VersionExtractFileInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
  4606. begin
  4607. Major := HiWord(FixedInfo.dwFileVersionMS);
  4608. Minor := LoWord(FixedInfo.dwFileVersionMS);
  4609. Build := HiWord(FixedInfo.dwFileVersionLS);
  4610. Revision := LoWord(FixedInfo.dwFileVersionLS);
  4611. end;
  4612. procedure VersionExtractProductInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
  4613. begin
  4614. Major := HiWord(FixedInfo.dwProductVersionMS);
  4615. Minor := LoWord(FixedInfo.dwProductVersionMS);
  4616. Build := HiWord(FixedInfo.dwProductVersionLS);
  4617. Revision := LoWord(FixedInfo.dwProductVersionLS);
  4618. end;
  4619. // Fixed Version Info routines
  4620. function VersionFixedFileInfo(const FileName: string; var FixedInfo: TVSFixedFileInfo): Boolean;
  4621. var
  4622. Size, FixInfoLen: DWORD;
  4623. Handle: DWORD;
  4624. Buffer: string;
  4625. FixInfoBuf: PVSFixedFileInfo;
  4626. begin
  4627. Result := False;
  4628. Handle := 0;
  4629. Size := GetFileVersionInfoSize(PChar(FileName), Handle);
  4630. if Size > 0 then
  4631. begin
  4632. SetLength(Buffer, Size);
  4633. FixInfoLen := 0;
  4634. FixInfoBuf := nil;
  4635. if GetFileVersionInfo(PChar(FileName), Handle, Size, Pointer(Buffer)) and
  4636. VerQueryValue(Pointer(Buffer), DirDelimiter, Pointer(FixInfoBuf), FixInfoLen) and
  4637. (FixInfoLen = SizeOf(TVSFixedFileInfo)) then
  4638. begin
  4639. Result := True;
  4640. FixedInfo := FixInfoBuf^;
  4641. end;
  4642. end;
  4643. end;
  4644. function VersionFixedFileInfoString(const FileName: string; VersionFormat: TFileVersionFormat;
  4645. const NotAvailableText: string): string;
  4646. var
  4647. FixedInfo: TVSFixedFileInfo;
  4648. begin
  4649. FixedInfo.dwSignature := 0;
  4650. if VersionFixedFileInfo(FileName, FixedInfo) then
  4651. Result := FormatVersionString(FixedInfo, VersionFormat)
  4652. else
  4653. Result := NotAvailableText;
  4654. end;
  4655. //=== { TJclFileVersionInfo } ================================================
  4656. constructor TJclFileVersionInfo.Attach(VersionInfoData: Pointer; Size: Integer);
  4657. begin
  4658. SetLength(FBuffer, Size);
  4659. CopyMemory(PAnsiChar(FBuffer), VersionInfoData, Size);
  4660. ExtractData;
  4661. end;
  4662. constructor TJclFileVersionInfo.Create(const FileName: string);
  4663. var
  4664. Handle: DWORD;
  4665. Size: DWORD;
  4666. begin
  4667. if not FileExists(FileName) then
  4668. raise EJclFileVersionInfoError.CreateResFmt(@RsFileUtilsFileDoesNotExist, [FileName]);
  4669. Handle := 0;
  4670. Size := GetFileVersionInfoSize(PChar(FileName), Handle);
  4671. if Size = 0 then
  4672. raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsNoVersionInfo);
  4673. SetLength(FBuffer, Size);
  4674. Win32Check(GetFileVersionInfo(PChar(FileName), Handle, Size, PAnsiChar(FBuffer)));
  4675. ExtractData;
  4676. end;
  4677. {$IFDEF MSWINDOWS}
  4678. {$IFDEF FPC}
  4679. constructor TJclFileVersionInfo.Create(const Window: HWND; Dummy: Pointer = nil);
  4680. {$ELSE}
  4681. constructor TJclFileVersionInfo.Create(const Window: HWND);
  4682. {$ENDIF}
  4683. begin
  4684. Create(WindowToModuleFileName(Window));
  4685. end;
  4686. constructor TJclFileVersionInfo.Create(const Module: HMODULE);
  4687. begin
  4688. if Module <> 0 then
  4689. Create(GetModulePath(Module))
  4690. else
  4691. raise EJclError.CreateResFmt(@RsEModuleNotValid, [Module]);
  4692. end;
  4693. {$ENDIF MSWINDOWS}
  4694. destructor TJclFileVersionInfo.Destroy;
  4695. begin
  4696. FreeAndNil(FItemList);
  4697. FreeAndNil(FItems);
  4698. inherited Destroy;
  4699. end;
  4700. class function TJclFileVersionInfo.FileHasVersionInfo(const FileName: string): boolean;
  4701. var
  4702. Dummy: DWord;
  4703. begin
  4704. Result := GetFileVersionInfoSize(PChar(FileName), Dummy) <> 0;
  4705. end;
  4706. procedure TJclFileVersionInfo.CheckLanguageIndex(Value: Integer);
  4707. begin
  4708. if (Value < 0) or (Value >= LanguageCount) then
  4709. raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsLanguageIndex);
  4710. end;
  4711. procedure TJclFileVersionInfo.CreateItemsForLanguage;
  4712. var
  4713. I: Integer;
  4714. begin
  4715. Items.Clear;
  4716. for I := 0 to FItemList.Count - 1 do
  4717. if Integer(FItemList.Objects[I]) = FLanguageIndex then
  4718. Items.AddObject(FItemList[I], Pointer(FLanguages[FLanguageIndex].Pair));
  4719. end;
  4720. procedure TJclFileVersionInfo.ExtractData;
  4721. var
  4722. Data, EndOfData: PAnsiChar;
  4723. Len, ValueLen, DataType: Word;
  4724. HeaderSize: Integer;
  4725. Key: string;
  4726. Error, IsUnicode: Boolean;
  4727. procedure Padding(var DataPtr: PAnsiChar);
  4728. begin
  4729. while TJclAddr(DataPtr) and 3 <> 0 do
  4730. Inc(DataPtr);
  4731. end;
  4732. procedure GetHeader;
  4733. var
  4734. P: PAnsiChar;
  4735. TempKey: PWideChar;
  4736. begin
  4737. Key := '';
  4738. P := Data;
  4739. Len := PWord(P)^;
  4740. if Len = 0 then
  4741. begin
  4742. // do not raise error in the case of resources padded with 0
  4743. while P < EndOfData do
  4744. begin
  4745. Error := P^ <> #0;
  4746. if Error then
  4747. Break;
  4748. Inc(P);
  4749. end;
  4750. Exit;
  4751. end;
  4752. Inc(P, SizeOf(Word));
  4753. ValueLen := PWord(P)^;
  4754. Inc(P, SizeOf(Word));
  4755. if IsUnicode then
  4756. begin
  4757. DataType := PWord(P)^;
  4758. Inc(P, SizeOf(Word));
  4759. TempKey := PWideChar(P);
  4760. Inc(P, (lstrlenW(TempKey) + 1) * SizeOf(WideChar)); // length + #0#0
  4761. Key := TempKey;
  4762. end
  4763. else
  4764. begin
  4765. DataType := 1;
  4766. Key := string(PAnsiChar(P));
  4767. Inc(P, lstrlenA(PAnsiChar(P)) + 1);
  4768. end;
  4769. Padding(P);
  4770. HeaderSize := P - Data;
  4771. Data := P;
  4772. end;
  4773. procedure FixKeyValue;
  4774. const
  4775. HexNumberCPrefix = '0x';
  4776. var
  4777. I: Integer;
  4778. begin // GAPI32.DLL version 5.5.2803.1 contanins '04050x04E2' value
  4779. repeat
  4780. I := Pos(HexNumberCPrefix, Key);
  4781. if I > 0 then
  4782. Delete(Key, I, Length(HexNumberCPrefix));
  4783. until I = 0;
  4784. I := 1;
  4785. while I <= Length(Key) do
  4786. if CharIsHexDigit(Key[I]) then
  4787. Inc(I)
  4788. else
  4789. Delete(Key, I, 1);
  4790. // Office16\1031\GrooveIntlResource.dll contains a '4094B0' key. Both parts (lang and codepage)
  4791. // are missing their leading zero. It should have been '040904B0'.
  4792. // The Windows file property dialog falls back to "English (United States) 1252", so do we.
  4793. if Length(Key) < 8 then
  4794. Key := '040904E4';
  4795. end;
  4796. procedure ProcessStringInfo(Size: Integer);
  4797. var
  4798. EndPtr, EndStringPtr: PAnsiChar;
  4799. LangIndex: Integer;
  4800. LangIdRec: TLangIdRec;
  4801. Value: string;
  4802. begin
  4803. EndPtr := Data + Size;
  4804. LangIndex := 0;
  4805. while not Error and (Data < EndPtr) do
  4806. begin
  4807. GetHeader; // StringTable
  4808. FixKeyValue;
  4809. if (ValueLen <> 0) or (Length(Key) <> 8) then
  4810. begin
  4811. Error := True;
  4812. Break;
  4813. end;
  4814. Padding(Data);
  4815. LangIdRec.LangId := StrToIntDef('$' + Copy(Key, 1, 4), 0);
  4816. LangIdRec.CodePage := StrToIntDef('$' + Copy(Key, 5, 4), 0);
  4817. SetLength(FLanguages, LangIndex + 1);
  4818. FLanguages[LangIndex] := LangIdRec;
  4819. EndStringPtr := Data + Len - HeaderSize;
  4820. while not Error and (Data < EndStringPtr) do
  4821. begin
  4822. GetHeader; // string
  4823. case DataType of
  4824. 0:
  4825. if ValueLen in [1..4] then
  4826. Value := Format('$%.*x', [ValueLen * 2, PInteger(Data)^])
  4827. else
  4828. begin
  4829. if (ValueLen > 0) and IsUnicode then
  4830. Value:=PWideChar(Data)
  4831. else
  4832. Value := '';
  4833. end;
  4834. 1:
  4835. if ValueLen = 0 then
  4836. Value := ''
  4837. else
  4838. if IsUnicode then
  4839. begin
  4840. Value := WideCharLenToString(PWideChar(Data), ValueLen);
  4841. StrResetLength(Value);
  4842. end
  4843. else
  4844. Value := string(PAnsiChar(Data));
  4845. else
  4846. Error := True;
  4847. Break;
  4848. end;
  4849. Inc(Data, Len - HeaderSize);
  4850. Padding(Data); // String.Padding
  4851. FItemList.AddObject(Format('%s=%s', [Key, Value]), Pointer(LangIndex));
  4852. end;
  4853. Inc(LangIndex);
  4854. end;
  4855. end;
  4856. procedure ProcessVarInfo;
  4857. var
  4858. TranslationIndex: Integer;
  4859. begin
  4860. GetHeader; // Var
  4861. if SameText(Key, 'Translation') then
  4862. begin
  4863. SetLength(FTranslations, ValueLen div SizeOf(TLangIdRec));
  4864. for TranslationIndex := 0 to Length(FTranslations) - 1 do
  4865. begin
  4866. FTranslations[TranslationIndex] := PLangIdRec(Data)^;
  4867. Inc(Data, SizeOf(TLangIdRec));
  4868. end;
  4869. end;
  4870. end;
  4871. begin
  4872. FItemList := TStringList.Create;
  4873. FItems := TStringList.Create;
  4874. Data := Pointer(FBuffer);
  4875. Assert(TJclAddr(Data) mod 4 = 0);
  4876. IsUnicode := (PWord(Data + 4)^ in [0, 1]);
  4877. Error := True;
  4878. GetHeader;
  4879. EndOfData := Data + Len - HeaderSize;
  4880. if SameText(Key, 'VS_VERSION_INFO') and (ValueLen = SizeOf(TVSFixedFileInfo)) then
  4881. begin
  4882. FFixedInfo := PVSFixedFileInfo(Data);
  4883. Error := FFixedInfo.dwSignature <> $FEEF04BD;
  4884. Inc(Data, ValueLen); // VS_FIXEDFILEINFO
  4885. Padding(Data); // VS_VERSIONINFO.Padding2
  4886. while not Error and (Data < EndOfData) do
  4887. begin
  4888. GetHeader;
  4889. Inc(Data, ValueLen); // some files (VREDIR.VXD 4.00.1111) has non zero value of ValueLen
  4890. Dec(Len, HeaderSize + ValueLen);
  4891. if SameText(Key, 'StringFileInfo') then
  4892. ProcessStringInfo(Len)
  4893. else
  4894. if SameText(Key, 'VarFileInfo') then
  4895. ProcessVarInfo
  4896. else
  4897. Break;
  4898. end;
  4899. ExtractFlags;
  4900. CreateItemsForLanguage;
  4901. end;
  4902. if Error then
  4903. raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsNoVersionInfo);
  4904. end;
  4905. procedure TJclFileVersionInfo.ExtractFlags;
  4906. var
  4907. Masked: DWORD;
  4908. begin
  4909. FFileFlags := [];
  4910. Masked := FFixedInfo^.dwFileFlags and FFixedInfo^.dwFileFlagsMask;
  4911. if (Masked and VS_FF_DEBUG) <> 0 then
  4912. Include(FFileFlags, ffDebug);
  4913. if (Masked and VS_FF_INFOINFERRED) <> 0 then
  4914. Include(FFileFlags, ffInfoInferred);
  4915. if (Masked and VS_FF_PATCHED) <> 0 then
  4916. Include(FFileFlags, ffPatched);
  4917. if (Masked and VS_FF_PRERELEASE) <> 0 then
  4918. Include(FFileFlags, ffPreRelease);
  4919. if (Masked and VS_FF_PRIVATEBUILD) <> 0 then
  4920. Include(FFileFlags, ffPrivateBuild);
  4921. if (Masked and VS_FF_SPECIALBUILD) <> 0 then
  4922. Include(FFileFlags, ffSpecialBuild);
  4923. end;
  4924. function TJclFileVersionInfo.GetBinFileVersion: string;
  4925. begin
  4926. Result := Format('%u.%u.%u.%u', [HiWord(FFixedInfo^.dwFileVersionMS),
  4927. LoWord(FFixedInfo^.dwFileVersionMS), HiWord(FFixedInfo^.dwFileVersionLS),
  4928. LoWord(FFixedInfo^.dwFileVersionLS)]);
  4929. end;
  4930. function TJclFileVersionInfo.GetBinProductVersion: string;
  4931. begin
  4932. Result := Format('%u.%u.%u.%u', [HiWord(FFixedInfo^.dwProductVersionMS),
  4933. LoWord(FFixedInfo^.dwProductVersionMS), HiWord(FFixedInfo^.dwProductVersionLS),
  4934. LoWord(FFixedInfo^.dwProductVersionLS)]);
  4935. end;
  4936. function TJclFileVersionInfo.GetCustomFieldValue(const FieldName: string): string;
  4937. var
  4938. ItemIndex: Integer;
  4939. begin
  4940. if FieldName <> '' then
  4941. begin
  4942. ItemIndex := FItems.IndexOfName(FieldName);
  4943. if ItemIndex <> -1 then
  4944. //Return the required value, the value the user passed in was found.
  4945. Result := FItems.Values[FieldName]
  4946. else
  4947. raise EJclFileVersionInfoError.CreateResFmt(@RsFileUtilsValueNotFound, [FieldName]);
  4948. end
  4949. else
  4950. raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsEmptyValue);
  4951. end;
  4952. function TJclFileVersionInfo.GetFileOS: DWORD;
  4953. begin
  4954. Result := FFixedInfo^.dwFileOS;
  4955. end;
  4956. function TJclFileVersionInfo.GetFileSubType: DWORD;
  4957. begin
  4958. Result := FFixedInfo^.dwFileSubtype;
  4959. end;
  4960. function TJclFileVersionInfo.GetFileType: DWORD;
  4961. begin
  4962. Result := FFixedInfo^.dwFileType;
  4963. end;
  4964. function TJclFileVersionInfo.GetFileVersionBuild: string;
  4965. var
  4966. Left: Integer;
  4967. begin
  4968. Result := FileVersion;
  4969. StrReplaceChar(Result, ',', '.');
  4970. Left := CharLastPos(Result, '.') + 1;
  4971. Result := StrMid(Result, Left, Length(Result) - Left + 1);
  4972. Result := Trim(Result);
  4973. end;
  4974. function TJclFileVersionInfo.GetFileVersionMajor: string;
  4975. begin
  4976. Result := FileVersion;
  4977. StrReplaceChar(Result, ',', '.');
  4978. Result := StrBefore('.', Result);
  4979. Result := Trim(Result);
  4980. end;
  4981. function TJclFileVersionInfo.GetFileVersionMinor: string;
  4982. var
  4983. Left, Right: integer;
  4984. begin
  4985. Result := FileVersion;
  4986. StrReplaceChar(Result, ',', '.');
  4987. Left := CharPos(Result, '.') + 1; // skip major
  4988. Right := CharPos(Result, '.', Left) {-1};
  4989. Result := StrMid(Result, Left, Right - Left {+1});
  4990. Result := Trim(Result);
  4991. end;
  4992. function TJclFileVersionInfo.GetFileVersionRelease: string;
  4993. var
  4994. Left, Right: Integer;
  4995. begin
  4996. Result := FileVersion;
  4997. StrReplaceChar(Result, ',', '.');
  4998. Left := CharPos(Result, '.') + 1; // skip major
  4999. Left := CharPos(Result, '.', Left) + 1; // skip minor
  5000. Right := CharPos(Result, '.', Left) {-1};
  5001. Result := StrMid(Result, Left, Right - Left {+1});
  5002. Result := Trim(Result);
  5003. end;
  5004. function TJclFileVersionInfo.GetFixedInfo: TVSFixedFileInfo;
  5005. begin
  5006. Result := FFixedInfo^;
  5007. end;
  5008. function TJclFileVersionInfo.GetItems: TStrings;
  5009. begin
  5010. Result := FItems;
  5011. end;
  5012. function TJclFileVersionInfo.GetLanguageCount: Integer;
  5013. begin
  5014. Result := Length(FLanguages);
  5015. end;
  5016. function TJclFileVersionInfo.GetLanguageIds(Index: Integer): string;
  5017. begin
  5018. CheckLanguageIndex(Index);
  5019. Result := VersionLanguageId(FLanguages[Index]);
  5020. end;
  5021. function TJclFileVersionInfo.GetLanguages(Index: Integer): TLangIdRec;
  5022. begin
  5023. CheckLanguageIndex(Index);
  5024. Result := FLanguages[Index];
  5025. end;
  5026. function TJclFileVersionInfo.GetLanguageNames(Index: Integer): string;
  5027. begin
  5028. CheckLanguageIndex(Index);
  5029. Result := VersionLanguageName(FLanguages[Index].LangId);
  5030. end;
  5031. function TJclFileVersionInfo.GetTranslationCount: Integer;
  5032. begin
  5033. Result := Length(FTranslations);
  5034. end;
  5035. function TJclFileVersionInfo.GetTranslations(Index: Integer): TLangIdRec;
  5036. begin
  5037. Result := FTranslations[Index];
  5038. end;
  5039. function TJclFileVersionInfo.GetProductVersionBuild: string;
  5040. var
  5041. Left: Integer;
  5042. begin
  5043. Result := ProductVersion;
  5044. StrReplaceChar(Result, ',', '.');
  5045. Left := CharLastPos(Result, '.') + 1;
  5046. Result := StrMid(Result, Left, Length(Result) - Left + 1);
  5047. Result := Trim(Result);
  5048. end;
  5049. function TJclFileVersionInfo.GetProductVersionMajor: string;
  5050. begin
  5051. Result := ProductVersion;
  5052. StrReplaceChar(Result, ',', '.');
  5053. Result := StrBefore('.', Result);
  5054. Result := Trim(Result);
  5055. end;
  5056. function TJclFileVersionInfo.GetProductVersionMinor: string;
  5057. var
  5058. Left, Right: integer;
  5059. begin
  5060. Result := ProductVersion;
  5061. StrReplaceChar(Result, ',', '.');
  5062. Left := CharPos(Result, '.') + 1; // skip major
  5063. Right := CharPos(Result, '.', Left) {-1};
  5064. Result := StrMid(Result, Left, Right - Left {+1});
  5065. Result := Trim(Result);
  5066. end;
  5067. function TJclFileVersionInfo.GetProductVersionRelease: string;
  5068. var
  5069. Left, Right: Integer;
  5070. begin
  5071. Result := ProductVersion;
  5072. StrReplaceChar(Result, ',', '.');
  5073. Left := CharPos(Result, '.') + 1; // skip major
  5074. Left := CharPos(Result, '.', Left) + 1; // skip minor
  5075. Right := CharPos(Result, '.', Left) {-1};
  5076. Result := StrMid(Result, Left, Right - Left {+1});
  5077. Result := Trim(Result);
  5078. end;
  5079. function TJclFileVersionInfo.GetVersionKeyValue(Index: Integer): string;
  5080. begin
  5081. Result := Items.Values[VerKeyNames[Index]];
  5082. end;
  5083. procedure TJclFileVersionInfo.SetLanguageIndex(const Value: Integer);
  5084. begin
  5085. CheckLanguageIndex(Value);
  5086. if FLanguageIndex <> Value then
  5087. begin
  5088. FLanguageIndex := Value;
  5089. CreateItemsForLanguage;
  5090. end;
  5091. end;
  5092. function TJclFileVersionInfo.TranslationMatchesLanguages(Exact: Boolean): Boolean;
  5093. var
  5094. TransIndex, LangIndex: Integer;
  5095. TranslationPair: DWORD;
  5096. begin
  5097. Result := (LanguageCount = TranslationCount) or (not Exact and (TranslationCount > 0));
  5098. if Result then
  5099. for TransIndex := 0 to TranslationCount - 1 do
  5100. begin
  5101. TranslationPair := FTranslations[TransIndex].Pair;
  5102. LangIndex := LanguageCount - 1;
  5103. while (LangIndex >= 0) and (TranslationPair <> FLanguages[LangIndex].Pair) do
  5104. Dec(LangIndex);
  5105. if LangIndex < 0 then
  5106. begin
  5107. Result := False;
  5108. Break;
  5109. end;
  5110. end;
  5111. end;
  5112. class function TJclFileVersionInfo.VersionLanguageId(const LangIdRec: TLangIdRec): string;
  5113. begin
  5114. with LangIdRec do
  5115. Result := Format('%.4x%.4x', [LangId, CodePage]);
  5116. end;
  5117. class function TJclFileVersionInfo.VersionLanguageName(const LangId: Word): string;
  5118. var
  5119. R: DWORD;
  5120. begin
  5121. SetLength(Result, MAX_PATH);
  5122. R := VerLanguageName(LangId, PChar(Result), MAX_PATH);
  5123. SetLength(Result, R);
  5124. end;
  5125. {$ENDIF MSWINDOWS}
  5126. //=== { TJclFileMaskComparator } =============================================
  5127. constructor TJclFileMaskComparator.Create;
  5128. begin
  5129. inherited Create;
  5130. FSeparator := DirSeparator;
  5131. end;
  5132. function TJclFileMaskComparator.Compare(const NameExt: string): Boolean;
  5133. var
  5134. I: Integer;
  5135. NamePart, ExtPart: string;
  5136. NameWild, ExtWild: Boolean;
  5137. begin
  5138. Result := False;
  5139. I := StrLastPos('.', NameExt);
  5140. if I = 0 then
  5141. begin
  5142. NamePart := NameExt;
  5143. ExtPart := '';
  5144. end
  5145. else
  5146. begin
  5147. NamePart := Copy(NameExt, 1, I - 1);
  5148. ExtPart := Copy(NameExt, I + 1, Length(NameExt));
  5149. end;
  5150. for I := 0 to Length(FNames) - 1 do
  5151. begin
  5152. NameWild := FWildChars[I] and 1 = 1;
  5153. ExtWild := FWildChars[I] and 2 = 2;
  5154. if ((not NameWild and StrSame(FNames[I], NamePart)) or
  5155. (NameWild and (StrMatches(FNames[I], NamePart, 1)))) and
  5156. ((not ExtWild and StrSame(FExts[I], ExtPart)) or
  5157. (ExtWild and (StrMatches(FExts[I], ExtPart, 1)))) then
  5158. begin
  5159. Result := True;
  5160. Break;
  5161. end;
  5162. end;
  5163. end;
  5164. procedure TJclFileMaskComparator.CreateMultiMasks;
  5165. var
  5166. List: TStringList;
  5167. I, N: Integer;
  5168. NS, ES: string;
  5169. begin
  5170. FExts := nil;
  5171. FNames := nil;
  5172. FWildChars := nil;
  5173. List := TStringList.Create;
  5174. try
  5175. StrToStrings(FFileMask, FSeparator, List);
  5176. SetLength(FExts, List.Count);
  5177. SetLength(FNames, List.Count);
  5178. SetLength(FWildChars, List.Count);
  5179. for I := 0 to List.Count - 1 do
  5180. begin
  5181. N := StrLastPos('.', List[I]);
  5182. if N = 0 then
  5183. begin
  5184. NS := List[I];
  5185. ES := '';
  5186. end
  5187. else
  5188. begin
  5189. NS := Copy(List[I], 1, N - 1);
  5190. ES := Copy(List[I], N + 1, 255);
  5191. end;
  5192. FNames[I] := NS;
  5193. FExts[I] := ES;
  5194. N := 0;
  5195. if StrContainsChars(NS, CharIsWildcard, False) then
  5196. N := N or 1;
  5197. if StrContainsChars(ES, CharIsWildcard, False) then
  5198. N := N or 2;
  5199. FWildChars[I] := N;
  5200. end;
  5201. finally
  5202. List.Free;
  5203. end;
  5204. end;
  5205. function TJclFileMaskComparator.GetCount: Integer;
  5206. begin
  5207. Result := Length(FWildChars);
  5208. end;
  5209. function TJclFileMaskComparator.GetExts(Index: Integer): string;
  5210. begin
  5211. Result := FExts[Index];
  5212. end;
  5213. function TJclFileMaskComparator.GetMasks(Index: Integer): string;
  5214. begin
  5215. Result := FNames[Index] + '.' + FExts[Index];
  5216. end;
  5217. function TJclFileMaskComparator.GetNames(Index: Integer): string;
  5218. begin
  5219. Result := FNames[Index];
  5220. end;
  5221. procedure TJclFileMaskComparator.SetFileMask(const Value: string);
  5222. begin
  5223. FFileMask := Value;
  5224. CreateMultiMasks;
  5225. end;
  5226. procedure TJclFileMaskComparator.SetSeparator(const Value: Char);
  5227. begin
  5228. if FSeparator <> Value then
  5229. begin
  5230. FSeparator := Value;
  5231. CreateMultiMasks;
  5232. end;
  5233. end;
  5234. function AdvBuildFileList(const Path: string; const Attr: Integer; const Files: TStrings;
  5235. const AttributeMatch: TJclAttributeMatch; const Options: TFileListOptions;
  5236. const SubfoldersMask: string; const FileMatchFunc: TFileMatchFunc): Boolean;
  5237. var
  5238. FileMask: string;
  5239. RootDir: string;
  5240. Folders: TStringList;
  5241. CurrentItem: Integer;
  5242. Counter: Integer;
  5243. FindAttr: Integer;
  5244. procedure BuildFolderList;
  5245. var
  5246. FindInfo: TSearchRec;
  5247. Rslt: Integer;
  5248. begin
  5249. Counter := Folders.Count - 1;
  5250. CurrentItem := 0;
  5251. while CurrentItem <= Counter do
  5252. begin
  5253. // searching for subfolders (including hidden ones)
  5254. Rslt := FindFirst(Folders[CurrentItem] + '*.*', faAnyFile, FindInfo);
  5255. try
  5256. while Rslt = 0 do
  5257. begin
  5258. if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') and
  5259. (FindInfo.Attr and faDirectory = faDirectory) then
  5260. Folders.Add(Folders[CurrentItem] + FindInfo.Name + DirDelimiter);
  5261. Rslt := FindNext(FindInfo);
  5262. end;
  5263. finally
  5264. FindClose(FindInfo);
  5265. end;
  5266. Counter := Folders.Count - 1;
  5267. Inc(CurrentItem);
  5268. end;
  5269. end;
  5270. procedure FillFileList(CurrentCounter: Integer);
  5271. var
  5272. FindInfo: TSearchRec;
  5273. Rslt: Integer;
  5274. CurrentFolder: string;
  5275. Matches: Boolean;
  5276. begin
  5277. CurrentFolder := Folders[CurrentCounter];
  5278. Rslt := FindFirst(CurrentFolder + FileMask, FindAttr, FindInfo);
  5279. try
  5280. while Rslt = 0 do
  5281. begin
  5282. Matches := False;
  5283. case AttributeMatch of
  5284. amAny:
  5285. Matches := True;
  5286. amExact:
  5287. Matches := Attr = FindInfo.Attr;
  5288. amSubSetOf:
  5289. Matches := (Attr and FindInfo.Attr) = Attr;
  5290. amSuperSetOf:
  5291. Matches := (Attr and FindInfo.Attr) = FindInfo.Attr;
  5292. amCustom:
  5293. if Assigned(FileMatchFunc) then
  5294. Matches := FileMatchFunc(Attr, FindInfo);
  5295. end;
  5296. if Matches then
  5297. if flFullNames in Options then
  5298. Files.Add(CurrentFolder + FindInfo.Name)
  5299. else
  5300. Files.Add(FindInfo.Name);
  5301. Rslt := FindNext(FindInfo);
  5302. end;
  5303. finally
  5304. FindClose(FindInfo);
  5305. end;
  5306. end;
  5307. begin
  5308. Assert(Assigned(Files));
  5309. FileMask := ExtractFileName(Path);
  5310. RootDir := ExtractFilePath(Path);
  5311. Folders := TStringList.Create;
  5312. Files.BeginUpdate;
  5313. try
  5314. Folders.Add(RootDir);
  5315. case AttributeMatch of
  5316. amExact, amSuperSetOf:
  5317. FindAttr := Attr;
  5318. else
  5319. FindAttr := faAnyFile;
  5320. end;
  5321. // here's the recursive search for nested folders
  5322. if flRecursive in Options then
  5323. BuildFolderList;
  5324. for Counter := 0 to Folders.Count - 1 do
  5325. begin
  5326. if (((flMaskedSubfolders in Options) and (StrMatches(SubfoldersMask,
  5327. Folders[Counter], 1))) or (not (flMaskedSubfolders in Options))) then
  5328. FillFileList(Counter);
  5329. end;
  5330. finally
  5331. Folders.Free;
  5332. Files.EndUpdate;
  5333. end;
  5334. Result := True;
  5335. end;
  5336. function VerifyFileAttributeMask(var RejectedAttributes, RequiredAttributes: Integer): Boolean;
  5337. begin
  5338. if RequiredAttributes and faNormalFile <> 0 then
  5339. RejectedAttributes := not faNormalFile or RejectedAttributes;
  5340. Result := RequiredAttributes and RejectedAttributes = 0;
  5341. end;
  5342. function AttributeMatch(FileAttributes, RejectedAttr, RequiredAttr: Integer): Boolean;
  5343. begin
  5344. if FileAttributes = 0 then
  5345. FileAttributes := faNormalFile;
  5346. {$IFDEF MSWINDOWS}
  5347. RequiredAttr := RequiredAttr and not faUnixSpecific;
  5348. {$ENDIF MSWINDOWS}
  5349. {$IFDEF UNIX}
  5350. RequiredAttr := RequiredAttr and not faWindowsSpecific;
  5351. {$ENDIF UNIX}
  5352. Result := (FileAttributes and RejectedAttr = 0)
  5353. and (FileAttributes and RequiredAttr = RequiredAttr);
  5354. end;
  5355. function IsFileAttributeMatch(FileAttributes, RejectedAttributes,
  5356. RequiredAttributes: Integer): Boolean;
  5357. begin
  5358. VerifyFileAttributeMask(RejectedAttributes, RequiredAttributes);
  5359. Result := AttributeMatch(FileAttributes, RejectedAttributes, RequiredAttributes);
  5360. end;
  5361. function FileAttributesStr(const FileInfo: TSearchRec): string;
  5362. {$IFDEF MSWINDOWS}
  5363. const
  5364. SAllAttrSet = 'rahs'; // readonly, archive, hidden, system
  5365. Attributes: array [1..4] of Integer =
  5366. (faReadOnly, faArchive, faHidden, faSysFile);
  5367. var
  5368. I: Integer;
  5369. begin
  5370. Result := SAllAttrSet;
  5371. for I := Low(Attributes) to High(Attributes) do
  5372. if (FileInfo.Attr and Attributes[I]) = 0 then
  5373. Result[I] := '-';
  5374. end;
  5375. {$ENDIF MSWINDOWS}
  5376. {$IFDEF UNIX}
  5377. const
  5378. SAllAttrSet = 'drwxrwxrwx';
  5379. var
  5380. I: Integer;
  5381. Flag: Cardinal;
  5382. begin
  5383. Result := SAllAttrSet;
  5384. if FileInfo.Attr and faDirectory = 0 then
  5385. Result[1] := '-'; // no directory
  5386. Flag := 1 shl 8;
  5387. for I := 2 to 10 do
  5388. begin
  5389. if FileInfo.Mode and Flag = 0 then
  5390. Result[I] := '-';
  5391. Flag := Flag shr 1;
  5392. end;
  5393. end;
  5394. {$ENDIF UNIX}
  5395. function IsFileNameMatch(FileName: string; const Mask: string;
  5396. const CaseSensitive: Boolean): Boolean;
  5397. begin
  5398. Result := True;
  5399. {$IFDEF MSWINDOWS}
  5400. if (Mask = '') or (Mask = '*') or (Mask = '*.*') then
  5401. Exit;
  5402. if Pos('.', FileName) = 0 then
  5403. FileName := FileName + '.'; // file names w/o extension match '*.'
  5404. {$ENDIF MSWINDOWS}
  5405. {$IFDEF UNIX}
  5406. if (Mask = '') or (Mask = '*') then
  5407. Exit;
  5408. {$ENDIF UNIX}
  5409. if CaseSensitive then
  5410. Result := StrMatches(Mask, FileName)
  5411. else
  5412. Result := StrMatches(AnsiUpperCase(Mask), AnsiUpperCase(FileName));
  5413. end;
  5414. // author: Robert Rossmair
  5415. function CanonicalizedSearchPath(const Directory: string): string;
  5416. begin
  5417. Result := PathCanonicalize(Directory);
  5418. {$IFDEF MSWINDOWS}
  5419. // avoid changing "X:" (current directory on drive X:) into "X:\" (root dir.)
  5420. if Result[Length(Result)] <> ':' then
  5421. {$ENDIF MSWINDOWS}
  5422. Result := PathAddSeparator(Result);
  5423. // strip leading "./" resp. ".\"
  5424. if Pos('.' + DirDelimiter, Result) = 1 then
  5425. Result := Copy(Result, 3, Length(Result) - 2);
  5426. end;
  5427. procedure EnumFiles(const Path: string; HandleFile: TFileHandlerEx;
  5428. RejectedAttributes: Integer; RequiredAttributes: Integer; Abort: PBoolean);
  5429. var
  5430. Directory: string;
  5431. FileInfo: TSearchRec;
  5432. Attr: Integer;
  5433. Found: Boolean;
  5434. begin
  5435. Assert(Assigned(HandleFile));
  5436. Assert(VerifyFileAttributeMask(RejectedAttributes, RequiredAttributes),
  5437. LoadResString(@RsFileSearchAttrInconsistency));
  5438. Directory := ExtractFilePath(Path);
  5439. Attr := faAnyFile and not RejectedAttributes;
  5440. Found := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Path, Attr, FileInfo) = 0;
  5441. try
  5442. while Found do
  5443. begin
  5444. if (Abort <> nil) and LongBool(Abort^) then
  5445. Exit;
  5446. if AttributeMatch(FileInfo.Attr, RejectedAttributes, RequiredAttributes) then
  5447. if ((FileInfo.Attr and faDirectory = 0)
  5448. or ((FileInfo.Name <> '.') and (FileInfo.Name <> '..'))) then
  5449. HandleFile(Directory, FileInfo);
  5450. Found := FindNext(FileInfo) = 0;
  5451. end;
  5452. finally
  5453. FindClose(FileInfo);
  5454. end;
  5455. end;
  5456. procedure EnumFiles(const Path: string; HandleFile: TFileInfoHandlerEx;
  5457. RejectedAttributes: Integer; RequiredAttributes: Integer; Abort: PBoolean);
  5458. var
  5459. FileInfo: TSearchRec;
  5460. Attr: Integer;
  5461. Found: Boolean;
  5462. begin
  5463. Assert(Assigned(HandleFile));
  5464. Assert(VerifyFileAttributeMask(RejectedAttributes, RequiredAttributes),
  5465. LoadResString(@RsFileSearchAttrInconsistency));
  5466. Attr := faAnyFile and not RejectedAttributes;
  5467. Found := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Path, Attr, FileInfo) = 0;
  5468. try
  5469. while Found do
  5470. begin
  5471. if (Abort <> nil) and LongBool(Abort^) then
  5472. Exit;
  5473. if AttributeMatch(FileInfo.Attr, RejectedAttributes, RequiredAttributes) then
  5474. if ((FileInfo.Attr and faDirectory = 0)
  5475. or ((FileInfo.Name <> '.') and (FileInfo.Name <> '..'))) then
  5476. HandleFile(FileInfo);
  5477. Found := FindNext(FileInfo) = 0;
  5478. end;
  5479. finally
  5480. FindClose(FileInfo);
  5481. end;
  5482. end;
  5483. procedure EnumDirectories(const Root: string; const HandleDirectory: TFileHandler;
  5484. const IncludeHiddenDirectories: Boolean; const SubDirectoriesMask: string;
  5485. Abort: PBoolean {$IFDEF UNIX}; ResolveSymLinks: Boolean {$ENDIF});
  5486. var
  5487. RootDir: string;
  5488. Attr: Integer;
  5489. procedure Process(const Directory: string);
  5490. var
  5491. DirInfo: TSearchRec;
  5492. SubDir: string;
  5493. Found: Boolean;
  5494. begin
  5495. HandleDirectory(Directory);
  5496. Found := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Directory + '*', Attr, DirInfo) = 0;
  5497. try
  5498. while Found do
  5499. begin
  5500. if (Abort <> nil) and LongBool(Abort^) then
  5501. Exit;
  5502. if (DirInfo.Name <> '.') and (DirInfo.Name <> '..') and
  5503. {$IFDEF UNIX}
  5504. (IncludeHiddenDirectories or (Pos('.', DirInfo.Name) <> 1)) and
  5505. ((DirInfo.Attr and faSymLink = 0) or ResolveSymLinks) and
  5506. {$ENDIF UNIX}
  5507. (DirInfo.Attr and faDirectory <> 0) then
  5508. begin
  5509. SubDir := Directory + DirInfo.Name + DirDelimiter;
  5510. if (SubDirectoriesMask = '') or StrMatches(SubDirectoriesMask, SubDir, Length(RootDir)) then
  5511. Process(SubDir);
  5512. end;
  5513. Found := FindNext(DirInfo) = 0;
  5514. end;
  5515. finally
  5516. FindClose(DirInfo);
  5517. end;
  5518. end;
  5519. begin
  5520. Assert(Assigned(HandleDirectory));
  5521. RootDir := CanonicalizedSearchPath(Root);
  5522. if IncludeHiddenDirectories then
  5523. Attr := faDirectory + faHidden // no effect on Linux
  5524. else
  5525. Attr := faDirectory;
  5526. Process(RootDir);
  5527. end;
  5528. //=== { TJclCustomFileAttributeMask } ==============================================
  5529. constructor TJclCustomFileAttrMask.Create;
  5530. begin
  5531. inherited Create;
  5532. FRejectedAttr := faRejectedByDefault;
  5533. end;
  5534. procedure TJclCustomFileAttrMask.Assign(Source: TPersistent);
  5535. begin
  5536. if Source is TJclCustomFileAttrMask then
  5537. begin
  5538. Required := TJclCustomFileAttrMask(Source).Required;
  5539. Rejected := TJclCustomFileAttrMask(Source).Rejected;
  5540. end
  5541. else
  5542. inherited Assign(Source);
  5543. end;
  5544. procedure TJclCustomFileAttrMask.Clear;
  5545. begin
  5546. Rejected := 0;
  5547. Required := 0;
  5548. end;
  5549. procedure TJclCustomFileAttrMask.DefineProperties(Filer: TFiler);
  5550. var
  5551. Ancestor: TJclCustomFileAttrMask;
  5552. Attr: Integer;
  5553. begin
  5554. Attr := 0;
  5555. Ancestor := TJclCustomFileAttrMask(Filer.Ancestor);
  5556. if Assigned(Ancestor) then
  5557. Attr := Ancestor.FRequiredAttr;
  5558. Filer.DefineProperty('Required', ReadRequiredAttributes, WriteRequiredAttributes,
  5559. Attr <> FRequiredAttr);
  5560. if Assigned(Ancestor) then
  5561. Attr := Ancestor.FRejectedAttr;
  5562. Filer.DefineProperty('Rejected', ReadRejectedAttributes, WriteRejectedAttributes,
  5563. Attr <> FRejectedAttr);
  5564. end;
  5565. function TJclCustomFileAttrMask.Match(FileAttributes: Integer): Boolean;
  5566. begin
  5567. Result := AttributeMatch(FileAttributes, Rejected, Required);
  5568. end;
  5569. function TJclCustomFileAttrMask.Match(const FileInfo: TSearchRec): Boolean;
  5570. begin
  5571. Result := Match(FileInfo.Attr);
  5572. end;
  5573. function TJclCustomFileAttrMask.GetAttr(Index: Integer): TAttributeInterest;
  5574. begin
  5575. if ((FRequiredAttr and Index) <> 0) or (Index = faNormalFile) and
  5576. (FRejectedAttr = not faNormalFile) then
  5577. Result := aiRequired
  5578. else
  5579. if (FRejectedAttr and Index) <> 0 then
  5580. Result := aiRejected
  5581. else
  5582. Result := aiIgnored;
  5583. end;
  5584. procedure TJclCustomFileAttrMask.ReadRejectedAttributes(Reader: TReader);
  5585. begin
  5586. FRejectedAttr := Reader.ReadInteger;
  5587. end;
  5588. procedure TJclCustomFileAttrMask.ReadRequiredAttributes(Reader: TReader);
  5589. begin
  5590. FRequiredAttr := Reader.ReadInteger;
  5591. end;
  5592. procedure TJclCustomFileAttrMask.SetAttr(Index: Integer; const Value: TAttributeInterest);
  5593. begin
  5594. case Value of
  5595. aiIgnored:
  5596. begin
  5597. FRequiredAttr := FRequiredAttr and not Index;
  5598. FRejectedAttr := FRejectedAttr and not Index;
  5599. end;
  5600. aiRejected:
  5601. begin
  5602. FRequiredAttr := FRequiredAttr and not Index;
  5603. FRejectedAttr := FRejectedAttr or Index;
  5604. end;
  5605. aiRequired:
  5606. begin
  5607. if Index = faNormalFile then
  5608. begin
  5609. FRequiredAttr := faNormalFile;
  5610. FRejectedAttr := not faNormalFile;
  5611. end
  5612. else
  5613. begin
  5614. FRequiredAttr := FRequiredAttr or Index;
  5615. FRejectedAttr := FRejectedAttr and not Index;
  5616. end;
  5617. end;
  5618. end;
  5619. end;
  5620. procedure TJclCustomFileAttrMask.WriteRejectedAttributes(Writer: TWriter);
  5621. begin
  5622. Writer.WriteInteger(FRejectedAttr);
  5623. end;
  5624. procedure TJclCustomFileAttrMask.WriteRequiredAttributes(Writer: TWriter);
  5625. begin
  5626. Writer.WriteInteger(FRequiredAttr);
  5627. end;
  5628. //=== { TJclFileAttributeMask } ==============================================
  5629. procedure TJclFileAttributeMask.ReadVolumeID(Reader: TReader);
  5630. begin
  5631. // Nothing, we are not interested in the value of the VolumeID property,
  5632. // this procedure and the associated DefineProperty call are here only
  5633. // to allow reading legacy DFMs that have this property defined.
  5634. end;
  5635. procedure TJclFileAttributeMask.DefineProperties(Filer: TFiler);
  5636. begin
  5637. inherited DefineProperties(Filer);
  5638. Filer.DefineProperty('VolumeID', ReadVolumeID, nil, False);
  5639. end;
  5640. //=== { TJclFileSearchOptions } ==============================================
  5641. constructor TJclFileSearchOptions.Create;
  5642. begin
  5643. inherited Create;
  5644. FAttributeMask := TJclFileAttributeMask.Create;
  5645. FRootDirectories := TStringList.Create;
  5646. FRootDirectories.Add('.');
  5647. FFileMasks := TStringList.Create;
  5648. FFileMasks.Add('*');
  5649. FSubDirectoryMask := '*';
  5650. FOptions := [fsIncludeSubDirectories];
  5651. FLastChangeAfter := MinDateTime;
  5652. FLastChangeBefore := MaxDateTime;
  5653. {$IFDEF UNIX}
  5654. FCaseSensitiveSearch := True;
  5655. {$ENDIF UNIX}
  5656. end;
  5657. destructor TJclFileSearchOptions.Destroy;
  5658. begin
  5659. FAttributeMask.Free;
  5660. FFileMasks.Free;
  5661. FRootDirectories.Free;
  5662. inherited Destroy;
  5663. end;
  5664. procedure TJclFileSearchOptions.Assign(Source: TPersistent);
  5665. var
  5666. Src: TJclFileSearchOptions;
  5667. begin
  5668. if Source is TJclFileSearchOptions then
  5669. begin
  5670. Src := TJclFileSearchOptions(Source);
  5671. FCaseSensitiveSearch := Src.FCaseSensitiveSearch;
  5672. FileMasks.Assign(Src.FileMasks);
  5673. RootDirectory := Src.RootDirectory;
  5674. SubDirectoryMask := Src.SubDirectoryMask;
  5675. AttributeMask := Src.AttributeMask;
  5676. Options := Src.Options;
  5677. FileSizeMin := Src.FileSizeMin;
  5678. FileSizeMax := Src.FileSizeMax;
  5679. LastChangeAfter := Src.LastChangeAfter;
  5680. LastChangeBefore := Src.LastChangeBefore;
  5681. end
  5682. else
  5683. inherited Assign(Source);
  5684. end;
  5685. function TJclFileSearchOptions.GetAttributeMask: TJclFileAttributeMask;
  5686. begin
  5687. Result := FAttributeMask;
  5688. end;
  5689. function TJclFileSearchOptions.GetCaseSensitiveSearch: Boolean;
  5690. begin
  5691. Result := FCaseSensitiveSearch;
  5692. end;
  5693. function TJclFileSearchOptions.GetFileMask: string;
  5694. begin
  5695. Result := StringsToStr(FileMasks, DirSeparator, False);
  5696. end;
  5697. function TJclFileSearchOptions.GetFileMasks: TStrings;
  5698. begin
  5699. Result := FFileMasks;
  5700. end;
  5701. function TJclFileSearchOptions.GetFileSizeMax: Int64;
  5702. begin
  5703. Result := FFileSizeMax;
  5704. end;
  5705. function TJclFileSearchOptions.GetFileSizeMin: Int64;
  5706. begin
  5707. Result := FFileSizeMin;
  5708. end;
  5709. function TJclFileSearchOptions.GetIncludeHiddenSubDirectories: Boolean;
  5710. begin
  5711. Result := fsIncludeHiddenSubDirectories in Options;
  5712. end;
  5713. function TJclFileSearchOptions.GetIncludeSubDirectories: Boolean;
  5714. begin
  5715. Result := fsIncludeSubDirectories in Options;
  5716. end;
  5717. function TJclFileSearchOptions.GetLastChangeAfter: TDateTime;
  5718. begin
  5719. Result := FLastChangeAfter;
  5720. end;
  5721. function TJclFileSearchOptions.GetLastChangeAfterStr: string;
  5722. begin
  5723. Result := DateTimeToStr(LastChangeAfter);
  5724. end;
  5725. function TJclFileSearchOptions.GetLastChangeBefore: TDateTime;
  5726. begin
  5727. Result := FLastChangeBefore;
  5728. end;
  5729. function TJclFileSearchOptions.GetLastChangeBeforeStr: string;
  5730. begin
  5731. Result := DateTimeToStr(LastChangeBefore);
  5732. end;
  5733. function TJclFileSearchOptions.GetOption(
  5734. const Option: TFileSearchOption): Boolean;
  5735. begin
  5736. Result := Option in FOptions;
  5737. end;
  5738. function TJclFileSearchOptions.GetOptions: TFileSearchoptions;
  5739. begin
  5740. Result := FOptions;
  5741. end;
  5742. function TJclFileSearchOptions.GetRootDirectories: TStrings;
  5743. begin
  5744. Result := FRootDirectories;
  5745. end;
  5746. function TJclFileSearchOptions.GetRootDirectory: string;
  5747. begin
  5748. if FRootDirectories.Count = 1 then
  5749. Result := FRootDirectories.Strings[0]
  5750. else
  5751. Result := '';
  5752. end;
  5753. function TJclFileSearchOptions.GetSubDirectoryMask: string;
  5754. begin
  5755. Result := FSubDirectoryMask;
  5756. end;
  5757. function TJclFileSearchOptions.IsLastChangeAfterStored: Boolean;
  5758. begin
  5759. Result := FLastChangeAfter <> MinDateTime;
  5760. end;
  5761. function TJclFileSearchOptions.IsLastChangeBeforeStored: Boolean;
  5762. begin
  5763. Result := FLastChangeBefore <> MaxDateTime;
  5764. end;
  5765. procedure TJclFileSearchOptions.SetAttributeMask(
  5766. const Value: TJclFileAttributeMask);
  5767. begin
  5768. FAttributeMask.Assign(Value);
  5769. end;
  5770. procedure TJclFileSearchOptions.SetCaseSensitiveSearch(const Value: Boolean);
  5771. begin
  5772. FCaseSensitiveSearch := Value;
  5773. end;
  5774. procedure TJclFileSearchOptions.SetFileMask(const Value: string);
  5775. begin
  5776. { TODO : UNIX : ? }
  5777. StrToStrings(Value, DirSeparator, FFileMasks, False);
  5778. end;
  5779. procedure TJclFileSearchOptions.SetFileMasks(const Value: TStrings);
  5780. begin
  5781. FileMasks.Assign(Value);
  5782. end;
  5783. procedure TJclFileSearchOptions.SetFileSizeMax(const Value: Int64);
  5784. begin
  5785. FFileSizeMax := Value;
  5786. end;
  5787. procedure TJclFileSearchOptions.SetFileSizeMin(const Value: Int64);
  5788. begin
  5789. FFileSizeMin := Value;
  5790. end;
  5791. procedure TJclFileSearchOptions.SetIncludeHiddenSubDirectories(
  5792. const Value: Boolean);
  5793. begin
  5794. SetOption(fsIncludeHiddenSubDirectories, Value);
  5795. end;
  5796. procedure TJclFileSearchOptions.SetIncludeSubDirectories(const Value: Boolean);
  5797. begin
  5798. SetOption(fsIncludeSubDirectories, Value);
  5799. end;
  5800. procedure TJclFileSearchOptions.SetLastChangeAfter(const Value: TDateTime);
  5801. begin
  5802. FLastChangeAfter := Value;
  5803. end;
  5804. procedure TJclFileSearchOptions.SetLastChangeAfterStr(const Value: string);
  5805. begin
  5806. if Value = '' then
  5807. LastChangeAfter := MinDateTime
  5808. else
  5809. LastChangeAfter := StrToDateTime(Value);
  5810. end;
  5811. procedure TJclFileSearchOptions.SetLastChangeBefore(const Value: TDateTime);
  5812. begin
  5813. FLastChangeBefore := Value;
  5814. end;
  5815. procedure TJclFileSearchOptions.SetLastChangeBeforeStr(const Value: string);
  5816. begin
  5817. if Value = '' then
  5818. LastChangeBefore := MaxDateTime
  5819. else
  5820. LastChangeBefore := StrToDateTime(Value);
  5821. end;
  5822. procedure TJclFileSearchOptions.SetOption(const Option: TFileSearchOption;
  5823. const Value: Boolean);
  5824. begin
  5825. if Value then
  5826. Include(FOptions, Option)
  5827. else
  5828. Exclude(FOptions, Option);
  5829. end;
  5830. procedure TJclFileSearchOptions.SetOptions(const Value: TFileSearchOptions);
  5831. begin
  5832. FOptions := Value;
  5833. end;
  5834. procedure TJclFileSearchOptions.SetRootDirectories(const Value: TStrings);
  5835. begin
  5836. FRootDirectories.Assign(Value);
  5837. end;
  5838. procedure TJclFileSearchOptions.SetRootDirectory(const Value: string);
  5839. begin
  5840. FRootDirectories.Clear;
  5841. FRootDirectories.Add(Value);
  5842. end;
  5843. procedure TJclFileSearchOptions.SetSubDirectoryMask(const Value: string);
  5844. begin
  5845. FSubDirectoryMask := Value;
  5846. end;
  5847. //=== { TEnumFileThread } ====================================================
  5848. type
  5849. TEnumFileThread = class(TThread)
  5850. private
  5851. FID: TFileSearchTaskID;
  5852. FFileMasks: TStringList;
  5853. FDirectories: TStrings;
  5854. FCurrentDirectory: string;
  5855. FSubDirectoryMask: string;
  5856. FOnEnterDirectory: TFileHandler;
  5857. FFileHandlerEx: TFileHandlerEx;
  5858. FFileHandler: TFileHandler;
  5859. FInternalDirHandler: TFileHandler;
  5860. FInternalFileInfoHandler: TFileInfoHandlerEx;
  5861. FFileInfo: TSearchRec;
  5862. FRejectedAttr: Integer;
  5863. FRequiredAttr: Integer;
  5864. FFileSizeMin: Int64;
  5865. FFileSizeMax: Int64;
  5866. {$IFDEF RTL220_UP}
  5867. FFileTimeMin: TDateTime;
  5868. FFileTimeMax: TDateTime;
  5869. {$ELSE ~RTL220_UP}
  5870. FFileTimeMin: Integer;
  5871. FFileTimeMax: Integer;
  5872. {$ENDIF ~RTL220_UP}
  5873. FSynchronizationMode: TFileEnumeratorSyncMode;
  5874. FIncludeSubDirectories: Boolean;
  5875. FIncludeHiddenSubDirectories: Boolean;
  5876. FNotifyOnTermination: Boolean;
  5877. FCaseSensitiveSearch: Boolean;
  5878. FAllNamesMatch: Boolean;
  5879. procedure EnterDirectory;
  5880. procedure AsyncProcessDirectory(const Directory: string);
  5881. procedure SyncProcessDirectory(const Directory: string);
  5882. procedure AsyncProcessFile(const FileInfo: TSearchRec);
  5883. procedure SyncProcessFile(const FileInfo: TSearchRec);
  5884. function GetDirectories: TStrings;
  5885. function GetFileMasks: TStrings;
  5886. procedure SetDirectories(const Value: TStrings);
  5887. procedure SetFileMasks(const Value: TStrings);
  5888. protected
  5889. procedure DoTerminate; override;
  5890. procedure Execute; override;
  5891. function FileMatch: Boolean;
  5892. function FileNameMatchesMask: Boolean;
  5893. procedure ProcessDirectory;
  5894. procedure ProcessDirFiles;
  5895. procedure ProcessFile;
  5896. property AllNamesMatch: Boolean read FAllNamesMatch;
  5897. property CaseSensitiveSearch: Boolean read FCaseSensitiveSearch write FCaseSensitiveSearch;
  5898. property FileMasks: TStrings read GetFileMasks write SetFileMasks;
  5899. property FileSizeMin: Int64 read FFileSizeMin write FFileSizeMin;
  5900. property FileSizeMax: Int64 read FFileSizeMax write FFileSizeMax;
  5901. {$IFDEF RTL220_UP}
  5902. property FileTimeMin: TDateTime read FFileTimeMin write FFileTimeMin;
  5903. property FileTimeMax: TDateTime read FFileTimeMax write FFileTimeMax;
  5904. {$ELSE ~RTL220_UP}
  5905. property FileTimeMin: Integer read FFileTimeMin write FFileTimeMin;
  5906. property FileTimeMax: Integer read FFileTimeMax write FFileTimeMax;
  5907. {$ENDIF ~RTL220_UP}
  5908. property Directories: TStrings read GetDirectories write SetDirectories;
  5909. property IncludeSubDirectories: Boolean
  5910. read FIncludeSubDirectories write FIncludeSubDirectories;
  5911. property IncludeHiddenSubDirectories: Boolean
  5912. read FIncludeHiddenSubDirectories write FIncludeHiddenSubDirectories;
  5913. property RejectedAttr: Integer read FRejectedAttr write FRejectedAttr;
  5914. property RequiredAttr: Integer read FRequiredAttr write FRequiredAttr;
  5915. property SynchronizationMode: TFileEnumeratorSyncMode
  5916. read FSynchronizationMode write FSynchronizationMode;
  5917. public
  5918. constructor Create;
  5919. destructor Destroy; override;
  5920. property ID: TFileSearchTaskID read FID;
  5921. {$IFDEF FPC} // protected property
  5922. property Terminated;
  5923. {$ENDIF FPC}
  5924. end;
  5925. constructor TEnumFileThread.Create;
  5926. begin
  5927. inherited Create(True);
  5928. FDirectories := TStringList.Create;
  5929. FFileMasks := TStringList.Create;
  5930. {$IFDEF RTL220_UP}
  5931. FFileTimeMin := -MaxDouble;
  5932. FFileTimeMax := MaxDouble;
  5933. {$ELSE ~RTL220_UP}
  5934. FFileTimeMin := Low(FFileInfo.Time);
  5935. FFileTimeMax := High(FFileInfo.Time);
  5936. {$ENDIF ~RTL220_UP}
  5937. FFileSizeMax := High(FFileSizeMax);
  5938. {$IFDEF MSWINDOWS}
  5939. Priority := tpIdle;
  5940. {$ENDIF MSWINDOWS}
  5941. {$IFDEF UNIX}
  5942. {$IFDEF FPC}
  5943. Priority := tpIdle;
  5944. {$ELSE ~FPC}
  5945. Priority := 0;
  5946. {$ENDIF ~FPC}
  5947. {$ENDIF UNIX}
  5948. FreeOnTerminate := True;
  5949. FNotifyOnTermination := True;
  5950. end;
  5951. destructor TEnumFileThread.Destroy;
  5952. begin
  5953. FFileMasks.Free;
  5954. FDirectories.Free;
  5955. inherited Destroy;
  5956. end;
  5957. procedure TEnumFileThread.Execute;
  5958. var
  5959. Index: Integer;
  5960. begin
  5961. if SynchronizationMode = smPerDirectory then
  5962. begin
  5963. FInternalDirHandler := SyncProcessDirectory;
  5964. FInternalFileInfoHandler := AsyncProcessFile;
  5965. end
  5966. else // SynchronizationMode = smPerFile
  5967. begin
  5968. FInternalDirHandler := AsyncProcessDirectory;
  5969. FInternalFileInfoHandler := SyncProcessFile;
  5970. end;
  5971. if FIncludeSubDirectories then
  5972. begin
  5973. for Index := 0 to FDirectories.Count - 1 do
  5974. EnumDirectories(FDirectories.Strings[Index], FInternalDirHandler, FIncludeHiddenSubDirectories,
  5975. FSubDirectoryMask, @Terminated)
  5976. end
  5977. else
  5978. begin
  5979. for Index := 0 to FDirectories.Count - 1 do
  5980. FInternalDirHandler(CanonicalizedSearchPath(FDirectories.Strings[Index]));
  5981. end;
  5982. end;
  5983. procedure TEnumFileThread.DoTerminate;
  5984. begin
  5985. if FNotifyOnTermination then
  5986. inherited DoTerminate;
  5987. end;
  5988. procedure TEnumFileThread.EnterDirectory;
  5989. begin
  5990. FOnEnterDirectory(FCurrentDirectory);
  5991. end;
  5992. procedure TEnumFileThread.ProcessDirectory;
  5993. begin
  5994. if Assigned(FOnEnterDirectory) then
  5995. EnterDirectory;
  5996. ProcessDirFiles;
  5997. end;
  5998. procedure TEnumFileThread.AsyncProcessDirectory(const Directory: string);
  5999. begin
  6000. FCurrentDirectory := Directory;
  6001. if Assigned(FOnEnterDirectory) then
  6002. Synchronize(EnterDirectory);
  6003. ProcessDirFiles;
  6004. end;
  6005. procedure TEnumFileThread.SyncProcessDirectory(const Directory: string);
  6006. begin
  6007. FCurrentDirectory := Directory;
  6008. Synchronize(ProcessDirectory);
  6009. end;
  6010. procedure TEnumFileThread.ProcessDirFiles;
  6011. begin
  6012. EnumFiles(FCurrentDirectory + '*', FInternalFileInfoHandler, FRejectedAttr, FRequiredAttr, @Terminated);
  6013. end;
  6014. function TEnumFileThread.FileMatch: Boolean;
  6015. var
  6016. FileSize: Int64;
  6017. begin
  6018. {$IFDEF RTL220_UP}
  6019. Result := FileNameMatchesMask and (FFileInfo.TimeStamp >= FFileTimeMin) and (FFileInfo.TimeStamp <= FFileTimeMax);
  6020. {$ELSE ~RTL220_UP}
  6021. Result := FileNameMatchesMask and (FFileInfo.Time >= FFileTimeMin) and (FFileInfo.Time <= FFileTimeMax);
  6022. {$ENDIF ~RTL220_UP}
  6023. if Result then
  6024. begin
  6025. FileSize := GetSizeOfFile(FFileInfo);
  6026. Result := (FileSize >= FFileSizeMin) and (FileSize <= FFileSizeMax);
  6027. end;
  6028. end;
  6029. function TEnumFileThread.FileNameMatchesMask: Boolean;
  6030. var
  6031. I: Integer;
  6032. begin
  6033. Result := AllNamesMatch;
  6034. if not Result then
  6035. for I := 0 to FileMasks.Count - 1 do
  6036. if IsFileNameMatch(FFileInfo.Name, FileMasks[I], CaseSensitiveSearch) then
  6037. begin
  6038. Result := True;
  6039. Break;
  6040. end;
  6041. end;
  6042. procedure TEnumFileThread.ProcessFile;
  6043. begin
  6044. if Assigned(FFileHandlerEx) then
  6045. FFileHandlerEx(FCurrentDirectory, FFileInfo)
  6046. else
  6047. FFileHandler(FCurrentDirectory + FFileInfo.Name);
  6048. end;
  6049. procedure TEnumFileThread.AsyncProcessFile(const FileInfo: TSearchRec);
  6050. begin
  6051. FFileInfo := FileInfo;
  6052. if FileMatch then
  6053. ProcessFile;
  6054. end;
  6055. procedure TEnumFileThread.SyncProcessFile(const FileInfo: TSearchRec);
  6056. begin
  6057. FFileInfo := FileInfo;
  6058. if FileMatch then
  6059. Synchronize(ProcessFile);
  6060. end;
  6061. function TEnumFileThread.GetDirectories: TStrings;
  6062. begin
  6063. Result := FDirectories;
  6064. end;
  6065. function TEnumFileThread.GetFileMasks: TStrings;
  6066. begin
  6067. Result := FFileMasks;
  6068. end;
  6069. procedure TEnumFileThread.SetDirectories(const Value: TStrings);
  6070. begin
  6071. FDirectories.Assign(Value);
  6072. end;
  6073. procedure TEnumFileThread.SetFileMasks(const Value: TStrings);
  6074. var
  6075. I: Integer;
  6076. begin
  6077. FAllNamesMatch := Value.Count = 0;
  6078. for I := 0 to Value.Count - 1 do
  6079. if (Value[I] = '*') {$IFDEF MSWINDOWS} or (Value[I] = '*.*') {$ENDIF} then
  6080. begin
  6081. FAllNamesMatch := True;
  6082. Break;
  6083. end;
  6084. if FAllNamesMatch then
  6085. FileMasks.Clear
  6086. else
  6087. FileMasks.Assign(Value);
  6088. end;
  6089. //=== { TJclFileEnumerator } =================================================
  6090. constructor TJclFileEnumerator.Create;
  6091. begin
  6092. inherited Create;
  6093. FTasks := TList.Create;
  6094. end;
  6095. destructor TJclFileEnumerator.Destroy;
  6096. begin
  6097. StopAllTasks(True);
  6098. FTasks.Free;
  6099. inherited Destroy;
  6100. end;
  6101. procedure TJclFileEnumerator.Assign(Source: TPersistent);
  6102. var
  6103. Src: TJclFileEnumerator;
  6104. begin
  6105. if Source is TJclFileEnumerator then
  6106. begin
  6107. Src := TJclFileEnumerator(Source);
  6108. SynchronizationMode := Src.SynchronizationMode;
  6109. OnEnterDirectory := Src.OnEnterDirectory;
  6110. OnTerminateTask := Src.OnTerminateTask;
  6111. end;
  6112. inherited Assign(Source);
  6113. end;
  6114. function TJclFileEnumerator.CreateTask: TThread;
  6115. var
  6116. Task: TEnumFileThread;
  6117. begin
  6118. Task := TEnumFileThread.Create;
  6119. Task.FID := NextTaskID;
  6120. Task.CaseSensitiveSearch := FCaseSensitiveSearch;
  6121. Task.FileMasks := FileMasks;
  6122. Task.Directories := RootDirectories;
  6123. Task.RejectedAttr := AttributeMask.Rejected;
  6124. Task.RequiredAttr := AttributeMask.Required;
  6125. Task.IncludeSubDirectories := IncludeSubDirectories;
  6126. Task.IncludeHiddenSubDirectories := IncludeHiddenSubDirectories;
  6127. if fsMinSize in Options then
  6128. Task.FileSizeMin := FileSizeMin;
  6129. if fsMaxSize in Options then
  6130. Task.FileSizeMax := FileSizeMax;
  6131. if fsLastChangeAfter in Options then
  6132. Task.FFileTimeMin := {$IFDEF RTL220_UP}LastChangeAfter{$ELSE}DateTimeToFileDate(LastChangeAfter){$ENDIF};
  6133. if fsLastChangeBefore in Options then
  6134. Task.FFileTimeMax := {$IFDEF RTL220_UP}LastChangeBefore{$ELSE}DateTimeToFileDate(LastChangeBefore){$ENDIF};
  6135. Task.SynchronizationMode := SynchronizationMode;
  6136. Task.FOnEnterDirectory := OnEnterDirectory;
  6137. Task.OnTerminate := TaskTerminated;
  6138. FTasks.Add(Task);
  6139. if FRefCount > 0 then
  6140. _AddRef;
  6141. Result := Task;
  6142. end;
  6143. function TJclFileEnumerator.FillList(List: TStrings): TFileSearchTaskID;
  6144. begin
  6145. List.BeginUpdate;
  6146. try
  6147. Result := ForEach(List.Append);
  6148. finally
  6149. List.EndUpdate;
  6150. end;
  6151. end;
  6152. function TJclFileEnumerator.ForEach(Handler: TFileHandlerEx): TFileSearchTaskID;
  6153. var
  6154. Task: TEnumFileThread;
  6155. begin
  6156. Task := TEnumFileThread(CreateTask);
  6157. Task.FFileHandlerEx := Handler;
  6158. Result := Task.ID;
  6159. {$IFDEF RTL210_UP}
  6160. Task.Suspended := False;
  6161. {$ELSE ~RTL210_UP}
  6162. Task.Resume;
  6163. {$ENDIF ~RTL210_UP}
  6164. end;
  6165. function TJclFileEnumerator.ForEach(Handler: TFileHandler): TFileSearchTaskID;
  6166. var
  6167. Task: TEnumFileThread;
  6168. begin
  6169. Task := TEnumFileThread(CreateTask);
  6170. Task.FFileHandler := Handler;
  6171. Result := Task.ID;
  6172. {$IFDEF RTL210_UP}
  6173. Task.Suspended := False;
  6174. {$ELSE ~RTL210_UP}
  6175. Task.Resume;
  6176. {$ENDIF ~RTL210_UP}
  6177. end;
  6178. function TJclFileEnumerator.GetRunningTasks: Integer;
  6179. begin
  6180. Result := FTasks.Count;
  6181. end;
  6182. procedure TJclFileEnumerator.StopTask(ID: TFileSearchTaskID);
  6183. var
  6184. Task: TEnumFileThread;
  6185. I: Integer;
  6186. begin
  6187. for I := 0 to FTasks.Count - 1 do
  6188. begin
  6189. Task := TEnumFileThread(FTasks[I]);
  6190. if Task.ID = ID then
  6191. begin
  6192. Task.Terminate;
  6193. Break;
  6194. end;
  6195. end;
  6196. end;
  6197. procedure TJclFileEnumerator.StopAllTasks(Silently: Boolean = False);
  6198. var
  6199. I: Integer;
  6200. begin
  6201. for I := 0 to FTasks.Count - 1 do
  6202. begin
  6203. TEnumFileThread(FTasks[I]).FNotifyOnTermination := not Silently;
  6204. TEnumFileThread(FTasks[I]).Terminate;
  6205. end;
  6206. end;
  6207. procedure TJclFileEnumerator.TaskTerminated(Sender: TObject);
  6208. begin
  6209. FTasks.Remove(Sender);
  6210. try
  6211. if Assigned(FOnTerminateTask) then
  6212. FOnTerminateTask(TEnumFileThread(Sender).ID, TEnumFileThread(Sender).Terminated);
  6213. finally
  6214. if FRefCount > 0 then
  6215. _Release;
  6216. end;
  6217. end;
  6218. function TJclFileEnumerator.GetNextTaskID: TFileSearchTaskID;
  6219. begin
  6220. Result := FNextTaskID;
  6221. Inc(FNextTaskID);
  6222. end;
  6223. function TJclFileEnumerator.GetOnEnterDirectory: TFileHandler;
  6224. begin
  6225. Result := FOnEnterDirectory;
  6226. end;
  6227. function TJclFileEnumerator.GetOnTerminateTask: TFileSearchTerminationEvent;
  6228. begin
  6229. Result := FOnTerminateTask;
  6230. end;
  6231. function TJclFileEnumerator.GetSynchronizationMode: TFileEnumeratorSyncMode;
  6232. begin
  6233. Result := FSynchronizationMode;
  6234. end;
  6235. procedure TJclFileEnumerator.SetOnEnterDirectory(
  6236. const Value: TFileHandler);
  6237. begin
  6238. FOnEnterDirectory := Value;
  6239. end;
  6240. procedure TJclFileEnumerator.SetOnTerminateTask(
  6241. const Value: TFileSearchTerminationEvent);
  6242. begin
  6243. FOnTerminateTask := Value;
  6244. end;
  6245. procedure TJclFileEnumerator.SetSynchronizationMode(
  6246. const Value: TFileEnumeratorSyncMode);
  6247. begin
  6248. FSynchronizationMode := Value;
  6249. end;
  6250. function FileSearch: IJclFileEnumerator;
  6251. begin
  6252. Result := TJclFileEnumerator.Create;
  6253. end;
  6254. function SamePath(const Path1, Path2: string): Boolean;
  6255. begin
  6256. {$IFDEF MSWINDOWS}
  6257. Result := AnsiSameText(PathGetLongName(Path1), PathGetLongName(Path2));
  6258. {$ELSE ~MSWINDOWS}
  6259. Result := Path1 = Path2;
  6260. {$ENDIF ~MSWINDOWS}
  6261. end;
  6262. // add items at the end
  6263. procedure PathListAddItems(var List: string; const Items: string);
  6264. begin
  6265. ListAddItems(List, DirSeparator, Items);
  6266. end;
  6267. // add items at the end if they are not present
  6268. procedure PathListIncludeItems(var List: string; const Items: string);
  6269. var
  6270. StrList, NewItems: TStringList;
  6271. IndexNew, IndexList: Integer;
  6272. Item: string;
  6273. Duplicate: Boolean;
  6274. begin
  6275. StrList := TStringList.Create;
  6276. try
  6277. StrToStrings(List, DirSeparator, StrList);
  6278. NewItems := TStringList.Create;
  6279. try
  6280. StrToStrings(Items, DirSeparator, NewItems);
  6281. for IndexNew := 0 to NewItems.Count - 1 do
  6282. begin
  6283. Item := NewItems.Strings[IndexNew];
  6284. Duplicate := False;
  6285. for IndexList := 0 to StrList.Count - 1 do
  6286. if SamePath(Item, StrList.Strings[IndexList]) then
  6287. begin
  6288. Duplicate := True;
  6289. Break;
  6290. end;
  6291. if not Duplicate then
  6292. StrList.Add(Item);
  6293. end;
  6294. List := StringsToStr(StrList, DirSeparator);
  6295. finally
  6296. NewItems.Free;
  6297. end;
  6298. finally
  6299. StrList.Free;
  6300. end;
  6301. end;
  6302. // delete multiple items
  6303. procedure PathListDelItems(var List: string; const Items: string);
  6304. var
  6305. StrList, RemItems: TStringList;
  6306. IndexRem, IndexList: Integer;
  6307. Item: string;
  6308. begin
  6309. StrList := TStringList.Create;
  6310. try
  6311. StrToStrings(List, DirSeparator, StrList);
  6312. RemItems := TStringList.Create;
  6313. try
  6314. StrToStrings(Items, DirSeparator, RemItems);
  6315. for IndexRem := 0 to RemItems.Count - 1 do
  6316. begin
  6317. Item := RemItems.Strings[IndexRem];
  6318. for IndexList := StrList.Count - 1 downto 0 do
  6319. if SamePath(Item, StrList.Strings[IndexList]) then
  6320. StrList.Delete(IndexList);
  6321. end;
  6322. List := StringsToStr(StrList, DirSeparator);
  6323. finally
  6324. RemItems.Free;
  6325. end;
  6326. finally
  6327. StrList.Free;
  6328. end;
  6329. end;
  6330. // delete one item
  6331. procedure PathListDelItem(var List: string; const Index: Integer);
  6332. begin
  6333. ListDelItem(List, DirSeparator, Index);
  6334. end;
  6335. // return the number of item
  6336. function PathListItemCount(const List: string): Integer;
  6337. begin
  6338. Result := ListItemCount(List, DirSeparator);
  6339. end;
  6340. // return the Nth item
  6341. function PathListGetItem(const List: string; const Index: Integer): string;
  6342. begin
  6343. Result := ListGetItem(List, DirSeparator, Index);
  6344. end;
  6345. // set the Nth item
  6346. procedure PathListSetItem(var List: string; const Index: Integer; const Value: string);
  6347. begin
  6348. ListSetItem(List, DirSeparator, Index, Value);
  6349. end;
  6350. // return the index of an item
  6351. function PathListItemIndex(const List, Item: string): Integer;
  6352. var
  6353. StrList: TStringList;
  6354. IndexList: Integer;
  6355. begin
  6356. StrList := TStringList.Create;
  6357. try
  6358. StrToStrings(List, DirSeparator, StrList);
  6359. Result := -1;
  6360. for IndexList := 0 to StrList.Count - 1 do
  6361. if SamePath(StrList.Strings[IndexList], Item) then
  6362. begin
  6363. Result := IndexList;
  6364. Break;
  6365. end;
  6366. finally
  6367. StrList.Free;
  6368. end;
  6369. end;
  6370. // additional functions to access the commandline parameters of an application
  6371. // returns the name of the command line parameter at position index, which is
  6372. // separated by the given separator, if the first character of the name part
  6373. // is one of the AllowedPrefixCharacters, this character will be deleted.
  6374. function ParamName(Index: Integer; const Separator: string;
  6375. const AllowedPrefixCharacters: string; TrimName: Boolean): string;
  6376. var
  6377. S: string;
  6378. P: Integer;
  6379. begin
  6380. if (Index > 0) and (Index <= ParamCount) then
  6381. begin
  6382. S := ParamStr(Index);
  6383. if Pos(Copy(S, 1, 1), AllowedPrefixCharacters) > 0 then
  6384. S := Copy(S, 2, Length(S) - 1);
  6385. P := Pos(Separator, S);
  6386. if P > 0 then
  6387. S := Copy(S, 1, P - 1);
  6388. if TrimName then
  6389. S := Trim(S);
  6390. Result := S;
  6391. end
  6392. else
  6393. Result := '';
  6394. end;
  6395. // returns the value of the command line parameter at position index, which is
  6396. // separated by the given separator
  6397. function ParamValue(Index: Integer; const Separator: string; TrimValue: Boolean): string;
  6398. var
  6399. S: string;
  6400. P: Integer;
  6401. begin
  6402. if (Index > 0) and (Index <= ParamCount) then
  6403. begin
  6404. S := ParamStr(Index);
  6405. P := Pos(Separator, S);
  6406. if P > 0 then
  6407. S := Copy(S, P + 1, Length(S) - P);
  6408. if TrimValue then
  6409. S := Trim(S);
  6410. Result := S;
  6411. end
  6412. else
  6413. Result := '';
  6414. end;
  6415. // seaches a command line parameter where the namepart is the searchname
  6416. // and returns the value which is which by the given separator.
  6417. // CaseSensitive defines the search type. if the first character of the name part
  6418. // is one of the AllowedPrefixCharacters, this character will be deleted.
  6419. function ParamValue(const SearchName: string; const Separator: string;
  6420. CaseSensitive: Boolean; const AllowedPrefixCharacters: string;
  6421. TrimValue: Boolean): string;
  6422. var
  6423. Name: string;
  6424. SearchS: String;
  6425. I: Integer;
  6426. begin
  6427. Result := '';
  6428. SearchS := Trim(SearchName);
  6429. for I := 1 to ParamCount do
  6430. begin
  6431. Name := ParamName(I, Separator, AllowedPrefixCharacters, True);
  6432. if (CaseSensitive and (Name = SearchS)) or
  6433. ((not CaseSensitive) and (CompareText(Name, SearchS) = 0)) then
  6434. begin
  6435. Result := ParamValue(I, Separator, TrimValue);
  6436. Exit;
  6437. end;
  6438. end;
  6439. end;
  6440. // seaches a command line parameter where the namepart is the searchname
  6441. // and returns the position index. if no separator is defined, the full paramstr is compared.
  6442. // CaseSensitive defines the search type. if the first character of the name part
  6443. // is one of the AllowedPrefixCharacters, this character will be deleted.
  6444. function ParamPos(const SearchName: string; const Separator: string;
  6445. CaseSensitive: Boolean; const AllowedPrefixCharacters: string): Integer;
  6446. var
  6447. Name: string;
  6448. SearchS: string;
  6449. I: Integer;
  6450. begin
  6451. Result := -1;
  6452. SearchS := Trim(SearchName);
  6453. for I := 1 to ParamCount do
  6454. begin
  6455. Name := ParamName(I, Separator, AllowedPrefixCharacters, True);
  6456. if (CaseSensitive and (Name = SearchS)) or
  6457. ((not CaseSensitive) and (CompareText(Name, SearchS) = 0)) then
  6458. begin
  6459. Result := I;
  6460. Exit;
  6461. end;
  6462. end;
  6463. end;
  6464. {$IFDEF UNITVERSIONING}
  6465. initialization
  6466. RegisterUnitVersion(HInstance, UnitVersioning);
  6467. finalization
  6468. UnregisterUnitVersion(HInstance);
  6469. {$ENDIF UNITVERSIONING}
  6470. end.