1
0

JclFileUtils.pas 217 KB

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