JclFileUtils.pas 219 KB

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