| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068 |
- {**************************************************************************************************}
- { }
- { Project JEDI Code Library (JCL) }
- { }
- { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
- { you may not use this file except in compliance with the License. You may obtain a copy of the }
- { License at http://www.mozilla.org/MPL/ }
- { }
- { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
- { ANY KIND, either express or implied. See the License for the specific language governing rights }
- { and limitations under the License. }
- { }
- { The Original Code is JclFileUtils.pas. }
- { }
- { The Initial Developer of the Original Code is Marcel van Brakel. }
- { Portions created by Marcel van Brakel are Copyright (C) Marcel van Brakel. All rights reserved. }
- { }
- { Contributors: }
- { Andre Snepvangers (asnepvangers) }
- { Andreas Hausladen (ahuser) }
- { Anthony Steele }
- { Rik Barker (rikbarker) }
- { Azret Botash }
- { Charlie Calvert }
- { David Hervieux }
- { Florent Ouchet (outchy) }
- { Jean-Fabien Connault (cycocrew) }
- { Jens Fudickar (jfudickar) }
- { JohnML }
- { John Molyneux }
- { Marcel Bestebroer }
- { Marcel van Brakel }
- { Massimo Maria Ghisalberti }
- { Matthias Thoma (mthoma) }
- { Olivier Sannier (obones) }
- { Pelle F. S. Liljendal }
- { Robert Marquardt (marquardt) }
- { Robert Rossmair (rrossmair) }
- { Rudy Velthuis }
- { Scott Price }
- { Wim De Cleen }
- { }
- {**************************************************************************************************}
- { }
- { This unit contains routines and classes for working with files, directories and path strings. }
- { Additionally it contains wrapper classes for file mapping objects and version resources. }
- { Generically speaking, everything that has to do with files and directories. Note that filesystem }
- { specific functionality has been extracted into external units, for example JclNTFS which }
- { contains NTFS specific utility routines, and that the JclShell unit contains some file related }
- { routines as well but they are specific to the Windows shell. }
- { }
- {**************************************************************************************************}
- { }
- { Last modified: $Date:: $ }
- { Revision: $Rev:: $ }
- { Author: $Author:: $ }
- { }
- {**************************************************************************************************}
- unit JclFileUtils;
- {$I jcl.inc}
- {$I crossplatform.inc}
- interface
- uses
- {$IFDEF UNITVERSIONING}
- JclUnitVersioning,
- {$ENDIF UNITVERSIONING}
- {$IFDEF HAS_UNIT_LIBC}
- Libc,
- {$ENDIF HAS_UNIT_LIBC}
- {$IFDEF HAS_UNITSCOPE}
- {$IFDEF MSWINDOWS}
- Winapi.Windows, JclWin32,
- {$ENDIF MSWINDOWS}
- System.Classes, System.SysUtils,
- {$ELSE ~HAS_UNITSCOPE}
- {$IFDEF MSWINDOWS}
- Windows, JclWin32,
- {$ENDIF MSWINDOWS}
- Classes, SysUtils,
- {$ENDIF ~HAS_UNITSCOPE}
- JclBase, JclSysUtils;
- // Path Manipulation
- //
- // Various support routines for working with path strings. For example, building a path from
- // elements or extracting the elements from a path, interpretation of paths and transformations of
- // paths.
- const
- {$IFDEF UNIX}
- // renamed to DirDelimiter
- // PathSeparator = '/';
- DirDelimiter = '/';
- DirSeparator = ':';
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- PathDevicePrefix = '\\.\';
- // renamed to DirDelimiter
- // PathSeparator = '\';
- DirDelimiter = '\';
- DirSeparator = ';';
- PathUncPrefix = '\\';
- {$ENDIF MSWINDOWS}
- faSymLink = $00000040 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF}; // defined since D7
- faNormalFile = $00000080;
- faTemporary = $00000100 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
- faSparseFile = $00000200 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
- faReparsePoint = $00000400 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
- faCompressed = $00000800 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
- faOffline = $00001000 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
- faNotContentIndexed = $00002000 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
- faEncrypted = $00004000 {$IFDEF SUPPORTS_PLATFORM} platform {$ENDIF};
- // Note: faVolumeID is potentially dangerous and its usage has been discontinued
- // Please see QC report 6003 for details, available online at this URL:
- // http://qc.embarcadero.com/wc/qcmain.aspx?d=6003
- faRejectedByDefault = faHidden + faSysFile + faDirectory;
- faWindowsSpecific = faArchive + faTemporary + faSparseFile + faReparsePoint +
- faCompressed + faOffline + faNotContentIndexed + faEncrypted;
- faUnixSpecific = faSymLink;
- type
- TCompactPath = ({cpBegin, }cpCenter, cpEnd);
- function CharIsDriveLetter(const C: char): Boolean;
- function PathAddSeparator(const Path: string): string;
- function PathAddExtension(const Path, Extension: string): string;
- function PathAppend(const Path, Append: string): string;
- function PathBuildRoot(const Drive: Byte): string;
- function PathCanonicalize(const Path: string): string;
- function PathCommonPrefix(const Path1, Path2: string): Integer;
- {$IFDEF MSWINDOWS}
- function PathCompactPath(const DC: HDC; const Path: string; const Width: Integer;
- CmpFmt: TCompactPath): string;
- {$ENDIF MSWINDOWS}
- procedure PathExtractElements(const Source: string; var Drive, Path, FileName, Ext: string);
- function PathExtractFileDirFixed(const S: string): string;
- function PathExtractFileNameNoExt(const Path: string): string;
- function PathExtractPathDepth(const Path: string; Depth: Integer): string;
- function PathGetDepth(const Path: string): Integer;
- {$IFDEF MSWINDOWS}
- function PathGetLongName(const Path: string): string;
- function PathGetShortName(const Path: string): string;
- {$ENDIF MSWINDOWS}
- function PathGetRelativePath(Origin, Destination: string): string;
- function PathGetTempPath: string;
- function PathIsAbsolute(const Path: string): Boolean;
- function PathIsChild(const Path, Base: string): Boolean;
- function PathIsEqualOrChild(const Path, Base: string): Boolean;
- function PathIsDiskDevice(const Path: string): Boolean;
- function PathIsUNC(const Path: string): Boolean;
- function PathRemoveSeparator(const Path: string): string;
- function PathRemoveExtension(const Path: string): string;
- // Windows Vista uses localized path names in the Windows Explorer but these
- // folders do not really exist on disk. This causes all I/O operations to fail
- // if the user specifies such a localized directory like "C:\Benutzer\MyName\Bilder"
- // instead of the physical folder "C:\Users\MyName\Pictures".
- // These two functions allow to convert the user's input from localized to
- // physical paths and vice versa.
- function PathGetPhysicalPath(const LocalizedPath: string): string;
- function PathGetLocalizedPath(const PhysicalPath: string): string;
- // Files and Directories
- //
- // Routines for working with files and directories. Includes routines to extract various file
- // attributes or update them, volume locking and routines for creating temporary files.
- type
- TDelTreeProgress = function (const FileName: string; Attr: DWORD): Boolean;
- TFileListOption = (flFullNames, flRecursive, flMaskedSubfolders);
- TFileListOptions = set of TFileListOption;
- TJclAttributeMatch = (amAny, amExact, amSubSetOf, amSuperSetOf, amCustom);
- TFileMatchFunc = function(const Attr: Integer; const FileInfo: TSearchRec): Boolean;
- TFileHandler = procedure (const FileName: string) of object;
- TFileHandlerEx = procedure (const Directory: string; const FileInfo: TSearchRec) of object;
- TFileInfoHandlerEx = procedure (const FileInfo: TSearchRec) of object;
- function BuildFileList(const Path: string; const Attr: Integer; const List: TStrings; IncludeDirectoryName: Boolean =
- False): Boolean;
- function AdvBuildFileList(const Path: string; const Attr: Integer; const Files: TStrings;
- const AttributeMatch: TJclAttributeMatch = amSuperSetOf; const Options: TFileListOptions = [];
- const SubfoldersMask: string = ''; const FileMatchFunc: TFileMatchFunc = nil): Boolean;
- function VerifyFileAttributeMask(var RejectedAttributes, RequiredAttributes: Integer): Boolean;
- function IsFileAttributeMatch(FileAttributes, RejectedAttributes,
- RequiredAttributes: Integer): Boolean;
- function FileAttributesStr(const FileInfo: TSearchRec): string;
- function IsFileNameMatch(FileName: string; const Mask: string;
- const CaseSensitive: Boolean = {$IFDEF MSWINDOWS} False {$ELSE} True {$ENDIF}): Boolean;
- procedure EnumFiles(const Path: string; HandleFile: TFileHandlerEx;
- RejectedAttributes: Integer = faRejectedByDefault; RequiredAttributes: Integer = 0;
- Abort: PBoolean = nil); overload;
- procedure EnumFiles(const Path: string; HandleFile: TFileInfoHandlerEx;
- RejectedAttributes: Integer = faRejectedByDefault; RequiredAttributes: Integer = 0;
- Abort: PBoolean = nil); overload;
- procedure EnumDirectories(const Root: string; const HandleDirectory: TFileHandler;
- const IncludeHiddenDirectories: Boolean = False; const SubDirectoriesMask: string = '';
- Abort: PBoolean = nil {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF});
- {$IFDEF MSWINDOWS}
- procedure CreateEmptyFile(const FileName: string);
- function CloseVolume(var Volume: THandle): Boolean;
- {$IFNDEF FPC}
- {$IFNDEF WINSCP}
- function DeleteDirectory(const DirectoryName: string; MoveToRecycleBin: Boolean): Boolean;
- {$ENDIF ~WINSCP}
- function CopyDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
- function MoveDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
- {$ENDIF ~FPC}
- function DelTree(const Path: string): Boolean;
- function DelTreeEx(const Path: string; AbortOnFailure: Boolean; Progress: TDelTreeProgress): Boolean;
- function DiskInDrive(Drive: Char): Boolean;
- {$ENDIF MSWINDOWS}
- function DirectoryExists(const Name: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): Boolean;
- function FileCreateTemp(var Prefix: string): THandle;
- {$IFNDEF WINSCP}
- function FileBackup(const FileName: string; Move: Boolean = False): Boolean;
- {$ENDIF ~WINSCP}
- function FileCopy(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
- function FileDateTime(const FileName: string): TDateTime;
- {$IFNDEF WINSCP}
- function FileDelete(const FileName: string; MoveToRecycleBin: Boolean = False): Boolean;
- {$ENDIF ~WINSCP}
- function FileExists(const FileName: string): Boolean;
- /// <summary>procedure FileHistory Creates a list of history files of a specified
- /// source file. Each version of the file get's an extention .~<Nr>~ The file with
- /// the lowest number is the youngest file.
- /// </summary>
- /// <param name="FileName"> (string) Name of the source file</param>
- /// <param name="HistoryPath"> (string) Folder where the history files should be
- /// created. If no folder is defined the folder of the source file is used.</param>
- /// <param name="MaxHistoryCount"> (Integer) Max number of files</param>
- /// <param name="MinFileDate"> (TDateTime) Timestamp how old the file has to be to
- /// create a new history version. For example: NOW-1/24 => Only once per hour a new
- /// history file is created. Default 0 means allways
- /// <param name="ReplaceExtention"> (boolean) Flag to define that the history file
- /// extention should replace the current extention or should be added at the
- /// end</param>
- /// </param>
- {$IFNDEF WINSCP}
- procedure FileHistory(const FileName: string; HistoryPath: string = ''; MaxHistoryCount: Integer = 100; MinFileDate:
- TDateTime = 0; ReplaceExtention: Boolean = true);
- function FileMove(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
- function FileRestore(const FileName: string): Boolean;
- {$ENDIF ~WINSCP}
- function GetBackupFileName(const FileName: string): string;
- function IsBackupFileName(const FileName: string): Boolean;
- function FileGetDisplayName(const FileName: string): string;
- {$IFNDEF WINSCP}
- function FileGetGroupName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
- function FileGetOwnerName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
- {$ENDIF ~WINSCP}
- function FileGetSize(const FileName: string): Int64;
- function FileGetTempName(const Prefix: string): string;
- {$IFDEF MSWINDOWS}
- function FileGetTypeName(const FileName: string): string;
- {$ENDIF MSWINDOWS}
- function FindUnusedFileName(FileName: string; const FileExt: string; NumberPrefix: string = ''): string;
- function ForceDirectories(Name: string): Boolean;
- function GetDirectorySize(const Path: string): Int64;
- {$IFDEF MSWINDOWS}
- function GetDriveTypeStr(const Drive: Char): string;
- function GetFileAgeCoherence(const FileName: string): Boolean;
- {$ENDIF MSWINDOWS}
- procedure GetFileAttributeList(const Items: TStrings; const Attr: Integer);
- {$IFDEF MSWINDOWS}
- procedure GetFileAttributeListEx(const Items: TStrings; const Attr: Integer);
- {$ENDIF MSWINDOWS}
- function GetFileInformation(const FileName: string; out FileInfo: TSearchRec): Boolean; overload;
- function GetFileInformation(const FileName: string): TSearchRec; overload;
- {$IFDEF UNIX}
- function GetFileStatus(const FileName: string; out StatBuf: TStatBuf64;
- const ResolveSymLinks: Boolean): Integer;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function GetFileLastWrite(const FileName: string): TFileTime; overload;
- {$IFNDEF WINSCP}
- function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime): Boolean; overload;
- {$ENDIF ~WINSCP}
- function GetFileLastAccess(const FileName: string): TFileTime; overload;
- {$IFNDEF WINSCP}
- function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime): Boolean; overload;
- {$ENDIF ~WINSCP}
- function GetFileCreation(const FileName: string): TFileTime; overload;
- {$IFNDEF WINSCP}
- function GetFileCreation(const FileName: string; out LocalTime: TDateTime): Boolean; overload;
- {$ENDIF ~WINSCP}
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function GetFileLastWrite(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean = True): Boolean; overload;
- function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean = True): Boolean; overload;
- function GetFileLastWrite(const FileName: string; ResolveSymLinks: Boolean = True): Integer; overload;
- function GetFileLastAccess(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean = True): Boolean; overload;
- function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean = True): Boolean; overload;
- function GetFileLastAccess(const FileName: string; ResolveSymLinks: Boolean = True): Integer; overload;
- function GetFileLastAttrChange(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean = True): Boolean; overload;
- function GetFileLastAttrChange(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean = True): Boolean; overload;
- function GetFileLastAttrChange(const FileName: string; ResolveSymLinks: Boolean = True): Integer; overload;
- {$ENDIF UNIX}
- function GetModulePath(const Module: HMODULE): string;
- function GetSizeOfFile(const FileName: string): Int64; overload;
- function GetSizeOfFile(const FileInfo: TSearchRec): Int64; overload;
- {$IFDEF MSWINDOWS}
- function GetSizeOfFile(Handle: THandle): Int64; overload;
- function GetStandardFileInfo(const FileName: string): TWin32FileAttributeData;
- {$ENDIF MSWINDOWS}
- function IsDirectory(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): Boolean;
- function IsRootDirectory(const CanonicFileName: string): Boolean;
- {$IFDEF MSWINDOWS}
- function LockVolume(const Volume: string; var Handle: THandle): Boolean;
- function OpenVolume(const Drive: Char): THandle;
- {$IFNDEF WINSCP}
- function SetDirLastWrite(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
- function SetDirLastAccess(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
- function SetDirCreation(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
- {$ENDIF ~WINSCP}
- {$ENDIF MSWINDOWS}
- function SetFileLastWrite(const FileName: string; const DateTime: TDateTime): Boolean;
- function SetFileLastAccess(const FileName: string; const DateTime: TDateTime): Boolean;
- {$IFDEF MSWINDOWS}
- function SetFileCreation(const FileName: string; const DateTime: TDateTime): Boolean;
- procedure ShredFile(const FileName: string; Times: Integer = 1);
- function UnlockVolume(var Handle: THandle): Boolean;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function CreateSymbolicLink(const Name, Target: string): Boolean;
- { This function gets the value of the symbolic link filename. }
- function SymbolicLinkTarget(const Name: string): string;
- {$ENDIF UNIX}
- // TJclFileAttributeMask
- //
- // File search helper class, allows to specify required/rejected attributes
- type
- TAttributeInterest = (aiIgnored, aiRejected, aiRequired);
- TJclCustomFileAttrMask = class(TPersistent)
- private
- FRequiredAttr: Integer;
- FRejectedAttr: Integer;
- function GetAttr(Index: Integer): TAttributeInterest;
- procedure SetAttr(Index: Integer; const Value: TAttributeInterest);
- procedure ReadRequiredAttributes(Reader: TReader);
- procedure ReadRejectedAttributes(Reader: TReader);
- procedure WriteRequiredAttributes(Writer: TWriter);
- procedure WriteRejectedAttributes(Writer: TWriter);
- protected
- procedure DefineProperties(Filer: TFiler); override;
- property ReadOnly: TAttributeInterest index faReadOnly
- read GetAttr write SetAttr stored False;
- property Hidden: TAttributeInterest index faHidden
- read GetAttr write SetAttr stored False;
- property System: TAttributeInterest index faSysFile
- read GetAttr write SetAttr stored False;
- property Directory: TAttributeInterest index faDirectory
- read GetAttr write SetAttr stored False;
- property SymLink: TAttributeInterest index faSymLink
- read GetAttr write SetAttr stored False;
- property Normal: TAttributeInterest index faNormalFile
- read GetAttr write SetAttr stored False;
- property Archive: TAttributeInterest index faArchive
- read GetAttr write SetAttr stored False;
- property Temporary: TAttributeInterest index faTemporary
- read GetAttr write SetAttr stored False;
- property SparseFile: TAttributeInterest index faSparseFile
- read GetAttr write SetAttr stored False;
- property ReparsePoint: TAttributeInterest index faReparsePoint
- read GetAttr write SetAttr stored False;
- property Compressed: TAttributeInterest index faCompressed
- read GetAttr write SetAttr stored False;
- property OffLine: TAttributeInterest index faOffline
- read GetAttr write SetAttr stored False;
- property NotContentIndexed: TAttributeInterest index faNotContentIndexed
- read GetAttr write SetAttr stored False;
- property Encrypted: TAttributeInterest index faEncrypted
- read GetAttr write SetAttr stored False;
- public
- constructor Create;
- procedure Assign(Source: TPersistent); override;
- procedure Clear;
- function Match(FileAttributes: Integer): Boolean; overload;
- function Match(const FileInfo: TSearchRec): Boolean; overload;
- property Required: Integer read FRequiredAttr write FRequiredAttr;
- property Rejected: Integer read FRejectedAttr write FRejectedAttr;
- property Attribute[Index: Integer]: TAttributeInterest read GetAttr write SetAttr; default;
- end;
- TJclFileAttributeMask = class(TJclCustomFileAttrMask)
- private
- procedure ReadVolumeID(Reader: TReader);
- protected
- procedure DefineProperties(Filer: TFiler); override;
- published
- property ReadOnly;
- property Hidden;
- property System;
- property Directory;
- property Normal;
- {$IFDEF UNIX}
- property SymLink;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- property Archive;
- property Temporary;
- property SparseFile;
- property ReparsePoint;
- property Compressed;
- property OffLine;
- property NotContentIndexed;
- property Encrypted;
- {$ENDIF MSWINDOWS}
- end;
- type
- TFileSearchOption = (fsIncludeSubDirectories, fsIncludeHiddenSubDirectories, fsLastChangeAfter,
- fsLastChangeBefore, fsMaxSize, fsMinSize);
- TFileSearchOptions = set of TFileSearchOption;
- TFileSearchTaskID = Integer;
- TFileSearchTerminationEvent = procedure (const ID: TFileSearchTaskID; const Aborted: Boolean) of object;
- TFileEnumeratorSyncMode = (smPerFile, smPerDirectory);
- // IJclFileSearchOptions
- //
- // Interface for file search options
- type
- IJclFileSearchOptions = interface
- ['{B73D9E3D-34C5-4DA9-88EF-4CA730328FC9}']
- function GetAttributeMask: TJclFileAttributeMask;
- function GetCaseSensitiveSearch: Boolean;
- function GetRootDirectories: TStrings;
- function GetRootDirectory: string;
- function GetFileMask: string;
- function GetFileMasks: TStrings;
- function GetFileSizeMax: Int64;
- function GetFileSizeMin: Int64;
- function GetIncludeSubDirectories: Boolean;
- function GetIncludeHiddenSubDirectories: Boolean;
- function GetLastChangeAfter: TDateTime;
- function GetLastChangeBefore: TDateTime;
- function GetLastChangeAfterStr: string;
- function GetLastChangeBeforeStr: string;
- function GetSubDirectoryMask: string;
- function GetOption(const Option: TFileSearchOption): Boolean;
- function GetOptions: TFileSearchoptions;
- procedure SetAttributeMask(const Value: TJclFileAttributeMask);
- procedure SetCaseSensitiveSearch(const Value: Boolean);
- procedure SetRootDirectories(const Value: TStrings);
- procedure SetRootDirectory(const Value: string);
- procedure SetFileMask(const Value: string);
- procedure SetFileMasks(const Value: TStrings);
- procedure SetFileSizeMax(const Value: Int64);
- procedure SetFileSizeMin(const Value: Int64);
- procedure SetIncludeSubDirectories(const Value: Boolean);
- procedure SetIncludeHiddenSubDirectories(const Value: Boolean);
- procedure SetLastChangeAfter(const Value: TDateTime);
- procedure SetLastChangeBefore(const Value: TDateTime);
- procedure SetLastChangeAfterStr(const Value: string);
- procedure SetLastChangeBeforeStr(const Value: string);
- procedure SetOption(const Option: TFileSearchOption; const Value: Boolean);
- procedure SetOptions(const Value: TFileSearchOptions);
- procedure SetSubDirectoryMask(const Value: string);
- // properties
- property CaseSensitiveSearch: Boolean read GetCaseSensitiveSearch write SetCaseSensitiveSearch;
- property RootDirectories: TStrings read GetRootDirectories write SetRootDirectories;
- property RootDirectory: string read GetRootDirectory write SetRootDirectory;
- property FileMask: string read GetFileMask write SetFileMask;
- property SubDirectoryMask: string read GetSubDirectoryMask write SetSubDirectoryMask;
- property AttributeMask: TJclFileAttributeMask read GetAttributeMask write SetAttributeMask;
- property FileSizeMin: Int64 read GetFileSizeMin write SetFileSizeMin;
- property FileSizeMax: Int64 read GetFileSizeMax write SetFileSizeMax; // default InvalidFileSize;
- property LastChangeAfter: TDateTime read GetLastChangeAfter write SetLastChangeAfter;
- property LastChangeBefore: TDateTime read GetLastChangeBefore write SetLastChangeBefore;
- property LastChangeAfterAsString: string read GetLastChangeAfterStr write SetLastChangeAfterStr;
- property LastChangeBeforeAsString: string read GetLastChangeBeforeStr write SetLastChangeBeforeStr;
- property IncludeSubDirectories: Boolean read GetIncludeSubDirectories
- write SetIncludeSubDirectories;
- property IncludeHiddenSubDirectories: Boolean read GetIncludeHiddenSubDirectories
- write SetIncludeHiddenSubDirectories;
- end;
- // IJclFileSearchOptions
- //
- // Interface for file search options
- type
- TJclFileSearchOptions = class(TJclInterfacedPersistent, IJclFileSearchOptions)
- protected
- FFileMasks: TStringList;
- FRootDirectories: TStringList;
- FSubDirectoryMask: string;
- FAttributeMask: TJclFileAttributeMask;
- FFileSizeMin: Int64;
- FFileSizeMax: Int64;
- FLastChangeBefore: TDateTime;
- FLastChangeAfter: TDateTime;
- FOptions: TFileSearchOptions;
- FCaseSensitiveSearch: Boolean;
- function IsLastChangeAfterStored: Boolean;
- function IsLastChangeBeforeStored: Boolean;
- public
- constructor Create;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- { IJclFileSearchOptions }
- function GetAttributeMask: TJclFileAttributeMask;
- function GetCaseSensitiveSearch: Boolean;
- function GetRootDirectories: TStrings;
- function GetRootDirectory: string;
- function GetFileMask: string;
- function GetFileMasks: TStrings;
- function GetFileSizeMax: Int64;
- function GetFileSizeMin: Int64;
- function GetIncludeSubDirectories: Boolean;
- function GetIncludeHiddenSubDirectories: Boolean;
- function GetLastChangeAfter: TDateTime;
- function GetLastChangeBefore: TDateTime;
- function GetLastChangeAfterStr: string;
- function GetLastChangeBeforeStr: string;
- function GetSubDirectoryMask: string;
- function GetOption(const Option: TFileSearchOption): Boolean;
- function GetOptions: TFileSearchoptions;
- procedure SetAttributeMask(const Value: TJclFileAttributeMask);
- procedure SetCaseSensitiveSearch(const Value: Boolean);
- procedure SetRootDirectories(const Value: TStrings);
- procedure SetRootDirectory(const Value: string);
- procedure SetFileMask(const Value: string);
- procedure SetFileMasks(const Value: TStrings);
- procedure SetFileSizeMax(const Value: Int64);
- procedure SetFileSizeMin(const Value: Int64);
- procedure SetIncludeSubDirectories(const Value: Boolean);
- procedure SetIncludeHiddenSubDirectories(const Value: Boolean);
- procedure SetLastChangeAfter(const Value: TDateTime);
- procedure SetLastChangeBefore(const Value: TDateTime);
- procedure SetLastChangeAfterStr(const Value: string);
- procedure SetLastChangeBeforeStr(const Value: string);
- procedure SetOption(const Option: TFileSearchOption; const Value: Boolean);
- procedure SetOptions(const Value: TFileSearchOptions);
- procedure SetSubDirectoryMask(const Value: string);
- published
- property CaseSensitiveSearch: Boolean read GetCaseSensitiveSearch write SetCaseSensitiveSearch
- default {$IFDEF MSWINDOWS} False {$ELSE} True {$ENDIF};
- property FileMasks: TStrings read GetFileMasks write SetFileMasks;
- property RootDirectories: TStrings read GetRootDirectories write SetRootDirectories;
- property RootDirectory: string read GetRootDirectory write SetRootDirectory;
- property SubDirectoryMask: string read FSubDirectoryMask write FSubDirectoryMask;
- property AttributeMask: TJclFileAttributeMask read FAttributeMask write SetAttributeMask;
- property FileSizeMin: Int64 read FFileSizeMin write FFileSizeMin;
- property FileSizeMax: Int64 read FFileSizeMax write FFileSizeMax;
- property LastChangeAfter: TDateTime read FLastChangeAfter write FLastChangeAfter
- stored IsLastChangeAfterStored;
- property LastChangeBefore: TDateTime read FLastChangeBefore write FLastChangeBefore
- stored IsLastChangeBeforeStored;
- property Options: TFileSearchOptions read FOptions write FOptions
- default [fsIncludeSubDirectories];
- end;
- // IJclFileEnumerator
- //
- // Interface for thread-based file search
- type
- IJclFileEnumerator = interface(IJclFileSearchOptions)
- ['{F7E747ED-1C41-441F-B25B-BB314E00C4E9}']
- // property access methods
- function GetRunningTasks: Integer;
- function GetSynchronizationMode: TFileEnumeratorSyncMode;
- function GetOnEnterDirectory: TFileHandler;
- function GetOnTerminateTask: TFileSearchTerminationEvent;
- procedure SetSynchronizationMode(const Value: TFileEnumeratorSyncMode);
- procedure SetOnEnterDirectory(const Value: TFileHandler);
- procedure SetOnTerminateTask(const Value: TFileSearchTerminationEvent);
- // other methods
- function FillList(List: TStrings): TFileSearchTaskID;
- function ForEach(Handler: TFileHandler): TFileSearchTaskID; overload;
- function ForEach(Handler: TFileHandlerEx): TFileSearchTaskID; overload;
- procedure StopTask(ID: TFileSearchTaskID);
- procedure StopAllTasks(Silently: Boolean = False); // Silently: Don't call OnTerminateTask
- // properties
- property RunningTasks: Integer read GetRunningTasks;
- property SynchronizationMode: TFileEnumeratorSyncMode read GetSynchronizationMode
- write SetSynchronizationMode;
- property OnEnterDirectory: TFileHandler read GetOnEnterDirectory write SetOnEnterDirectory;
- property OnTerminateTask: TFileSearchTerminationEvent read GetOnTerminateTask
- write SetOnTerminateTask;
- end;
- // TJclFileEnumerator
- //
- // Class for thread-based file search
- type
- TJclFileEnumerator = class(TJclFileSearchOptions, IInterface, IJclFileSearchOptions, IJclFileEnumerator)
- private
- FTasks: TList;
- FOnEnterDirectory: TFileHandler;
- FOnTerminateTask: TFileSearchTerminationEvent;
- FNextTaskID: TFileSearchTaskID;
- FSynchronizationMode: TFileEnumeratorSyncMode;
- function GetNextTaskID: TFileSearchTaskID;
- protected
- function CreateTask: TThread;
- procedure TaskTerminated(Sender: TObject);
- property NextTaskID: TFileSearchTaskID read GetNextTaskID;
- public
- constructor Create;
- destructor Destroy; override;
- { IJclFileEnumerator }
- function GetRunningTasks: Integer;
- function GetSynchronizationMode: TFileEnumeratorSyncMode;
- function GetOnEnterDirectory: TFileHandler;
- function GetOnTerminateTask: TFileSearchTerminationEvent;
- procedure SetSynchronizationMode(const Value: TFileEnumeratorSyncMode);
- procedure SetOnEnterDirectory(const Value: TFileHandler);
- procedure SetOnTerminateTask(const Value: TFileSearchTerminationEvent);
- procedure Assign(Source: TPersistent); override;
- function FillList(List: TStrings): TFileSearchTaskID;
- function ForEach(Handler: TFileHandler): TFileSearchTaskID; overload;
- function ForEach(Handler: TFileHandlerEx): TFileSearchTaskID; overload;
- procedure StopTask(ID: TFileSearchTaskID);
- procedure StopAllTasks(Silently: Boolean = False); // Silently: Don't call OnTerminateTask
- property FileMask: string read GetFileMask write SetFileMask;
- property IncludeSubDirectories: Boolean
- read GetIncludeSubDirectories write SetIncludeSubDirectories;
- property IncludeHiddenSubDirectories: Boolean
- read GetIncludeHiddenSubDirectories write SetIncludeHiddenSubDirectories;
- property SearchOption[const Option: TFileSearchOption]: Boolean read GetOption write SetOption;
- property LastChangeAfterAsString: string read GetLastChangeAfterStr write SetLastChangeAfterStr;
- property LastChangeBeforeAsString: string read GetLastChangeBeforeStr write SetLastChangeBeforeStr;
- published
- property RunningTasks: Integer read GetRunningTasks;
- property SynchronizationMode: TFileEnumeratorSyncMode read FSynchronizationMode write FSynchronizationMode
- default smPerDirectory;
- property OnEnterDirectory: TFileHandler read FOnEnterDirectory write FOnEnterDirectory;
- property OnTerminateTask: TFileSearchTerminationEvent read FOnTerminateTask write FOnTerminateTask;
- end;
- function FileSearch: IJclFileEnumerator;
- {$IFDEF MSWINDOWS}
- // TFileVersionInfo
- //
- // Class that enables reading the version information stored in a PE file.
- type
- TFileFlag = (ffDebug, ffInfoInferred, ffPatched, ffPreRelease, ffPrivateBuild, ffSpecialBuild);
- TFileFlags = set of TFileFlag;
- PLangIdRec = ^TLangIdRec;
- TLangIdRec = packed record
- case Integer of
- 0: (
- LangId: Word;
- CodePage: Word);
- 1: (
- Pair: DWORD);
- end;
- EJclFileVersionInfoError = class(EJclError);
- TJclFileVersionInfo = class(TObject)
- private
- FBuffer: AnsiString;
- FFixedInfo: PVSFixedFileInfo;
- FFileFlags: TFileFlags;
- FItemList: TStringList;
- FItems: TStringList;
- FLanguages: array of TLangIdRec;
- FLanguageIndex: Integer;
- FTranslations: array of TLangIdRec;
- function GetFixedInfo: TVSFixedFileInfo;
- function GetItems: TStrings;
- function GetLanguageCount: Integer;
- function GetLanguageIds(Index: Integer): string;
- function GetLanguageNames(Index: Integer): string;
- function GetLanguages(Index: Integer): TLangIdRec;
- function GetTranslationCount: Integer;
- function GetTranslations(Index: Integer): TLangIdRec;
- procedure SetLanguageIndex(const Value: Integer);
- protected
- procedure CreateItemsForLanguage;
- procedure CheckLanguageIndex(Value: Integer);
- procedure ExtractData;
- procedure ExtractFlags;
- function GetBinFileVersion: string;
- function GetBinProductVersion: string;
- function GetFileOS: DWORD;
- function GetFileSubType: DWORD;
- function GetFileType: DWORD;
- function GetFileVersionBuild: string;
- function GetFileVersionMajor: string;
- function GetFileVersionMinor: string;
- function GetFileVersionRelease: string;
- function GetProductVersionBuild: string;
- function GetProductVersionMajor: string;
- function GetProductVersionMinor: string;
- function GetProductVersionRelease: string;
- function GetVersionKeyValue(Index: Integer): string;
- public
- constructor Attach(VersionInfoData: Pointer; Size: Integer);
- constructor Create(const FileName: string); overload;
- {$IFDEF MSWINDOWS}
- {$IFDEF FPC}
- constructor Create(const Window: HWND; Dummy: Pointer = nil); overload;
- {$ELSE}
- constructor Create(const Window: HWND); overload;
- {$ENDIF}
- constructor Create(const Module: HMODULE); overload;
- {$ENDIF MSWINDOWS}
- destructor Destroy; override;
- function GetCustomFieldValue(const FieldName: string): string;
- class function VersionLanguageId(const LangIdRec: TLangIdRec): string;
- class function VersionLanguageName(const LangId: Word): string;
- class function FileHasVersionInfo(const FileName: string): boolean;
- function TranslationMatchesLanguages(Exact: Boolean = True): Boolean;
- property BinFileVersion: string read GetBinFileVersion;
- property BinProductVersion: string read GetBinProductVersion;
- property Comments: string index 1 read GetVersionKeyValue;
- property CompanyName: string index 2 read GetVersionKeyValue;
- property FileDescription: string index 3 read GetVersionKeyValue;
- property FixedInfo: TVSFixedFileInfo read GetFixedInfo;
- property FileFlags: TFileFlags read FFileFlags;
- property FileOS: DWORD read GetFileOS;
- property FileSubType: DWORD read GetFileSubType;
- property FileType: DWORD read GetFileType;
- property FileVersion: string index 4 read GetVersionKeyValue;
- property FileVersionBuild: string read GetFileVersionBuild;
- property FileVersionMajor: string read GetFileVersionMajor;
- property FileVersionMinor: string read GetFileVersionMinor;
- property FileVersionRelease: string read GetFileVersionRelease;
- property Items: TStrings read GetItems;
- property InternalName: string index 5 read GetVersionKeyValue;
- property LanguageCount: Integer read GetLanguageCount;
- property LanguageIds[Index: Integer]: string read GetLanguageIds;
- property LanguageIndex: Integer read FLanguageIndex write SetLanguageIndex;
- property Languages[Index: Integer]: TLangIdRec read GetLanguages;
- property LanguageNames[Index: Integer]: string read GetLanguageNames;
- property LegalCopyright: string index 6 read GetVersionKeyValue;
- property LegalTradeMarks: string index 7 read GetVersionKeyValue;
- property OriginalFilename: string index 8 read GetVersionKeyValue;
- property PrivateBuild: string index 12 read GetVersionKeyValue;
- property ProductName: string index 9 read GetVersionKeyValue;
- property ProductVersion: string index 10 read GetVersionKeyValue;
- property ProductVersionBuild: string read GetProductVersionBuild;
- property ProductVersionMajor: string read GetProductVersionMajor;
- property ProductVersionMinor: string read GetProductVersionMinor;
- property ProductVersionRelease: string read GetProductVersionRelease;
- property SpecialBuild: string index 11 read GetVersionKeyValue;
- property TranslationCount: Integer read GetTranslationCount;
- property Translations[Index: Integer]: TLangIdRec read GetTranslations;
- end;
- function OSIdentToString(const OSIdent: DWORD): string;
- function OSFileTypeToString(const OSFileType: DWORD; const OSFileSubType: DWORD = 0): string;
- function VersionResourceAvailable(const FileName: string): Boolean; overload;
- function VersionResourceAvailable(const Window: HWND): Boolean; overload;
- function VersionResourceAvailable(const Module: HMODULE): Boolean; overload;
- function WindowToModuleFileName(const Window: HWND): string;
- {$ENDIF MSWINDOWS}
- // Version Info formatting
- type
- TFileVersionFormat = (vfMajorMinor, vfFull);
- function FormatVersionString(const HiV, LoV: Word): string; overload;
- function FormatVersionString(const Major, Minor, Build, Revision: Word): string; overload;
- {$IFDEF MSWINDOWS}
- function FormatVersionString(const FixedInfo: TVSFixedFileInfo; VersionFormat: TFileVersionFormat = vfFull): string; overload;
- // Version Info extracting
- procedure VersionExtractFileInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
- procedure VersionExtractProductInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
- // Fixed Version Info routines
- function VersionFixedFileInfo(const FileName: string; var FixedInfo: TVSFixedFileInfo): Boolean;
- function VersionFixedFileInfoString(const FileName: string; VersionFormat: TFileVersionFormat = vfFull;
- const NotAvailableText: string = ''): string;
- {$ENDIF MSWINDOWS}
- // Streams
- //
- // TStream descendent classes for dealing with temporary files and for using file mapping objects.
- type
- TJclTempFileStream = class(THandleStream)
- private
- FFileName: string;
- public
- constructor Create(const Prefix: string);
- destructor Destroy; override;
- property FileName: string read FFileName;
- end;
- {$IFDEF MSWINDOWS}
- TJclCustomFileMapping = class;
- TJclFileMappingView = class(TCustomMemoryStream)
- private
- FFileMapping: TJclCustomFileMapping;
- FOffsetHigh: Cardinal;
- FOffsetLow: Cardinal;
- function GetIndex: Integer;
- function GetOffset: Int64;
- public
- constructor Create(const FileMap: TJclCustomFileMapping;
- Access, Size: Cardinal; ViewOffset: Int64);
- constructor CreateAt(FileMap: TJclCustomFileMapping; Access,
- Size: Cardinal; ViewOffset: Int64; Address: Pointer);
- destructor Destroy; override;
- function Flush(const Count: Cardinal): Boolean;
- procedure LoadFromStream(const Stream: TStream);
- procedure LoadFromFile(const FileName: string);
- function Write(const Buffer; Count: Longint): Longint; override;
- property Index: Integer read GetIndex;
- property FileMapping: TJclCustomFileMapping read FFileMapping;
- property Offset: Int64 read GetOffset;
- end;
- TJclFileMappingRoundOffset = (rvDown, rvUp);
- TJclCustomFileMapping = class(TObject)
- private
- FExisted: Boolean;
- FHandle: THandle;
- FName: string;
- FRoundViewOffset: TJclFileMappingRoundOffset;
- FViews: TList;
- function GetCount: Integer;
- function GetView(Index: Integer): TJclFileMappingView;
- protected
- procedure ClearViews;
- procedure InternalCreate(const FileHandle: THandle; const Name: string;
- const Protect: Cardinal; MaximumSize: Int64; SecAttr: PSecurityAttributes);
- procedure InternalOpen(const Name: string; const InheritHandle: Boolean;
- const DesiredAccess: Cardinal);
- public
- constructor Create;
- constructor Open(const Name: string; const InheritHandle: Boolean; const DesiredAccess: Cardinal);
- destructor Destroy; override;
- function Add(const Access, Count: Cardinal; const Offset: Int64): Integer;
- function AddAt(const Access, Count: Cardinal; const Offset: Int64; const Address: Pointer): Integer;
- procedure Delete(const Index: Integer);
- function IndexOf(const View: TJclFileMappingView): Integer;
- property Count: Integer read GetCount;
- property Existed: Boolean read FExisted;
- property Handle: THandle read FHandle;
- property Name: string read FName;
- property RoundViewOffset: TJclFileMappingRoundOffset read FRoundViewOffset write FRoundViewOffset;
- property Views[index: Integer]: TJclFileMappingView read GetView;
- end;
- TJclFileMapping = class(TJclCustomFileMapping)
- private
- FFileHandle: THandle;
- public
- constructor Create(const FileName: string; FileMode: Cardinal;
- const Name: string; Protect: Cardinal; const MaximumSize: Int64;
- SecAttr: PSecurityAttributes); overload;
- constructor Create(const FileHandle: THandle; const Name: string;
- Protect: Cardinal; const MaximumSize: Int64;
- SecAttr: PSecurityAttributes); overload;
- destructor Destroy; override;
- property FileHandle: THandle read FFileHandle;
- end;
- TJclSwapFileMapping = class(TJclCustomFileMapping)
- public
- constructor Create(const Name: string; Protect: Cardinal;
- const MaximumSize: Int64; SecAttr: PSecurityAttributes);
- end;
- TJclFileMappingStream = class(TCustomMemoryStream)
- private
- FFileHandle: THandle;
- FMapping: THandle;
- protected
- procedure Close;
- public
- constructor Create(const FileName: string; FileMode: Word = fmOpenRead or fmShareDenyWrite);
- destructor Destroy; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- end;
- {$ENDIF MSWINDOWS}
- TJclMappedTextReaderIndex = (tiNoIndex, tiFull);
- PPAnsiCharArray = ^TPAnsiCharArray;
- TPAnsiCharArray = array [0..0] of PAnsiChar;
- TJclAnsiMappedTextReader = class(TPersistent)
- private
- FContent: PAnsiChar;
- FEnd: PAnsiChar;
- FIndex: PPAnsiCharArray;
- FIndexOption: TJclMappedTextReaderIndex;
- FFreeStream: Boolean;
- FLastLineNumber: Integer;
- FLastPosition: PAnsiChar;
- FLineCount: Integer;
- FMemoryStream: TCustomMemoryStream;
- FPosition: PAnsiChar;
- FSize: Integer;
- function GetAsString: AnsiString;
- function GetEof: Boolean;
- function GetChars(Index: Integer): AnsiChar;
- function GetLineCount: Integer;
- function GetLines(LineNumber: Integer): AnsiString;
- function GetPosition: Integer;
- function GetPositionFromLine(LineNumber: Integer): Integer;
- procedure SetPosition(const Value: Integer);
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure CreateIndex;
- procedure Init;
- function PtrFromLine(LineNumber: Integer): PAnsiChar;
- function StringFromPosition(var StartPos: PAnsiChar): AnsiString;
- public
- constructor Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean = True;
- const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
- constructor Create(const FileName: TFileName;
- const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
- destructor Destroy; override;
- procedure GoBegin;
- function Read: AnsiChar;
- function ReadLn: AnsiString;
- property AsString: AnsiString read GetAsString;
- property Chars[Index: Integer]: AnsiChar read GetChars;
- property Content: PAnsiChar read FContent;
- property Eof: Boolean read GetEof;
- property IndexOption: TJclMappedTextReaderIndex read FIndexOption;
- property Lines[LineNumber: Integer]: AnsiString read GetLines;
- property LineCount: Integer read GetLineCount;
- property PositionFromLine[LineNumber: Integer]: Integer read GetPositionFromLine;
- property Position: Integer read GetPosition write SetPosition;
- property Size: Integer read FSize;
- end;
- PPWideCharArray = ^TPWideCharArray;
- TPWideCharArray = array [0..0] of PWideChar;
- TJclWideMappedTextReader = class(TPersistent)
- private
- FContent: PWideChar;
- FEnd: PWideChar;
- FIndex: PPWideCharArray;
- FIndexOption: TJclMappedTextReaderIndex;
- FFreeStream: Boolean;
- FLastLineNumber: Integer;
- FLastPosition: PWideChar;
- FLineCount: Integer;
- FMemoryStream: TCustomMemoryStream;
- FPosition: PWideChar;
- FSize: Integer;
- function GetAsString: WideString;
- function GetEof: Boolean;
- function GetChars(Index: Integer): WideChar;
- function GetLineCount: Integer;
- function GetLines(LineNumber: Integer): WideString;
- function GetPosition: Integer;
- function GetPositionFromLine(LineNumber: Integer): Integer;
- procedure SetPosition(const Value: Integer);
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure CreateIndex;
- procedure Init;
- function PtrFromLine(LineNumber: Integer): PWideChar;
- function StringFromPosition(var StartPos: PWideChar): WideString;
- public
- constructor Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean = True;
- const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
- constructor Create(const FileName: TFileName;
- const AIndexOption: TJclMappedTextReaderIndex = tiNoIndex); overload;
- destructor Destroy; override;
- procedure GoBegin;
- function Read: WideChar;
- function ReadLn: WideString;
- property AsString: WideString read GetAsString;
- property Chars[Index: Integer]: WideChar read GetChars;
- property Content: PWideChar read FContent;
- property Eof: Boolean read GetEof;
- property IndexOption: TJclMappedTextReaderIndex read FIndexOption;
- property Lines[LineNumber: Integer]: WideString read GetLines;
- property LineCount: Integer read GetLineCount;
- property PositionFromLine[LineNumber: Integer]: Integer read GetPositionFromLine;
- property Position: Integer read GetPosition write SetPosition;
- property Size: Integer read FSize;
- end;
- { TODO : UNTESTED/UNDOCUMENTED }
- type
- TJclFileMaskComparator = class(TObject)
- private
- FFileMask: string;
- FExts: array of string;
- FNames: array of string;
- FWildChars: array of Byte;
- FSeparator: Char;
- procedure CreateMultiMasks;
- function GetCount: Integer;
- function GetExts(Index: Integer): string;
- function GetMasks(Index: Integer): string;
- function GetNames(Index: Integer): string;
- procedure SetFileMask(const Value: string);
- procedure SetSeparator(const Value: Char);
- public
- constructor Create;
- function Compare(const NameExt: string): Boolean;
- property Count: Integer read GetCount;
- property Exts[Index: Integer]: string read GetExts;
- property FileMask: string read FFileMask write SetFileMask;
- property Masks[Index: Integer]: string read GetMasks;
- property Names[Index: Integer]: string read GetNames;
- property Separator: Char read FSeparator write SetSeparator;
- end;
- EJclPathError = class(EJclError);
- EJclFileUtilsError = class(EJclError);
- {$IFDEF UNIX}
- EJclTempFileStreamError = class(EJclFileUtilsError);
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- EJclTempFileStreamError = class(EJclWin32Error);
- EJclFileMappingError = class(EJclWin32Error);
- EJclFileMappingViewError = class(EJclWin32Error);
- {$ENDIF MSWINDOWS}
- function SamePath(const Path1, Path2: string): Boolean;
- // functions to add/delete paths from a separated list of paths
- // on windows the separator is a semi-colon ';'
- // on linux the separator is a colon ':'
- // add items at the end
- procedure PathListAddItems(var List: string; const Items: string);
- // add items at the end if they are not present
- procedure PathListIncludeItems(var List: string; const Items: string);
- // delete multiple items
- procedure PathListDelItems(var List: string; const Items: string);
- // delete one item
- procedure PathListDelItem(var List: string; const Index: Integer);
- // return the number of item
- function PathListItemCount(const List: string): Integer;
- // return the Nth item
- function PathListGetItem(const List: string; const Index: Integer): string;
- // set the Nth item
- procedure PathListSetItem(var List: string; const Index: Integer; const Value: string);
- // return the index of an item
- function PathListItemIndex(const List, Item: string): Integer;
- // additional functions to access the commandline parameters of an application
- // returns the name of the command line parameter at position index, which is
- // separated by the given separator, if the first character of the name part
- // is one of the AllowedPrefixCharacters, this character will be deleted.
- function ParamName (Index : Integer; const Separator : string = '=';
- const AllowedPrefixCharacters : string = '-/'; TrimName : Boolean = true) : string;
- // returns the value of the command line parameter at position index, which is
- // separated by the given separator
- function ParamValue (Index : Integer; const Separator : string = '='; TrimValue : Boolean = true) : string; overload;
- // seaches a command line parameter where the namepart is the searchname
- // and returns the value which is which by the given separator.
- // CaseSensitive defines the search type. if the first character of the name part
- // is one of the AllowedPrefixCharacters, this character will be deleted.
- function ParamValue (const SearchName : string; const Separator : string = '=';
- CaseSensitive : Boolean = False;
- const AllowedPrefixCharacters : string = '-/'; TrimValue : Boolean = true) : string; overload;
- // seaches a command line parameter where the namepart is the searchname
- // and returns the position index. if no separator is defined, the full paramstr is compared.
- // CaseSensitive defines the search type. if the first character of the name part
- // is one of the AllowedPrefixCharacters, this character will be deleted.
- function ParamPos (const SearchName : string; const Separator : string = '=';
- CaseSensitive : Boolean = False;
- const AllowedPrefixCharacters : string = '-/'): Integer;
- {$IFDEF UNITVERSIONING}
- const
- UnitVersioning: TUnitVersionInfo = (
- RCSfile: '$URL$';
- Revision: '$Revision$';
- Date: '$Date$';
- LogPath: 'JCL\source\common';
- Extra: '';
- Data: nil
- );
- {$ENDIF UNITVERSIONING}
- implementation
- uses
- {$IFDEF HAS_UNITSCOPE}
- System.Types, // inlining of TList.Remove
- {$IFDEF HAS_UNIT_CHARACTER}
- System.Character,
- {$ENDIF HAS_UNIT_CHARACTER}
- System.Math,
- {$IFDEF MSWINDOWS}
- Winapi.ShellApi, Winapi.ActiveX, System.Win.ComObj, Winapi.ShlObj,
- {$IFNDEF WINSCP}JclShell,{$ENDIF ~WINSCP} JclSysInfo, {$IFNDEF WINSCP}JclSecurity,{$ENDIF ~WINSCP}
- {$ENDIF MSWINDOWS}
- {$ELSE ~HAS_UNITSCOPE}
- {$IFDEF HAS_UNIT_CHARACTER}
- Character,
- {$ENDIF HAS_UNIT_CHARACTER}
- Math,
- {$IFDEF MSWINDOWS}
- ShellApi, ActiveX, ComObj, ShlObj,
- JclShell, JclSysInfo, JclSecurity,
- {$ENDIF MSWINDOWS}
- {$ENDIF ~HAS_UNITSCOPE}
- {$IFNDEF WINSCP}JclDateTime,{$ENDIF ~WINSCP} JclResources,
- JclStrings;
- { Some general notes:
- This unit redeclares some functions from FileCtrl.pas to avoid a dependency on that unit in the
- JCL. The problem is that FileCtrl.pas uses some units (eg Forms.pas) which have ridiculous
- initialization requirements. They add 4KB (!) to the executable and roughly 1 second of startup.
- That initialization is only necessary for GUI applications and is unacceptable for high
- performance services or console apps.
- The routines which query files or directories for their attributes deliberately use FindFirst
- even though there may be easier ways to get at the required information. This is because FindFirst
- is about the only routine which doesn't cause the file's last modification/accessed time to be
- changed which is usually an undesired side-effect. }
- {$IFDEF UNIX}
- const
- ERROR_NO_MORE_FILES = -1;
- INVALID_HANDLE_VALUE = THandle(-1);
- {$ENDIF UNIX}
- //=== { TJclTempFileStream } =================================================
- constructor TJclTempFileStream.Create(const Prefix: string);
- var
- FileHandle: THandle;
- begin
- FFileName := Prefix;
- FileHandle := FileCreateTemp(FFileName);
- // (rom) is it really wise to throw an exception before calling inherited?
- if FileHandle = INVALID_HANDLE_VALUE then
- raise EJclTempFileStreamError.CreateRes(@RsFileStreamCreate);
- inherited Create(FileHandle);
- end;
- destructor TJclTempFileStream.Destroy;
- begin
- if THandle(Handle) <> INVALID_HANDLE_VALUE then
- FileClose(Handle);
- inherited Destroy;
- end;
- //=== { TJclFileMappingView } ================================================
- {$IFDEF MSWINDOWS}
- constructor TJclFileMappingView.Create(const FileMap: TJclCustomFileMapping;
- Access, Size: Cardinal; ViewOffset: Int64);
- var
- BaseAddress: Pointer;
- OffsetLow, OffsetHigh: Cardinal;
- begin
- inherited Create;
- if FileMap = nil then
- raise EJclFileMappingViewError.CreateRes(@RsViewNeedsMapping);
- FFileMapping := FileMap;
- // Offset must be a multiple of system memory allocation granularity
- RoundToAllocGranularity64(ViewOffset, FFileMapping.RoundViewOffset = rvUp);
- I64ToCardinals(ViewOffset, OffsetLow, OffsetHigh);
- FOffsetHigh := OffsetHigh;
- FOffsetLow := OffsetLow;
- BaseAddress := MapViewOfFile(FFileMapping.Handle, Access, FOffsetHigh, FOffsetLow, Size);
- if BaseAddress = nil then
- raise EJclFileMappingViewError.CreateRes(@RsCreateFileMappingView);
- // If we are mapping a file and size = 0 then MapViewOfFile has mapped the entire file. We must
- // figure out the size ourselves before we can call SetPointer. Since in case of failure to
- // retrieve the size we raise an exception, we also have to explicitly unmap the view which
- // otherwise would have been done by the destructor.
- if (Size = 0) and (FileMap is TJclFileMapping) then
- begin
- Size := GetFileSize(TJclFileMapping(FileMap).FFileHandle, nil);
- if Size = DWORD(-1) then
- begin
- UnMapViewOfFile(BaseAddress);
- raise EJclFileMappingViewError.CreateRes(@RsFailedToObtainSize);
- end;
- end;
- SetPointer(BaseAddress, Size);
- FFileMapping.FViews.Add(Self);
- end;
- constructor TJclFileMappingView.CreateAt(FileMap: TJclCustomFileMapping;
- Access, Size: Cardinal; ViewOffset: Int64; Address: Pointer);
- var
- BaseAddress: Pointer;
- OffsetLow, OffsetHigh: Cardinal;
- begin
- inherited Create;
- if FileMap = nil then
- raise EJclFileMappingViewError.CreateRes(@RsViewNeedsMapping);
- FFileMapping := FileMap;
- // Offset must be a multiple of system memory allocation granularity
- RoundToAllocGranularity64(ViewOffset, FFileMapping.RoundViewOffset = rvUp);
- RoundToAllocGranularityPtr(Address, FFileMapping.RoundViewOffset = rvUp);
- I64ToCardinals(ViewOffset, OffsetLow, OffsetHigh);
- FOffsetHigh := OffsetHigh;
- FOffsetLow := OffsetLow;
- BaseAddress := MapViewOfFileEx(FFileMapping.Handle, Access, FOffsetHigh,
- FOffsetLow, Size, Address);
- if BaseAddress = nil then
- raise EJclFileMappingViewError.CreateRes(@RsCreateFileMappingView);
- // If we are mapping a file and size = 0 then MapViewOfFile has mapped the entire file. We must
- // figure out the size ourselves before we can call SetPointer. Since in case of failure to
- // retrieve the size we raise an exception, we also have to explicitly unmap the view which
- // otherwise would have been done by the destructor.
- if (Size = 0) and (FileMap is TJclFileMapping) then
- begin
- Size := GetFileSize(TJclFileMapping(FileMap).FFileHandle, nil);
- if Size = DWORD(-1) then
- begin
- UnMapViewOfFile(BaseAddress);
- raise EJclFileMappingViewError.CreateRes(@RsFailedToObtainSize);
- end;
- end;
- SetPointer(BaseAddress, Size);
- FFileMapping.FViews.Add(Self);
- end;
- destructor TJclFileMappingView.Destroy;
- var
- IndexOfSelf: Integer;
- begin
- if Memory <> nil then
- begin
- UnMapViewOfFile(Memory);
- SetPointer(nil, 0);
- end;
- if FFileMapping <> nil then
- begin
- IndexOfSelf := FFileMapping.IndexOf(Self);
- if IndexOfSelf <> -1 then
- FFileMapping.FViews.Delete(IndexOfSelf);
- end;
- inherited Destroy;
- end;
- function TJclFileMappingView.Flush(const Count: Cardinal): Boolean;
- begin
- Result := FlushViewOfFile(Memory, Count);
- end;
- function TJclFileMappingView.GetIndex: Integer;
- begin
- Result := FFileMapping.IndexOf(Self);
- end;
- function TJclFileMappingView.GetOffset: Int64;
- begin
- CardinalsToI64(Result, FOffsetLow, FOffsetHigh);
- end;
- procedure TJclFileMappingView.LoadFromFile(const FileName: string);
- var
- Stream: TFileStream;
- begin
- Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
- try
- LoadFromStream(Stream);
- finally
- FreeAndNil(Stream);
- end;
- end;
- procedure TJclFileMappingView.LoadFromStream(const Stream: TStream);
- begin
- if Stream.Size > Size then
- raise EJclFileMappingViewError.CreateRes(@RsLoadFromStreamSize);
- Stream.Position := 0;
- Stream.ReadBuffer(Memory^, Stream.Size);
- end;
- function TJclFileMappingView.Write(const Buffer; Count: Integer): Longint;
- begin
- Result := 0;
- if (Size - Position) >= Count then
- begin
- System.Move(Buffer, Pointer(TJclAddr(Memory) + TJclAddr(Position))^, Count);
- Position := Position + Count;
- Result := Count;
- end;
- end;
- //=== { TJclCustomFileMapping } ==============================================
- constructor TJclCustomFileMapping.Create;
- begin
- inherited Create;
- FViews := TList.Create;
- FRoundViewOffset := rvDown;
- end;
- constructor TJclCustomFileMapping.Open(const Name: string;
- const InheritHandle: Boolean; const DesiredAccess: Cardinal);
- begin
- Create;
- InternalOpen(Name, InheritHandle, DesiredAccess);
- end;
- destructor TJclCustomFileMapping.Destroy;
- begin
- ClearViews;
- if FHandle <> 0 then
- CloseHandle(FHandle);
- FreeAndNil(FViews);
- inherited Destroy;
- end;
- function TJclCustomFileMapping.Add(const Access, Count: Cardinal; const Offset: Int64): Integer;
- var
- View: TJclFileMappingView;
- begin
- // The view adds itself to the FViews list
- View := TJclFileMappingView.Create(Self, Access, Count, Offset);
- Result := View.Index;
- end;
- function TJclCustomFileMapping.AddAt(const Access, Count: Cardinal;
- const Offset: Int64; const Address: Pointer): Integer;
- var
- View: TJclFileMappingView;
- begin
- // The view adds itself to the FViews list
- View := TJclFileMappingView.CreateAt(Self, Access, Count, Offset, Address);
- Result := View.Index;
- end;
- procedure TJclCustomFileMapping.ClearViews;
- var
- I: Integer;
- begin
- // Note that the view destructor removes the view object from the FViews list so we must loop
- // downwards from count to 0
- for I := FViews.Count - 1 downto 0 do
- TJclFileMappingView(FViews[I]).Free;
- end;
- procedure TJclCustomFileMapping.Delete(const Index: Integer);
- begin
- // Note that the view destructor removes itself from FViews
- TJclFileMappingView(FViews[Index]).Free;
- end;
- function TJclCustomFileMapping.GetCount: Integer;
- begin
- Result := FViews.Count;
- end;
- function TJclCustomFileMapping.GetView(Index: Integer): TJclFileMappingView;
- begin
- Result := TJclFileMappingView(FViews.Items[index]);
- end;
- function TJclCustomFileMapping.IndexOf(const View: TJclFileMappingView): Integer;
- begin
- Result := FViews.IndexOf(View);
- end;
- procedure TJclCustomFileMapping.InternalCreate(const FileHandle: THandle;
- const Name: string; const Protect: Cardinal; MaximumSize: Int64;
- SecAttr: PSecurityAttributes);
- var
- MaximumSizeLow, MaximumSizeHigh: Cardinal;
- begin
- FName := Name;
- I64ToCardinals(MaximumSize, MaximumSizeLow, MaximumSizeHigh);
- FHandle := CreateFileMapping(FileHandle, SecAttr, Protect, MaximumSizeHigh,
- MaximumSizeLow, PChar(Name));
- if FHandle = 0 then
- raise EJclFileMappingError.CreateRes(@RsCreateFileMapping);
- FExisted := GetLastError = ERROR_ALREADY_EXISTS;
- end;
- procedure TJclCustomFileMapping.InternalOpen(const Name: string;
- const InheritHandle: Boolean; const DesiredAccess: Cardinal);
- begin
- FExisted := True;
- FName := Name;
- FHandle := OpenFileMapping(DesiredAccess, InheritHandle, PChar(Name));
- if FHandle = 0 then
- raise EJclFileMappingError.CreateRes(@RsCreateFileMapping);
- end;
- //=== { TJclFileMapping } ====================================================
- constructor TJclFileMapping.Create(const FileName: string; FileMode: Cardinal;
- const Name: string; Protect: Cardinal; const MaximumSize: Int64;
- SecAttr: PSecurityAttributes);
- begin
- FFileHandle := INVALID_HANDLE_VALUE;
- inherited Create;
- FFileHandle := THandle(FileOpen(FileName, FileMode));
- if FFileHandle = INVALID_HANDLE_VALUE then
- raise EJclFileMappingError.CreateRes(@RsFileMappingOpenFile);
- InternalCreate(FFileHandle, Name, Protect, MaximumSize, SecAttr);
- end;
- constructor TJclFileMapping.Create(const FileHandle: THandle; const Name: string;
- Protect: Cardinal; const MaximumSize: Int64; SecAttr: PSecurityAttributes);
- begin
- FFileHandle := INVALID_HANDLE_VALUE;
- inherited Create;
- if FileHandle = INVALID_HANDLE_VALUE then
- raise EJclFileMappingError.CreateRes(@RsFileMappingInvalidHandle);
- InternalCreate(FileHandle, Name, Protect, MaximumSize, SecAttr);
- // Duplicate the handle into FFileHandle as opposed to assigning it directly. This will cause
- // FFileHandle to retrieve a unique copy which is independent of FileHandle. This makes the
- // remainder of the class, especially the destructor, easier. The caller will have to close it's
- // own copy of the handle explicitly.
- DuplicateHandle(GetCurrentProcess, FileHandle, GetCurrentProcess,
- @FFileHandle, 0, False, DUPLICATE_SAME_ACCESS);
- end;
- destructor TJclFileMapping.Destroy;
- begin
- if FFileHandle <> INVALID_HANDLE_VALUE then
- CloseHandle(FFileHandle);
- inherited Destroy;
- end;
- //=== { TJclSwapFileMapping } ================================================
- constructor TJclSwapFileMapping.Create(const Name: string; Protect: Cardinal;
- const MaximumSize: Int64; SecAttr: PSecurityAttributes);
- begin
- inherited Create;
- InternalCreate(INVALID_HANDLE_VALUE, Name, Protect, MaximumSize, SecAttr);
- end;
- //=== { TJclFileMappingStream } ==============================================
- constructor TJclFileMappingStream.Create(const FileName: string; FileMode: Word);
- var
- Protect, Access, Size: DWORD;
- BaseAddress: Pointer;
- begin
- inherited Create;
- FFileHandle := THandle(FileOpen(FileName, FileMode));
- if FFileHandle = INVALID_HANDLE_VALUE then
- RaiseLastOSError;
- if (FileMode and $0F) = fmOpenReadWrite then
- begin
- Protect := PAGE_WRITECOPY;
- Access := FILE_MAP_COPY;
- end
- else
- begin
- Protect := PAGE_READONLY;
- Access := FILE_MAP_READ;
- end;
- FMapping := CreateFileMapping(FFileHandle, nil, Protect, 0, 0, nil);
- if FMapping = 0 then
- begin
- Close;
- raise EJclFileMappingError.CreateRes(@RsCreateFileMapping);
- end;
- BaseAddress := MapViewOfFile(FMapping, Access, 0, 0, 0);
- if BaseAddress = nil then
- begin
- Close;
- raise EJclFileMappingViewError.CreateRes(@RsCreateFileMappingView);
- end;
- Size := GetFileSize(FFileHandle, nil);
- if Size = DWORD(-1) then
- begin
- UnMapViewOfFile(BaseAddress);
- Close;
- raise EJclFileMappingViewError.CreateRes(@RsFailedToObtainSize);
- end;
- SetPointer(BaseAddress, Size);
- end;
- destructor TJclFileMappingStream.Destroy;
- begin
- Close;
- inherited Destroy;
- end;
- procedure TJclFileMappingStream.Close;
- begin
- if Memory <> nil then
- begin
- UnMapViewOfFile(Memory);
- SetPointer(nil, 0);
- end;
- if FMapping <> 0 then
- begin
- CloseHandle(FMapping);
- FMapping := 0;
- end;
- if FFileHandle <> INVALID_HANDLE_VALUE then
- begin
- FileClose(FFileHandle);
- FFileHandle := INVALID_HANDLE_VALUE;
- end;
- end;
- function TJclFileMappingStream.Write(const Buffer; Count: Integer): Longint;
- begin
- Result := 0;
- if (Size - Position) >= Count then
- begin
- System.Move(Buffer, Pointer(TJclAddr(Memory) + TJclAddr(Position))^, Count);
- Position := Position + Count;
- Result := Count;
- end;
- end;
- {$ENDIF MSWINDOWS}
- //=== { TJclAnsiMappedTextReader } ===========================================
- constructor TJclAnsiMappedTextReader.Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean;
- const AIndexOption: TJclMappedTextReaderIndex);
- begin
- inherited Create;
- FMemoryStream := MemoryStream;
- FFreeStream := FreeStream;
- FIndexOption := AIndexOption;
- Init;
- end;
- constructor TJclAnsiMappedTextReader.Create(const FileName: TFileName;
- const AIndexOption: TJclMappedTextReaderIndex);
- begin
- inherited Create;
- {$IFDEF MSWINDOWS}
- FMemoryStream := TJclFileMappingStream.Create(FileName);
- {$ELSE ~ MSWINDOWS}
- FMemoryStream := TMemoryStream.Create;
- TMemoryStream(FMemoryStream).LoadFromFile(FileName);
- {$ENDIF ~ MSWINDOWS}
- FFreeStream := True;
- FIndexOption := AIndexOption;
- Init;
- end;
- destructor TJclAnsiMappedTextReader.Destroy;
- begin
- if FFreeStream then
- FMemoryStream.Free;
- FreeMem(FIndex);
- inherited Destroy;
- end;
- procedure TJclAnsiMappedTextReader.AssignTo(Dest: TPersistent);
- begin
- if Dest is TStrings then
- begin
- GoBegin;
- TStrings(Dest).BeginUpdate;
- try
- while not Eof do
- TStrings(Dest).Add(string(ReadLn));
- finally
- TStrings(Dest).EndUpdate;
- end;
- end
- else
- inherited AssignTo(Dest);
- end;
- procedure TJclAnsiMappedTextReader.CreateIndex;
- var
- P, LastLineStart: PAnsiChar;
- I: Integer;
- begin
- {$RANGECHECKS OFF}
- P := FContent;
- I := 0;
- LastLineStart := P;
- while P < FEnd do
- begin
- // CRLF, CR, LF and LFCR are seen as valid sets of chars for EOL marker
- if CharIsReturn(Char(P^)) then
- begin
- if I and $FFFF = 0 then
- ReallocMem(FIndex, (I + $10000) * SizeOf(Pointer));
- FIndex[I] := LastLineStart;
- Inc(I);
- case P^ of
- NativeLineFeed:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeCarriageReturn) then
- Inc(P);
- end;
- NativeCarriageReturn:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeLineFeed) then
- Inc(P);
- end;
- end;
- LastLineStart := P;
- end
- else
- Inc(P);
- end;
- if P > LastLineStart then
- begin
- ReallocMem(FIndex, (I + 1) * SizeOf(Pointer));
- FIndex[I] := LastLineStart;
- Inc(I);
- end
- else
- ReallocMem(FIndex, I * SizeOf(Pointer));
- FLineCount := I;
- {$IFDEF RANGECHECKS_ON}
- {$RANGECHECKS ON}
- {$ENDIF RANGECHECKS_ON}
- end;
- function TJclAnsiMappedTextReader.GetEof: Boolean;
- begin
- Result := FPosition >= FEnd;
- end;
- function TJclAnsiMappedTextReader.GetAsString: AnsiString;
- begin
- SetString(Result, Content, Size);
- end;
- function TJclAnsiMappedTextReader.GetChars(Index: Integer): AnsiChar;
- begin
- if (Index < 0) or (Index >= Size) then
- raise EJclError.CreateRes(@RsFileIndexOutOfRange);
- Result := AnsiChar(PByte(FContent + Index)^);
- end;
- function TJclAnsiMappedTextReader.GetLineCount: Integer;
- var
- P: PAnsiChar;
- begin
- if FLineCount = -1 then
- begin
- FLineCount := 0;
- if FContent < FEnd then
- begin
- P := FContent;
- while P < FEnd do
- begin
- case P^ of
- NativeLineFeed:
- begin
- Inc(FLineCount);
- Inc(P);
- if (P < FEnd) and (P^ = NativeCarriageReturn) then
- Inc(P);
- end;
- NativeCarriageReturn:
- begin
- Inc(FLineCount);
- Inc(P);
- if (P < FEnd) and (P^ = NativeLineFeed) then
- Inc(P);
- end;
- else
- Inc(P);
- end;
- end;
- if (P = FEnd) and (P > FContent) and not CharIsReturn(Char((P-1)^)) then
- Inc(FLineCount);
- end;
- end;
- Result := FLineCount;
- end;
- function TJclAnsiMappedTextReader.GetLines(LineNumber: Integer): AnsiString;
- var
- P: PAnsiChar;
- begin
- P := PtrFromLine(LineNumber);
- Result := StringFromPosition(P);
- end;
- function TJclAnsiMappedTextReader.GetPosition: Integer;
- begin
- Result := FPosition - FContent;
- end;
- procedure TJclAnsiMappedTextReader.GoBegin;
- begin
- Position := 0;
- end;
- procedure TJclAnsiMappedTextReader.Init;
- begin
- FContent := FMemoryStream.Memory;
- FSize := FMemoryStream.Size;
- FEnd := FContent + FSize;
- FPosition := FContent;
- FLineCount := -1;
- FLastLineNumber := 0;
- FLastPosition := FContent;
- if IndexOption = tiFull then
- CreateIndex;
- end;
- function TJclAnsiMappedTextReader.GetPositionFromLine(LineNumber: Integer): Integer;
- var
- P: PAnsiChar;
- begin
- P := PtrFromLine(LineNumber);
- if P = nil then
- Result := -1
- else
- Result := P - FContent;
- end;
- function TJclAnsiMappedTextReader.PtrFromLine(LineNumber: Integer): PAnsiChar;
- var
- LineOffset: Integer;
- begin
- Result := nil;
- {$RANGECHECKS OFF}
- if (IndexOption <> tiNoIndex) and (LineNumber < FLineCount) and (FIndex[LineNumber] <> nil) then
- Result := FIndex[LineNumber]
- {$IFDEF RANGECHECKS_ON}
- {$RANGECHECKS ON}
- {$ENDIF RANGECHECKS_ON}
- else
- begin
- LineOffset := LineNumber - FLastLineNumber;
- if (FLineCount <> -1) and (LineNumber > 0) then
- begin
- if -LineOffset > LineNumber then
- begin
- FLastLineNumber := 0;
- FLastPosition := FContent;
- LineOffset := LineNumber;
- end
- else
- if LineOffset > FLineCount - LineNumber then
- begin
- FLastLineNumber := FLineCount;
- FLastPosition := FEnd;
- LineOffset := LineNumber - FLineCount;
- end;
- end;
- if LineNumber <= 0 then
- Result := FContent
- else
- if LineOffset = 0 then
- Result := FLastPosition
- else
- if LineOffset > 0 then
- begin
- Result := FLastPosition;
- while (Result < FEnd) and (LineOffset > 0) do
- begin
- case Result^ of
- NativeLineFeed:
- begin
- Dec(LineOffset);
- Inc(Result);
- if (Result < FEnd) and (Result^ = NativeCarriageReturn) then
- Inc(Result);
- end;
- NativeCarriageReturn:
- begin
- Dec(LineOffset);
- Inc(Result);
- if (Result < FEnd) and (Result^ = NativeLineFeed) then
- Inc(Result);
- end;
- else
- Inc(Result);
- end;
- end;
- end
- else
- if LineOffset < 0 then
- begin
- Result := FLastPosition;
- while (Result > FContent) and (LineOffset < 1) do
- begin
- Dec(Result);
- case Result^ of
- NativeLineFeed:
- begin
- Inc(LineOffset);
- if LineOffset >= 1 then
- Inc(Result)
- else
- if (Result > FContent) and ((Result-1)^ = NativeCarriageReturn) then
- Dec(Result);
- end;
- NativeCarriageReturn:
- begin
- Inc(LineOffset);
- if LineOffset >= 1 then
- Inc(Result)
- else
- if (Result > FContent) and ((Result-1)^ = NativeLineFeed) then
- Dec(Result);
- end;
- end;
- end;
- end;
- FLastLineNumber := LineNumber;
- FLastPosition := Result;
- end;
- end;
- function TJclAnsiMappedTextReader.Read: AnsiChar;
- begin
- if FPosition >= FEnd then
- Result := #0
- else
- begin
- Result := FPosition^;
- Inc(FPosition);
- end;
- end;
- function TJclAnsiMappedTextReader.ReadLn: AnsiString;
- begin
- Result := StringFromPosition(FPosition);
- end;
- procedure TJclAnsiMappedTextReader.SetPosition(const Value: Integer);
- begin
- FPosition := FContent + Value;
- end;
- function TJclAnsiMappedTextReader.StringFromPosition(var StartPos: PAnsiChar): AnsiString;
- var
- P: PAnsiChar;
- begin
- if (StartPos = nil) or (StartPos >= FEnd) then
- Result := ''
- else
- begin
- P := StartPos;
- while (P < FEnd) and (not CharIsReturn(Char(P^))) do
- Inc(P);
- SetString(Result, StartPos, P - StartPos);
- if P < FEnd then
- begin
- case P^ of
- NativeLineFeed:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeCarriageReturn) then
- Inc(P);
- end;
- NativeCarriageReturn:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeLineFeed) then
- Inc(P);
- end;
- end;
- end;
- StartPos := P;
- end;
- end;
- //=== { TJclWideMappedTextReader } ===========================================
- constructor TJclWideMappedTextReader.Create(MemoryStream: TCustomMemoryStream; FreeStream: Boolean;
- const AIndexOption: TJclMappedTextReaderIndex);
- begin
- inherited Create;
- FMemoryStream := MemoryStream;
- FFreeStream := FreeStream;
- FIndexOption := AIndexOption;
- Init;
- end;
- constructor TJclWideMappedTextReader.Create(const FileName: TFileName;
- const AIndexOption: TJclMappedTextReaderIndex);
- begin
- inherited Create;
- {$IFDEF MSWINDOWS}
- FMemoryStream := TJclFileMappingStream.Create(FileName);
- {$ELSE ~ MSWINDOWS}
- FMemoryStream := TMemoryStream.Create;
- TMemoryStream(FMemoryStream).LoadFromFile(FileName);
- {$ENDIF ~ MSWINDOWS}
- FFreeStream := True;
- FIndexOption := AIndexOption;
- Init;
- end;
- destructor TJclWideMappedTextReader.Destroy;
- begin
- if FFreeStream then
- FMemoryStream.Free;
- FreeMem(FIndex);
- inherited Destroy;
- end;
- procedure TJclWideMappedTextReader.AssignTo(Dest: TPersistent);
- begin
- if Dest is TStrings then
- begin
- GoBegin;
- TStrings(Dest).BeginUpdate;
- try
- while not Eof do
- TStrings(Dest).Add(string(ReadLn));
- finally
- TStrings(Dest).EndUpdate;
- end;
- end
- else
- inherited AssignTo(Dest);
- end;
- procedure TJclWideMappedTextReader.CreateIndex;
- var
- P, LastLineStart: PWideChar;
- I: Integer;
- begin
- {$RANGECHECKS OFF}
- P := FContent;
- I := 0;
- LastLineStart := P;
- while P < FEnd do
- begin
- // CRLF, CR, LF and LFCR are seen as valid sets of chars for EOL marker
- if CharIsReturn(Char(P^)) then
- begin
- if I and $FFFF = 0 then
- ReallocMem(FIndex, (I + $10000) * SizeOf(Pointer));
- FIndex[I] := LastLineStart;
- Inc(I);
- case P^ of
- NativeLineFeed:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeCarriageReturn) then
- Inc(P);
- end;
- NativeCarriageReturn:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeLineFeed) then
- Inc(P);
- end;
- end;
- LastLineStart := P;
- end
- else
- Inc(P);
- end;
- if P > LastLineStart then
- begin
- ReallocMem(FIndex, (I + 1) * SizeOf(Pointer));
- FIndex[I] := LastLineStart;
- Inc(I);
- end
- else
- ReallocMem(FIndex, I * SizeOf(Pointer));
- FLineCount := I;
- {$IFDEF RANGECHECKS_ON}
- {$RANGECHECKS ON}
- {$ENDIF RANGECHECKS_ON}
- end;
- function TJclWideMappedTextReader.GetEof: Boolean;
- begin
- Result := FPosition >= FEnd;
- end;
- function TJclWideMappedTextReader.GetAsString: WideString;
- begin
- SetString(Result, Content, Size);
- end;
- function TJclWideMappedTextReader.GetChars(Index: Integer): WideChar;
- begin
- if (Index < 0) or (Index >= Size) then
- raise EJclError.CreateRes(@RsFileIndexOutOfRange);
- Result := WideChar(PByte(FContent + Index)^);
- end;
- function TJclWideMappedTextReader.GetLineCount: Integer;
- var
- P: PWideChar;
- begin
- if FLineCount = -1 then
- begin
- FLineCount := 0;
- if FContent < FEnd then
- begin
- P := FContent;
- while P < FEnd do
- begin
- case P^ of
- NativeLineFeed:
- begin
- Inc(FLineCount);
- Inc(P);
- if (P < FEnd) and (P^ = NativeCarriageReturn) then
- Inc(P);
- end;
- NativeCarriageReturn:
- begin
- Inc(FLineCount);
- Inc(P);
- if (P < FEnd) and (P^ = NativeLineFeed) then
- Inc(P);
- end;
- else
- Inc(P);
- end;
- end;
- if (P = FEnd) and (P > FContent) and not CharIsReturn(Char((P-1)^)) then
- Inc(FLineCount);
- end;
- end;
- Result := FLineCount;
- end;
- function TJclWideMappedTextReader.GetLines(LineNumber: Integer): WideString;
- var
- P: PWideChar;
- begin
- P := PtrFromLine(LineNumber);
- Result := StringFromPosition(P);
- end;
- function TJclWideMappedTextReader.GetPosition: Integer;
- begin
- Result := FPosition - FContent;
- end;
- procedure TJclWideMappedTextReader.GoBegin;
- begin
- Position := 0;
- end;
- procedure TJclWideMappedTextReader.Init;
- begin
- FContent := FMemoryStream.Memory;
- FSize := FMemoryStream.Size;
- FEnd := FContent + FSize;
- FPosition := FContent;
- FLineCount := -1;
- FLastLineNumber := 0;
- FLastPosition := FContent;
- if IndexOption = tiFull then
- CreateIndex;
- end;
- function TJclWideMappedTextReader.GetPositionFromLine(LineNumber: Integer): Integer;
- var
- P: PWideChar;
- begin
- P := PtrFromLine(LineNumber);
- if P = nil then
- Result := -1
- else
- Result := P - FContent;
- end;
- function TJclWideMappedTextReader.PtrFromLine(LineNumber: Integer): PWideChar;
- var
- LineOffset: Integer;
- begin
- Result := nil;
- {$RANGECHECKS OFF}
- if (IndexOption <> tiNoIndex) and (LineNumber < FLineCount) and (FIndex[LineNumber] <> nil) then
- Result := FIndex[LineNumber]
- {$IFDEF RANGECHECKS_ON}
- {$RANGECHECKS ON}
- {$ENDIF RANGECHECKS_ON}
- else
- begin
- LineOffset := LineNumber - FLastLineNumber;
- if (FLineCount <> -1) and (LineNumber > 0) then
- begin
- if -LineOffset > LineNumber then
- begin
- FLastLineNumber := 0;
- FLastPosition := FContent;
- LineOffset := LineNumber;
- end
- else
- if LineOffset > FLineCount - LineNumber then
- begin
- FLastLineNumber := FLineCount;
- FLastPosition := FEnd;
- LineOffset := LineNumber - FLineCount;
- end;
- end;
- if LineNumber <= 0 then
- Result := FContent
- else
- if LineOffset = 0 then
- Result := FLastPosition
- else
- if LineOffset > 0 then
- begin
- Result := FLastPosition;
- while (Result < FEnd) and (LineOffset > 0) do
- begin
- case Result^ of
- NativeLineFeed:
- begin
- Dec(LineOffset);
- Inc(Result);
- if (Result < FEnd) and (Result^ = NativeCarriageReturn) then
- Inc(Result);
- end;
- NativeCarriageReturn:
- begin
- Dec(LineOffset);
- Inc(Result);
- if (Result < FEnd) and (Result^ = NativeLineFeed) then
- Inc(Result);
- end;
- else
- Inc(Result);
- end;
- end;
- end
- else
- if LineOffset < 0 then
- begin
- Result := FLastPosition;
- while (Result > FContent) and (LineOffset < 1) do
- begin
- Dec(Result);
- case Result^ of
- NativeLineFeed:
- begin
- Inc(LineOffset);
- if LineOffset >= 1 then
- Inc(Result)
- else
- if (Result > FContent) and ((Result-1)^ = NativeCarriageReturn) then
- Dec(Result);
- end;
- NativeCarriageReturn:
- begin
- Inc(LineOffset);
- if LineOffset >= 1 then
- Inc(Result)
- else
- if (Result > FContent) and ((Result-1)^ = NativeLineFeed) then
- Dec(Result);
- end;
- end;
- end;
- end;
- FLastLineNumber := LineNumber;
- FLastPosition := Result;
- end;
- end;
- function TJclWideMappedTextReader.Read: WideChar;
- begin
- if FPosition >= FEnd then
- Result := #0
- else
- begin
- Result := FPosition^;
- Inc(FPosition);
- end;
- end;
- function TJclWideMappedTextReader.ReadLn: WideString;
- begin
- Result := StringFromPosition(FPosition);
- end;
- procedure TJclWideMappedTextReader.SetPosition(const Value: Integer);
- begin
- FPosition := FContent + Value;
- end;
- function TJclWideMappedTextReader.StringFromPosition(var StartPos: PWideChar): WideString;
- var
- P: PWideChar;
- begin
- if (StartPos = nil) or (StartPos >= FEnd) then
- Result := ''
- else
- begin
- P := StartPos;
- while (P < FEnd) and (not CharIsReturn(Char(P^))) do
- Inc(P);
- SetString(Result, StartPos, P - StartPos);
- if P < FEnd then
- begin
- case P^ of
- NativeLineFeed:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeCarriageReturn) then
- Inc(P);
- end;
- NativeCarriageReturn:
- begin
- Inc(P);
- if (P < FEnd) and (P^ = NativeLineFeed) then
- Inc(P);
- end;
- end;
- end;
- StartPos := P;
- end;
- end;
- function CharIsDriveLetter(const C: Char): Boolean;
- begin
- case C of
- 'a'..'z',
- 'A'..'Z':
- Result := True;
- else
- Result := False;
- end;
- end;
- //=== Path manipulation ======================================================
- function PathAddSeparator(const Path: string): string;
- begin
- Result := Path;
- if (Path = '') or (Path[Length(Path)] <> DirDelimiter) then
- Result := Path + DirDelimiter;
- end;
- function PathAddExtension(const Path, Extension: string): string;
- begin
- Result := Path;
- // (obones) Extension may not contain the leading dot while ExtractFileExt
- // always returns it. Hence the need to use StrEnsurePrefix for the SameText
- // test to return an accurate value.
- if (Path <> '') and (Extension <> '') and
- not SameText(ExtractFileExt(Path), StrEnsurePrefix('.', Extension)) then
- begin
- if Path[Length(Path)] = '.' then
- Delete(Result, Length(Path), 1);
- if Extension[1] = '.' then
- Result := Result + Extension
- else
- Result := Result + '.' + Extension;
- end;
- end;
- function PathAppend(const Path, Append: string): string;
- var
- PathLength: Integer;
- B1, B2: Boolean;
- begin
- if Append = '' then
- Result := Path
- else
- begin
- PathLength := Length(Path);
- if PathLength = 0 then
- Result := Append
- else
- begin
- // The following code may look a bit complex but all it does is add Append to Path ensuring
- // that there is one and only one path separator character between them
- B1 := Path[PathLength] = DirDelimiter;
- B2 := Append[1] = DirDelimiter;
- if B1 and B2 then
- Result := Copy(Path, 1, PathLength - 1) + Append
- else
- begin
- if not (B1 or B2) then
- Result := Path + DirDelimiter + Append
- else
- Result := Path + Append;
- end;
- end;
- end;
- end;
- function PathBuildRoot(const Drive: Byte): string;
- begin
- {$IFDEF UNIX}
- Result := DirDelimiter;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- // Remember, Win32 only allows 'a' to 'z' as drive letters (mapped to 0..25)
- if Drive < 26 then
- Result := Char(Drive + 65) + ':\'
- else
- raise EJclPathError.CreateResFmt(@RsPathInvalidDrive, [IntToStr(Drive)]);
- {$ENDIF MSWINDOWS}
- end;
- function PathCanonicalize(const Path: string): string;
- var
- List: TStringList;
- S: string;
- I, K: Integer;
- IsAbsolute: Boolean;
- begin
- I := Pos(':', Path); // for Windows' sake
- K := Pos(DirDelimiter, Path);
- IsAbsolute := K - I = 1;
- if IsAbsolute then begin
- if Copy(Path, 1, Length(PathUncPrefix)) = PathUncPrefix then // UNC path
- K := 2;
- end else
- K := I;
- if K = 0 then
- S := Path
- else
- S := Copy(Path, K + 1, Length(Path));
- List := TStringList.Create;
- try
- StrIToStrings(S, DirDelimiter, List, True);
- I := 0;
- while I < List.Count do
- begin
- if List[I] = '.' then
- List.Delete(I)
- else
- if (IsAbsolute or (I > 0) and not (List[I-1] = '..')) and (List[I] = '..') then
- begin
- List.Delete(I);
- if I > 0 then
- begin
- Dec(I);
- List.Delete(I);
- end;
- end
- else Inc(I);
- end;
- Result := StringsToStr(List, DirDelimiter, True);
- finally
- List.Free;
- end;
- if K > 0 then
- Result := Copy(Path, 1, K) + Result
- else
- if Result = '' then
- Result := '.';
- end;
- function PathCommonPrefix(const Path1, Path2: string): Integer;
- var
- Index1, Index2: Integer;
- LastSeparator, LenS1: Integer;
- S1, S2: string;
- begin
- Result := 0;
- if (Path1 <> '') and (Path2 <> '') then
- begin
- // Initialize P1 to the shortest of the two paths so that the actual comparison loop below can
- // use the terminating #0 of that string to terminate the loop.
- if Length(Path1) <= Length(Path2) then
- begin
- S1 := Path1;
- S2 := Path2;
- end
- else
- begin
- S1 := Path2;
- S2 := Path1;
- end;
- Index1 := 1;
- Index2 := 1;
- LenS1 := Length(S1);
- LastSeparator := 0;
- while (S1[Index1] = S2[Index2]) and (Index1 <= LenS1) do
- begin
- Inc(Result);
- if (S1[Index1] = DirDelimiter) or (S1[Index1] = ':') then
- LastSeparator := Result;
- Inc(Index1);
- Inc(Index2);
- end;
- if (LastSeparator < Result) and (Index1 <= LenS1) then
- Result := LastSeparator;
- end;
- end;
- {$IFDEF MSWINDOWS}
- function PathCompactPath(const DC: HDC; const Path: string;
- const Width: Integer; CmpFmt: TCompactPath): string;
- const
- Compacts: array [TCompactPath] of Cardinal = (DT_PATH_ELLIPSIS, DT_END_ELLIPSIS);
- var
- TextRect: TRect;
- Fmt: Cardinal;
- begin
- Result := '';
- if (DC <> 0) and (Path <> '') and (Width > 0) then
- begin
- { Here's a note from the Platform SDK to explain the + 5 in the call below:
- "If dwDTFormat includes DT_MODIFYSTRING, the function could add up to four additional characters
- to this string. The buffer containing the string should be large enough to accommodate these
- extra characters." }
- SetString(Result, PChar(Path), Length(Path) + 4);
- TextRect := Rect(0, 0, Width, 255);
- Fmt := DT_MODIFYSTRING or DT_CALCRECT or Compacts[CmpFmt];
- if DrawTextEx(DC, PChar(Result), -1, TextRect, Fmt, nil) <> 0 then
- StrResetLength(Result)
- else
- Result := ''; // in case of error
- end;
- end;
- {$ENDIF MSWINDOWS}
- procedure PathExtractElements(const Source: string; var Drive, Path, FileName, Ext: string);
- begin
- Drive := ExtractFileDrive(Source);
- Path := ExtractFilePath(Source);
- // Path includes drive so remove that
- if Drive <> '' then
- Delete(Path, 1, Length(Drive));
- // add/remove separators
- Drive := PathAddSeparator(Drive);
- Path := PathRemoveSeparator(Path);
- if (Path <> '') and (Path[1] = DirDelimiter) then
- Delete(Path, 1, 1);
- // and extract the remaining elements
- FileName := PathExtractFileNameNoExt(Source);
- Ext := ExtractFileExt(Source);
- end;
- function PathExtractFileDirFixed(const S: string): string;
- begin
- Result := PathAddSeparator(ExtractFileDir(S));
- end;
- function PathExtractFileNameNoExt(const Path: string): string;
- begin
- Result := PathRemoveExtension(ExtractFileName(Path));
- end;
- function PathExtractPathDepth(const Path: string; Depth: Integer): string;
- var
- List: TStringList;
- LocalPath: string;
- I: Integer;
- begin
- List := TStringList.Create;
- try
- if IsDirectory(Path) then
- LocalPath := Path
- else
- LocalPath := ExtractFilePath(Path);
- StrIToStrings(LocalPath, DirDelimiter, List, True);
- I := Depth + 1;
- if PathIsUNC(LocalPath) then
- I := I + 2;
- while I < List.Count do
- List.Delete(I);
- Result := PathAddSeparator(StringsToStr(List, DirDelimiter, True));
- finally
- List.Free;
- end;
- end;
- // Notes: maybe this function should first apply PathCanonicalize() ?
- function PathGetDepth(const Path: string): Integer;
- var
- List: TStringList;
- LocalPath: string;
- I, Start: Integer;
- begin
- Result := 0;
- List := TStringList.Create;
- try
- if IsDirectory(Path) then
- LocalPath := Path
- else
- LocalPath := ExtractFilePath(Path);
- StrIToStrings(LocalPath, DirDelimiter, List, False);
- if PathIsUNC(LocalPath) then
- Start := 1
- else
- Start := 0;
- for I := Start to List.Count - 1 do
- begin
- if Pos(':', List[I]) = 0 then
- Inc(Result);
- end;
- finally
- List.Free;
- end;
- end;
- {$IFDEF MSWINDOWS}
- function ShellGetLongPathName(const Path: string): string;
- {$IFDEF FPC}
- // As of 2004-10-17, FPC's ShlObj unit is just a dummy
- begin
- Result := Path;
- end;
- {$ElSE ~FPC}
- var
- PIDL: PItemIDList;
- Desktop: IShellFolder;
- {$IFNDEF SUPPORTS_UNICODE}
- AnsiName: string;
- WideName: array [0..MAX_PATH] of WideChar;
- {$ENDIF ~SUPPORTS_UNICODE}
- Eaten, Attr: ULONG; // both unused but API requires them (incorrect translation)
- begin
- Result := Path;
- if Path <> '' then
- begin
- if Succeeded(SHGetDesktopFolder(Desktop)) then
- begin
- {$IFDEF SUPPORTS_UNICODE}
- if Succeeded(Desktop.ParseDisplayName(0, nil, PChar(Path), Eaten, PIDL, Attr)) then
- try
- SetLength(Result, MAX_PATH);
- if SHGetPathFromIDList(PIDL, PChar(Result)) then
- StrResetLength(Result);
- finally
- CoTaskMemFree(PIDL);
- end;
- {$ELSE ~SUPPORTS_UNICODE}
- MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PAnsiChar(Path), -1, WideName, MAX_PATH);
- if Succeeded(Desktop.ParseDisplayName(0, nil, WideName, Eaten, PIDL, Attr)) then
- try
- SetLength(AnsiName, MAX_PATH);
- if SHGetPathFromIDList(PIDL, PChar(AnsiName)) then
- StrResetLength(AnsiName);
- Result := AnsiName;
- finally
- CoTaskMemFree(PIDL);
- end;
- {$ENDIF ~SUPPORTS_UNICODE}
- end;
- end;
- end;
- {$ENDIF ~FPC}
- { TODO : Move RTDL code over to JclWin32 when JclWin32 gets overhauled. }
- var
- _Kernel32Handle: TModuleHandle = INVALID_MODULEHANDLE_VALUE;
- _GetLongPathName: function (lpszShortPath: PChar; lpszLongPath: PChar;
- cchBuffer: DWORD): DWORD; stdcall;
- function Kernel32Handle: HMODULE;
- begin
- JclSysUtils.LoadModule(_Kernel32Handle, kernel32);
- Result := _Kernel32Handle;
- end;
- function RtdlGetLongPathName(const Path: string): string;
- begin
- Result := Path;
- if not Assigned(_GetLongPathName) then
- _GetLongPathName := GetModuleSymbol(Kernel32Handle, 'GetLongPathName' + AWSuffix);
- if not Assigned(_GetLongPathName) then
- Result := ShellGetLongPathName(Path)
- else
- begin
- SetLength(Result, MAX_PATH);
- SetLength(Result, _GetLongPathName(PChar(Path), PChar(Result), MAX_PATH));
- end;
- end;
- function PathGetLongName(const Path: string): string;
- begin
- if Pos('::', Path) > 0 then // Path contains '::{<GUID>}'
- Result := ShellGetLongPathName(Path)
- else
- Result := RtdlGetLongPathName(Path);
- if Result = '' then
- Result := Path;
- end;
- function PathGetShortName(const Path: string): string;
- var
- Required: Integer;
- begin
- Result := Path;
- Required := GetShortPathName(PChar(Path), nil, 0);
- if Required <> 0 then
- begin
- SetLength(Result, Required);
- Required := GetShortPathName(PChar(Path), PChar(Result), Required);
- if (Required <> 0) and (Required = Length(Result) - 1) then
- SetLength(Result, Required)
- else
- Result := Path;
- end;
- end;
- {$ENDIF MSWINDOWS}
- function PathGetRelativePath(Origin, Destination: string): string;
- var
- {$IFDEF MSWINDOWS}
- OrigDrive: string;
- DestDrive: string;
- {$ENDIF MSWINDOWS}
- OrigList: TStringList;
- DestList: TStringList;
- DiffIndex: Integer;
- I: Integer;
- function StartsFromRoot(const Path: string): Boolean;
- {$IFDEF MSWINDOWS}
- var
- I: Integer;
- begin
- I := Length(ExtractFileDrive(Path));
- Result := (Length(Path) > I) and (Path[I + 1] = DirDelimiter);
- end;
- {$ELSE ~MSWINDOWS}
- begin
- Result := Pos(DirDelimiter, Path) = 1;
- end;
- {$ENDIF ~MSWINDOWS}
- function Equal(const Path1, Path2: string): Boolean;
- begin
- {$IFDEF MSWINDOWS} // case insensitive
- Result := StrSame(Path1, Path2);
- {$ELSE ~MSWINDOWS} // case sensitive
- Result := Path1 = Path2;
- {$ENDIF ~MSWINDOWS}
- end;
- begin
- Origin := PathCanonicalize(Origin);
- Destination := PathCanonicalize(Destination);
- {$IFDEF MSWINDOWS}
- OrigDrive := ExtractFileDrive(Origin);
- DestDrive := ExtractFileDrive(Destination);
- {$ENDIF MSWINDOWS}
- if Equal(Origin, Destination) or (Destination = '') then
- Result := '.'
- else
- if Origin = '' then
- Result := Destination
- else
- {$IFDEF MSWINDOWS}
- if (DestDrive <> '') and ((OrigDrive = '') or ((OrigDrive <> '') and not Equal(OrigDrive, DestDrive))) then
- Result := Destination
- else
- if (OrigDrive <> '') and (Pos(DirDelimiter, Destination) = 1)
- and not Equal(PathUncPrefix,Copy(Destination,1,Length(PathUncPrefix))) then
- Result := OrigDrive + Destination // prepend drive part from Origin
- else
- {$ENDIF MSWINDOWS}
- if StartsFromRoot(Origin) and not StartsFromRoot(Destination) then
- Result := StrEnsureSuffix(DirDelimiter, Origin) +
- StrEnsureNoPrefix(DirDelimiter, Destination)
- else
- begin
- // create a list of paths as separate strings
- OrigList := TStringList.Create;
- DestList := TStringList.Create;
- try
- // NOTE: DO NOT USE DELIMITER AND DELIMITEDTEXT FROM
- // TSTRINGS, THEY WILL SPLIT PATHS WITH SPACES !!!!
- StrToStrings(Origin, DirDelimiter, OrigList, False);
- StrToStrings(Destination, DirDelimiter, DestList, False);
- begin
- // find the first directory that is not the same
- DiffIndex := OrigList.Count;
- if DestList.Count < DiffIndex then
- DiffIndex := DestList.Count;
- for I := 0 to DiffIndex - 1 do
- if not Equal(OrigList[I], DestList[I]) then
- begin
- DiffIndex := I;
- Break;
- end;
- Result := StrRepeat('..' + DirDelimiter, OrigList.Count - DiffIndex);
- Result := PathRemoveSeparator(Result);
- for I := DiffIndex to DestList.Count - 1 do
- begin
- if Result <> '' then
- Result := Result + DirDelimiter;
- Result := Result + DestList[i];
- end;
- end;
- finally
- DestList.Free;
- OrigList.Free;
- end;
- end;
- end;
- function PathGetTempPath: string;
- {$IFDEF MSWINDOWS}
- var
- BufSize: Cardinal;
- begin
- BufSize := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetTempPath(0, nil);
- SetLength(Result, BufSize);
- { TODO : Check length (-1 or not) }
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetTempPath(BufSize, PChar(Result));
- StrResetLength(Result);
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- begin
- Result := GetEnvironmentVariable('TMPDIR');
- end;
- {$ENDIF UNIX}
- function PathIsAbsolute(const Path: string): Boolean;
- {$IFDEF MSWINDOWS}
- var
- I: Integer;
- {$ENDIF MSWINDOWS}
- begin
- Result := False;
- if Path <> '' then
- begin
- {$IFDEF UNIX}
- Result := (Path[1] = DirDelimiter);
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- if not PathIsUnc(Path) then
- begin
- I := 0;
- if PathIsDiskDevice(Path) then
- I := Length(PathDevicePrefix);
- Result := (Length(Path) > I + 2) and CharIsDriveLetter(Path[I + 1]) and
- (Path[I + 2] = ':') and (Path[I + 3] = DirDelimiter);
- end
- else
- Result := True;
- {$ENDIF MSWINDOWS}
- end;
- end;
- function PathIsChild(const Path, Base: string): Boolean;
- var
- L: Integer;
- B, P: string;
- begin
- Result := False;
- B := PathRemoveSeparator(Base);
- P := PathRemoveSeparator(Path);
- // an empty path or one that's not longer than base cannot be a subdirectory
- L := Length(B);
- if (P = '') or (L >= Length(P)) then
- Exit;
- {$IFDEF MSWINDOWS}
- Result := AnsiSameText(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- Result := AnsiSameStr(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
- {$ENDIF UNIX}
- end;
- function PathIsEqualOrChild(const Path, Base: string): Boolean;
- var
- L: Integer;
- B, P: string;
- begin
- B := PathRemoveSeparator(Base);
- P := PathRemoveSeparator(Path);
- // an empty path or one that's not longer than base cannot be a subdirectory
- L := Length(B);
- {$IFDEF MSWINDOWS}
- Result := AnsiSameText(P, B);
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- Result := AnsiSameStr(P, B);
- {$ENDIF UNIX}
- if Result or (P = '') or (L >= Length(P)) then
- Exit;
- {$IFDEF MSWINDOWS}
- Result := AnsiSameText(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- Result := AnsiSameStr(StrLeft(P, L), B) and (P[L+1] = DirDelimiter);
- {$ENDIF UNIX}
- end;
- function PathIsDiskDevice(const Path: string): Boolean;
- {$IFDEF UNIX}
- var
- FullPath: string;
- F: PIOFile;
- Buffer: array [0..255] of Char;
- MountEntry: TMountEntry;
- FsTypes: TStringList;
- procedure GetAvailableFileSystems(const List: TStrings);
- var
- F: TextFile;
- S: string;
- begin
- AssignFile(F, '/proc/filesystems');
- Reset(F);
- repeat
- Readln(F, S);
- if Pos('nodev', S) = 0 then // how portable is this ?
- List.Add(Trim(S));
- until Eof(F);
- List.Add('supermount');
- CloseFile(F);
- end;
- begin
- Result := False;
- SetLength(FullPath, _POSIX_PATH_MAX);
- if realpath(PChar(Path), PChar(FullPath)) = nil then
- RaiseLastOSError;
- StrResetLength(FullPath);
- FsTypes := TStringList.Create;
- try
- GetAvailableFileSystems(FsTypes);
- F := setmntent(_PATH_MOUNTED, 'r'); // PATH_MOUNTED is deprecated,
- // but PATH_MNTTAB is defective in Libc.pas
- try
- // get drives from mtab
- while not Result and (getmntent_r(F, MountEntry, Buffer, SizeOf(Buffer)) <> nil) do
- if FsTypes.IndexOf(MountEntry.mnt_type) <> -1 then
- Result := MountEntry.mnt_dir = FullPath;
- finally
- endmntent(F);
- end;
- finally
- FsTypes.Free;
- end;
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- begin
- Result := Copy(Path, 1, Length(PathDevicePrefix)) = PathDevicePrefix;
- end;
- {$ENDIF MSWINDOWS}
- function CharIsMachineName(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- begin
- case C of
- 'a'..'z',
- 'A'..'Z',
- '-', '_', '.':
- Result := True;
- else
- Result := False;
- end;
- end;
- function CharIsInvalidPathCharacter(const C: Char): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
- begin
- case C of
- '<', '>', '?', '/', ',', '*', '+', '=', '[', ']', '|', ':', ';', '"', '''':
- Result := True;
- else
- Result := False;
- end;
- end;
- function PathIsUNC(const Path: string): Boolean;
- {$IFDEF MSWINDOWS}
- const
- cUNCSuffix = '?\UNC';
- var
- P: PChar;
- function AbsorbSeparator: Boolean;
- begin
- Result := (P <> nil) and (P^ = DirDelimiter);
- if Result then
- Inc(P);
- end;
- function AbsorbMachineName: Boolean;
- var
- NonDigitFound: Boolean;
- begin
- // a valid machine name is a string composed of the set [a-z, A-Z, 0-9, -, _] but it may not
- // consist entirely out of numbers
- Result := True;
- NonDigitFound := False;
- while (P^ <> #0) and (P^ <> DirDelimiter) do
- begin
- if CharIsMachineName(P^) then
- begin
- NonDigitFound := True;
- Inc(P);
- end
- else
- if CharIsDigit(P^) then
- Inc(P)
- else
- begin
- Result := False;
- Break;
- end;
- end;
- Result := Result and NonDigitFound;
- end;
- function AbsorbShareName: Boolean;
- begin
- // a valid share name is a string composed of a set the set !InvalidCharacters note that a
- // leading '$' is valid (indicates a hidden share)
- Result := True;
- while (P^ <> #0) and (P^ <> DirDelimiter) do
- begin
- if CharIsInvalidPathCharacter(P^) then
- begin
- Result := False;
- Break;
- end;
- Inc(P);
- end;
- end;
- begin
- Result := Copy(Path, 1, Length(PathUncPrefix)) = PathUncPrefix;
- if Result then
- begin
- if Copy(Path, 1, Length(PathUncPrefix + cUNCSuffix)) = PathUncPrefix + cUNCSuffix then
- P := @Path[Length(PathUncPrefix + cUNCSuffix)]
- else
- begin
- P := @Path[Length(PathUncPrefix)];
- Result := AbsorbSeparator and AbsorbMachineName;
- end;
- Result := Result and AbsorbSeparator;
- if Result then
- begin
- Result := AbsorbShareName;
- // remaining, if anything, is path and or filename (optional) check those?
- end;
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- begin
- Result := False;
- end;
- {$ENDIF UNIX}
- function PathRemoveSeparator(const Path: string): string;
- var
- L: Integer;
- begin
- L := Length(Path);
- if (L <> 0) and (Path[L] = DirDelimiter) then
- Result := Copy(Path, 1, L - 1)
- else
- Result := Path;
- end;
- function PathRemoveExtension(const Path: string): string;
- var
- I: Integer;
- begin
- I := LastDelimiter(':.' + DirDelimiter, Path);
- if (I > 0) and (Path[I] = '.') then
- Result := Copy(Path, 1, I - 1)
- else
- Result := Path;
- end;
- {$IFDEF MSWINDOWS}
- function SHGetDisplayName(ShellFolder: IShellFolder; PIDL: PItemIDList; ForParsing: Boolean): string;
- const
- Flags: array[Boolean] of DWORD = (SHGDN_NORMAL, SHGDN_FORPARSING);
- var
- StrRet: TStrRet;
- P: PChar;
- begin
- Result := '';
- StrRet.utype := 0;
- ShellFolder.GetDisplayNameOf(PIDL, Flags[ForParsing], StrRet);
- case StrRet.uType of
- STRRET_CSTR:
- SetString(Result, StrRet.cStr, lstrlenA(StrRet.cStr));
- STRRET_OFFSET:
- begin
- P := @PIDL.mkid.abID[StrRet.uOffset - SizeOf(PIDL.mkid.cb)];
- SetString(Result, P, PIDL.mkid.cb - StrRet.uOffset);
- end;
- STRRET_WSTR:
- Result := StrRet.pOleStr;
- end;
- Result := Copy(Result, 1, lstrlen(PChar(Result)));
- end;
- function CutFirstDirectory(var Path: string): string;
- var
- ps: Integer;
- begin
- ps := AnsiPos(DirDelimiter, Path);
- if ps > 0 then
- begin
- Result := Copy(Path, 1, ps - 1);
- Path := Copy(Path, ps + 1, Length(Path));
- end
- else
- begin
- Result := Path;
- Path := '';
- end;
- end;
- function PathGetPhysicalPath(const LocalizedPath: string): string;
- var
- Malloc: IMalloc;
- DesktopFolder: IShellFolder;
- RootFolder: IShellFolder;
- Eaten: Cardinal;
- Attributes: Cardinal;
- pidl: PItemIDList;
- EnumIDL: IEnumIDList;
- Drive: WideString;
- Featched: Cardinal;
- ParsePath: WideString;
- Path, Name: string;
- Found: Boolean;
- begin
- if StrCompareRange('\\', LocalizedPath, 1, 2) = 0 then
- begin
- Result := LocalizedPath;
- Exit;
- end;
- Drive := ExtractFileDrive(LocalizedPath);
- if Drive = '' then
- begin
- Result := LocalizedPath;
- Exit;
- end;
- Path := Copy(LocalizedPath, Length(Drive) + 2, Length(LocalizedPath));
- ParsePath := Drive;
- OLECheck( SHGetMalloc(Malloc) );
- OleCheck( SHGetDesktopFolder(DesktopFolder) );
- while Path <> '' do
- begin
- Name := CutFirstDirectory(Path);
- Found := False;
- pidl := nil;
- Attributes := 0;
- if Succeeded( DesktopFolder.ParseDisplayName(0, nil, PWideChar(ParsePath), Eaten, pidl, Attributes) ) then
- begin
- OleCheck( DesktopFolder.BindToObject(pidl, nil, IShellFolder, RootFolder) );
- Malloc.Free(pidl);
- OleCheck( RootFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, EnumIDL) );
- Featched := 0;
- while EnumIDL.Next(1, pidl, Featched) = NOERROR do
- begin
- if AnsiCompareText(Name, SHGetDisplayName(RootFolder, pidl, False)) = 0 then
- begin
- ParsePath := SHGetDisplayName(RootFolder, pidl, True);
- Malloc.Free(pidl);
- Found := True;
- Break;
- end;
- Malloc.Free(pidl);
- end;
- EnumIDL := nil;
- RootFolder := nil;
- end;
- if not Found then
- ParsePath := ParsePath + DirDelimiter + Name;
- end;
- Result := ParsePath;
- end;
- function PathGetLocalizedPath(const PhysicalPath: string): string;
- var
- Malloc: IMalloc;
- DesktopFolder: IShellFolder;
- RootFolder: IShellFolder;
- Eaten: Cardinal;
- Attributes: Cardinal;
- pidl: PItemIDList;
- EnumIDL: IEnumIDList;
- Drive: WideString;
- Featched: Cardinal;
- ParsePath: WideString;
- Path, Name, ParseName, DisplayName: string;
- Found: Boolean;
- begin
- if StrCompareRange('\\', PhysicalPath, 1, 2) = 0 then
- begin
- Result := PhysicalPath;
- Exit;
- end;
- Drive := ExtractFileDrive(PhysicalPath);
- if Drive = '' then
- begin
- Result := PhysicalPath;
- Exit;
- end;
- Path := Copy(PhysicalPath, Length(Drive) + 2, Length(PhysicalPath));
- ParsePath := Drive;
- Result := Drive;
- OLECheck( SHGetMalloc(Malloc) );
- OleCheck( SHGetDesktopFolder(DesktopFolder) );
- while Path <> '' do
- begin
- Name := CutFirstDirectory(Path);
- Found := False;
- pidl := nil;
- Attributes := 0;
- if Succeeded( DesktopFolder.ParseDisplayName(0, nil, PWideChar(ParsePath), Eaten, pidl, Attributes) ) then
- begin
- OleCheck( DesktopFolder.BindToObject(pidl, nil, IShellFolder, RootFolder) );
- Malloc.Free(pidl);
- OleCheck( RootFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, EnumIDL) );
- Featched := 0;
- while EnumIDL.Next(1, pidl, Featched) = NOERROR do
- begin
- ParseName := SHGetDisplayName(RootFolder, pidl, True);
- DisplayName := SHGetDisplayName(RootFolder, pidl, False);
- Malloc.Free(pidl);
- if (AnsiCompareText(Name, ExtractFileName(ParseName)) = 0) or
- (AnsiCompareText(Name, DisplayName) = 0) then
- begin
- Name := DisplayName;
- ParsePath := ParseName;
- Found := True;
- Break;
- end;
- end;
- EnumIDL := nil;
- RootFolder := nil;
- end;
- Result := Result + DirDelimiter + Name;
- if not Found then
- ParsePath := ParsePath + DirDelimiter + Name;
- end;
- end;
- {$ELSE ~MSWINDOWS}
- function PathGetPhysicalPath(const LocalizedPath: string): string;
- begin
- Result := LocalizedPath;
- end;
- function PathGetLocalizedPath(const PhysicalPath: string): string;
- begin
- Result := PhysicalPath;
- end;
- {$ENDIF ~MSWINDOWS}
- //=== Files and Directories ==================================================
- {* Extended version of JclFileUtils.BuildFileList:
- function parameter Path can include multiple FileMasks as:
- c:\aaa\*.pas; pro*.dpr; *.d??
- FileMask Seperator = ';'
- *}
- function BuildFileList(const Path: string; const Attr: Integer; const List: TStrings; IncludeDirectoryName: Boolean =
- False): Boolean;
- var
- SearchRec: TSearchRec;
- IndexMask: Integer;
- MaskList: TStringList;
- Masks, Directory: string;
- begin
- Assert(List <> nil);
- MaskList := TStringList.Create;
- try
- {* extract the Directory *}
- Directory := ExtractFileDir(Path);
- {* files can be searched in the current directory *}
- if Directory <> '' then
- begin
- Directory := PathAddSeparator(Directory);
- {* extract the FileMasks portion out of Path *}
- Masks := StrAfter(Directory, Path);
- end
- else
- Masks := Path;
- {* put the Masks into TStringlist *}
- StrTokenToStrings(Masks, DirSeparator, MaskList);
- {* search all files in the directory *}
- Result := FindFirst(Directory + '*', faAnyFile, SearchRec) = 0;
- List.BeginUpdate;
- try
- while Result do
- begin
- {* if the filename matches any mask then it is added to the list *}
- for IndexMask := 0 to MaskList.Count - 1 do
- if (SearchRec.Name <> '.') and (SearchRec.Name <> '..')
- and ((SearchRec.Attr and Attr) = (SearchRec.Attr and faAnyFile))
- and IsFileNameMatch(SearchRec.Name, MaskList.Strings[IndexMask]) then
- begin
- if IncludeDirectoryName then
- List.Add(Directory+SearchRec.Name)
- else
- List.Add(SearchRec.Name);
- Break;
- end;
- case FindNext(SearchRec) of
- 0:
- ;
- ERROR_NO_MORE_FILES:
- Break;
- else
- Result := False;
- end;
- end;
- finally
- {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindClose(SearchRec);
- List.EndUpdate;
- end;
- finally
- MaskList.Free;
- end;
- end;
- {$IFDEF MSWINDOWS}
- procedure CreateEmptyFile(const FileName: string);
- var
- Handle: THandle;
- begin
- Handle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
- if Handle <> INVALID_HANDLE_VALUE then
- CloseHandle(Handle)
- else
- RaiseLastOSError;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF MSWINDOWS}
- function CloseVolume(var Volume: THandle): Boolean;
- begin
- Result := False;
- if Volume <> INVALID_HANDLE_VALUE then
- begin
- Result := CloseHandle(Volume);
- if Result then
- Volume := INVALID_HANDLE_VALUE;
- end;
- end;
- {$IFNDEF FPC} // needs JclShell
- {$IFNDEF WINSCP}
- function DeleteDirectory(const DirectoryName: string; MoveToRecycleBin: Boolean): Boolean;
- begin
- if MoveToRecycleBin then
- Result := SHDeleteFolder(0, DirectoryName, [doSilent, doAllowUndo])
- else
- Result := DelTree(DirectoryName);
- end;
- {$ENDIF ~WINSCP}
- function CopyDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
- var
- SH: SHFILEOPSTRUCT;
- begin
- ResetMemory(SH, SizeOf(SH));
- SH.Wnd := 0;
- SH.wFunc := FO_COPY;
- SH.pFrom := PChar(PathRemoveSeparator(ExistingDirectoryName) + #0);
- SH.pTo := PChar(PathRemoveSeparator(NewDirectoryName) + #0);
- SH.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_SILENT;
- Result := SHFileOperation(SH) = 0;
- end;
- function MoveDirectory(ExistingDirectoryName, NewDirectoryName: string): Boolean;
- var
- SH: SHFILEOPSTRUCT;
- begin
- ResetMemory(SH, SizeOf(SH));
- SH.Wnd := 0;
- SH.wFunc := FO_MOVE;
- SH.pFrom := PChar(PathRemoveSeparator(ExistingDirectoryName) + #0);
- SH.pTo := PChar(PathRemoveSeparator(NewDirectoryName) + #0);
- SH.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_SILENT;
- Result := SHFileOperation(SH) = 0;
- end;
- {$ENDIF ~FPC}
- function DelTree(const Path: string): Boolean;
- begin
- Result := DelTreeEx(Path, False, nil);
- end;
- function DelTreeEx(const Path: string; AbortOnFailure: Boolean; Progress: TDelTreeProgress): Boolean;
- var
- Files: TStringList;
- LPath: string; // writable copy of Path
- FileName: string;
- I: Integer;
- PartialResult: Boolean;
- Attr: DWORD;
- begin
- Assert(Path <> '', LoadResString(@RsDelTreePathIsEmpty));
- {$IFNDEF ASSERTIONS_ON}
- if Path = '' then
- begin
- Result := False;
- Exit;
- end;
- {$ENDIF ~ASSERTIONS_ON}
- Result := True;
- Files := TStringList.Create;
- try
- LPath := PathRemoveSeparator(Path);
- BuildFileList(LPath + '\*.*', faAnyFile, Files);
- for I := 0 to Files.Count - 1 do
- begin
- FileName := LPath + DirDelimiter + Files[I];
- PartialResult := True;
- // If the current file is itself a directory then recursively delete it
- Attr := GetFileAttributes(PChar(FileName));
- if (Attr <> DWORD(-1)) and ((Attr and FILE_ATTRIBUTE_DIRECTORY) <> 0) then
- PartialResult := DelTreeEx(FileName, AbortOnFailure, Progress)
- else
- begin
- if Assigned(Progress) then
- PartialResult := Progress(FileName, Attr);
- if PartialResult then
- begin
- // Set attributes to normal in case it's a readonly file
- PartialResult := SetFileAttributes(PChar(FileName), FILE_ATTRIBUTE_NORMAL);
- if PartialResult then
- PartialResult := DeleteFile(FileName);
- end;
- end;
- if not PartialResult then
- begin
- Result := False;
- if AbortOnFailure then
- Break;
- end;
- end;
- finally
- FreeAndNil(Files);
- end;
- if Result then
- begin
- // Finally remove the directory itself
- Result := SetFileAttributes(PChar(LPath), FILE_ATTRIBUTE_NORMAL);
- if Result then
- begin
- {$IOCHECKS OFF}
- RmDir(LPath);
- {$IFDEF IOCHECKS_ON}
- {$IOCHECKS ON}
- {$ENDIF IOCHECKS_ON}
- Result := IOResult = 0;
- end;
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF MSWINDOWS}
- function DirectoryExists(const Name: string): Boolean;
- var
- R: DWORD;
- begin
- R := GetFileAttributes(PChar(Name));
- Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) <> 0);
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function DirectoryExists(const Name: string; ResolveSymLinks: Boolean): Boolean;
- begin
- Result := IsDirectory(Name, ResolveSymLinks);
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function DiskInDrive(Drive: Char): Boolean;
- var
- ErrorMode: Cardinal;
- begin
- Result := False;
- Assert(CharIsDriveLetter(Drive));
- if CharIsDriveLetter(Drive) then
- begin
- Drive := CharUpper(Drive);
- { try to access the drive, it doesn't really matter how we access the drive and as such calling
- DiskSize is more or less a random choice. The call to SetErrorMode supresses the system provided
- error dialog if there is no disk in the drive and causes the to DiskSize to fail. }
- ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
- try
- Result := DiskSize(Ord(Drive) - $40) <> -1;
- finally
- SetErrorMode(ErrorMode);
- end;
- end;
- end;
- {$ENDIF MSWINDOWS}
- function FileCreateTemp(var Prefix: string): THandle;
- {$IFDEF MSWINDOWS}
- var
- TempName: string;
- begin
- Result := INVALID_HANDLE_VALUE;
- TempName := FileGetTempName(Prefix);
- if TempName <> '' then
- begin
- Result := CreateFile(PChar(TempName), GENERIC_READ or GENERIC_WRITE, 0, nil,
- OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
- // In certain situations it's possible that CreateFile fails yet the file is actually created,
- // therefore explicitly delete it upon failure.
- if Result = INVALID_HANDLE_VALUE then
- DeleteFile(TempName);
- Prefix := TempName;
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- var
- Template: string;
- begin
- // The mkstemp function generates a unique file name just as mktemp does, but
- // it also opens the file for you with open. If successful, it modifies
- // template in place and returns a file descriptor for that file open for
- // reading and writing. If mkstemp cannot create a uniquely-named file, it
- // returns -1. If template does not end with `XXXXXX', mkstemp returns -1 and
- // does not modify template.
- // The file is opened using mode 0600. If the file is meant to be used by
- // other users this mode must be changed explicitly.
- // Unlike mktemp, mkstemp is actually guaranteed to create a unique file that
- // cannot possibly clash with any other program trying to create a temporary
- // file. This is because it works by calling open with the O_EXCL flag, which
- // says you want to create a new file and get an error if the file already
- // exists.
- Template := Prefix + 'XXXXXX';
- Result := mkstemp(PChar(Template));
- Prefix := Template;
- end;
- {$ENDIF UNIX}
- {$IFNDEF WINSCP}
- function FileBackup(const FileName: string; Move: Boolean = False): Boolean;
- begin
- if Move then
- Result := FileMove(FileName, GetBackupFileName(FileName), True)
- else
- Result := FileCopy(FileName, GetBackupFileName(FileName), True);
- end;
- {$ENDIF ~WINSCP}
- function FileCopy(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
- var
- {$IFDEF UNIX}
- SrcFile, DstFile: file;
- Buf: array[0..511] of Byte;
- BytesRead: Integer;
- {$ENDIF UNIX}
- DestFileName: string;
- begin
- if IsDirectory(NewFileName) then
- DestFileName := PathAddSeparator(NewFileName) + ExtractFileName(ExistingFileName)
- else
- DestFileName := NewFileName;
- {$IFDEF MSWINDOWS}
- { TODO : Use CopyFileEx where available? }
- Result := CopyFile(PChar(ExistingFileName), PChar(DestFileName), not ReplaceExisting);
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- Result := False;
- if not FileExists(DestFileName) or ReplaceExisting then
- begin
- AssignFile(SrcFile, ExistingFileName);
- Reset(SrcFile, 1);
- AssignFile(DstFile, DestFileName);
- Rewrite(DstFile, 1);
- while not Eof(SrcFile) do
- begin
- BlockRead(SrcFile, Buf, SizeOf(Buf), BytesRead);
- BlockWrite(DstFile, Buf, BytesRead);
- end;
- CloseFile(DstFile);
- CloseFile(SrcFile);
- Result := True;
- end;
- {$ENDIF UNIX}
- end;
- function FileDateTime(const FileName: string): TDateTime;
- {$IFNDEF COMPILER10_UP}
- var
- Age: Longint;
- {$ENDIF !COMPILER10_UP}
- begin
- {$IFDEF COMPILER10_UP}
- if not FileAge(Filename, Result) then
- Result := 0;
- {$ELSE}
- Age := FileAge(FileName);
- {$IFDEF MSWINDOWS}
- // [roko] -1 is valid FileAge value on Linux
- if Age = -1 then
- Result := 0
- else
- {$ENDIF MSWINDOWS}
- Result := FileDateToDateTime(Age);
- {$ENDIF COMPILER10_UP}
- end;
- {$IFNDEF WINSCP}
- function FileDelete(const FileName: string; MoveToRecycleBin: Boolean = False): Boolean;
- {$IFDEF MSWINDOWS}
- begin
- if MoveToRecycleBin then
- Result := SHDeleteFiles(0, FileName, [doSilent, doAllowUndo, doFilesOnly])
- else
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.DeleteFile(PChar(FileName));
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- { TODO : implement MoveToRecycleBin for appropriate Desktops (e.g. KDE) }
- begin
- Result := remove(PChar(FileName)) <> -1;
- end;
- {$ENDIF UNIX}
- {$ENDIF ~WINSCP}
- function FileExists(const FileName: string): Boolean;
- {$IFDEF MSWINDOWS}
- var
- Attr: Cardinal;
- {$ENDIF MSWINDOWS}
- begin
- if FileName <> '' then
- begin
- {$IFDEF MSWINDOWS}
- // FileGetSize is very slow, GetFileAttributes is much faster
- Attr := GetFileAttributes(Pointer(Filename));
- Result := (Attr <> $FFFFFFFF) and (Attr and FILE_ATTRIBUTE_DIRECTORY = 0);
- {$ELSE ~MSWINDOWS}
- // Attempt to access the file, doesn't matter how, using FileGetSize is as good as anything else.
- Result := FileGetSize(FileName) <> -1;
- {$ENDIF ~MSWINDOWS}
- end
- else
- Result := False;
- end;
- {$IFNDEF WINSCP}
- procedure FileHistory(const FileName: string; HistoryPath: string = ''; MaxHistoryCount: Integer = 100; MinFileDate:
- TDateTime = 0; ReplaceExtention: Boolean = true);
- Function Extention (Number : Integer) : String;
- begin
- Result := inttostr(Number);
- while Length(Result) < 3 do
- Result := '0' + Result;
- Result := '.~'+Result+'~';
- end;
- procedure RenameToNumber(const RenameFileName: string; Number: Integer);
- var
- f1: string;
- f2: string;
- begin
- f1 := ChangeFileExt(RenameFileName,Extention(Number-1));
- f2 := ChangeFileExt(RenameFileName,Extention(Number));
- if FileExists(f2) then
- if Number >= MaxHistoryCount then
- if not FileDelete(f2) then
- Exception.Create('Unable to delete file "' + f2 + '".')
- else
- else
- RenameToNumber(RenameFileName, Number + 1);
- if FileExists(f1) then
- if not FileMove(f1, f2, true) then
- Exception.Create('Unable to rename file "' + f1 + '" to "' + f2 + '".')
- end;
- Var FirstFile : string;
- begin
- // TODO -cMM: FileHistory default body inserted
- if not FileExists(FileName) or (MaxHistoryCount <= 0) then
- Exit;
- if HistoryPath = '' then
- HistoryPath := ExtractFilePath(FileName);
- FirstFile := PathAppend(HistoryPath, ExtractFileName(FileName));
- if ReplaceExtention then
- FirstFile := ChangeFileExt(FirstFile, Extention(1))
- else
- FirstFile := FirstFile+Extention(1);
- if (FileDateTime(FirstFile) > MinFileDate) and (MinFileDate <> 0) then
- Exit;
- RenameToNumber(FirstFile, 2);
- FileCopy(FileName, FirstFile, True);
- end;
- {$ENDIF ~WINSCP}
- {$IFNDEF WINSCP}
- function FileMove(const ExistingFileName, NewFileName: string; ReplaceExisting: Boolean = False): Boolean;
- {$IFDEF MSWINDOWS}
- const
- Flag: array[Boolean] of Cardinal = (0, MOVEFILE_REPLACE_EXISTING);
- {$ENDIF MSWINDOWS}
- begin
- {$IFDEF MSWINDOWS}
- Result := MoveFileEx(PChar(ExistingFileName), PChar(NewFileName), Flag[ReplaceExisting]);
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- Result := __rename(PChar(ExistingFileName), PChar(NewFileName)) = 0;
- {$ENDIF UNIX}
- if not Result then
- begin
- Result := FileCopy(ExistingFileName, NewFileName, ReplaceExisting);
- if Result then
- FileDelete(ExistingFileName);
- end;
- end;
- function FileRestore(const FileName: string): Boolean;
- var
- TempFileName: string;
- begin
- Result := False;
- TempFileName := FileGetTempName('');
- if FileMove(GetBackupFileName(FileName), TempFileName, True) then
- if FileBackup(FileName, False) then
- Result := FileMove(TempFileName, FileName, True);
- end;
- {$ENDIF ~WINSCP}
- function GetBackupFileName(const FileName: string): string;
- var
- NewExt: string;
- begin
- NewExt := ExtractFileExt(FileName);
- if Length(NewExt) > 0 then
- begin
- NewExt[1] := '~';
- NewExt := '.' + NewExt
- end
- else
- NewExt := '.~';
- Result := ChangeFileExt(FileName, NewExt);
- end;
- function IsBackupFileName(const FileName: string): Boolean;
- begin
- Result := (pos('.~', ExtractFileExt(FileName)) = 1);
- end;
- function FileGetDisplayName(const FileName: string): string;
- {$IFDEF MSWINDOWS}
- var
- FileInfo: TSHFileInfo;
- begin
- ResetMemory(FileInfo, SizeOf(FileInfo));
- if SHGetFileInfo(PChar(FileName), 0, FileInfo, SizeOf(FileInfo), SHGFI_DISPLAYNAME) <> 0 then
- Result := FileInfo.szDisplayName
- else
- Result := FileName;
- end;
- {$ELSE ~MSWINDOWS}
- begin
- { TODO -cHelp : mention this reduced solution }
- Result := FileName;
- end;
- {$ENDIF ~MSWINDOWS}
- {$IFNDEF WINSCP}
- function FileGetGroupName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
- {$IFDEF MSWINDOWS}
- var
- DomainName: WideString;
- TmpResult: WideString;
- pSD: PSecurityDescriptor;
- BufSize: DWORD;
- begin
- if IsWinNT then
- begin
- BufSize := 0;
- GetFileSecurity(PChar(FileName), GROUP_SECURITY_INFORMATION, nil, 0, BufSize);
- if BufSize > 0 then
- begin
- GetMem(pSD, BufSize);
- GetFileSecurity(PChar(FileName), GROUP_SECURITY_INFORMATION,
- pSD, BufSize, BufSize);
- LookupAccountBySid(Pointer(TJclAddr(pSD) + TJclAddr(pSD^.Group)), TmpResult, DomainName, True);
- FreeMem(pSD);
- Result := Trim(TmpResult);
- end;
- end;
- end;
- {$ENDIF ~MSWINDOWS}
- {$IFDEF UNIX}
- var
- Buf: TStatBuf64;
- ResultBuf: TGroup;
- ResultBufPtr: PGroup;
- Buffer: array of Char;
- begin
- if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
- begin
- SetLength(Buffer, 128);
- while getgrgid_r(Buf.st_gid, ResultBuf, @Buffer[0], Length(Buffer), ResultBufPtr) = ERANGE do
- SetLength(Buffer, Length(Buffer) * 2);
- Result := ResultBuf.gr_name;
- end;
- end;
- {$ENDIF ~UNIX}
- function FileGetOwnerName(const FileName: string {$IFDEF UNIX}; ResolveSymLinks: Boolean = True {$ENDIF}): string;
- {$IFDEF MSWINDOWS}
- var
- DomainName: WideString;
- TmpResult: WideString;
- pSD: PSecurityDescriptor;
- BufSize: DWORD;
- begin
- if IsWinNT then
- begin
- BufSize := 0;
- GetFileSecurity(PChar(FileName), OWNER_SECURITY_INFORMATION, nil, 0, BufSize);
- if BufSize > 0 then
- begin
- GetMem(pSD, BufSize);
- try
- GetFileSecurity(PChar(FileName), OWNER_SECURITY_INFORMATION,
- pSD, BufSize, BufSize);
- LookupAccountBySid(Pointer(TJclAddr(pSD) + TJclAddr(pSD^.Owner)), TmpResult, DomainName, True);
- finally
- FreeMem(pSD);
- end;
- Result := Trim(TmpResult);
- end;
- end;
- end;
- {$ENDIF ~MSWINDOWS}
- {$IFDEF UNIX}
- var
- Buf: TStatBuf64;
- ResultBuf: TPasswordRecord;
- ResultBufPtr: PPasswordRecord;
- Buffer: array of Char;
- begin
- if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
- begin
- SetLength(Buffer, 128);
- while getpwuid_r(Buf.st_uid, ResultBuf, @Buffer[0], Length(Buffer), ResultBufPtr) = ERANGE do
- SetLength(Buffer, Length(Buffer) * 2);
- Result := ResultBuf.pw_name;
- end;
- end;
- {$ENDIF ~UNIX}
- {$ENDIF ~WINSCP}
- function FileGetSize(const FileName: string): Int64;
- {$IFDEF MSWINDOWS}
- var
- FileAttributesEx: WIN32_FILE_ATTRIBUTE_DATA;
- OldMode: Cardinal;
- Size: TJclULargeInteger;
- begin
- Result := -1;
- OldMode := SetErrorMode(SEM_FAILCRITICALERRORS);
- try
- if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @FileAttributesEx) then
- begin
- Size.LowPart := FileAttributesEx.nFileSizeLow;
- Size.HighPart := FileAttributesEx.nFileSizeHigh;
- Result := Size.QuadPart;
- end;
- finally
- SetErrorMode(OldMode);
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- var
- Buf: TStatBuf64;
- begin
- Result := -1;
- if GetFileStatus(FileName, Buf, False) = 0 then
- Result := Buf.st_size;
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- {$IFDEF FPC}
- { TODO : Move this over to JclWin32 when JclWin32 gets overhauled. }
- function GetTempFileName(lpPathName, lpPrefixString: PChar;
- uUnique: UINT; lpTempFileName: PChar): UINT; stdcall;
- external kernel32 name 'GetTempFileNameA';
- {$ENDIF FPC}
- {$ENDIF MSWINDOWS}
- function FileGetTempName(const Prefix: string): string;
- {$IFDEF MSWINDOWS}
- var
- TempPath, TempFile: string;
- R: Cardinal;
- begin
- Result := '';
- TempPath := PathGetTempPath;
- if TempPath <> '' then
- begin
- SetLength(TempFile, MAX_PATH);
- R := GetTempFileName(PChar(TempPath), PChar(Prefix), 0, PChar(TempFile));
- if R <> 0 then
- begin
- StrResetLength(TempFile);
- Result := TempFile;
- end;
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- // Warning: Between the time the pathname is constructed and the file is created
- // another process might have created a file with the same name using tmpnam,
- // leading to a possible security hole. The implementation generates names which
- // can hardly be predicted, but when opening the file you should use the O_EXCL
- // flag. Using tmpfile or mkstemp is a safe way to avoid this problem.
- var
- P: PChar;
- begin
- P := tempnam(PChar(PathGetTempPath), PChar(Prefix));
- Result := P;
- Libc.free(P);
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function FileGetTypeName(const FileName: string): string;
- var
- FileInfo: TSHFileInfo;
- RetVal: DWORD;
- begin
- ResetMemory(FileInfo, SizeOf(FileInfo));
- RetVal := SHGetFileInfo(PChar(FileName), 0, FileInfo, SizeOf(FileInfo),
- SHGFI_TYPENAME or SHGFI_USEFILEATTRIBUTES);
- if RetVal <> 0 then
- Result := FileInfo.szTypeName;
- if (RetVal = 0) or (Trim(Result) = '') then
- begin
- // Lookup failed so mimic explorer behaviour by returning "XYZ File"
- Result := ExtractFileExt(FileName);
- Delete(Result, 1, 1);
- Result := TrimLeft(UpperCase(Result) + LoadResString(@RsDefaultFileTypeName));
- end;
- end;
- {$ENDIF MSWINDOWS}
- function FindUnusedFileName(FileName: string; const FileExt: string; NumberPrefix: string = ''): string;
- var
- I: Integer;
- begin
- Result := PathAddExtension(FileName, FileExt);
- if not FileExists(Result) then
- Exit;
- if SameText(Result, FileName) then
- Delete(FileName, Length(FileName) - Length(FileExt) + 1, Length(FileExt));
- I := 0;
- repeat
- Inc(I);
- Result := PathAddExtension(FileName + NumberPrefix + IntToStr(I), FileExt);
- until not FileExists(Result);
- end;
- // This routine is copied from FileCtrl.pas to avoid dependency on that unit.
- // See the remark at the top of this section
- function ForceDirectories(Name: string): Boolean;
- var
- ExtractPath: string;
- begin
- Result := True;
- if Length(Name) = 0 then
- raise EJclFileUtilsError.CreateRes(@RsCannotCreateDir);
- Name := PathRemoveSeparator(Name);
- {$IFDEF MSWINDOWS}
- ExtractPath := ExtractFilePath(Name);
- if ((Length(Name) = 2) and (Copy(Name, 2,1) = ':')) or DirectoryExists(Name) or (ExtractPath = Name) then
- Exit;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- if (Length(Name) = 0) or DirectoryExists(Name) then
- Exit;
- ExtractPath := ExtractFilePath(Name);
- {$ENDIF UNIX}
- Result := (ExtractPath = '') or ForceDirectories(ExtractPath);
- if Result then
- begin
- {$IFDEF MSWINDOWS}
- SetLastError(ERROR_SUCCESS);
- {$ENDIF MSWINDOWS}
- Result := Result and CreateDir(Name);
- {$IFDEF MSWINDOWS}
- Result := Result or (GetLastError = ERROR_ALREADY_EXISTS);
- {$ENDIF MSWINDOWS}
- end;
- end;
- function GetDirectorySize(const Path: string): Int64;
- function RecurseFolder(const Path: string): Int64;
- var
- F: TSearchRec;
- R: Integer;
- {$IFDEF MSWINDOWS}
- TempSize: TJclULargeInteger;
- {$ENDIF MSWINDOWS}
- begin
- Result := 0;
- R := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Path + '*.*', faAnyFile, F);
- if R = 0 then
- try
- while R = 0 do
- begin
- if (F.Name <> '.') and (F.Name <> '..') then
- begin
- if (F.Attr and faDirectory) = faDirectory then
- Inc(Result, RecurseFolder(Path + F.Name + DirDelimiter))
- else
- {$IFDEF MSWINDOWS}
- begin
- TempSize.LowPart := F.FindData.nFileSizeLow;
- TempSize.HighPart := F.FindData.nFileSizeHigh;
- Inc(Result, TempSize.QuadPart);
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- // SysUtils.Find* don't perceive files >= 2 GB anyway
- Inc(Result, Int64(F.Size));
- {$ENDIF UNIX}
- end;
- R := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindNext(F);
- end;
- if R <> ERROR_NO_MORE_FILES then
- Abort;
- finally
- {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindClose(F);
- end;
- end;
- begin
- if not DirectoryExists(PathRemoveSeparator(Path)) then
- Result := -1
- else
- try
- Result := RecurseFolder(PathAddSeparator(Path))
- except
- Result := -1;
- end;
- end;
- {$IFDEF MSWINDOWS}
- function GetDriveTypeStr(const Drive: Char): string;
- var
- DriveType: Integer;
- DriveStr: string;
- begin
- if not CharIsDriveLetter(Drive) then
- raise EJclPathError.CreateResFmt(@RsPathInvalidDrive, [Drive]);
- DriveStr := Drive + ':\';
- DriveType := GetDriveType(PChar(DriveStr));
- case DriveType of
- DRIVE_REMOVABLE:
- Result := LoadResString(@RsRemovableDrive);
- DRIVE_FIXED:
- Result := LoadResString(@RsHardDisk);
- DRIVE_REMOTE:
- Result := LoadResString(@RsRemoteDrive);
- DRIVE_CDROM:
- Result := LoadResString(@RsCDRomDrive);
- DRIVE_RAMDISK:
- Result := LoadResString(@RsRamDisk);
- else
- Result := LoadResString(@RsUnknownDrive);
- end;
- end;
- function GetFileAgeCoherence(const FileName: string): Boolean;
- var
- FileAttributesEx: WIN32_FILE_ATTRIBUTE_DATA;
- begin
- Result := False;
- if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @FileAttributesEx) then
- {$IFDEF FPC}
- Result := CompareFileTime(@FileAttributesEx.ftCreationTime, @FileAttributesEx.ftLastWriteTime) <= 0;
- {$ELSE ~FPC}
- Result := CompareFileTime(FileAttributesEx.ftCreationTime, FileAttributesEx.ftLastWriteTime) <= 0;
- {$ENDIF ~FPC}
- end;
- {$ENDIF MSWINDOWS}
- procedure GetFileAttributeList(const Items: TStrings; const Attr: Integer);
- begin
- { TODO : clear list? }
- Assert(Assigned(Items));
- if not Assigned(Items) then
- Exit;
- Items.BeginUpdate;
- try
- { TODO : differentiate Windows/UNIX idents }
- if Attr and faDirectory = faDirectory then
- Items.Add(LoadResString(@RsAttrDirectory));
- if Attr and faReadOnly = faReadOnly then
- Items.Add(LoadResString(@RsAttrReadOnly));
- if Attr and faSysFile = faSysFile then
- Items.Add(LoadResString(@RsAttrSystemFile));
- if Attr and faArchive = faArchive then
- Items.Add(LoadResString(@RsAttrArchive));
- if Attr and faAnyFile = faAnyFile then
- Items.Add(LoadResString(@RsAttrAnyFile));
- if Attr and faHidden = faHidden then
- Items.Add(LoadResString(@RsAttrHidden));
- finally
- Items.EndUpdate;
- end;
- end;
- {$IFDEF MSWINDOWS}
- { TODO : GetFileAttributeListEx - Unix version }
- procedure GetFileAttributeListEx(const Items: TStrings; const Attr: Integer);
- begin
- { TODO : clear list? }
- Assert(Assigned(Items));
- if not Assigned(Items) then
- Exit;
- Items.BeginUpdate;
- try
- if Attr and FILE_ATTRIBUTE_READONLY = FILE_ATTRIBUTE_READONLY then
- Items.Add(LoadResString(@RsAttrReadOnly));
- if Attr and FILE_ATTRIBUTE_HIDDEN = FILE_ATTRIBUTE_HIDDEN then
- Items.Add(LoadResString(@RsAttrHidden));
- if Attr and FILE_ATTRIBUTE_SYSTEM = FILE_ATTRIBUTE_SYSTEM then
- Items.Add(LoadResString(@RsAttrSystemFile));
- if Attr and FILE_ATTRIBUTE_DIRECTORY = FILE_ATTRIBUTE_DIRECTORY then
- Items.Add(LoadResString(@RsAttrDirectory));
- if Attr and FILE_ATTRIBUTE_ARCHIVE = FILE_ATTRIBUTE_ARCHIVE then
- Items.Add(LoadResString(@RsAttrArchive));
- if Attr and FILE_ATTRIBUTE_NORMAL = FILE_ATTRIBUTE_NORMAL then
- Items.Add(LoadResString(@RsAttrNormal));
- if Attr and FILE_ATTRIBUTE_TEMPORARY = FILE_ATTRIBUTE_TEMPORARY then
- Items.Add(LoadResString(@RsAttrTemporary));
- if Attr and FILE_ATTRIBUTE_COMPRESSED = FILE_ATTRIBUTE_COMPRESSED then
- Items.Add(LoadResString(@RsAttrCompressed));
- if Attr and FILE_ATTRIBUTE_OFFLINE = FILE_ATTRIBUTE_OFFLINE then
- Items.Add(LoadResString(@RsAttrOffline));
- if Attr and FILE_ATTRIBUTE_ENCRYPTED = FILE_ATTRIBUTE_ENCRYPTED then
- Items.Add(LoadResString(@RsAttrEncrypted));
- if Attr and FILE_ATTRIBUTE_REPARSE_POINT = FILE_ATTRIBUTE_REPARSE_POINT then
- Items.Add(LoadResString(@RsAttrReparsePoint));
- if Attr and FILE_ATTRIBUTE_SPARSE_FILE = FILE_ATTRIBUTE_SPARSE_FILE then
- Items.Add(LoadResString(@RsAttrSparseFile));
- finally
- Items.EndUpdate;
- end;
- end;
- {$ENDIF MSWINDOWS}
- function GetFileInformation(const FileName: string; out FileInfo: TSearchRec): Boolean;
- begin
- Result := FindFirst(FileName, faAnyFile, FileInfo) = 0;
- if Result then
- {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindClose(FileInfo);
- end;
- function GetFileInformation(const FileName: string): TSearchRec;
- begin
- if not GetFileInformation(FileName, Result) then
- RaiseLastOSError;
- end;
- {$IFDEF UNIX}
- { TODO -cHelp : Author: Robert Rossmair }
- function GetFileStatus(const FileName: string; out StatBuf: TStatBuf64;
- const ResolveSymLinks: Boolean): Integer;
- begin
- if ResolveSymLinks then
- Result := stat64(PChar(FileName), StatBuf)
- else
- Result := lstat64(PChar(FileName), StatBuf);
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function GetFileLastWrite(const FileName: string): TFileTime;
- begin
- Result := GetFileInformation(FileName).FindData.ftLastWriteTime;
- end;
- {$IFNDEF WINSCP}
- function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime): Boolean;
- var
- FileInfo: TSearchRec;
- begin
- Result := GetFileInformation(FileName, FileInfo);
- if Result then
- LocalTime := FileTimeToLocalDateTime(FileInfo.FindData.ftLastWriteTime);
- end;
- {$ENDIF ~WINSCP}
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function GetFileLastWrite(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean): Boolean;
- var
- Buf: TStatBuf64;
- begin
- Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
- if Result then
- TimeStamp := Buf.st_mtime
- end;
- function GetFileLastWrite(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean): Boolean;
- var
- Buf: TStatBuf64;
- begin
- Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
- if Result then
- LocalTime := FileDateToDateTime(Buf.st_mtime);
- end;
- function GetFileLastWrite(const FileName: string; ResolveSymLinks: Boolean): Integer;
- var
- Buf: TStatBuf64;
- begin
- if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
- Result := Buf.st_mtime
- else
- Result := -1;
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function GetFileLastAccess(const FileName: string): TFileTime;
- begin
- Result := GetFileInformation(FileName).FindData.ftLastAccessTime;
- end;
- {$IFNDEF WINSCP}
- function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime): Boolean;
- var
- FileInfo: TSearchRec;
- begin
- Result := GetFileInformation(FileName, FileInfo);
- if Result then
- LocalTime := FileTimeToLocalDateTime(GetFileInformation(FileName).FindData.ftLastAccessTime);
- end;
- {$ENDIF ~WINSCP}
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function GetFileLastAccess(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean): Boolean;
- var
- Buf: TStatBuf64;
- begin
- Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
- if Result then
- TimeStamp := Buf.st_atime
- end;
- function GetFileLastAccess(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean): Boolean;
- var
- Buf: TStatBuf64;
- begin
- Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
- if Result then
- LocalTime := FileDateToDateTime(Buf.st_atime);
- end;
- function GetFileLastAccess(const FileName: string; ResolveSymLinks: Boolean): Integer;
- var
- Buf: TStatBuf64;
- begin
- if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
- Result := Buf.st_atime
- else
- Result := -1;
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function GetFileCreation(const FileName: string): TFileTime;
- begin
- Result := GetFileInformation(FileName).FindData.ftCreationTime;
- end;
- {$IFNDEF WINSCP}
- function GetFileCreation(const FileName: string; out LocalTime: TDateTime): Boolean;
- var
- FileInfo: TSearchRec;
- begin
- Result := GetFileInformation(FileName, FileInfo);
- if Result then
- LocalTime := FileTimeToLocalDateTime(GetFileInformation(FileName).FindData.ftCreationTime);
- end;
- {$ENDIF ~WINSCP}
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function GetFileLastAttrChange(const FileName: string; out TimeStamp: Integer; ResolveSymLinks: Boolean): Boolean;
- var
- Buf: TStatBuf64;
- begin
- Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
- if Result then
- TimeStamp := Buf.st_ctime
- end;
- function GetFileLastAttrChange(const FileName: string; out LocalTime: TDateTime; ResolveSymLinks: Boolean): Boolean;
- var
- Buf: TStatBuf64;
- begin
- Result := GetFileStatus(FileName, Buf, ResolveSymLinks) = 0;
- if Result then
- LocalTime := FileDateToDateTime(Buf.st_ctime);
- end;
- function GetFileLastAttrChange(const FileName: string; ResolveSymLinks: Boolean): Integer;
- var
- Buf: TStatBuf64;
- begin
- if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
- Result := Buf.st_ctime
- else
- Result := -1;
- end;
- {$ENDIF UNIX}
- function GetModulePath(const Module: HMODULE): string;
- var
- L: Integer;
- begin
- L := MAX_PATH + 1;
- SetLength(Result, L);
- {$IFDEF MSWINDOWS}
- L := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetModuleFileName(Module, Pointer(Result), L);
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- {$IFDEF FPC}
- L := 0; // FIXME
- {$ELSE ~FPC}
- L := GetModuleFileName(Module, Pointer(Result), L);
- {$ENDIF ~FPC}
- {$ENDIF UNIX}
- SetLength(Result, L);
- end;
- function GetSizeOfFile(const FileName: string): Int64;
- {$IFDEF MSWINDOWS}
- var
- FileAttributesEx: WIN32_FILE_ATTRIBUTE_DATA;
- Size: TJclULargeInteger;
- begin
- Result := 0;
- if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @FileAttributesEx) then
- begin
- Size.LowPart := FileAttributesEx.nFileSizeLow;
- Size.HighPart := FileAttributesEx.nFileSizeHigh;
- Result := Size.QuadPart;
- end
- else
- RaiseLastOSError;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- var
- Buf: TStatBuf64;
- begin
- if GetFileStatus(FileName, Buf, False) <> 0 then
- RaiseLastOSError;
- Result := Buf.st_size;
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function GetSizeOfFile(Handle: THandle): Int64; overload;
- var
- Size: TJclULargeInteger;
- begin
- Size.LowPart := GetFileSize(Handle, @Size.HighPart);
- Result := Size.QuadPart;
- end;
- {$ENDIF MSWINDOWS}
- function GetSizeOfFile(const FileInfo: TSearchRec): Int64;
- {$IFDEF MSWINDOWS}
- begin
- Int64Rec(Result).Lo := FileInfo.FindData.nFileSizeLow;
- Int64Rec(Result).Hi := FileInfo.FindData.nFileSizeHigh;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- var
- Buf: TStatBuf64;
- begin
- // rr: Note that SysUtils.FindFirst/Next ignore files >= 2 GB under Linux,
- // thus the following code is rather pointless at the moment of this writing.
- // We apparently need to write our own set of Findxxx functions to overcome this limitation.
- if GetFileStatus(FileInfo.PathOnly + FileInfo.Name, Buf, True) <> 0 then
- Result := -1
- else
- Result := Buf.st_size
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- {$IFDEF FPC}
- { TODO : Move this over to JclWin32 when JclWin32 gets overhauled. }
- function GetFileAttributesEx(lpFileName: PChar;
- fInfoLevelId: TGetFileExInfoLevels; lpFileInformation: Pointer): BOOL; stdcall;
- external kernel32 name 'GetFileAttributesExA';
- {$ENDIF FPC}
- function GetStandardFileInfo(const FileName: string): TWin32FileAttributeData;
- var
- Handle: THandle;
- FileInfo: TByHandleFileInformation;
- begin
- Assert(FileName <> '');
- { TODO : Use RTDL-Version of GetFileAttributesEx }
- if IsWin95 or IsWin95OSR2 or IsWinNT3 then
- begin
- Handle := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
- if Handle <> INVALID_HANDLE_VALUE then
- try
- FileInfo.dwFileAttributes := 0;
- if not GetFileInformationByHandle(Handle, FileInfo) then
- raise EJclFileUtilsError.CreateResFmt(@RsFileUtilsAttrUnavailable, [FileName]);
- Result.dwFileAttributes := FileInfo.dwFileAttributes;
- Result.ftCreationTime := FileInfo.ftCreationTime;
- Result.ftLastAccessTime := FileInfo.ftLastAccessTime;
- Result.ftLastWriteTime := FileInfo.ftLastWriteTime;
- Result.nFileSizeHigh := FileInfo.nFileSizeHigh;
- Result.nFileSizeLow := FileInfo.nFileSizeLow;
- finally
- CloseHandle(Handle);
- end
- else
- raise EJclFileUtilsError.CreateResFmt(@RsFileUtilsAttrUnavailable, [FileName]);
- end
- else
- begin
- if not GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @Result) then
- raise EJclFileUtilsError.CreateResFmt(@RsFileUtilsAttrUnavailable, [FileName]);
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF MSWINDOWS}
- function IsDirectory(const FileName: string): Boolean;
- var
- R: DWORD;
- begin
- R := GetFileAttributes(PChar(FileName));
- Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) <> 0);
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function IsDirectory(const FileName: string; ResolveSymLinks: Boolean): Boolean;
- var
- Buf: TStatBuf64;
- begin
- Result := False;
- if GetFileStatus(FileName, Buf, ResolveSymLinks) = 0 then
- Result := S_ISDIR(Buf.st_mode);
- end;
- {$ENDIF UNIX}
- function IsRootDirectory(const CanonicFileName: string): Boolean;
- {$IFDEF MSWINDOWS}
- var
- I: Integer;
- begin
- I := Pos(':\', CanonicFileName);
- Result := (I > 0) and (I + 1 = Length(CanonicFileName));
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- begin
- Result := CanonicFileName = DirDelimiter;
- end;
- {$ENDIF UNIX}
- {$IFDEF MSWINDOWS}
- function LockVolume(const Volume: string; var Handle: THandle): Boolean;
- var
- BytesReturned: DWORD;
- begin
- Result := False;
- Handle := CreateFile(PChar('\\.\' + Volume), GENERIC_READ or GENERIC_WRITE,
- FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,
- FILE_FLAG_NO_BUFFERING, 0);
- if Handle <> INVALID_HANDLE_VALUE then
- begin
- BytesReturned := 0;
- Result := DeviceIoControl(Handle, FSCTL_LOCK_VOLUME, nil, 0, nil, 0,
- BytesReturned, nil);
- if not Result then
- begin
- CloseHandle(Handle);
- Handle := INVALID_HANDLE_VALUE;
- end;
- end;
- end;
- function OpenVolume(const Drive: Char): THandle;
- var
- VolumeName: array [0..6] of Char;
- begin
- VolumeName := '\\.\A:';
- VolumeName[4] := Drive;
- Result := CreateFile(VolumeName, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE,
- nil, OPEN_EXISTING, 0, 0);
- end;
- {$ENDIF MSWINDOWS}
- type
- // indicates the file time to set, used by SetFileTimesHelper and SetDirTimesHelper
- TFileTimes = (ftLastAccess, ftLastWrite {$IFDEF MSWINDOWS}, ftCreation {$ENDIF});
- {$IFDEF MSWINDOWS}
- function SetFileTimesHelper(const FileName: string; const DateTime: TDateTime; Times: TFileTimes): Boolean;
- var
- Handle: THandle;
- FileTime: TFileTime;
- SystemTime: TSystemTime;
- begin
- Result := False;
- Handle := CreateFile(PChar(FileName), GENERIC_WRITE, FILE_SHARE_READ, nil,
- OPEN_EXISTING, 0, 0);
- if Handle <> INVALID_HANDLE_VALUE then
- try
- //SysUtils.DateTimeToSystemTime(DateTimeToLocalDateTime(DateTime), SystemTime);
- {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.DateTimeToSystemTime(DateTime, SystemTime);
- FileTime.dwLowDateTime := 0;
- FileTime.dwHighDateTime := 0;
- if {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SystemTimeToFileTime(SystemTime, FileTime) then
- begin
- case Times of
- ftLastAccess:
- Result := SetFileTime(Handle, nil, @FileTime, nil);
- ftLastWrite:
- Result := SetFileTime(Handle, nil, nil, @FileTime);
- ftCreation:
- Result := SetFileTime(Handle, @FileTime, nil, nil);
- end;
- end;
- finally
- CloseHandle(Handle);
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function SetFileTimesHelper(const FileName: string; const DateTime: TDateTime; Times: TFileTimes): Boolean;
- var
- FileTime: Integer;
- StatBuf: TStatBuf64;
- TimeBuf: utimbuf;
- begin
- Result := False;
- FileTime := DateTimeToFileDate(DateTime);
- if GetFileStatus(FileName, StatBuf, False) = 0 then
- begin
- TimeBuf.actime := StatBuf.st_atime;
- TimeBuf.modtime := StatBuf.st_mtime;
- case Times of
- ftLastAccess:
- TimeBuf.actime := FileTime;
- ftLastWrite:
- TimeBuf.modtime := FileTime;
- end;
- Result := utime(PChar(FileName), @TimeBuf) = 0;
- end;
- end;
- {$ENDIF UNIX}
- function SetFileLastAccess(const FileName: string; const DateTime: TDateTime): Boolean;
- begin
- Result := SetFileTimesHelper(FileName, DateTime, ftLastAccess);
- end;
- function SetFileLastWrite(const FileName: string; const DateTime: TDateTime): Boolean;
- begin
- Result := SetFileTimesHelper(FileName, DateTime, ftLastWrite);
- end;
- {$IFDEF MSWINDOWS}
- function SetFileCreation(const FileName: string; const DateTime: TDateTime): Boolean;
- begin
- Result := SetFileTimesHelper(FileName, DateTime, ftCreation);
- end;
- // utility function for SetDirTimesHelper
- {$IFNDEF WINSCP}
- function BackupPrivilegesEnabled: Boolean;
- begin
- Result := IsPrivilegeEnabled(SE_BACKUP_NAME) and IsPrivilegeEnabled(SE_RESTORE_NAME);
- end;
- function SetDirTimesHelper(const DirName: string; const DateTime: TDateTime;
- Times: TFileTimes; RequireBackupRestorePrivileges: Boolean): Boolean;
- var
- Handle: THandle;
- FileTime: TFileTime;
- SystemTime: TSystemTime;
- begin
- Result := False;
- if IsDirectory(DirName) and (not RequireBackupRestorePrivileges or BackupPrivilegesEnabled) then
- begin
- Handle := CreateFile(PChar(DirName), GENERIC_WRITE, FILE_SHARE_READ, nil,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
- if Handle <> INVALID_HANDLE_VALUE then
- try
- {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.DateTimeToSystemTime(DateTime, SystemTime);
- FileTime.dwLowDateTime := 0;
- FileTime.dwHighDateTime := 0;
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SystemTimeToFileTime(SystemTime, FileTime);
- case Times of
- ftLastAccess:
- Result := SetFileTime(Handle, nil, @FileTime, nil);
- ftLastWrite:
- Result := SetFileTime(Handle, nil, nil, @FileTime);
- ftCreation:
- Result := SetFileTime(Handle, @FileTime, nil, nil);
- end;
- finally
- CloseHandle(Handle);
- end;
- end;
- end;
- function SetDirLastWrite(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
- begin
- Result := SetDirTimesHelper(DirName, DateTime, ftLastWrite, RequireBackupRestorePrivileges);
- end;
- function SetDirLastAccess(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
- begin
- Result := SetDirTimesHelper(DirName, DateTime, ftLastAccess, RequireBackupRestorePrivileges);
- end;
- function SetDirCreation(const DirName: string; const DateTime: TDateTime; RequireBackupRestorePrivileges: Boolean = True): Boolean;
- begin
- Result := SetDirTimesHelper(DirName, DateTime, ftCreation, RequireBackupRestorePrivileges);
- end;
- {$ENDIF ~WINSCP}
- procedure FillByteArray(var Bytes: array of Byte; Count: Cardinal; B: Byte);
- begin
- FillMemory(@Bytes[0], Count, B);
- end;
- procedure ShredFile(const FileName: string; Times: Integer);
- const
- BUFSIZE = 4096;
- ODD_FILL = $C1;
- EVEN_FILL = $3E;
- var
- Fs: TFileStream;
- Size: Integer;
- N: Integer;
- ContentPtr: array of Byte;
- begin
- Size := FileGetSize(FileName);
- if Size > 0 then
- begin
- if Times < 0 then
- Times := 2
- else
- Times := Times * 2;
- ContentPtr := nil;
- Fs := TFileStream.Create(FileName, fmOpenReadWrite);
- try
- SetLength(ContentPtr, BUFSIZE);
- while Times > 0 do
- begin
- if Times mod 2 = 0 then
- FillByteArray(ContentPtr, BUFSIZE, EVEN_FILL)
- else
- FillByteArray(ContentPtr, BUFSIZE, ODD_FILL);
- Fs.Seek(0, soBeginning);
- N := Size div BUFSIZE;
- while N > 0 do
- begin
- Fs.Write(ContentPtr[0], BUFSIZE);
- Dec(N);
- end;
- N := Size mod BUFSIZE;
- if N > 0 then
- Fs.Write(ContentPtr[0], N);
- FlushFileBuffers(Fs.Handle);
- Dec(Times);
- end;
- finally
- ContentPtr := nil;
- Fs.Free;
- DeleteFile(FileName);
- end;
- end
- else
- DeleteFile(FileName);
- end;
- function UnlockVolume(var Handle: THandle): Boolean;
- var
- BytesReturned: DWORD;
- begin
- Result := False;
- if Handle <> INVALID_HANDLE_VALUE then
- begin
- BytesReturned := 0;
- Result := DeviceIoControl(Handle, FSCTL_UNLOCK_VOLUME, nil, 0, nil, 0,
- BytesReturned, nil);
- if Result then
- begin
- CloseHandle(Handle);
- Handle := INVALID_HANDLE_VALUE;
- end;
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- function CreateSymbolicLink(const Name, Target: string): Boolean;
- begin
- Result := symlink(PChar(Target), PChar(Name)) = 0;
- end;
- function SymbolicLinkTarget(const Name: string): string;
- var
- N, BufLen: Integer;
- begin
- BufLen := 128;
- repeat
- Inc(BufLen, BufLen);
- SetLength(Result, BufLen);
- N := readlink(PChar(Name), PChar(Result), BufLen);
- if N < 0 then // Error
- begin
- Result := '';
- Exit;
- end;
- until N < BufLen;
- SetLength(Result, N);
- end;
- {$ENDIF UNIX}
- //=== File Version info routines =============================================
- {$IFDEF MSWINDOWS}
- const
- VerKeyNames: array [1..12] of string =
- ('Comments',
- 'CompanyName',
- 'FileDescription',
- 'FileVersion',
- 'InternalName',
- 'LegalCopyright',
- 'LegalTradeMarks',
- 'OriginalFilename',
- 'ProductName',
- 'ProductVersion',
- 'SpecialBuild',
- 'PrivateBuild');
- function OSIdentToString(const OSIdent: DWORD): string;
- begin
- case OSIdent of
- VOS_UNKNOWN:
- Result := LoadResString(@RsVosUnknown);
- VOS_DOS:
- Result := LoadResString(@RsVosDos);
- VOS_OS216:
- Result := LoadResString(@RsVosOS216);
- VOS_OS232:
- Result := LoadResString(@RsVosOS232);
- VOS_NT:
- Result := LoadResString(@RsVosNT);
- VOS__WINDOWS16:
- Result := LoadResString(@RsVosWindows16);
- VOS__PM16:
- Result := LoadResString(@RsVosPM16);
- VOS__PM32:
- Result := LoadResString(@RsVosPM32);
- VOS__WINDOWS32:
- Result := LoadResString(@RsVosWindows32);
- VOS_DOS_WINDOWS16:
- Result := LoadResString(@RsVosDosWindows16);
- VOS_DOS_WINDOWS32:
- Result := LoadResString(@RsVosDosWindows32);
- VOS_OS216_PM16:
- Result := LoadResString(@RsVosOS216PM16);
- VOS_OS232_PM32:
- Result := LoadResString(@RsVosOS232PM32);
- VOS_NT_WINDOWS32:
- Result := LoadResString(@RsVosNTWindows32);
- else
- Result := '';
- end;
- if Result = '' then
- Result := LoadResString(@RsVosUnknown)
- else
- Result := Format(LoadResString(@RsVosDesignedFor), [Result]);
- end;
- function OSFileTypeToString(const OSFileType: DWORD; const OSFileSubType: DWORD): string;
- begin
- case OSFileType of
- VFT_UNKNOWN:
- Result := LoadResString(@RsVftUnknown);
- VFT_APP:
- Result := LoadResString(@RsVftApp);
- VFT_DLL:
- Result := LoadResString(@RsVftDll);
- VFT_DRV:
- begin
- case OSFileSubType of
- VFT2_DRV_PRINTER:
- Result := LoadResString(@RsVft2DrvPRINTER);
- VFT2_DRV_KEYBOARD:
- Result := LoadResString(@RsVft2DrvKEYBOARD);
- VFT2_DRV_LANGUAGE:
- Result := LoadResString(@RsVft2DrvLANGUAGE);
- VFT2_DRV_DISPLAY:
- Result := LoadResString(@RsVft2DrvDISPLAY);
- VFT2_DRV_MOUSE:
- Result := LoadResString(@RsVft2DrvMOUSE);
- VFT2_DRV_NETWORK:
- Result := LoadResString(@RsVft2DrvNETWORK);
- VFT2_DRV_SYSTEM:
- Result := LoadResString(@RsVft2DrvSYSTEM);
- VFT2_DRV_INSTALLABLE:
- Result := LoadResString(@RsVft2DrvINSTALLABLE);
- VFT2_DRV_SOUND:
- Result := LoadResString(@RsVft2DrvSOUND);
- VFT2_DRV_COMM:
- Result := LoadResString(@RsVft2DrvCOMM);
- else
- Result := '';
- end;
- Result := Result + ' ' + LoadResString(@RsVftDrv);
- end;
- VFT_FONT:
- begin
- case OSFileSubType of
- VFT2_FONT_RASTER:
- Result := LoadResString(@RsVft2FontRASTER);
- VFT2_FONT_VECTOR:
- Result := LoadResString(@RsVft2FontVECTOR);
- VFT2_FONT_TRUETYPE:
- Result := LoadResString(@RsVft2FontTRUETYPE);
- else
- Result := '';
- end;
- Result := Result + ' ' + LoadResString(@RsVftFont);
- end;
- VFT_VXD:
- Result := LoadResString(@RsVftVxd);
- VFT_STATIC_LIB:
- Result := LoadResString(@RsVftStaticLib);
- else
- Result := '';
- end;
- Result := TrimLeft(Result);
- end;
- function VersionResourceAvailable(const FileName: string): Boolean;
- var
- Size: DWORD;
- Handle: DWORD;
- Buffer: string;
- begin
- Result := False;
- Handle := 0;
- Size := GetFileVersionInfoSize(PChar(FileName), Handle);
- if Size > 0 then
- begin
- SetLength(Buffer, Size);
- Result := GetFileVersionInfo(PChar(FileName), Handle, Size, PChar(Buffer));
- end;
- end;
- function VersionResourceAvailable(const Window: HWND): Boolean;
- begin
- Result := VersionResourceAvailable(WindowToModuleFileName(Window));
- end;
- function VersionResourceAvailable(const Module: HMODULE): Boolean;
- begin
- if Module <> 0 then
- Result :=VersionResourceAvailable(GetModulePath(Module))
- else
- raise EJclError.CreateResFmt(@RsEModuleNotValid, [Module]);
- end;
- function WindowToModuleFileName(const Window: HWND): string;
- type
- {$IFDEF SUPPORTS_UNICODE}
- TGetModuleFileNameEx = function(hProcess: THandle; hModule: HMODULE; FileName: PWideChar; nSize: DWORD): DWORD; stdcall;
- TQueryFullProcessImageName = function(HProcess: THandle; dwFlags: DWORD; lpExeName: PWideChar; lpdwSize: PDWORD): integer; stdcall;
- {$ELSE ~SUPPORTS_UNICODE}
- TGetModuleFileNameEx = function(hProcess: THandle; hModule: HMODULE; FileName: PAnsiChar; nSize: DWORD): DWORD; stdcall;
- TQueryFullProcessImageName = function(HProcess: THandle; dwFlags: DWORD; lpExeName: PAnsiChar; lpdwSize: PDWORD): integer; stdcall;
- {$ENDIF ~SUPPORTS_UNICODE}
- var
- FileName: array[0..300] of Char;
- DllHinst: HMODULE;
- ProcessID: DWORD;
- HProcess: THandle;
- GetModuleFileNameExAddress: TGetModuleFileNameEx;
- QueryFullProcessImageNameAddress: TQueryFullProcessImageName;
- begin
- Result := '';
- if Window <> 0 then
- begin
- if not JclCheckWinVersion(5, 0) then // Win2k or newer required
- raise EJclWin32Error.CreateRes(@RsEWindowsVersionNotSupported);
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetWindowThreadProcessId(Window, @ProcessID);
- hProcess := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, ProcessID);
- if hProcess <> 0 then
- begin
- try
- if JclCheckWinVersion(6, 0) then // WinVista or newer
- begin
- DllHinst := LoadLibrary('Kernel32.dll');
- if DllHinst < HINSTANCE_ERROR then
- begin
- try
- {$IFDEF SUPPORTS_UNICODE}
- QueryFullProcessImageNameAddress := GetProcAddress(DllHinst, 'QueryFullProcessImageNameW');
- {$ELSE ~SUPPORTS_UNICODE}
- QueryFullProcessImageNameAddress := GetProcAddress(DllHinst, 'QueryFullProcessImageNameA');
- {$ENDIF ~SUPPORTS_UNICODE}
- if Assigned(QueryFullProcessImageNameAddress) then
- begin
- QueryFullProcessImageNameAddress(hProcess, 0, FileName, PDWORD(sizeof(FileName)));
- Result := FileName;
- end
- else
- begin
- raise EJclError.CreateResFmt(@RsEFunctionNotFound, ['Kernel32.dll', 'QueryFullProcessImageName']);
- end
- finally
- FreeLibrary(DllHinst);
- end;
- end
- else
- raise EJclError.CreateResFmt(@RsELibraryNotFound, ['Kernel32.dll']);
- end
- else
- begin
- DllHinst := LoadLibrary('Psapi.dll');
- if DllHinst < HINSTANCE_ERROR then
- begin
- try
- {$IFDEF SUPPORTS_UNICODE}
- GetModuleFileNameExAddress := GetProcAddress(DllHinst, 'GetModuleFileNameExW');
- {$ELSE ~SUPPORTS_UNICODE}
- GetModuleFileNameExAddress := GetProcAddress(DllHinst, 'GetModuleFileNameExA');
- {$ENDIF ~SUPPORTS_UNICODE}
- if Assigned(GetModuleFileNameExAddress) then
- begin
- GetModuleFileNameExAddress(hProcess, 0, FileName, sizeof(FileName));
- Result := FileName;
- end
- else
- begin
- raise EJclError.CreateResFmt(@RsEFunctionNotFound, ['Psapi.dll', 'GetModuleFileNameEx']);
- end
- finally
- FreeLibrary(DllHinst);
- end;
- end
- else
- raise EJclError.CreateResFmt(@RsELibraryNotFound, ['Psapi.dll']);
- end;
- finally
- CloseHandle(hProcess);
- end;
- end
- else
- raise EJclError.CreateResFmt(@RsEProcessNotValid, [ProcessID]);
- end
- else
- raise EJclError.CreateResFmt(@RsEWindowNotValid, [Window]);
- end;
- {$ENDIF MSWINDOWS}
- // Version Info formatting
- function FormatVersionString(const HiV, LoV: Word): string;
- begin
- Result := Format('%u.%.2u', [HiV, LoV]);
- end;
- function FormatVersionString(const Major, Minor, Build, Revision: Word): string;
- begin
- Result := Format('%u.%u.%u.%u', [Major, Minor, Build, Revision]);
- end;
- {$IFDEF MSWINDOWS}
- function FormatVersionString(const FixedInfo: TVSFixedFileInfo; VersionFormat: TFileVersionFormat): string;
- begin
- case VersionFormat of
- vfMajorMinor:
- Result := Format('%u.%u', [HiWord(FixedInfo.dwFileVersionMS), LoWord(FixedInfo.dwFileVersionMS)]);
- vfFull:
- Result := Format('%u.%u.%u.%u', [HiWord(FixedInfo.dwFileVersionMS), LoWord(FixedInfo.dwFileVersionMS),
- HiWord(FixedInfo.dwFileVersionLS), LoWord(FixedInfo.dwFileVersionLS)]);
- end;
- end;
- // Version Info extracting
- procedure VersionExtractFileInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
- begin
- Major := HiWord(FixedInfo.dwFileVersionMS);
- Minor := LoWord(FixedInfo.dwFileVersionMS);
- Build := HiWord(FixedInfo.dwFileVersionLS);
- Revision := LoWord(FixedInfo.dwFileVersionLS);
- end;
- procedure VersionExtractProductInfo(const FixedInfo: TVSFixedFileInfo; var Major, Minor, Build, Revision: Word);
- begin
- Major := HiWord(FixedInfo.dwProductVersionMS);
- Minor := LoWord(FixedInfo.dwProductVersionMS);
- Build := HiWord(FixedInfo.dwProductVersionLS);
- Revision := LoWord(FixedInfo.dwProductVersionLS);
- end;
- // Fixed Version Info routines
- function VersionFixedFileInfo(const FileName: string; var FixedInfo: TVSFixedFileInfo): Boolean;
- var
- Size, FixInfoLen: DWORD;
- Handle: DWORD;
- Buffer: string;
- FixInfoBuf: PVSFixedFileInfo;
- begin
- Result := False;
- Handle := 0;
- Size := GetFileVersionInfoSize(PChar(FileName), Handle);
- if Size > 0 then
- begin
- SetLength(Buffer, Size);
- FixInfoLen := 0;
- FixInfoBuf := nil;
- if GetFileVersionInfo(PChar(FileName), Handle, Size, Pointer(Buffer)) and
- VerQueryValue(Pointer(Buffer), DirDelimiter, Pointer(FixInfoBuf), FixInfoLen) and
- (FixInfoLen = SizeOf(TVSFixedFileInfo)) then
- begin
- Result := True;
- FixedInfo := FixInfoBuf^;
- end;
- end;
- end;
- function VersionFixedFileInfoString(const FileName: string; VersionFormat: TFileVersionFormat;
- const NotAvailableText: string): string;
- var
- FixedInfo: TVSFixedFileInfo;
- begin
- FixedInfo.dwSignature := 0;
- if VersionFixedFileInfo(FileName, FixedInfo) then
- Result := FormatVersionString(FixedInfo, VersionFormat)
- else
- Result := NotAvailableText;
- end;
- //=== { TJclFileVersionInfo } ================================================
- constructor TJclFileVersionInfo.Attach(VersionInfoData: Pointer; Size: Integer);
- begin
- SetLength(FBuffer, Size);
- CopyMemory(PAnsiChar(FBuffer), VersionInfoData, Size);
- ExtractData;
- end;
- constructor TJclFileVersionInfo.Create(const FileName: string);
- var
- Handle: DWORD;
- Size: DWORD;
- begin
- if not FileExists(FileName) then
- raise EJclFileVersionInfoError.CreateResFmt(@RsFileUtilsFileDoesNotExist, [FileName]);
- Handle := 0;
- Size := GetFileVersionInfoSize(PChar(FileName), Handle);
- if Size = 0 then
- raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsNoVersionInfo);
- SetLength(FBuffer, Size);
- Win32Check(GetFileVersionInfo(PChar(FileName), Handle, Size, PAnsiChar(FBuffer)));
- ExtractData;
- end;
- {$IFDEF MSWINDOWS}
- {$IFDEF FPC}
- constructor TJclFileVersionInfo.Create(const Window: HWND; Dummy: Pointer = nil);
- {$ELSE}
- constructor TJclFileVersionInfo.Create(const Window: HWND);
- {$ENDIF}
- begin
- Create(WindowToModuleFileName(Window));
- end;
- constructor TJclFileVersionInfo.Create(const Module: HMODULE);
- begin
- if Module <> 0 then
- Create(GetModulePath(Module))
- else
- raise EJclError.CreateResFmt(@RsEModuleNotValid, [Module]);
- end;
- {$ENDIF MSWINDOWS}
- destructor TJclFileVersionInfo.Destroy;
- begin
- FreeAndNil(FItemList);
- FreeAndNil(FItems);
- inherited Destroy;
- end;
- class function TJclFileVersionInfo.FileHasVersionInfo(const FileName: string): boolean;
- var
- Dummy: DWord;
- begin
- Result := GetFileVersionInfoSize(PChar(FileName), Dummy) <> 0;
- end;
- procedure TJclFileVersionInfo.CheckLanguageIndex(Value: Integer);
- begin
- if (Value < 0) or (Value >= LanguageCount) then
- raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsLanguageIndex);
- end;
- procedure TJclFileVersionInfo.CreateItemsForLanguage;
- var
- I: Integer;
- begin
- Items.Clear;
- for I := 0 to FItemList.Count - 1 do
- if Integer(FItemList.Objects[I]) = FLanguageIndex then
- Items.AddObject(FItemList[I], Pointer(FLanguages[FLanguageIndex].Pair));
- end;
- procedure TJclFileVersionInfo.ExtractData;
- var
- Data, EndOfData: PAnsiChar;
- Len, ValueLen, DataType: Word;
- HeaderSize: Integer;
- Key: string;
- Error, IsUnicode: Boolean;
- procedure Padding(var DataPtr: PAnsiChar);
- begin
- while TJclAddr(DataPtr) and 3 <> 0 do
- Inc(DataPtr);
- end;
- procedure GetHeader;
- var
- P: PAnsiChar;
- TempKey: PWideChar;
- begin
- Key := '';
- P := Data;
- Len := PWord(P)^;
- if Len = 0 then
- begin
- // do not raise error in the case of resources padded with 0
- while P < EndOfData do
- begin
- Error := P^ <> #0;
- if Error then
- Break;
- Inc(P);
- end;
- Exit;
- end;
- Inc(P, SizeOf(Word));
- ValueLen := PWord(P)^;
- Inc(P, SizeOf(Word));
- if IsUnicode then
- begin
- DataType := PWord(P)^;
- Inc(P, SizeOf(Word));
- TempKey := PWideChar(P);
- Inc(P, (lstrlenW(TempKey) + 1) * SizeOf(WideChar)); // length + #0#0
- Key := TempKey;
- end
- else
- begin
- DataType := 1;
- Key := string(PAnsiChar(P));
- Inc(P, lstrlenA(PAnsiChar(P)) + 1);
- end;
- Padding(P);
- HeaderSize := P - Data;
- Data := P;
- end;
- procedure FixKeyValue;
- const
- HexNumberCPrefix = '0x';
- var
- I: Integer;
- begin // GAPI32.DLL version 5.5.2803.1 contanins '04050x04E2' value
- repeat
- I := Pos(HexNumberCPrefix, Key);
- if I > 0 then
- Delete(Key, I, Length(HexNumberCPrefix));
- until I = 0;
- I := 1;
- while I <= Length(Key) do
- if CharIsHexDigit(Key[I]) then
- Inc(I)
- else
- Delete(Key, I, 1);
- end;
- procedure ProcessStringInfo(Size: Integer);
- var
- EndPtr, EndStringPtr: PAnsiChar;
- LangIndex: Integer;
- LangIdRec: TLangIdRec;
- Value: string;
- begin
- EndPtr := Data + Size;
- LangIndex := 0;
- while not Error and (Data < EndPtr) do
- begin
- GetHeader; // StringTable
- FixKeyValue;
- if (ValueLen <> 0) or (Length(Key) <> 8) then
- begin
- Error := True;
- Break;
- end;
- Padding(Data);
- LangIdRec.LangId := StrToIntDef('$' + Copy(Key, 1, 4), 0);
- LangIdRec.CodePage := StrToIntDef('$' + Copy(Key, 5, 4), 0);
- SetLength(FLanguages, LangIndex + 1);
- FLanguages[LangIndex] := LangIdRec;
- EndStringPtr := Data + Len - HeaderSize;
- while not Error and (Data < EndStringPtr) do
- begin
- GetHeader; // string
- case DataType of
- 0:
- if ValueLen in [1..4] then
- Value := Format('$%.*x', [ValueLen * 2, PInteger(Data)^])
- else
- begin
- if (ValueLen > 0) and IsUnicode then
- Value:=PWideChar(Data)
- else
- Value := '';
- end;
- 1:
- if ValueLen = 0 then
- Value := ''
- else
- if IsUnicode then
- begin
- Value := WideCharLenToString(PWideChar(Data), ValueLen);
- StrResetLength(Value);
- end
- else
- Value := string(PAnsiChar(Data));
- else
- Error := True;
- Break;
- end;
- Inc(Data, Len - HeaderSize);
- Padding(Data); // String.Padding
- FItemList.AddObject(Format('%s=%s', [Key, Value]), Pointer(LangIndex));
- end;
- Inc(LangIndex);
- end;
- end;
- procedure ProcessVarInfo;
- var
- TranslationIndex: Integer;
- begin
- GetHeader; // Var
- if SameText(Key, 'Translation') then
- begin
- SetLength(FTranslations, ValueLen div SizeOf(TLangIdRec));
- for TranslationIndex := 0 to Length(FTranslations) - 1 do
- begin
- FTranslations[TranslationIndex] := PLangIdRec(Data)^;
- Inc(Data, SizeOf(TLangIdRec));
- end;
- end;
- end;
- begin
- FItemList := TStringList.Create;
- FItems := TStringList.Create;
- Data := Pointer(FBuffer);
- Assert(TJclAddr(Data) mod 4 = 0);
- IsUnicode := (PWord(Data + 4)^ in [0, 1]);
- Error := True;
- GetHeader;
- EndOfData := Data + Len - HeaderSize;
- if SameText(Key, 'VS_VERSION_INFO') and (ValueLen = SizeOf(TVSFixedFileInfo)) then
- begin
- FFixedInfo := PVSFixedFileInfo(Data);
- Error := FFixedInfo.dwSignature <> $FEEF04BD;
- Inc(Data, ValueLen); // VS_FIXEDFILEINFO
- Padding(Data); // VS_VERSIONINFO.Padding2
- while not Error and (Data < EndOfData) do
- begin
- GetHeader;
- Inc(Data, ValueLen); // some files (VREDIR.VXD 4.00.1111) has non zero value of ValueLen
- Dec(Len, HeaderSize + ValueLen);
- if SameText(Key, 'StringFileInfo') then
- ProcessStringInfo(Len)
- else
- if SameText(Key, 'VarFileInfo') then
- ProcessVarInfo
- else
- Break;
- end;
- ExtractFlags;
- CreateItemsForLanguage;
- end;
- if Error then
- raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsNoVersionInfo);
- end;
- procedure TJclFileVersionInfo.ExtractFlags;
- var
- Masked: DWORD;
- begin
- FFileFlags := [];
- Masked := FFixedInfo^.dwFileFlags and FFixedInfo^.dwFileFlagsMask;
- if (Masked and VS_FF_DEBUG) <> 0 then
- Include(FFileFlags, ffDebug);
- if (Masked and VS_FF_INFOINFERRED) <> 0 then
- Include(FFileFlags, ffInfoInferred);
- if (Masked and VS_FF_PATCHED) <> 0 then
- Include(FFileFlags, ffPatched);
- if (Masked and VS_FF_PRERELEASE) <> 0 then
- Include(FFileFlags, ffPreRelease);
- if (Masked and VS_FF_PRIVATEBUILD) <> 0 then
- Include(FFileFlags, ffPrivateBuild);
- if (Masked and VS_FF_SPECIALBUILD) <> 0 then
- Include(FFileFlags, ffSpecialBuild);
- end;
- function TJclFileVersionInfo.GetBinFileVersion: string;
- begin
- Result := Format('%u.%u.%u.%u', [HiWord(FFixedInfo^.dwFileVersionMS),
- LoWord(FFixedInfo^.dwFileVersionMS), HiWord(FFixedInfo^.dwFileVersionLS),
- LoWord(FFixedInfo^.dwFileVersionLS)]);
- end;
- function TJclFileVersionInfo.GetBinProductVersion: string;
- begin
- Result := Format('%u.%u.%u.%u', [HiWord(FFixedInfo^.dwProductVersionMS),
- LoWord(FFixedInfo^.dwProductVersionMS), HiWord(FFixedInfo^.dwProductVersionLS),
- LoWord(FFixedInfo^.dwProductVersionLS)]);
- end;
- function TJclFileVersionInfo.GetCustomFieldValue(const FieldName: string): string;
- var
- ItemIndex: Integer;
- begin
- if FieldName <> '' then
- begin
- ItemIndex := FItems.IndexOfName(FieldName);
- if ItemIndex <> -1 then
- //Return the required value, the value the user passed in was found.
- Result := FItems.Values[FieldName]
- else
- raise EJclFileVersionInfoError.CreateResFmt(@RsFileUtilsValueNotFound, [FieldName]);
- end
- else
- raise EJclFileVersionInfoError.CreateRes(@RsFileUtilsEmptyValue);
- end;
- function TJclFileVersionInfo.GetFileOS: DWORD;
- begin
- Result := FFixedInfo^.dwFileOS;
- end;
- function TJclFileVersionInfo.GetFileSubType: DWORD;
- begin
- Result := FFixedInfo^.dwFileSubtype;
- end;
- function TJclFileVersionInfo.GetFileType: DWORD;
- begin
- Result := FFixedInfo^.dwFileType;
- end;
- function TJclFileVersionInfo.GetFileVersionBuild: string;
- var
- Left: Integer;
- begin
- Result := FileVersion;
- StrReplaceChar(Result, ',', '.');
- Left := CharLastPos(Result, '.') + 1;
- Result := StrMid(Result, Left, Length(Result) - Left + 1);
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetFileVersionMajor: string;
- begin
- Result := FileVersion;
- StrReplaceChar(Result, ',', '.');
- Result := StrBefore('.', Result);
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetFileVersionMinor: string;
- var
- Left, Right: integer;
- begin
- Result := FileVersion;
- StrReplaceChar(Result, ',', '.');
- Left := CharPos(Result, '.') + 1; // skip major
- Right := CharPos(Result, '.', Left) {-1};
- Result := StrMid(Result, Left, Right - Left {+1});
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetFileVersionRelease: string;
- var
- Left, Right: Integer;
- begin
- Result := FileVersion;
- StrReplaceChar(Result, ',', '.');
- Left := CharPos(Result, '.') + 1; // skip major
- Left := CharPos(Result, '.', Left) + 1; // skip minor
- Right := CharPos(Result, '.', Left) {-1};
- Result := StrMid(Result, Left, Right - Left {+1});
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetFixedInfo: TVSFixedFileInfo;
- begin
- Result := FFixedInfo^;
- end;
- function TJclFileVersionInfo.GetItems: TStrings;
- begin
- Result := FItems;
- end;
- function TJclFileVersionInfo.GetLanguageCount: Integer;
- begin
- Result := Length(FLanguages);
- end;
- function TJclFileVersionInfo.GetLanguageIds(Index: Integer): string;
- begin
- CheckLanguageIndex(Index);
- Result := VersionLanguageId(FLanguages[Index]);
- end;
- function TJclFileVersionInfo.GetLanguages(Index: Integer): TLangIdRec;
- begin
- CheckLanguageIndex(Index);
- Result := FLanguages[Index];
- end;
- function TJclFileVersionInfo.GetLanguageNames(Index: Integer): string;
- begin
- CheckLanguageIndex(Index);
- Result := VersionLanguageName(FLanguages[Index].LangId);
- end;
- function TJclFileVersionInfo.GetTranslationCount: Integer;
- begin
- Result := Length(FTranslations);
- end;
- function TJclFileVersionInfo.GetTranslations(Index: Integer): TLangIdRec;
- begin
- Result := FTranslations[Index];
- end;
- function TJclFileVersionInfo.GetProductVersionBuild: string;
- var
- Left: Integer;
- begin
- Result := ProductVersion;
- StrReplaceChar(Result, ',', '.');
- Left := CharLastPos(Result, '.') + 1;
- Result := StrMid(Result, Left, Length(Result) - Left + 1);
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetProductVersionMajor: string;
- begin
- Result := ProductVersion;
- StrReplaceChar(Result, ',', '.');
- Result := StrBefore('.', Result);
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetProductVersionMinor: string;
- var
- Left, Right: integer;
- begin
- Result := ProductVersion;
- StrReplaceChar(Result, ',', '.');
- Left := CharPos(Result, '.') + 1; // skip major
- Right := CharPos(Result, '.', Left) {-1};
- Result := StrMid(Result, Left, Right - Left {+1});
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetProductVersionRelease: string;
- var
- Left, Right: Integer;
- begin
- Result := ProductVersion;
- StrReplaceChar(Result, ',', '.');
- Left := CharPos(Result, '.') + 1; // skip major
- Left := CharPos(Result, '.', Left) + 1; // skip minor
- Right := CharPos(Result, '.', Left) {-1};
- Result := StrMid(Result, Left, Right - Left {+1});
- Result := Trim(Result);
- end;
- function TJclFileVersionInfo.GetVersionKeyValue(Index: Integer): string;
- begin
- Result := Items.Values[VerKeyNames[Index]];
- end;
- procedure TJclFileVersionInfo.SetLanguageIndex(const Value: Integer);
- begin
- CheckLanguageIndex(Value);
- if FLanguageIndex <> Value then
- begin
- FLanguageIndex := Value;
- CreateItemsForLanguage;
- end;
- end;
- function TJclFileVersionInfo.TranslationMatchesLanguages(Exact: Boolean): Boolean;
- var
- TransIndex, LangIndex: Integer;
- TranslationPair: DWORD;
- begin
- Result := (LanguageCount = TranslationCount) or (not Exact and (TranslationCount > 0));
- if Result then
- for TransIndex := 0 to TranslationCount - 1 do
- begin
- TranslationPair := FTranslations[TransIndex].Pair;
- LangIndex := LanguageCount - 1;
- while (LangIndex >= 0) and (TranslationPair <> FLanguages[LangIndex].Pair) do
- Dec(LangIndex);
- if LangIndex < 0 then
- begin
- Result := False;
- Break;
- end;
- end;
- end;
- class function TJclFileVersionInfo.VersionLanguageId(const LangIdRec: TLangIdRec): string;
- begin
- with LangIdRec do
- Result := Format('%.4x%.4x', [LangId, CodePage]);
- end;
- class function TJclFileVersionInfo.VersionLanguageName(const LangId: Word): string;
- var
- R: DWORD;
- begin
- SetLength(Result, MAX_PATH);
- R := VerLanguageName(LangId, PChar(Result), MAX_PATH);
- SetLength(Result, R);
- end;
- {$ENDIF MSWINDOWS}
- //=== { TJclFileMaskComparator } =============================================
- constructor TJclFileMaskComparator.Create;
- begin
- inherited Create;
- FSeparator := DirSeparator;
- end;
- function TJclFileMaskComparator.Compare(const NameExt: string): Boolean;
- var
- I: Integer;
- NamePart, ExtPart: string;
- NameWild, ExtWild: Boolean;
- begin
- Result := False;
- I := StrLastPos('.', NameExt);
- if I = 0 then
- begin
- NamePart := NameExt;
- ExtPart := '';
- end
- else
- begin
- NamePart := Copy(NameExt, 1, I - 1);
- ExtPart := Copy(NameExt, I + 1, Length(NameExt));
- end;
- for I := 0 to Length(FNames) - 1 do
- begin
- NameWild := FWildChars[I] and 1 = 1;
- ExtWild := FWildChars[I] and 2 = 2;
- if ((not NameWild and StrSame(FNames[I], NamePart)) or
- (NameWild and (StrMatches(FNames[I], NamePart, 1)))) and
- ((not ExtWild and StrSame(FExts[I], ExtPart)) or
- (ExtWild and (StrMatches(FExts[I], ExtPart, 1)))) then
- begin
- Result := True;
- Break;
- end;
- end;
- end;
- procedure TJclFileMaskComparator.CreateMultiMasks;
- var
- List: TStringList;
- I, N: Integer;
- NS, ES: string;
- begin
- FExts := nil;
- FNames := nil;
- FWildChars := nil;
- List := TStringList.Create;
- try
- StrToStrings(FFileMask, FSeparator, List);
- SetLength(FExts, List.Count);
- SetLength(FNames, List.Count);
- SetLength(FWildChars, List.Count);
- for I := 0 to List.Count - 1 do
- begin
- N := StrLastPos('.', List[I]);
- if N = 0 then
- begin
- NS := List[I];
- ES := '';
- end
- else
- begin
- NS := Copy(List[I], 1, N - 1);
- ES := Copy(List[I], N + 1, 255);
- end;
- FNames[I] := NS;
- FExts[I] := ES;
- N := 0;
- if StrContainsChars(NS, CharIsWildcard, False) then
- N := N or 1;
- if StrContainsChars(ES, CharIsWildcard, False) then
- N := N or 2;
- FWildChars[I] := N;
- end;
- finally
- List.Free;
- end;
- end;
- function TJclFileMaskComparator.GetCount: Integer;
- begin
- Result := Length(FWildChars);
- end;
- function TJclFileMaskComparator.GetExts(Index: Integer): string;
- begin
- Result := FExts[Index];
- end;
- function TJclFileMaskComparator.GetMasks(Index: Integer): string;
- begin
- Result := FNames[Index] + '.' + FExts[Index];
- end;
- function TJclFileMaskComparator.GetNames(Index: Integer): string;
- begin
- Result := FNames[Index];
- end;
- procedure TJclFileMaskComparator.SetFileMask(const Value: string);
- begin
- FFileMask := Value;
- CreateMultiMasks;
- end;
- procedure TJclFileMaskComparator.SetSeparator(const Value: Char);
- begin
- if FSeparator <> Value then
- begin
- FSeparator := Value;
- CreateMultiMasks;
- end;
- end;
- function AdvBuildFileList(const Path: string; const Attr: Integer; const Files: TStrings;
- const AttributeMatch: TJclAttributeMatch; const Options: TFileListOptions;
- const SubfoldersMask: string; const FileMatchFunc: TFileMatchFunc): Boolean;
- var
- FileMask: string;
- RootDir: string;
- Folders: TStringList;
- CurrentItem: Integer;
- Counter: Integer;
- FindAttr: Integer;
- procedure BuildFolderList;
- var
- FindInfo: TSearchRec;
- Rslt: Integer;
- begin
- Counter := Folders.Count - 1;
- CurrentItem := 0;
- while CurrentItem <= Counter do
- begin
- // searching for subfolders (including hidden ones)
- Rslt := FindFirst(Folders[CurrentItem] + '*.*', faAnyFile, FindInfo);
- try
- while Rslt = 0 do
- begin
- if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') and
- (FindInfo.Attr and faDirectory = faDirectory) then
- Folders.Add(Folders[CurrentItem] + FindInfo.Name + DirDelimiter);
- Rslt := FindNext(FindInfo);
- end;
- finally
- FindClose(FindInfo);
- end;
- Counter := Folders.Count - 1;
- Inc(CurrentItem);
- end;
- end;
- procedure FillFileList(CurrentCounter: Integer);
- var
- FindInfo: TSearchRec;
- Rslt: Integer;
- CurrentFolder: string;
- Matches: Boolean;
- begin
- CurrentFolder := Folders[CurrentCounter];
- Rslt := FindFirst(CurrentFolder + FileMask, FindAttr, FindInfo);
- try
- while Rslt = 0 do
- begin
- Matches := False;
- case AttributeMatch of
- amAny:
- Matches := True;
- amExact:
- Matches := Attr = FindInfo.Attr;
- amSubSetOf:
- Matches := (Attr and FindInfo.Attr) = Attr;
- amSuperSetOf:
- Matches := (Attr and FindInfo.Attr) = FindInfo.Attr;
- amCustom:
- if Assigned(FileMatchFunc) then
- Matches := FileMatchFunc(Attr, FindInfo);
- end;
- if Matches then
- if flFullNames in Options then
- Files.Add(CurrentFolder + FindInfo.Name)
- else
- Files.Add(FindInfo.Name);
- Rslt := FindNext(FindInfo);
- end;
- finally
- FindClose(FindInfo);
- end;
- end;
- begin
- Assert(Assigned(Files));
- FileMask := ExtractFileName(Path);
- RootDir := ExtractFilePath(Path);
- Folders := TStringList.Create;
- Files.BeginUpdate;
- try
- Folders.Add(RootDir);
- case AttributeMatch of
- amExact, amSuperSetOf:
- FindAttr := Attr;
- else
- FindAttr := faAnyFile;
- end;
- // here's the recursive search for nested folders
- if flRecursive in Options then
- BuildFolderList;
- for Counter := 0 to Folders.Count - 1 do
- begin
- if (((flMaskedSubfolders in Options) and (StrMatches(SubfoldersMask,
- Folders[Counter], 1))) or (not (flMaskedSubfolders in Options))) then
- FillFileList(Counter);
- end;
- finally
- Folders.Free;
- Files.EndUpdate;
- end;
- Result := True;
- end;
- function VerifyFileAttributeMask(var RejectedAttributes, RequiredAttributes: Integer): Boolean;
- begin
- if RequiredAttributes and faNormalFile <> 0 then
- RejectedAttributes := not faNormalFile or RejectedAttributes;
- Result := RequiredAttributes and RejectedAttributes = 0;
- end;
- function AttributeMatch(FileAttributes, RejectedAttr, RequiredAttr: Integer): Boolean;
- begin
- if FileAttributes = 0 then
- FileAttributes := faNormalFile;
- {$IFDEF MSWINDOWS}
- RequiredAttr := RequiredAttr and not faUnixSpecific;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- RequiredAttr := RequiredAttr and not faWindowsSpecific;
- {$ENDIF UNIX}
- Result := (FileAttributes and RejectedAttr = 0)
- and (FileAttributes and RequiredAttr = RequiredAttr);
- end;
- function IsFileAttributeMatch(FileAttributes, RejectedAttributes,
- RequiredAttributes: Integer): Boolean;
- begin
- VerifyFileAttributeMask(RejectedAttributes, RequiredAttributes);
- Result := AttributeMatch(FileAttributes, RejectedAttributes, RequiredAttributes);
- end;
- function FileAttributesStr(const FileInfo: TSearchRec): string;
- {$IFDEF MSWINDOWS}
- const
- SAllAttrSet = 'rahs'; // readonly, archive, hidden, system
- Attributes: array [1..4] of Integer =
- (faReadOnly, faArchive, faHidden, faSysFile);
- var
- I: Integer;
- begin
- Result := SAllAttrSet;
- for I := Low(Attributes) to High(Attributes) do
- if (FileInfo.Attr and Attributes[I]) = 0 then
- Result[I] := '-';
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- const
- SAllAttrSet = 'drwxrwxrwx';
- var
- I: Integer;
- Flag: Cardinal;
- begin
- Result := SAllAttrSet;
- if FileInfo.Attr and faDirectory = 0 then
- Result[1] := '-'; // no directory
- Flag := 1 shl 8;
- for I := 2 to 10 do
- begin
- if FileInfo.Mode and Flag = 0 then
- Result[I] := '-';
- Flag := Flag shr 1;
- end;
- end;
- {$ENDIF UNIX}
- function IsFileNameMatch(FileName: string; const Mask: string;
- const CaseSensitive: Boolean): Boolean;
- begin
- Result := True;
- {$IFDEF MSWINDOWS}
- if (Mask = '') or (Mask = '*') or (Mask = '*.*') then
- Exit;
- if Pos('.', FileName) = 0 then
- FileName := FileName + '.'; // file names w/o extension match '*.'
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- if (Mask = '') or (Mask = '*') then
- Exit;
- {$ENDIF UNIX}
- if CaseSensitive then
- Result := StrMatches(Mask, FileName)
- else
- Result := StrMatches(AnsiUpperCase(Mask), AnsiUpperCase(FileName));
- end;
- // author: Robert Rossmair
- function CanonicalizedSearchPath(const Directory: string): string;
- begin
- Result := PathCanonicalize(Directory);
- {$IFDEF MSWINDOWS}
- // avoid changing "X:" (current directory on drive X:) into "X:\" (root dir.)
- if Result[Length(Result)] <> ':' then
- {$ENDIF MSWINDOWS}
- Result := PathAddSeparator(Result);
- // strip leading "./" resp. ".\"
- if Pos('.' + DirDelimiter, Result) = 1 then
- Result := Copy(Result, 3, Length(Result) - 2);
- end;
- procedure EnumFiles(const Path: string; HandleFile: TFileHandlerEx;
- RejectedAttributes: Integer; RequiredAttributes: Integer; Abort: PBoolean);
- var
- Directory: string;
- FileInfo: TSearchRec;
- Attr: Integer;
- Found: Boolean;
- begin
- Assert(Assigned(HandleFile));
- Assert(VerifyFileAttributeMask(RejectedAttributes, RequiredAttributes),
- LoadResString(@RsFileSearchAttrInconsistency));
- Directory := ExtractFilePath(Path);
- Attr := faAnyFile and not RejectedAttributes;
- Found := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Path, Attr, FileInfo) = 0;
- try
- while Found do
- begin
- if (Abort <> nil) and LongBool(Abort^) then
- Exit;
- if AttributeMatch(FileInfo.Attr, RejectedAttributes, RequiredAttributes) then
- if ((FileInfo.Attr and faDirectory = 0)
- or ((FileInfo.Name <> '.') and (FileInfo.Name <> '..'))) then
- HandleFile(Directory, FileInfo);
- Found := FindNext(FileInfo) = 0;
- end;
- finally
- FindClose(FileInfo);
- end;
- end;
- procedure EnumFiles(const Path: string; HandleFile: TFileInfoHandlerEx;
- RejectedAttributes: Integer; RequiredAttributes: Integer; Abort: PBoolean);
- var
- FileInfo: TSearchRec;
- Attr: Integer;
- Found: Boolean;
- begin
- Assert(Assigned(HandleFile));
- Assert(VerifyFileAttributeMask(RejectedAttributes, RequiredAttributes),
- LoadResString(@RsFileSearchAttrInconsistency));
- Attr := faAnyFile and not RejectedAttributes;
- Found := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Path, Attr, FileInfo) = 0;
- try
- while Found do
- begin
- if (Abort <> nil) and LongBool(Abort^) then
- Exit;
- if AttributeMatch(FileInfo.Attr, RejectedAttributes, RequiredAttributes) then
- if ((FileInfo.Attr and faDirectory = 0)
- or ((FileInfo.Name <> '.') and (FileInfo.Name <> '..'))) then
- HandleFile(FileInfo);
- Found := FindNext(FileInfo) = 0;
- end;
- finally
- FindClose(FileInfo);
- end;
- end;
- procedure EnumDirectories(const Root: string; const HandleDirectory: TFileHandler;
- const IncludeHiddenDirectories: Boolean; const SubDirectoriesMask: string;
- Abort: PBoolean {$IFDEF UNIX}; ResolveSymLinks: Boolean {$ENDIF});
- var
- RootDir: string;
- Attr: Integer;
- procedure Process(const Directory: string);
- var
- DirInfo: TSearchRec;
- SubDir: string;
- Found: Boolean;
- begin
- HandleDirectory(Directory);
- Found := {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}SysUtils.FindFirst(Directory + '*', Attr, DirInfo) = 0;
- try
- while Found do
- begin
- if (Abort <> nil) and LongBool(Abort^) then
- Exit;
- if (DirInfo.Name <> '.') and (DirInfo.Name <> '..') and
- {$IFDEF UNIX}
- (IncludeHiddenDirectories or (Pos('.', DirInfo.Name) <> 1)) and
- ((DirInfo.Attr and faSymLink = 0) or ResolveSymLinks) and
- {$ENDIF UNIX}
- (DirInfo.Attr and faDirectory <> 0) then
- begin
- SubDir := Directory + DirInfo.Name + DirDelimiter;
- if (SubDirectoriesMask = '') or StrMatches(SubDirectoriesMask, SubDir, Length(RootDir)) then
- Process(SubDir);
- end;
- Found := FindNext(DirInfo) = 0;
- end;
- finally
- FindClose(DirInfo);
- end;
- end;
- begin
- Assert(Assigned(HandleDirectory));
- RootDir := CanonicalizedSearchPath(Root);
- if IncludeHiddenDirectories then
- Attr := faDirectory + faHidden // no effect on Linux
- else
- Attr := faDirectory;
- Process(RootDir);
- end;
- //=== { TJclCustomFileAttributeMask } ==============================================
- constructor TJclCustomFileAttrMask.Create;
- begin
- inherited Create;
- FRejectedAttr := faRejectedByDefault;
- end;
- procedure TJclCustomFileAttrMask.Assign(Source: TPersistent);
- begin
- if Source is TJclCustomFileAttrMask then
- begin
- Required := TJclCustomFileAttrMask(Source).Required;
- Rejected := TJclCustomFileAttrMask(Source).Rejected;
- end
- else
- inherited Assign(Source);
- end;
- procedure TJclCustomFileAttrMask.Clear;
- begin
- Rejected := 0;
- Required := 0;
- end;
- procedure TJclCustomFileAttrMask.DefineProperties(Filer: TFiler);
- var
- Ancestor: TJclCustomFileAttrMask;
- Attr: Integer;
- begin
- Attr := 0;
- Ancestor := TJclCustomFileAttrMask(Filer.Ancestor);
- if Assigned(Ancestor) then
- Attr := Ancestor.FRequiredAttr;
- Filer.DefineProperty('Required', ReadRequiredAttributes, WriteRequiredAttributes,
- Attr <> FRequiredAttr);
- if Assigned(Ancestor) then
- Attr := Ancestor.FRejectedAttr;
- Filer.DefineProperty('Rejected', ReadRejectedAttributes, WriteRejectedAttributes,
- Attr <> FRejectedAttr);
- end;
- function TJclCustomFileAttrMask.Match(FileAttributes: Integer): Boolean;
- begin
- Result := AttributeMatch(FileAttributes, Rejected, Required);
- end;
- function TJclCustomFileAttrMask.Match(const FileInfo: TSearchRec): Boolean;
- begin
- Result := Match(FileInfo.Attr);
- end;
- function TJclCustomFileAttrMask.GetAttr(Index: Integer): TAttributeInterest;
- begin
- if ((FRequiredAttr and Index) <> 0) or (Index = faNormalFile) and
- (FRejectedAttr = not faNormalFile) then
- Result := aiRequired
- else
- if (FRejectedAttr and Index) <> 0 then
- Result := aiRejected
- else
- Result := aiIgnored;
- end;
- procedure TJclCustomFileAttrMask.ReadRejectedAttributes(Reader: TReader);
- begin
- FRejectedAttr := Reader.ReadInteger;
- end;
- procedure TJclCustomFileAttrMask.ReadRequiredAttributes(Reader: TReader);
- begin
- FRequiredAttr := Reader.ReadInteger;
- end;
- procedure TJclCustomFileAttrMask.SetAttr(Index: Integer; const Value: TAttributeInterest);
- begin
- case Value of
- aiIgnored:
- begin
- FRequiredAttr := FRequiredAttr and not Index;
- FRejectedAttr := FRejectedAttr and not Index;
- end;
- aiRejected:
- begin
- FRequiredAttr := FRequiredAttr and not Index;
- FRejectedAttr := FRejectedAttr or Index;
- end;
- aiRequired:
- begin
- if Index = faNormalFile then
- begin
- FRequiredAttr := faNormalFile;
- FRejectedAttr := not faNormalFile;
- end
- else
- begin
- FRequiredAttr := FRequiredAttr or Index;
- FRejectedAttr := FRejectedAttr and not Index;
- end;
- end;
- end;
- end;
- procedure TJclCustomFileAttrMask.WriteRejectedAttributes(Writer: TWriter);
- begin
- Writer.WriteInteger(FRejectedAttr);
- end;
- procedure TJclCustomFileAttrMask.WriteRequiredAttributes(Writer: TWriter);
- begin
- Writer.WriteInteger(FRequiredAttr);
- end;
- //=== { TJclFileAttributeMask } ==============================================
- procedure TJclFileAttributeMask.ReadVolumeID(Reader: TReader);
- begin
- // Nothing, we are not interested in the value of the VolumeID property,
- // this procedure and the associated DefineProperty call are here only
- // to allow reading legacy DFMs that have this property defined.
- end;
- procedure TJclFileAttributeMask.DefineProperties(Filer: TFiler);
- begin
- inherited DefineProperties(Filer);
- Filer.DefineProperty('VolumeID', ReadVolumeID, nil, False);
- end;
- //=== { TJclFileSearchOptions } ==============================================
- constructor TJclFileSearchOptions.Create;
- begin
- inherited Create;
- FAttributeMask := TJclFileAttributeMask.Create;
- FRootDirectories := TStringList.Create;
- FRootDirectories.Add('.');
- FFileMasks := TStringList.Create;
- FFileMasks.Add('*');
- FSubDirectoryMask := '*';
- FOptions := [fsIncludeSubDirectories];
- FLastChangeAfter := MinDateTime;
- FLastChangeBefore := MaxDateTime;
- {$IFDEF UNIX}
- FCaseSensitiveSearch := True;
- {$ENDIF UNIX}
- end;
- destructor TJclFileSearchOptions.Destroy;
- begin
- FAttributeMask.Free;
- FFileMasks.Free;
- FRootDirectories.Free;
- inherited Destroy;
- end;
- procedure TJclFileSearchOptions.Assign(Source: TPersistent);
- var
- Src: TJclFileSearchOptions;
- begin
- if Source is TJclFileSearchOptions then
- begin
- Src := TJclFileSearchOptions(Source);
- FCaseSensitiveSearch := Src.FCaseSensitiveSearch;
- FileMasks.Assign(Src.FileMasks);
- RootDirectory := Src.RootDirectory;
- SubDirectoryMask := Src.SubDirectoryMask;
- AttributeMask := Src.AttributeMask;
- Options := Src.Options;
- FileSizeMin := Src.FileSizeMin;
- FileSizeMax := Src.FileSizeMax;
- LastChangeAfter := Src.LastChangeAfter;
- LastChangeBefore := Src.LastChangeBefore;
- end
- else
- inherited Assign(Source);
- end;
- function TJclFileSearchOptions.GetAttributeMask: TJclFileAttributeMask;
- begin
- Result := FAttributeMask;
- end;
- function TJclFileSearchOptions.GetCaseSensitiveSearch: Boolean;
- begin
- Result := FCaseSensitiveSearch;
- end;
- function TJclFileSearchOptions.GetFileMask: string;
- begin
- Result := StringsToStr(FileMasks, DirSeparator, False);
- end;
- function TJclFileSearchOptions.GetFileMasks: TStrings;
- begin
- Result := FFileMasks;
- end;
- function TJclFileSearchOptions.GetFileSizeMax: Int64;
- begin
- Result := FFileSizeMax;
- end;
- function TJclFileSearchOptions.GetFileSizeMin: Int64;
- begin
- Result := FFileSizeMin;
- end;
- function TJclFileSearchOptions.GetIncludeHiddenSubDirectories: Boolean;
- begin
- Result := fsIncludeHiddenSubDirectories in Options;
- end;
- function TJclFileSearchOptions.GetIncludeSubDirectories: Boolean;
- begin
- Result := fsIncludeSubDirectories in Options;
- end;
- function TJclFileSearchOptions.GetLastChangeAfter: TDateTime;
- begin
- Result := FLastChangeAfter;
- end;
- function TJclFileSearchOptions.GetLastChangeAfterStr: string;
- begin
- Result := DateTimeToStr(LastChangeAfter);
- end;
- function TJclFileSearchOptions.GetLastChangeBefore: TDateTime;
- begin
- Result := FLastChangeBefore;
- end;
- function TJclFileSearchOptions.GetLastChangeBeforeStr: string;
- begin
- Result := DateTimeToStr(LastChangeBefore);
- end;
- function TJclFileSearchOptions.GetOption(
- const Option: TFileSearchOption): Boolean;
- begin
- Result := Option in FOptions;
- end;
- function TJclFileSearchOptions.GetOptions: TFileSearchoptions;
- begin
- Result := FOptions;
- end;
- function TJclFileSearchOptions.GetRootDirectories: TStrings;
- begin
- Result := FRootDirectories;
- end;
- function TJclFileSearchOptions.GetRootDirectory: string;
- begin
- if FRootDirectories.Count = 1 then
- Result := FRootDirectories.Strings[0]
- else
- Result := '';
- end;
- function TJclFileSearchOptions.GetSubDirectoryMask: string;
- begin
- Result := FSubDirectoryMask;
- end;
- function TJclFileSearchOptions.IsLastChangeAfterStored: Boolean;
- begin
- Result := FLastChangeAfter <> MinDateTime;
- end;
- function TJclFileSearchOptions.IsLastChangeBeforeStored: Boolean;
- begin
- Result := FLastChangeBefore <> MaxDateTime;
- end;
- procedure TJclFileSearchOptions.SetAttributeMask(
- const Value: TJclFileAttributeMask);
- begin
- FAttributeMask.Assign(Value);
- end;
- procedure TJclFileSearchOptions.SetCaseSensitiveSearch(const Value: Boolean);
- begin
- FCaseSensitiveSearch := Value;
- end;
- procedure TJclFileSearchOptions.SetFileMask(const Value: string);
- begin
- { TODO : UNIX : ? }
- StrToStrings(Value, DirSeparator, FFileMasks, False);
- end;
- procedure TJclFileSearchOptions.SetFileMasks(const Value: TStrings);
- begin
- FileMasks.Assign(Value);
- end;
- procedure TJclFileSearchOptions.SetFileSizeMax(const Value: Int64);
- begin
- FFileSizeMax := Value;
- end;
- procedure TJclFileSearchOptions.SetFileSizeMin(const Value: Int64);
- begin
- FFileSizeMin := Value;
- end;
- procedure TJclFileSearchOptions.SetIncludeHiddenSubDirectories(
- const Value: Boolean);
- begin
- SetOption(fsIncludeHiddenSubDirectories, Value);
- end;
- procedure TJclFileSearchOptions.SetIncludeSubDirectories(const Value: Boolean);
- begin
- SetOption(fsIncludeSubDirectories, Value);
- end;
- procedure TJclFileSearchOptions.SetLastChangeAfter(const Value: TDateTime);
- begin
- FLastChangeAfter := Value;
- end;
- procedure TJclFileSearchOptions.SetLastChangeAfterStr(const Value: string);
- begin
- if Value = '' then
- LastChangeAfter := MinDateTime
- else
- LastChangeAfter := StrToDateTime(Value);
- end;
- procedure TJclFileSearchOptions.SetLastChangeBefore(const Value: TDateTime);
- begin
- FLastChangeBefore := Value;
- end;
- procedure TJclFileSearchOptions.SetLastChangeBeforeStr(const Value: string);
- begin
- if Value = '' then
- LastChangeBefore := MaxDateTime
- else
- LastChangeBefore := StrToDateTime(Value);
- end;
- procedure TJclFileSearchOptions.SetOption(const Option: TFileSearchOption;
- const Value: Boolean);
- begin
- if Value then
- Include(FOptions, Option)
- else
- Exclude(FOptions, Option);
- end;
- procedure TJclFileSearchOptions.SetOptions(const Value: TFileSearchOptions);
- begin
- FOptions := Value;
- end;
- procedure TJclFileSearchOptions.SetRootDirectories(const Value: TStrings);
- begin
- FRootDirectories.Assign(Value);
- end;
- procedure TJclFileSearchOptions.SetRootDirectory(const Value: string);
- begin
- FRootDirectories.Clear;
- FRootDirectories.Add(Value);
- end;
- procedure TJclFileSearchOptions.SetSubDirectoryMask(const Value: string);
- begin
- FSubDirectoryMask := Value;
- end;
- //=== { TEnumFileThread } ====================================================
- type
- TEnumFileThread = class(TThread)
- private
- FID: TFileSearchTaskID;
- FFileMasks: TStringList;
- FDirectories: TStrings;
- FCurrentDirectory: string;
- FSubDirectoryMask: string;
- FOnEnterDirectory: TFileHandler;
- FFileHandlerEx: TFileHandlerEx;
- FFileHandler: TFileHandler;
- FInternalDirHandler: TFileHandler;
- FInternalFileInfoHandler: TFileInfoHandlerEx;
- FFileInfo: TSearchRec;
- FRejectedAttr: Integer;
- FRequiredAttr: Integer;
- FFileSizeMin: Int64;
- FFileSizeMax: Int64;
- {$IFDEF RTL220_UP}
- FFileTimeMin: TDateTime;
- FFileTimeMax: TDateTime;
- {$ELSE ~RTL220_UP}
- FFileTimeMin: Integer;
- FFileTimeMax: Integer;
- {$ENDIF ~RTL220_UP}
- FSynchronizationMode: TFileEnumeratorSyncMode;
- FIncludeSubDirectories: Boolean;
- FIncludeHiddenSubDirectories: Boolean;
- FNotifyOnTermination: Boolean;
- FCaseSensitiveSearch: Boolean;
- FAllNamesMatch: Boolean;
- procedure EnterDirectory;
- procedure AsyncProcessDirectory(const Directory: string);
- procedure SyncProcessDirectory(const Directory: string);
- procedure AsyncProcessFile(const FileInfo: TSearchRec);
- procedure SyncProcessFile(const FileInfo: TSearchRec);
- function GetDirectories: TStrings;
- function GetFileMasks: TStrings;
- procedure SetDirectories(const Value: TStrings);
- procedure SetFileMasks(const Value: TStrings);
- protected
- procedure DoTerminate; override;
- procedure Execute; override;
- function FileMatch: Boolean;
- function FileNameMatchesMask: Boolean;
- procedure ProcessDirectory;
- procedure ProcessDirFiles;
- procedure ProcessFile;
- property AllNamesMatch: Boolean read FAllNamesMatch;
- property CaseSensitiveSearch: Boolean read FCaseSensitiveSearch write FCaseSensitiveSearch;
- property FileMasks: TStrings read GetFileMasks write SetFileMasks;
- property FileSizeMin: Int64 read FFileSizeMin write FFileSizeMin;
- property FileSizeMax: Int64 read FFileSizeMax write FFileSizeMax;
- {$IFDEF RTL220_UP}
- property FileTimeMin: TDateTime read FFileTimeMin write FFileTimeMin;
- property FileTimeMax: TDateTime read FFileTimeMax write FFileTimeMax;
- {$ELSE ~RTL220_UP}
- property FileTimeMin: Integer read FFileTimeMin write FFileTimeMin;
- property FileTimeMax: Integer read FFileTimeMax write FFileTimeMax;
- {$ENDIF ~RTL220_UP}
- property Directories: TStrings read GetDirectories write SetDirectories;
- property IncludeSubDirectories: Boolean
- read FIncludeSubDirectories write FIncludeSubDirectories;
- property IncludeHiddenSubDirectories: Boolean
- read FIncludeHiddenSubDirectories write FIncludeHiddenSubDirectories;
- property RejectedAttr: Integer read FRejectedAttr write FRejectedAttr;
- property RequiredAttr: Integer read FRequiredAttr write FRequiredAttr;
- property SynchronizationMode: TFileEnumeratorSyncMode
- read FSynchronizationMode write FSynchronizationMode;
- public
- constructor Create;
- destructor Destroy; override;
- property ID: TFileSearchTaskID read FID;
- {$IFDEF FPC} // protected property
- property Terminated;
- {$ENDIF FPC}
- end;
- constructor TEnumFileThread.Create;
- begin
- inherited Create(True);
- FDirectories := TStringList.Create;
- FFileMasks := TStringList.Create;
- {$IFDEF RTL220_UP}
- FFileTimeMin := -MaxDouble;
- FFileTimeMax := MaxDouble;
- {$ELSE ~RTL220_UP}
- FFileTimeMin := Low(FFileInfo.Time);
- FFileTimeMax := High(FFileInfo.Time);
- {$ENDIF ~RTL220_UP}
- FFileSizeMax := High(FFileSizeMax);
- {$IFDEF MSWINDOWS}
- Priority := tpIdle;
- {$ENDIF MSWINDOWS}
- {$IFDEF UNIX}
- {$IFDEF FPC}
- Priority := tpIdle;
- {$ELSE ~FPC}
- Priority := 0;
- {$ENDIF ~FPC}
- {$ENDIF UNIX}
- FreeOnTerminate := True;
- FNotifyOnTermination := True;
- end;
- destructor TEnumFileThread.Destroy;
- begin
- FFileMasks.Free;
- FDirectories.Free;
- inherited Destroy;
- end;
- procedure TEnumFileThread.Execute;
- var
- Index: Integer;
- begin
- if SynchronizationMode = smPerDirectory then
- begin
- FInternalDirHandler := SyncProcessDirectory;
- FInternalFileInfoHandler := AsyncProcessFile;
- end
- else // SynchronizationMode = smPerFile
- begin
- FInternalDirHandler := AsyncProcessDirectory;
- FInternalFileInfoHandler := SyncProcessFile;
- end;
- if FIncludeSubDirectories then
- begin
- for Index := 0 to FDirectories.Count - 1 do
- EnumDirectories(FDirectories.Strings[Index], FInternalDirHandler, FIncludeHiddenSubDirectories,
- FSubDirectoryMask, @Terminated)
- end
- else
- begin
- for Index := 0 to FDirectories.Count - 1 do
- FInternalDirHandler(CanonicalizedSearchPath(FDirectories.Strings[Index]));
- end;
- end;
- procedure TEnumFileThread.DoTerminate;
- begin
- if FNotifyOnTermination then
- inherited DoTerminate;
- end;
- procedure TEnumFileThread.EnterDirectory;
- begin
- FOnEnterDirectory(FCurrentDirectory);
- end;
- procedure TEnumFileThread.ProcessDirectory;
- begin
- if Assigned(FOnEnterDirectory) then
- EnterDirectory;
- ProcessDirFiles;
- end;
- procedure TEnumFileThread.AsyncProcessDirectory(const Directory: string);
- begin
- FCurrentDirectory := Directory;
- if Assigned(FOnEnterDirectory) then
- Synchronize(EnterDirectory);
- ProcessDirFiles;
- end;
- procedure TEnumFileThread.SyncProcessDirectory(const Directory: string);
- begin
- FCurrentDirectory := Directory;
- Synchronize(ProcessDirectory);
- end;
- procedure TEnumFileThread.ProcessDirFiles;
- begin
- EnumFiles(FCurrentDirectory + '*', FInternalFileInfoHandler, FRejectedAttr, FRequiredAttr, @Terminated);
- end;
- function TEnumFileThread.FileMatch: Boolean;
- var
- FileSize: Int64;
- begin
- {$IFDEF RTL220_UP}
- Result := FileNameMatchesMask and (FFileInfo.TimeStamp >= FFileTimeMin) and (FFileInfo.TimeStamp <= FFileTimeMax);
- {$ELSE ~RTL220_UP}
- Result := FileNameMatchesMask and (FFileInfo.Time >= FFileTimeMin) and (FFileInfo.Time <= FFileTimeMax);
- {$ENDIF ~RTL220_UP}
- if Result then
- begin
- FileSize := GetSizeOfFile(FFileInfo);
- Result := (FileSize >= FFileSizeMin) and (FileSize <= FFileSizeMax);
- end;
- end;
- function TEnumFileThread.FileNameMatchesMask: Boolean;
- var
- I: Integer;
- begin
- Result := AllNamesMatch;
- if not Result then
- for I := 0 to FileMasks.Count - 1 do
- if IsFileNameMatch(FFileInfo.Name, FileMasks[I], CaseSensitiveSearch) then
- begin
- Result := True;
- Break;
- end;
- end;
- procedure TEnumFileThread.ProcessFile;
- begin
- if Assigned(FFileHandlerEx) then
- FFileHandlerEx(FCurrentDirectory, FFileInfo)
- else
- FFileHandler(FCurrentDirectory + FFileInfo.Name);
- end;
- procedure TEnumFileThread.AsyncProcessFile(const FileInfo: TSearchRec);
- begin
- FFileInfo := FileInfo;
- if FileMatch then
- ProcessFile;
- end;
- procedure TEnumFileThread.SyncProcessFile(const FileInfo: TSearchRec);
- begin
- FFileInfo := FileInfo;
- if FileMatch then
- Synchronize(ProcessFile);
- end;
- function TEnumFileThread.GetDirectories: TStrings;
- begin
- Result := FDirectories;
- end;
- function TEnumFileThread.GetFileMasks: TStrings;
- begin
- Result := FFileMasks;
- end;
- procedure TEnumFileThread.SetDirectories(const Value: TStrings);
- begin
- FDirectories.Assign(Value);
- end;
- procedure TEnumFileThread.SetFileMasks(const Value: TStrings);
- var
- I: Integer;
- begin
- FAllNamesMatch := Value.Count = 0;
- for I := 0 to Value.Count - 1 do
- if (Value[I] = '*') {$IFDEF MSWINDOWS} or (Value[I] = '*.*') {$ENDIF} then
- begin
- FAllNamesMatch := True;
- Break;
- end;
- if FAllNamesMatch then
- FileMasks.Clear
- else
- FileMasks.Assign(Value);
- end;
- //=== { TJclFileEnumerator } =================================================
- constructor TJclFileEnumerator.Create;
- begin
- inherited Create;
- FTasks := TList.Create;
- end;
- destructor TJclFileEnumerator.Destroy;
- begin
- StopAllTasks(True);
- FTasks.Free;
- inherited Destroy;
- end;
- procedure TJclFileEnumerator.Assign(Source: TPersistent);
- var
- Src: TJclFileEnumerator;
- begin
- if Source is TJclFileEnumerator then
- begin
- Src := TJclFileEnumerator(Source);
- SynchronizationMode := Src.SynchronizationMode;
- OnEnterDirectory := Src.OnEnterDirectory;
- OnTerminateTask := Src.OnTerminateTask;
- end;
- inherited Assign(Source);
- end;
- function TJclFileEnumerator.CreateTask: TThread;
- var
- Task: TEnumFileThread;
- begin
- Task := TEnumFileThread.Create;
- Task.FID := NextTaskID;
- Task.CaseSensitiveSearch := FCaseSensitiveSearch;
- Task.FileMasks := FileMasks;
- Task.Directories := RootDirectories;
- Task.RejectedAttr := AttributeMask.Rejected;
- Task.RequiredAttr := AttributeMask.Required;
- Task.IncludeSubDirectories := IncludeSubDirectories;
- Task.IncludeHiddenSubDirectories := IncludeHiddenSubDirectories;
- if fsMinSize in Options then
- Task.FileSizeMin := FileSizeMin;
- if fsMaxSize in Options then
- Task.FileSizeMax := FileSizeMax;
- if fsLastChangeAfter in Options then
- Task.FFileTimeMin := {$IFDEF RTL220_UP}LastChangeAfter{$ELSE}DateTimeToFileDate(LastChangeAfter){$ENDIF};
- if fsLastChangeBefore in Options then
- Task.FFileTimeMax := {$IFDEF RTL220_UP}LastChangeBefore{$ELSE}DateTimeToFileDate(LastChangeBefore){$ENDIF};
- Task.SynchronizationMode := SynchronizationMode;
- Task.FOnEnterDirectory := OnEnterDirectory;
- Task.OnTerminate := TaskTerminated;
- FTasks.Add(Task);
- if FRefCount > 0 then
- _AddRef;
- Result := Task;
- end;
- function TJclFileEnumerator.FillList(List: TStrings): TFileSearchTaskID;
- begin
- List.BeginUpdate;
- try
- Result := ForEach(List.Append);
- finally
- List.EndUpdate;
- end;
- end;
- function TJclFileEnumerator.ForEach(Handler: TFileHandlerEx): TFileSearchTaskID;
- var
- Task: TEnumFileThread;
- begin
- Task := TEnumFileThread(CreateTask);
- Task.FFileHandlerEx := Handler;
- Result := Task.ID;
- {$IFDEF RTL210_UP}
- Task.Suspended := False;
- {$ELSE ~RTL210_UP}
- Task.Resume;
- {$ENDIF ~RTL210_UP}
- end;
- function TJclFileEnumerator.ForEach(Handler: TFileHandler): TFileSearchTaskID;
- var
- Task: TEnumFileThread;
- begin
- Task := TEnumFileThread(CreateTask);
- Task.FFileHandler := Handler;
- Result := Task.ID;
- {$IFDEF RTL210_UP}
- Task.Suspended := False;
- {$ELSE ~RTL210_UP}
- Task.Resume;
- {$ENDIF ~RTL210_UP}
- end;
- function TJclFileEnumerator.GetRunningTasks: Integer;
- begin
- Result := FTasks.Count;
- end;
- procedure TJclFileEnumerator.StopTask(ID: TFileSearchTaskID);
- var
- Task: TEnumFileThread;
- I: Integer;
- begin
- for I := 0 to FTasks.Count - 1 do
- begin
- Task := TEnumFileThread(FTasks[I]);
- if Task.ID = ID then
- begin
- Task.Terminate;
- Break;
- end;
- end;
- end;
- procedure TJclFileEnumerator.StopAllTasks(Silently: Boolean = False);
- var
- I: Integer;
- begin
- for I := 0 to FTasks.Count - 1 do
- begin
- TEnumFileThread(FTasks[I]).FNotifyOnTermination := not Silently;
- TEnumFileThread(FTasks[I]).Terminate;
- end;
- end;
- procedure TJclFileEnumerator.TaskTerminated(Sender: TObject);
- begin
- FTasks.Remove(Sender);
- try
- if Assigned(FOnTerminateTask) then
- FOnTerminateTask(TEnumFileThread(Sender).ID, TEnumFileThread(Sender).Terminated);
- finally
- if FRefCount > 0 then
- _Release;
- end;
- end;
- function TJclFileEnumerator.GetNextTaskID: TFileSearchTaskID;
- begin
- Result := FNextTaskID;
- Inc(FNextTaskID);
- end;
- function TJclFileEnumerator.GetOnEnterDirectory: TFileHandler;
- begin
- Result := FOnEnterDirectory;
- end;
- function TJclFileEnumerator.GetOnTerminateTask: TFileSearchTerminationEvent;
- begin
- Result := FOnTerminateTask;
- end;
- function TJclFileEnumerator.GetSynchronizationMode: TFileEnumeratorSyncMode;
- begin
- Result := FSynchronizationMode;
- end;
- procedure TJclFileEnumerator.SetOnEnterDirectory(
- const Value: TFileHandler);
- begin
- FOnEnterDirectory := Value;
- end;
- procedure TJclFileEnumerator.SetOnTerminateTask(
- const Value: TFileSearchTerminationEvent);
- begin
- FOnTerminateTask := Value;
- end;
- procedure TJclFileEnumerator.SetSynchronizationMode(
- const Value: TFileEnumeratorSyncMode);
- begin
- FSynchronizationMode := Value;
- end;
- function FileSearch: IJclFileEnumerator;
- begin
- Result := TJclFileEnumerator.Create;
- end;
- function SamePath(const Path1, Path2: string): Boolean;
- begin
- {$IFDEF MSWINDOWS}
- Result := AnsiSameText(PathGetLongName(Path1), PathGetLongName(Path2));
- {$ELSE ~MSWINDOWS}
- Result := Path1 = Path2;
- {$ENDIF ~MSWINDOWS}
- end;
- // add items at the end
- procedure PathListAddItems(var List: string; const Items: string);
- begin
- ListAddItems(List, DirSeparator, Items);
- end;
- // add items at the end if they are not present
- procedure PathListIncludeItems(var List: string; const Items: string);
- var
- StrList, NewItems: TStringList;
- IndexNew, IndexList: Integer;
- Item: string;
- Duplicate: Boolean;
- begin
- StrList := TStringList.Create;
- try
- StrToStrings(List, DirSeparator, StrList);
- NewItems := TStringList.Create;
- try
- StrToStrings(Items, DirSeparator, NewItems);
- for IndexNew := 0 to NewItems.Count - 1 do
- begin
- Item := NewItems.Strings[IndexNew];
- Duplicate := False;
- for IndexList := 0 to StrList.Count - 1 do
- if SamePath(Item, StrList.Strings[IndexList]) then
- begin
- Duplicate := True;
- Break;
- end;
- if not Duplicate then
- StrList.Add(Item);
- end;
- List := StringsToStr(StrList, DirSeparator);
- finally
- NewItems.Free;
- end;
- finally
- StrList.Free;
- end;
- end;
- // delete multiple items
- procedure PathListDelItems(var List: string; const Items: string);
- var
- StrList, RemItems: TStringList;
- IndexRem, IndexList: Integer;
- Item: string;
- begin
- StrList := TStringList.Create;
- try
- StrToStrings(List, DirSeparator, StrList);
- RemItems := TStringList.Create;
- try
- StrToStrings(Items, DirSeparator, RemItems);
- for IndexRem := 0 to RemItems.Count - 1 do
- begin
- Item := RemItems.Strings[IndexRem];
- for IndexList := StrList.Count - 1 downto 0 do
- if SamePath(Item, StrList.Strings[IndexList]) then
- StrList.Delete(IndexList);
- end;
- List := StringsToStr(StrList, DirSeparator);
- finally
- RemItems.Free;
- end;
- finally
- StrList.Free;
- end;
- end;
- // delete one item
- procedure PathListDelItem(var List: string; const Index: Integer);
- begin
- ListDelItem(List, DirSeparator, Index);
- end;
- // return the number of item
- function PathListItemCount(const List: string): Integer;
- begin
- Result := ListItemCount(List, DirSeparator);
- end;
- // return the Nth item
- function PathListGetItem(const List: string; const Index: Integer): string;
- begin
- Result := ListGetItem(List, DirSeparator, Index);
- end;
- // set the Nth item
- procedure PathListSetItem(var List: string; const Index: Integer; const Value: string);
- begin
- ListSetItem(List, DirSeparator, Index, Value);
- end;
- // return the index of an item
- function PathListItemIndex(const List, Item: string): Integer;
- var
- StrList: TStringList;
- IndexList: Integer;
- begin
- StrList := TStringList.Create;
- try
- StrToStrings(List, DirSeparator, StrList);
- Result := -1;
- for IndexList := 0 to StrList.Count - 1 do
- if SamePath(StrList.Strings[IndexList], Item) then
- begin
- Result := IndexList;
- Break;
- end;
- finally
- StrList.Free;
- end;
- end;
- // additional functions to access the commandline parameters of an application
- // returns the name of the command line parameter at position index, which is
- // separated by the given separator, if the first character of the name part
- // is one of the AllowedPrefixCharacters, this character will be deleted.
- function ParamName (Index : Integer; const Separator : string = '=';
- const AllowedPrefixCharacters : string = '-/'; TrimName : Boolean = true) : string;
- var s: string;
- p: Integer;
- begin
- if (index > 0) and (index <= ParamCount) then
- begin
- s := ParamStr(index);
- if Pos(Copy(s, 1, 1), AllowedPrefixCharacters) > 0 then
- s := Copy (s, 2, Length(s)-1);
- p := Pos(Separator, s);
- if p > 0 then
- s := Copy (s, 1, p-1);
- if TrimName then
- s := Trim(s);
- Result := s;
- end
- else
- Result := '';
- end;
- // returns the value of the command line parameter at position index, which is
- // separated by the given separator
- function ParamValue (Index : Integer; const Separator : string = '='; TrimValue : Boolean = true) : string;
- var s: string;
- p: Integer;
- begin
- if (index > 0) and (index <= ParamCount) then
- begin
- s := ParamStr(index);
- p := Pos(Separator, s);
- if p > 0 then
- s := Copy (s, p+1, Length(s)-p);
- if TrimValue then
- s := Trim(s);
- Result := s;
- end
- else
- Result := '';
- end;
- // seaches a command line parameter where the namepart is the searchname
- // and returns the value which is which by the given separator.
- // CaseSensitive defines the search type. if the first character of the name part
- // is one of the AllowedPrefixCharacters, this character will be deleted.
- function ParamValue (const SearchName : string; const Separator : string = '=';
- CaseSensitive : Boolean = False;
- const AllowedPrefixCharacters : string = '-/'; TrimValue : Boolean = true) : string;
- var pName : string;
- i : Integer;
- begin
- Result := '';
- for i := 1 to ParamCount do
- begin
- pName := ParamName(i, Separator, AllowedPrefixCharacters, True);
- if (CaseSensitive and (pName = Trim(SearchName))) or
- (UpperCase(pName) = Trim(UpperCase(SearchName))) then
- begin
- Result := ParamValue (i, Separator, TrimValue);
- exit;
- end;
- end;
- end;
- // seaches a command line parameter where the namepart is the searchname
- // and returns the position index. if no separator is defined, the full paramstr is compared.
- // CaseSensitive defines the search type. if the first character of the name part
- // is one of the AllowedPrefixCharacters, this character will be deleted.
- function ParamPos (const SearchName : string; const Separator : string = '=';
- CaseSensitive : Boolean = False;
- const AllowedPrefixCharacters : string = '-/'): Integer;
- var pName : string;
- i : Integer;
- begin
- Result := -1;
- for i := 1 to ParamCount do
- begin
- pName := ParamName(i, Separator, AllowedPrefixCharacters, True);
- if (CaseSensitive and (pName = SearchName)) or
- (UpperCase(pName) = UpperCase(SearchName)) then
- begin
- Result := i;
- Exit;
- end;
- end;
- end;
- {$IFDEF UNITVERSIONING}
- initialization
- RegisterUnitVersion(HInstance, UnitVersioning);
- finalization
- UnregisterUnitVersion(HInstance);
- {$ENDIF UNITVERSIONING}
- end.
|