JclFileUtils.pas 218 KB

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