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