JclFileUtils.pas 219 KB

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