JclFileUtils.pas 218 KB

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