dclib-file.c 174 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639
  1. /***************************************************************************
  2. * *
  3. * _____ ____ *
  4. * | __ \ / __ \ _ _ _____ *
  5. * | | \ \ / / \_\ | | | | _ \ *
  6. * | | \ \| | | | | | |_| | *
  7. * | | | || | | | | | ___/ *
  8. * | | / /| | __ | | | | _ \ *
  9. * | |__/ / \ \__/ / | |___| | |_| | *
  10. * |_____/ \____/ |_____|_|_____/ *
  11. * *
  12. * Wiimms source code library *
  13. * *
  14. ***************************************************************************
  15. * *
  16. * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> *
  17. * *
  18. ***************************************************************************
  19. * *
  20. * This library is free software; you can redistribute it and/or modify *
  21. * it under the terms of the GNU General Public License as published by *
  22. * the Free Software Foundation; either version 2 of the License, or *
  23. * (at your option) any later version. *
  24. * *
  25. * This library is distributed in the hope that it will be useful, *
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  28. * GNU General Public License for more details. *
  29. * *
  30. * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
  31. * *
  32. ***************************************************************************/
  33. #define _GNU_SOURCE 1
  34. #include <sys/types.h>
  35. #include <sys/ioctl.h>
  36. #include <sys/time.h>
  37. #include <fcntl.h>
  38. #include <string.h>
  39. #include <time.h>
  40. #include <unistd.h>
  41. #include <termios.h>
  42. #include <utime.h>
  43. #include <errno.h>
  44. #include <dirent.h>
  45. #include <poll.h>
  46. #include <signal.h>
  47. #include <sys/wait.h>
  48. #include <sys/socket.h>
  49. #include <sys/un.h>
  50. #include <sys/resource.h>
  51. #include "dclib-basics.h"
  52. #include "dclib-file.h"
  53. #include "dclib-debug.h"
  54. #include "dclib-network.h"
  55. //
  56. ///////////////////////////////////////////////////////////////////////////////
  57. /////////////// termios support ///////////////
  58. ///////////////////////////////////////////////////////////////////////////////
  59. static struct termios termios_data;
  60. int termios_valid = 0;
  61. void ResetTermios()
  62. {
  63. if ( termios_valid > 0 )
  64. tcsetattr(0,TCSANOW,&termios_data);
  65. }
  66. static bool LoadTermios()
  67. {
  68. if (!termios_valid)
  69. {
  70. termios_valid = -1;
  71. if ( isatty(0) && !tcgetattr(0,&termios_data) )
  72. termios_valid = 1;
  73. }
  74. return termios_valid > 0;
  75. }
  76. bool EnableSingleCharInput()
  77. {
  78. if (!LoadTermios())
  79. return false;
  80. struct termios tios;
  81. memcpy(&tios,&termios_data,sizeof(tios));
  82. tios.c_lflag &= ~(ICANON|ECHO);
  83. tcsetattr(0,TCSANOW,&tios);
  84. return true;
  85. }
  86. //
  87. ///////////////////////////////////////////////////////////////////////////////
  88. /////////////// log helpers ///////////////
  89. ///////////////////////////////////////////////////////////////////////////////
  90. LogFile_t GlobalLogFile = {0};
  91. ///////////////////////////////////////////////////////////////////////////////
  92. FILE * TryOpenFile ( FILE *f, ccp fname, ccp mode )
  93. {
  94. if ( f == TRY_OPEN_FILE )
  95. f = fopen(fname,mode);
  96. else if (f)
  97. {
  98. rewind(f);
  99. fflush(f);
  100. }
  101. return f;
  102. }
  103. ///////////////////////////////////////////////////////////////////////////////
  104. int GetLogTimestamp ( char *buf, uint buf_size, TimestampMode_t ts_mode )
  105. {
  106. DASSERT(buf);
  107. DASSERT(buf_size>2);
  108. uint len;
  109. if ( ts_mode >= TSM_USEC )
  110. {
  111. const u64 usec = GetTimeUSec(false);
  112. const uint sec = usec/1000000 % SEC_PER_DAY;
  113. len = snprintf(buf,buf_size,
  114. "%02u:%02u:%02u.%06llu ",
  115. sec / 3600,
  116. sec / 60 % 60,
  117. sec % 60,
  118. usec % 1000000 );
  119. }
  120. else if ( ts_mode >= TSM_MSEC )
  121. {
  122. const u64 msec = GetTimeMSec(false);
  123. const uint sec = msec/USEC_PER_MSEC % SEC_PER_DAY;
  124. len = snprintf(buf,buf_size,
  125. "%02u:%02u:%02u.%03llu ",
  126. sec / 3600,
  127. sec / 60 % 60,
  128. sec % 60,
  129. msec % 1000 );
  130. }
  131. else if ( ts_mode >= TSM_SEC )
  132. {
  133. const uint sec = GetTimeSec(false) % SEC_PER_DAY;
  134. len = snprintf(buf,buf_size,
  135. "%02u:%02u:%02u ",
  136. sec / 3600,
  137. sec / 60 % 60,
  138. sec % 60 );
  139. }
  140. else
  141. {
  142. *buf = 0;
  143. len = 0;
  144. }
  145. if ( len >= buf_size )
  146. {
  147. len = buf_size - 1;
  148. buf[len] = 0;
  149. }
  150. return len;
  151. }
  152. ///////////////////////////////////////////////////////////////////////////////
  153. int PrintLogTimestamp ( LogFile_t *lf )
  154. {
  155. DASSERT(lf);
  156. DASSERT(lf->log);
  157. int stat = 0;
  158. if ( lf && lf->log )
  159. {
  160. char buf[50];
  161. stat = GetLogTimestamp(buf,sizeof(buf),lf->ts_mode);
  162. if ( stat > 0 )
  163. fwrite(buf,stat,1,lf->log);
  164. }
  165. return stat;
  166. }
  167. ///////////////////////////////////////////////////////////////////////////////
  168. ///////////////////////////////////////////////////////////////////////////////
  169. int PutLogFile ( LogFile_t *lf, ccp text, int text_len )
  170. {
  171. if (!lf)
  172. lf = &GlobalLogFile;
  173. if ( !lf->log || !text )
  174. return 0;
  175. //--- collect data first to have a single print
  176. char buf[2000];
  177. char *dest = buf + GetLogTimestamp( buf, sizeof(buf), lf->ts_mode );
  178. dest = StringCopyEMem( dest, buf+sizeof(buf), lf->tag );
  179. dest = StringCopyEM( dest, buf+sizeof(buf), text,
  180. text_len < 0 ? strlen(text) : text_len );
  181. const int len = dest - buf;
  182. fwrite(buf,len,1,lf->log);
  183. if (lf->flush)
  184. fflush(lf->log);
  185. if ( lf->log == stdout )
  186. stdout_seq_count++;
  187. return len;
  188. }
  189. ///////////////////////////////////////////////////////////////////////////////
  190. int PrintArgLogFile ( LogFile_t *lf, ccp format, va_list arg )
  191. {
  192. int stat = 0;
  193. if (format)
  194. {
  195. char buf[2000];
  196. int len = vsnprintf(buf,sizeof(buf),format,arg);
  197. if ( len >= 0 )
  198. {
  199. if ( len >= sizeof(buf)-1 )
  200. {
  201. len = sizeof(buf)-1;
  202. buf[sizeof(buf)-1] = 0;
  203. }
  204. stat = PutLogFile(lf,buf,len);
  205. }
  206. }
  207. return stat;
  208. }
  209. ///////////////////////////////////////////////////////////////////////////////
  210. int PrintLogFile ( LogFile_t *lf, ccp format, ... )
  211. {
  212. va_list arg;
  213. va_start(arg,format);
  214. const int stat = PrintArgLogFile(lf,format,arg);
  215. va_end(arg);
  216. return stat;
  217. }
  218. //
  219. ///////////////////////////////////////////////////////////////////////////////
  220. /////////////// search tool ///////////////
  221. ///////////////////////////////////////////////////////////////////////////////
  222. exmem_t SearchToolByPATH ( ccp tool )
  223. {
  224. exmem_t dest = {};
  225. if ( tool && *tool )
  226. {
  227. char fname[PATH_MAX];
  228. if (strchr(tool,'/'))
  229. {
  230. //PRINT1("SEARCH ABS: %s\n",tool);
  231. struct stat st;
  232. if ( !stat(tool,&st) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH) )
  233. {
  234. //PRINT1("FOUND ABS: %s\n",tool);
  235. AssignExMemS(&dest,tool,-1,CPM_COPY);
  236. }
  237. }
  238. else
  239. {
  240. #ifdef __CYGWINxxx__
  241. if ( ProgInfo.progpath && *ProgInfo.progpath )
  242. {
  243. PathCatPP(fname,sizeof(fname),ProgInfo.progpath,tool);
  244. //PRINT1("SEARCH+: %s\n",fname);
  245. struct stat st;
  246. if ( !stat(fname,&st) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH) )
  247. {
  248. //PRINT1("FOUND+: %s\n",fname);
  249. AssignExMemS(&dest,fname,-1,CPM_COPY);
  250. return dest;
  251. }
  252. }
  253. #endif
  254. ccp path = getenv("PATH");
  255. for(;;)
  256. {
  257. while ( *path == ':' )
  258. path++;
  259. ccp start = path;
  260. while ( *path && *path != ':' )
  261. path++;
  262. const int len = path - start;
  263. if (!len)
  264. break;
  265. const int flen = snprintf(fname,sizeof(fname),"%.*s/%s",len,start,tool);
  266. //PRINT1("SEARCH: %s\n",fname);
  267. struct stat st;
  268. if ( !stat(fname,&st) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH) )
  269. {
  270. //PRINT1("FOUND: %s\n",fname);
  271. AssignExMemS(&dest,fname,flen,CPM_COPY);
  272. break;
  273. }
  274. }
  275. }
  276. }
  277. return dest;
  278. }
  279. ///////////////////////////////////////////////////////////////////////////////
  280. exmem_t SearchToolByList ( ccp * list, int max )
  281. {
  282. exmem_t dest = {};
  283. if (list)
  284. {
  285. if ( max < 0 )
  286. {
  287. max = 0;
  288. for ( ccp *ptr = list; *ptr; ptr++ )
  289. max++;
  290. }
  291. while ( max-- > 0 )
  292. {
  293. ccp arg = *list++;
  294. if (arg)
  295. {
  296. //PRINT1("SEARCH: %s\n",arg);
  297. if ( *arg == '>' )
  298. {
  299. ccp env = getenv(arg+1);
  300. if ( env && *env )
  301. {
  302. AssignExMemS(&dest,env,-1,CPM_LINK);
  303. break;
  304. }
  305. }
  306. else
  307. {
  308. dest = SearchToolByPATH(arg);
  309. if (dest.data.len)
  310. {
  311. //AssignExMemS(&dest,arg,-1,CPM_LINK);
  312. break;
  313. }
  314. }
  315. }
  316. }
  317. }
  318. return dest;
  319. }
  320. //
  321. ///////////////////////////////////////////////////////////////////////////////
  322. /////////////// pipe to pager ///////////////
  323. ///////////////////////////////////////////////////////////////////////////////
  324. static FILE *pager_file = 0;
  325. ///////////////////////////////////////////////////////////////////////////////
  326. FILE * OpenPipeToPager()
  327. {
  328. static bool done = false;
  329. if (!done)
  330. {
  331. done = true;
  332. // first: set less option -R to enable colors
  333. ccp less = getenv("LESS");
  334. if ( less && *less )
  335. {
  336. char buf[200];
  337. snprintf(buf,sizeof(buf),"%s -R",less);
  338. setenv("LESS",buf,1);
  339. }
  340. else
  341. setenv("LESS","-R",1);
  342. static ccp search_tab[] =
  343. {
  344. ">DC_PAGER",
  345. ">DC_PAGER1",
  346. ">DC_PAGER2",
  347. ">PAGER",
  348. #ifdef __CYGWINxxx__
  349. "/usr/bin/less",
  350. "/cygdrive/c/Program Files/Wiimm/SZS/less",
  351. "/usr/bin/more",
  352. "/cygdrive/c/Program Files/Wiimm/SZS/more",
  353. #endif
  354. "less",
  355. "more",
  356. 0
  357. };
  358. exmem_t pager = SearchToolByList(search_tab,-1);
  359. if (pager.data.ptr)
  360. {
  361. #ifdef __CYGWINxxx__
  362. PRINT1("popen(%s)\n",pager.data.ptr);
  363. char buf[500];
  364. StringCat3S(buf,sizeof(buf),"'",pager.data.ptr,"'");
  365. PRINT1("popen(%s)\n",buf);
  366. pager_file = popen(buf,"we");
  367. PRINT_IF1(!pager_file,"popen(%s) FAILED\n",buf);
  368. #else
  369. pager_file = popen(pager.data.ptr,"we");
  370. #endif
  371. }
  372. FreeExMem(&pager);
  373. }
  374. return pager_file;
  375. }
  376. ///////////////////////////////////////////////////////////////////////////////
  377. void ClosePagerFile()
  378. {
  379. if (pager_file)
  380. {
  381. if ( stdout == pager_file ) stdout = 0;
  382. if ( stderr == pager_file ) stderr = 0;
  383. if ( stdwrn == pager_file ) stdwrn = 0;
  384. if ( stdlog == pager_file ) stdlog = 0;
  385. pclose(pager_file);
  386. pager_file = 0;
  387. }
  388. }
  389. ///////////////////////////////////////////////////////////////////////////////
  390. bool StdoutToPager()
  391. {
  392. FILE *f = OpenPipeToPager();
  393. if (f)
  394. {
  395. fflush(stdout);
  396. stdout = f;
  397. if ( stderr && isatty(fileno(stderr)) )
  398. stderr = f;
  399. if ( stdwrn && isatty(fileno(stdwrn)) )
  400. stdwrn = f;
  401. if ( stdlog && isatty(fileno(stdlog)) )
  402. stdlog = f;
  403. return true;
  404. }
  405. return false;
  406. }
  407. ///////////////////////////////////////////////////////////////////////////////
  408. void CloseStdoutToPager()
  409. {
  410. if ( pager_file && pager_file == stdout )
  411. ClosePagerFile();
  412. }
  413. //
  414. ///////////////////////////////////////////////////////////////////////////////
  415. /////////////// enum FileMode_t ///////////////
  416. ///////////////////////////////////////////////////////////////////////////////
  417. ccp GetFileModeStatus
  418. (
  419. char *buf, // result buffer
  420. // NULL: use a local circulary static buffer
  421. size_t buf_size, // size of 'buf', ignored if buf==NULL
  422. FileMode_t file_mode, // filemode to print
  423. uint print_mode // 0: short vector: 1 char for 1 attrib
  424. // 1: long mode: keyword for each set attrib
  425. )
  426. {
  427. struct info_t
  428. {
  429. FileMode_t mode;
  430. char ch;
  431. ccp name;
  432. };
  433. static const struct info_t info[] =
  434. {
  435. { FM_TEST, 't', "test" },
  436. { FM_SILENT, 's', "silent" },
  437. { FM_IGNORE, 'i', "ignore" },
  438. { FM_MODIFY, 'M', "modify" },
  439. { FM_APPEND, 'A', "append" },
  440. { FM_UPDATE, 'U', "update" },
  441. { FM_OVERWRITE, 'O', "overwrite" },
  442. { FM_NUMBER, 'N', "number" },
  443. { FM_REMOVE, 'R', "remove" },
  444. { FM_MKDIR, 'M', "mkdir" },
  445. { FM_STDIO, 'S', "stdio" },
  446. { FM_TCP, 'I', "tcp" },
  447. { FM_DEV, 'D', "dev" },
  448. { FM_SPC, 'P', "spc" },
  449. { FM_TOUCH, 'H', "touch" },
  450. { FM_TEMP, 'T', "temp" },
  451. {0,0,0}
  452. };
  453. if ( print_mode == 0 )
  454. {
  455. if ( !buf || !buf_size )
  456. buf = GetCircBuf( buf_size = 16 );
  457. char *dest = buf, *end = buf + buf_size - 1;
  458. const struct info_t *ip;
  459. for ( ip = info; ip->mode && dest < end; ip++ )
  460. *dest++ = ip->mode & file_mode ? ip->ch : '-';
  461. *dest = 0;
  462. return buf;
  463. }
  464. if ( !buf || !buf_size )
  465. buf = GetCircBuf( buf_size = 100 );
  466. char *dest = buf, *end = buf + buf_size - 1;
  467. const struct info_t *ip;
  468. for ( ip = info; ip->mode && dest < end; ip++ )
  469. if ( ip->mode & file_mode )
  470. {
  471. if ( dest > buf )
  472. *dest++ = ',';
  473. dest = StringCopyE(dest,end,ip->name);
  474. }
  475. *dest = 0;
  476. return buf;
  477. }
  478. ///////////////////////////////////////////////////////////////////////////////
  479. ccp GetFileOpenMode
  480. (
  481. bool create, // false: open, true: create
  482. FileMode_t file_mode // open modes
  483. )
  484. {
  485. if ( file_mode & FM_TEST )
  486. return "r";
  487. if (!create)
  488. return file_mode & FM_MODIFY ? "r+b" : "rb";
  489. switch ( (uint)file_mode & (FM_MODIFY|FM_APPEND) )
  490. {
  491. case FM_MODIFY | FM_APPEND : return "a+b";
  492. case FM_MODIFY : return "w+b";
  493. case FM_APPEND : return "ab";
  494. default : return "wb";
  495. }
  496. }
  497. //
  498. ///////////////////////////////////////////////////////////////////////////////
  499. /////////////// struct FileAttrib_t ///////////////
  500. ///////////////////////////////////////////////////////////////////////////////
  501. FileAttrib_t * ClearFileAttrib
  502. (
  503. FileAttrib_t * dest // NULL or destination attribute
  504. )
  505. {
  506. if (dest)
  507. {
  508. memset(dest,0,sizeof(*dest));
  509. dest->atime.tv_nsec =
  510. dest->mtime.tv_nsec =
  511. dest->ctime.tv_nsec =
  512. dest->itime.tv_nsec =
  513. #if SUPPORT_UTIMENSAT
  514. UTIME_OMIT;
  515. #else
  516. -2;
  517. #endif
  518. }
  519. return dest;
  520. }
  521. ///////////////////////////////////////////////////////////////////////////////
  522. FileAttrib_t * TouchFileAttrib
  523. (
  524. FileAttrib_t * dest // valid destination attribute
  525. )
  526. {
  527. DASSERT(dest);
  528. dest->atime = GetClockTime(false);
  529. dest->mtime = dest->ctime = dest->itime = dest->atime;
  530. return dest;
  531. }
  532. ///////////////////////////////////////////////////////////////////////////////
  533. FileAttrib_t * SetFileAttrib
  534. (
  535. FileAttrib_t * dest, // valid destination attribute
  536. const FileAttrib_t * src_fa, // NULL or source attribute
  537. const struct stat * src_stat // NULL or source attribute
  538. // only used if 'src_fa==NULL'
  539. )
  540. {
  541. DASSERT(dest);
  542. if (src_fa)
  543. memcpy(dest,src_fa,sizeof(*dest));
  544. else if (src_stat)
  545. {
  546. ZeroFileAttrib(dest);
  547. if (S_ISREG(src_stat->st_mode))
  548. {
  549. #if HAVE_STATTIME_NSEC
  550. dest->atime = src_stat->st_atim;
  551. dest->mtime = src_stat->st_mtim;
  552. dest->ctime = src_stat->st_ctim;
  553. #else
  554. dest->atime.tv_sec = src_stat->st_atime;
  555. dest->mtime.tv_sec = src_stat->st_mtime;
  556. dest->ctime.tv_sec = src_stat->st_ctime;
  557. #endif
  558. dest->itime = CompareTimeSpec(&dest->mtime,&dest->ctime) > 0
  559. ? dest->mtime : dest->ctime;
  560. dest->size = src_stat->st_size;
  561. }
  562. else
  563. ClearFileAttrib(dest);
  564. dest->mode = src_stat->st_mode;
  565. }
  566. return dest;
  567. }
  568. ///////////////////////////////////////////////////////////////////////////////
  569. FileAttrib_t * MaxFileAttrib
  570. (
  571. FileAttrib_t * dest, // valid source and destination attribute
  572. const FileAttrib_t * src_fa, // NULL or second source attribute
  573. const struct stat * src_stat // NULL or third source attribute
  574. )
  575. {
  576. DASSERT(dest);
  577. if (src_fa)
  578. {
  579. if (CompareTimeSpec(&dest->atime,&src_fa->atime)<0) dest->atime = src_fa->atime;
  580. if (CompareTimeSpec(&dest->mtime,&src_fa->mtime)<0) dest->mtime = src_fa->mtime;
  581. if (CompareTimeSpec(&dest->ctime,&src_fa->ctime)<0) dest->ctime = src_fa->ctime;
  582. if (CompareTimeSpec(&dest->itime,&src_fa->itime)<0) dest->itime = src_fa->itime;
  583. if ( dest->size < src_fa->size )
  584. dest->size = src_fa->size;
  585. dest->mode = src_fa->mode;
  586. }
  587. if (src_stat)
  588. {
  589. if ( S_ISREG(src_stat->st_mode) )
  590. {
  591. #if HAVE_STATTIME_NSEC
  592. if ( CompareTimeSpec(&dest->atime,&src_stat->st_atim) < 0 )
  593. dest->atime = src_stat->st_atim;
  594. if ( CompareTimeSpec(&dest->mtime,&src_stat->st_mtim) < 0 )
  595. dest->mtime = src_stat->st_mtim;
  596. if ( CompareTimeSpec(&dest->ctime,&src_stat->st_ctim) < 0 )
  597. dest->ctime = src_stat->st_ctim;
  598. if ( CompareTimeSpec(&dest->itime,&src_stat->st_mtim) < 0 )
  599. dest->itime = src_stat->st_mtim;
  600. if ( CompareTimeSpec(&dest->itime,&src_stat->st_ctim) < 0 )
  601. dest->itime = src_stat->st_ctim;
  602. #else
  603. if ( CompareTimeSpecTime(&dest->atime,src_stat->st_atime) < 0 )
  604. {
  605. dest->atime.tv_sec = src_stat->st_atime;
  606. dest->atime.tv_nsec = 0;
  607. }
  608. if ( CompareTimeSpecTime(&dest->mtime,src_stat->st_mtime) < 0 )
  609. {
  610. dest->mtime.tv_sec = src_stat->st_mtime;
  611. dest->mtime.tv_nsec = 0;
  612. }
  613. if ( CompareTimeSpecTime(&dest->ctime,src_stat->st_ctime) < 0 )
  614. {
  615. dest->ctime.tv_sec = src_stat->st_ctime;
  616. dest->ctime.tv_nsec = 0;
  617. }
  618. if ( CompareTimeSpecTime(&dest->itime,src_stat->st_mtime) < 0 )
  619. {
  620. dest->itime.tv_sec = src_stat->st_mtime;
  621. dest->itime.tv_nsec = 0;
  622. }
  623. if ( CompareTimeSpecTime(&dest->itime,src_stat->st_ctime) < 0 )
  624. {
  625. dest->itime.tv_sec = src_stat->st_ctime;
  626. dest->itime.tv_nsec = 0;
  627. }
  628. #endif
  629. if ( dest->size < src_stat->st_size )
  630. dest->size = src_stat->st_size;
  631. }
  632. dest->mode = src_stat->st_mode;
  633. }
  634. return dest;
  635. }
  636. ///////////////////////////////////////////////////////////////////////////////
  637. FileAttrib_t * NormalizeFileAttrib
  638. (
  639. FileAttrib_t * fa // valid attribute
  640. )
  641. {
  642. DASSERT(fa);
  643. if ( fa->size < 0 )
  644. fa->size = 0;
  645. if (IsTimeSpecNull(&fa->mtime))
  646. {
  647. if (!IsTimeSpecNull(&fa->itime))
  648. fa->mtime = fa->itime;
  649. else if (!IsTimeSpecNull(&fa->ctime))
  650. fa->mtime = fa->ctime;
  651. }
  652. if (IsTimeSpecNull(&fa->itime))
  653. fa->itime = CompareTimeSpec(&fa->ctime,&fa->mtime) > 0
  654. ? fa->ctime : fa->mtime;
  655. if (IsTimeSpecNull(&fa->ctime))
  656. fa->ctime = CompareTimeSpec(&fa->itime,&fa->mtime) > 0
  657. ? fa->itime : fa->mtime;
  658. if (IsTimeSpecNull(&fa->atime))
  659. fa->atime = CompareTimeSpec(&fa->itime,&fa->ctime) > 0
  660. ? fa->itime : fa->ctime;
  661. return fa;
  662. }
  663. ///////////////////////////////////////////////////////////////////////////////
  664. // struct timespec helpers
  665. const struct timespec null_timespec = {0,0};
  666. ///////////////////////////////////////////////////////////////////////////////
  667. int CompareTimeSpec0 ( const struct timespec *a, const struct timespec *b )
  668. {
  669. if (!a) a = &null_timespec;
  670. if (!b) b = &null_timespec;
  671. return CompareTimeSpec(a,b);
  672. }
  673. ///////////////////////////////////////////////////////////////////////////////
  674. void SetAMTimes ( ccp fname, const struct timespec times[2] )
  675. {
  676. #if SUPPORT_UTIMENSAT
  677. utimensat(AT_FDCWD,fname,times,0);
  678. #else
  679. struct timeval tv[2];
  680. tv[1].tv_sec = times[1].tv_sec;
  681. tv[1].tv_usec = times[1].tv_nsec / 1000;
  682. if (IsTimeSpecNull(times+0))
  683. {
  684. tv[0].tv_sec = times[0].tv_sec;
  685. tv[0].tv_usec = times[0].tv_nsec / 1000;
  686. }
  687. else
  688. tv[0] = tv[1];
  689. utimes(fname,tv);
  690. #endif
  691. }
  692. //
  693. ///////////////////////////////////////////////////////////////////////////////
  694. /////////////// simple UNIX+TCP socket connect ///////////////
  695. ///////////////////////////////////////////////////////////////////////////////
  696. // not stored in dclib-network.c to allow static linking!
  697. int ConnectUnixTCP
  698. (
  699. ccp fname, // unix socket filename
  700. bool silent // true: suppress error messages
  701. )
  702. {
  703. DASSERT(fname);
  704. //--- check file name
  705. struct sockaddr_un sa;
  706. const uint flen = fname ? strlen(fname)+1 : 0;
  707. if ( flen > sizeof(sa.sun_path) )
  708. {
  709. if (!silent)
  710. ERROR0(ERR_CANT_CONNECT,
  711. "Path name to long for UNIX/STREAM socket: %s\n",fname);
  712. return -1;
  713. }
  714. memset(&sa,0,sizeof(sa));
  715. memcpy(sa.sun_path,fname,flen);
  716. sa.sun_family = AF_UNIX;
  717. //--- create socket
  718. const int sock = socket(AF_UNIX,SOCK_STREAM,0);
  719. if ( sock == -1 )
  720. {
  721. if (!silent)
  722. ERROR1(ERR_CANT_CONNECT,
  723. "Can't create UNIX/STREAM socket: %s\n",fname);
  724. return -1;
  725. }
  726. //--- connect
  727. if (connect(sock,(struct sockaddr *)&sa,sizeof(sa)))
  728. {
  729. if (!silent)
  730. ERROR1(ERR_CANT_CONNECT,
  731. "Can't connect to UNIX/STREAM socket: %s\n",fname);
  732. close(sock);
  733. return -1;
  734. }
  735. return sock;
  736. }
  737. ///////////////////////////////////////////////////////////////////////////////
  738. int (*ConnectTCP_Hook)
  739. (
  740. // if set: ConnectNumericTCP() uses ConnectTCP()
  741. ccp addr, // TCP address: ['tcp':] IPv4 [:PORT]
  742. int default_port, // NULL or default port, if not found in 'addr'
  743. bool silent // true: suppress error messages
  744. ) = 0;
  745. ///////////////////////////////////////////////////////////////////////////////
  746. char * ScanNumericIP4
  747. (
  748. // returns next unread character or NULL on error
  749. ccp addr, // address to scan
  750. u32 *r_ipv4, // not NULL: store result here (local endian)
  751. u32 *r_port, // not NULL: scan port too (local endian)
  752. uint default_port // return this if no port found
  753. )
  754. {
  755. if (!addr)
  756. {
  757. abort:
  758. if (r_ipv4)
  759. *r_ipv4 = 0;
  760. if (r_port)
  761. *r_port = 0;
  762. return 0;
  763. }
  764. u32 num[4] = {0};
  765. uint i;
  766. ccp ptr = addr;
  767. for ( i = 0; i < 4; )
  768. {
  769. char *end;
  770. uint temp = str2ul(ptr,&end,10);
  771. if ( !end || end == ptr )
  772. break;
  773. num[i++] = temp;
  774. ptr = addr = end;
  775. if ( *ptr != '.' )
  776. break;
  777. ptr++;
  778. }
  779. uint ip4;
  780. switch (i)
  781. {
  782. case 1:
  783. ip4 = num[0];
  784. break;
  785. case 2:
  786. ip4 = num[0] << 24 | num[1];
  787. break;
  788. case 3:
  789. ip4 = num[0] << 24 | num[1] << 16 | num[2];
  790. break;
  791. case 4:
  792. ip4 = num[0] << 24 | num[1] << 16 | num[2] << 8 | num[3];
  793. break;
  794. default:
  795. goto abort;
  796. }
  797. if (r_ipv4)
  798. *r_ipv4 = ip4;
  799. if (r_port)
  800. {
  801. if ( *addr == ':' )
  802. {
  803. char *end;
  804. uint num = str2ul(addr+1,&end,10);
  805. if ( end && end > addr+1 && num < 0x10000 )
  806. {
  807. addr = end;
  808. default_port = num;
  809. }
  810. }
  811. *r_port = default_port;
  812. }
  813. return (char*)addr;
  814. }
  815. ///////////////////////////////////////////////////////////////////////////////
  816. mem_t ScanNumericIP4Mem
  817. (
  818. // returns unread character or NullMem on error
  819. mem_t addr, // address to scan
  820. u32 *r_ipv4, // not NULL: store result here (local endian)
  821. u32 *r_port, // not NULL: scan port too (local endian)
  822. uint default_port // return this if no port found
  823. )
  824. {
  825. if ( !addr.ptr || !addr.len )
  826. {
  827. abort:
  828. if (r_ipv4)
  829. *r_ipv4 = 0;
  830. if (r_port)
  831. *r_port = 0;
  832. return NullMem;
  833. }
  834. u32 num[4] = {0};
  835. uint i;
  836. ccp ptr = addr.ptr;
  837. ccp end = ptr + addr.len;
  838. for ( i = 0; i < 4; )
  839. {
  840. uint temp;
  841. ccp next = ScanNumber(&temp,ptr,end,10,10);
  842. if ( !next || next == ptr )
  843. break;
  844. num[i++] = temp;
  845. addr = BehindMem(addr,next);
  846. ptr = addr.ptr;
  847. if ( *ptr != '.' )
  848. break;
  849. ptr++;
  850. }
  851. uint ip4;
  852. switch (i)
  853. {
  854. case 1:
  855. ip4 = num[0];
  856. break;
  857. case 2:
  858. ip4 = num[0] << 24 | num[1];
  859. break;
  860. case 3:
  861. ip4 = num[0] << 24 | num[1] << 16 | num[2];
  862. break;
  863. case 4:
  864. ip4 = num[0] << 24 | num[1] << 16 | num[2] << 8 | num[3];
  865. break;
  866. default:
  867. goto abort;
  868. }
  869. if (r_ipv4)
  870. *r_ipv4 = ip4;
  871. if (r_port)
  872. {
  873. ptr = addr.ptr;
  874. if ( ptr < end && *ptr == ':' )
  875. {
  876. ptr++;
  877. uint num;
  878. ccp next = ScanNumber(&num,ptr,end,10,10);
  879. if ( next > ptr && num < 0x10000 )
  880. {
  881. addr = BehindMem(addr,next);
  882. default_port = num;
  883. }
  884. }
  885. *r_port = default_port;
  886. }
  887. return addr;
  888. }
  889. ///////////////////////////////////////////////////////////////////////////////
  890. // Returns a NETWORK MASK "/a.b.c.d" or as CIDR number "/num" between 0 and 32.
  891. // An optional slash '/' at the beginning is skipped.
  892. // Returns modified 'source' if a MASK or CDIR is detected.
  893. // If no one is detected, source is unmodified and returned mask = ~0.
  894. mem_t ScanNetworkMaskMem ( u32 *mask, mem_t source )
  895. {
  896. u32 m = M1(m);
  897. if ( source.ptr && source.len )
  898. {
  899. ccp ptr = source.ptr;
  900. ccp end = ptr + source.len;
  901. if ( *ptr == '/' )
  902. ptr++;
  903. uint num;
  904. ccp next = ScanNumber(&num,ptr,end,10,10);
  905. if ( next > ptr )
  906. {
  907. if ( next < end && *next == '.' )
  908. {
  909. // is a network mask
  910. source = ScanNumericIP4Mem(BehindMem(source,ptr),&m,0,0);
  911. }
  912. else if ( num <= 32 )
  913. {
  914. m = num ? ~(( 1 << (32-num) ) - 1) : 0;
  915. source = BehindMem(source,next);
  916. }
  917. }
  918. }
  919. if (mask)
  920. *mask = m;
  921. return source;
  922. }
  923. ///////////////////////////////////////////////////////////////////////////////
  924. int ConnectNumericTCP
  925. (
  926. // like ConnectTCP(), but without name resolution (only numeric ip+port)
  927. ccp addr, // TCP address: ['tcp':] IPv4 [:PORT]
  928. int default_port, // NULL or default port, if not found in 'addr'
  929. bool silent // true: suppress error messages
  930. )
  931. {
  932. if (ConnectTCP_Hook)
  933. return ConnectTCP_Hook(addr,default_port,silent);
  934. ccp unix_path = CheckUnixSocketPath(addr,0);
  935. if (unix_path)
  936. return ConnectUnixTCP(unix_path,silent);
  937. if ( !strncasecmp(addr,"tcp:",4) )
  938. addr += 4;
  939. u32 ip4, port;
  940. ccp end = ScanNumericIP4(addr,&ip4,&port,default_port);
  941. if (!end)
  942. {
  943. if (!silent)
  944. ERROR1(ERR_CANT_CONNECT,
  945. "Invalid IPv4 address: %s\n",addr);
  946. return -1;
  947. }
  948. int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  949. if ( sock == -1 )
  950. {
  951. if (!silent)
  952. ERROR1(ERR_CANT_CONNECT,
  953. "Can't create socket: %s\n",addr);
  954. return -1;
  955. }
  956. struct sockaddr_in sa;
  957. sa.sin_family = AF_INET;
  958. sa.sin_addr.s_addr = htonl(ip4);
  959. sa.sin_port = htons(port);
  960. if (connect(sock,(struct sockaddr *)&sa,sizeof(sa)))
  961. {
  962. if (!silent)
  963. ERROR1(ERR_CANT_CONNECT,
  964. "Can't connect to %s\n",PrintIP4(0,0,ip4,port) );
  965. close(sock);
  966. return -1;
  967. }
  968. return sock;
  969. }
  970. //
  971. ///////////////////////////////////////////////////////////////////////////////
  972. /////////////// struct File_t ///////////////
  973. ///////////////////////////////////////////////////////////////////////////////
  974. void InitializeFile
  975. (
  976. File_t * f // file structure
  977. )
  978. {
  979. DASSERT(f);
  980. memset(f,0,sizeof(*f));
  981. f->fname = EmptyString;
  982. }
  983. ///////////////////////////////////////////////////////////////////////////////
  984. enumError ResetFile
  985. (
  986. File_t * f, // file structure
  987. uint set_time // 0: don't set
  988. // 1: set time before closing using 'fatt'
  989. // 2: set current time before closing
  990. )
  991. {
  992. DASSERT(f);
  993. const enumError err = CloseFile(f,set_time);
  994. FreeString(f->fname);
  995. if (f->data_alloced)
  996. FREE(f->data);
  997. InitializeFile(f);
  998. return err;
  999. }
  1000. ///////////////////////////////////////////////////////////////////////////////
  1001. enumError CloseFile
  1002. (
  1003. File_t * f, // file structure
  1004. uint set_time // 0: don't set
  1005. // 1: set time before closing using 'fatt'
  1006. // 2: set current time before closing
  1007. )
  1008. {
  1009. DASSERT(f);
  1010. TRACE("CloseFile(%p,%d) fname=%s\n",f,set_time,f->fname);
  1011. if (f->f)
  1012. {
  1013. if (!f->is_stdio)
  1014. {
  1015. if ( fclose(f->f) && f->max_err <= ERR_WARNING )
  1016. f->max_err = ERROR1(ERR_WRITE_FAILED,
  1017. "Error while closing file: %s\n",f->fname);
  1018. if (!f->is_socket)
  1019. {
  1020. if ( f->fmode & FM_TEMP )
  1021. {
  1022. TRACE("UNLINK TEMP %s\n",f->fname);
  1023. unlink(f->fname);
  1024. }
  1025. else if ( f->is_writing )
  1026. {
  1027. if ( f->max_err > ERR_WARNING )
  1028. {
  1029. TRACE("UNLINK %s\n",f->fname);
  1030. unlink(f->fname);
  1031. }
  1032. else if ( set_time == 1 && !IsTimeSpecNull(&f->fatt.mtime) )
  1033. SetAMTimes(f->fname,f->fatt.times);
  1034. else if ( set_time > 1 || f->fmode & FM_TOUCH )
  1035. {
  1036. utime(f->fname,0);
  1037. }
  1038. }
  1039. }
  1040. }
  1041. f->f = 0;
  1042. }
  1043. return f->max_err;
  1044. }
  1045. ///////////////////////////////////////////////////////////////////////////////
  1046. static bool is_seekable
  1047. (
  1048. File_t * F // file structure
  1049. )
  1050. {
  1051. const mode_t mode = F->st.st_mode;
  1052. F->is_seekable = ( S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode) )
  1053. && F->st.st_size
  1054. && lseek(fileno(F->f),0,SEEK_SET) != (off_t)-1;
  1055. return F->is_seekable;
  1056. }
  1057. ///////////////////////////////////////////////////////////////////////////////
  1058. ///////////////////////////////////////////////////////////////////////////////
  1059. enumError OpenFile
  1060. (
  1061. File_t * f, // file structure
  1062. bool initialize, // true: initialize 'f'
  1063. ccp fname, // file to open
  1064. FileMode_t file_mode, // open modes
  1065. off_t limit, // >0: don't open, if file size > limit
  1066. ccp limit_message // NULL or a with LF terminated text.
  1067. // It is printed after the message.
  1068. )
  1069. {
  1070. DASSERT(f);
  1071. DASSERT(fname);
  1072. if (initialize)
  1073. InitializeFile(f);
  1074. else
  1075. ResetFile(f,false);
  1076. f->fmode = file_mode & FM_M_OPEN;
  1077. TRACE("OpenFile(%p,%d,,%s,%llu) [%s] %s\n",
  1078. f, initialize, GetFileModeStatus(0,0,file_mode,0), (u64)limit,
  1079. GetFileModeStatus(0,0,f->fmode,1), fname );
  1080. //--- test for TCP
  1081. if ( file_mode & FM_TCP && !strncasecmp(fname,"tcp:",4) )
  1082. {
  1083. const int fd = ConnectNumericTCP(fname,0,(f->fmode&FM_SILENT)!=0);
  1084. if ( fd == -1 )
  1085. return f->max_err = ERR_CANT_OPEN;
  1086. f->f = fdopen(fd,"rw");
  1087. f->is_socket = f->is_reading = f->is_writing = true;
  1088. return f->max_err;
  1089. }
  1090. //--- test for stdin
  1091. if ( *fname == '-' && !fname[1] )
  1092. {
  1093. f->fname = MinusString;
  1094. if ( f->fmode & FM_STDIO )
  1095. {
  1096. f->is_stdio = true;
  1097. f->f = stdin;
  1098. fstat(fileno(stdin),&f->st);
  1099. is_seekable(f);
  1100. return ERR_OK;
  1101. }
  1102. }
  1103. f->fname = STRDUP(fname);
  1104. //--- test 'unix:' prefix
  1105. bool have_unix = file_mode & FM_TCP && !strncasecmp(fname,"unix:",5);
  1106. if (have_unix)
  1107. fname += 5;
  1108. //--- does file exist?
  1109. for(;;)
  1110. {
  1111. if (!stat(fname,&f->st))
  1112. break;
  1113. if (have_unix)
  1114. {
  1115. have_unix = false;
  1116. fname -= 5;
  1117. continue;
  1118. }
  1119. not_found:
  1120. memset(&f->st,0,sizeof(f->st));
  1121. if ( f->fmode & FM_IGNORE )
  1122. return f->max_err = ERR_NOT_EXISTS;
  1123. if ( !(f->fmode & FM_SILENT) )
  1124. ERROR1(ERR_CANT_OPEN,"Can't find file: %s\n",fname);
  1125. return f->max_err = ERR_CANT_OPEN;
  1126. }
  1127. //--- UNIX socket -> try STREAM connect
  1128. if (S_ISSOCK(f->st.st_mode))
  1129. {
  1130. f->fmode &= ~(FM_REMOVE|FM_TEMP);
  1131. const int fd = ConnectUnixTCP(fname,(f->fmode&FM_SILENT)!=0);
  1132. if ( fd == -1 )
  1133. return f->max_err = ERR_CANT_OPEN;
  1134. f->f = fdopen(fd,"rw");
  1135. f->is_socket = f->is_reading = f->is_writing = true;
  1136. return f->max_err;
  1137. }
  1138. if (have_unix)
  1139. goto not_found;
  1140. //--- check size limit
  1141. SetFileAttrib(&f->fatt,0,&f->st);
  1142. if ( limit && f->st.st_size > limit )
  1143. {
  1144. char sbuf[12], lbuf[12];
  1145. PrintSize1024(sbuf,sizeof(sbuf),(u64)f->st.st_size,0);
  1146. PrintSize1024(lbuf,sizeof(lbuf),(u64)limit,0);
  1147. // be tolerant, if both values produce same text
  1148. if (strcmp(sbuf,lbuf))
  1149. {
  1150. if ( !(f->fmode & FM_SILENT) )
  1151. ERROR0(ERR_CANT_OPEN,
  1152. "File too large (size=%s, limit=%s): %s\n%s",
  1153. sbuf, lbuf, fname,
  1154. limit_message ? limit_message : "" );
  1155. return f->max_err = ERR_CANT_OPEN;
  1156. }
  1157. }
  1158. //--- open file
  1159. ccp omode = GetFileOpenMode(false,f->fmode);
  1160. f->f = fopen(fname,omode);
  1161. TRACE("OpenFile(%s,%s) : [%s], %p\n",
  1162. fname, omode, GetFileModeStatus(0,0,f->fmode,1), f->f );
  1163. if (!f->f)
  1164. {
  1165. if ( !(f->fmode & FM_SILENT) )
  1166. ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",fname);
  1167. return f->max_err = ERR_CANT_OPEN;
  1168. }
  1169. f->is_reading = true;
  1170. if ( f->fmode & FM_MODIFY )
  1171. f->is_writing = true;
  1172. is_seekable(f);
  1173. return f->max_err;
  1174. }
  1175. ///////////////////////////////////////////////////////////////////////////////
  1176. ///////////////////////////////////////////////////////////////////////////////
  1177. enumError CheckCreateFile
  1178. (
  1179. // returns:
  1180. // ERR_DIFFER: [FM_STDIO] source is "-" => 'st' is zeroed
  1181. // ERR_WARNING: [FM_DEV,FM_SOCK,FM_SPC] not a regular file
  1182. // ERR_WRONG_FILE_TYPE: file exists, but is not a regular file
  1183. // ERR_ALREADY_EXISTS: file already exists
  1184. // ERR_INVALID_VERSION: already exists, but FM_NUMBER set => no msg printed
  1185. // ERR_CANT_CREATE: FM_UPDATE is set, but file don't exist
  1186. // ERR_OK: file not exist or can be overwritten
  1187. ccp fname, // filename to open
  1188. FileMode_t file_mode, // open modes
  1189. struct stat *st // not NULL: store file status here
  1190. )
  1191. {
  1192. DASSERT(fname);
  1193. if ( file_mode & FM_STDIO && fname[0] == '-' && !fname[1] )
  1194. {
  1195. if (st)
  1196. memset(st,0,sizeof(*st));
  1197. return ERR_DIFFER;
  1198. }
  1199. struct stat local_st;
  1200. if (!st)
  1201. st = &local_st;
  1202. if (!stat(fname,st))
  1203. {
  1204. if ( S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) )
  1205. {
  1206. if ( file_mode & FM_DEV )
  1207. return ERR_WARNING;
  1208. if ( !(file_mode & FM_SILENT) )
  1209. ERROR0( ERR_ALREADY_EXISTS,
  1210. "Can't write to %s device: %s\n",
  1211. S_ISBLK(st->st_mode) ? "block" : "character", fname );
  1212. return ERR_WRONG_FILE_TYPE;
  1213. }
  1214. if ( S_ISSOCK(st->st_mode) )
  1215. {
  1216. if ( file_mode & FM_SOCK )
  1217. return ERR_WARNING;
  1218. if ( !(file_mode & FM_SILENT) )
  1219. ERROR0( ERR_ALREADY_EXISTS,
  1220. "Can't write to UNIX socket: %s\n", fname );
  1221. return ERR_WRONG_FILE_TYPE;
  1222. }
  1223. if (!S_ISREG(st->st_mode))
  1224. {
  1225. if ( file_mode & FM_SPC )
  1226. return ERR_WARNING;
  1227. if ( !(file_mode & FM_SILENT) )
  1228. ERROR0( ERR_WRONG_FILE_TYPE,
  1229. "Not a plain file: %s\n", fname );
  1230. return ERR_WRONG_FILE_TYPE;
  1231. }
  1232. if (!( file_mode & (FM_OVERWRITE|FM_REMOVE|FM_UPDATE|FM_APPEND) ))
  1233. {
  1234. if ( file_mode & FM_NUMBER )
  1235. return ERR_INVALID_VERSION;
  1236. if ( !(file_mode & FM_SILENT) )
  1237. ERROR0( ERR_ALREADY_EXISTS,
  1238. "File already exists: %s\n", fname );
  1239. return ERR_ALREADY_EXISTS;
  1240. }
  1241. }
  1242. else
  1243. {
  1244. memset(st,0,sizeof(*st));
  1245. if ( file_mode & FM_UPDATE )
  1246. {
  1247. if ( !(file_mode & FM_SILENT) )
  1248. ERROR0(ERR_CANT_CREATE,
  1249. "Try to update non existing file: %s\n", fname );
  1250. return ERR_CANT_CREATE;
  1251. }
  1252. }
  1253. return ERR_OK;
  1254. }
  1255. ///////////////////////////////////////////////////////////////////////////////
  1256. enumError CreateFile
  1257. (
  1258. File_t * f, // file structure
  1259. bool initialize, // true: initialize 'f'
  1260. ccp fname, // file to open
  1261. FileMode_t file_mode // open modes
  1262. )
  1263. {
  1264. DASSERT(f);
  1265. DASSERT(fname);
  1266. if (initialize)
  1267. InitializeFile(f);
  1268. else
  1269. ResetFile(f,false);
  1270. f->fmode = file_mode & FM_M_CREATE;
  1271. noPRINT("CreateFile(%p,%d,,%s) [%s] %s\n",
  1272. f, initialize, GetFileModeStatus(0,0,file_mode,0),
  1273. GetFileModeStatus(0,0,f->fmode,1), fname );
  1274. //--- test for stdout
  1275. if ( *fname == '-' && !fname[1] )
  1276. {
  1277. f->fname = MinusString;
  1278. if ( f->fmode & FM_STDIO )
  1279. {
  1280. f->is_stdio = true;
  1281. f->f = stdout;
  1282. fstat(fileno(stdout),&f->st);
  1283. is_seekable(f);
  1284. return ERR_OK;
  1285. }
  1286. }
  1287. //--- mode calculations
  1288. // if ( f->fmode & FM_UPDATE )
  1289. // f->fmode = f->fmode & ~(FM_REMOVE|FM_MKDIR) | FM_OVERWRITE;
  1290. enumError err = CheckCreateFile(fname,f->fmode,&f->st);
  1291. char num_fname[PATH_MAX];
  1292. if ( err == ERR_INVALID_VERSION )
  1293. {
  1294. NumberedFilename(num_fname,sizeof(num_fname),fname,0,0,false);
  1295. fname = num_fname;
  1296. err = CheckCreateFile( fname, f->fmode & ~FM_NUMBER, &f->st );
  1297. }
  1298. f->fname = STRDUP(fname);
  1299. if ( f->fmode & (FM_MODIFY|FM_APPEND) )
  1300. SetFileAttrib(&f->fatt,0,&f->st);
  1301. if (err)
  1302. {
  1303. if ( err > ERR_WARNING )
  1304. return err;
  1305. // is a device, socket or special file
  1306. err = ERR_OK;
  1307. f->fmode = f->fmode & ~(FM_REMOVE|FM_TEMP) | FM_OVERWRITE;
  1308. }
  1309. if ( f->fmode & FM_TEST )
  1310. {
  1311. // never create or remove a file in TEST mode.
  1312. return ERR_OK;
  1313. }
  1314. if (S_ISSOCK(f->st.st_mode))
  1315. {
  1316. const int fd = ConnectUnixTCP(fname,(f->fmode&FM_SILENT)!=0);
  1317. if ( fd == -1 )
  1318. return f->max_err = ERR_CANT_OPEN;
  1319. f->f = fdopen(fd,"rw");
  1320. f->is_socket = f->is_reading = f->is_writing = true;
  1321. return ERR_OK;
  1322. }
  1323. if ( f->st.st_mode && f->fmode & FM_REMOVE )
  1324. {
  1325. unlink(fname);
  1326. struct stat st;
  1327. if (!stat(fname,&st))
  1328. {
  1329. if ( !(f->fmode & FM_SILENT) )
  1330. ERROR0(ERR_REMOVE_FAILED,"Can't remove file: %s\n",fname);
  1331. return f->max_err = ERR_REMOVE_FAILED;
  1332. }
  1333. }
  1334. ccp omode = GetFileOpenMode(true,f->fmode);
  1335. f->f = fopen(fname,omode);
  1336. noPRINT("CreateFile(%s,%s) : [%s], %p\n",
  1337. fname, omode, GetFileModeStatus(0,0,f->fmode,1), f->f );
  1338. if ( !f->f )
  1339. {
  1340. if ( f->fmode & FM_MKDIR )
  1341. {
  1342. CreatePath(fname,false);
  1343. f->f = fopen(fname,omode);
  1344. }
  1345. if ( !f->f )
  1346. {
  1347. if ( !(f->fmode & FM_SILENT) )
  1348. ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",fname);
  1349. return f->max_err = ERR_CANT_CREATE;
  1350. }
  1351. }
  1352. f->is_writing = true;
  1353. if ( f->fmode & FM_MODIFY)
  1354. f->is_reading = true;
  1355. if (!fstat(fileno(f->f),&f->st))
  1356. {
  1357. const mode_t mode = f->st.st_mode;
  1358. f->is_seekable = S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode);
  1359. }
  1360. return f->max_err;
  1361. }
  1362. ///////////////////////////////////////////////////////////////////////////////
  1363. ///////////////////////////////////////////////////////////////////////////////
  1364. enumError WriteFileAt
  1365. (
  1366. File_t * F, // file to write
  1367. size_t * cur_offset, // pointer to current file offset, modified
  1368. size_t offset, // offset to write
  1369. const void * data, // data to write
  1370. size_t size // size of 'data'
  1371. )
  1372. {
  1373. DASSERT(F);
  1374. DASSERT(F->f);
  1375. DASSERT(data||!size);
  1376. DASSERT(cur_offset);
  1377. if (!size)
  1378. return ERR_OK;
  1379. if ( offset != *cur_offset )
  1380. {
  1381. if (F->is_seekable)
  1382. {
  1383. noPRINT("FSEEK %zx -> %zx\n",*cur_offset,offset);
  1384. if (fseek(F->f,offset,SEEK_SET))
  1385. return ERROR1(ERR_WRITE_FAILED,"Can't set file pointer: %s\n",F->fname);
  1386. }
  1387. else if ( offset < *cur_offset )
  1388. {
  1389. noPRINT("FSEEK %zx -> %zx\n",*cur_offset,offset);
  1390. return ERROR0(ERR_WRITE_FAILED,
  1391. "Can't set file pointer on non seekable file: %s\n",F->fname);
  1392. }
  1393. else
  1394. {
  1395. char buf[0x8000] = {0};
  1396. size_t fill_size = offset - *cur_offset;
  1397. while (fill_size)
  1398. {
  1399. const size_t write_size = fill_size < sizeof(buf) ? fill_size : sizeof(buf);
  1400. noPRINT("FILL/ZERO %zx/%zx\n",write_size,fill_size);
  1401. const size_t written = fwrite(buf,1,write_size,F->f);
  1402. if ( written != write_size )
  1403. return ERROR1(ERR_WRITE_FAILED,"Writing %zu NULL bytes failed: %s\n",
  1404. write_size, F->fname );
  1405. fill_size -= write_size;
  1406. }
  1407. }
  1408. }
  1409. noPRINT("WRITE %zx @%zx -> %zx\n",size,offset,offset+size);
  1410. const size_t written = fwrite(data,1,size,F->f);
  1411. if ( written != size )
  1412. return ERROR1(ERR_WRITE_FAILED,"Writing %zu bytes at offset %zu failed: %s\n",
  1413. size,offset,F->fname);
  1414. *cur_offset = offset + size;
  1415. return ERR_OK;
  1416. }
  1417. ///////////////////////////////////////////////////////////////////////////////
  1418. enumError SetFileSize
  1419. (
  1420. File_t * F, // file to write
  1421. size_t * cur_offset, // pointer to current file offset, modified
  1422. size_t size // offset to write
  1423. )
  1424. {
  1425. DASSERT(F);
  1426. DASSERT(F->f);
  1427. DASSERT(cur_offset);
  1428. fflush(F->f);
  1429. if (!F->is_seekable)
  1430. return WriteFileAt(F,cur_offset,size,0,0);
  1431. if (ftruncate(fileno(F->f),size))
  1432. {
  1433. if ( F->max_err < ERR_WRITE_FAILED )
  1434. F->max_err = ERR_WRITE_FAILED;
  1435. return ERROR1( ERR_WRITE_FAILED,
  1436. "Set file size to %llu failed: %s\n",
  1437. (u64)size, F->fname );
  1438. }
  1439. return ERR_OK;
  1440. }
  1441. ///////////////////////////////////////////////////////////////////////////////
  1442. enumError SkipFile
  1443. (
  1444. File_t *F, // file to write
  1445. size_t skip // number of bytes to skip
  1446. )
  1447. {
  1448. DASSERT(F);
  1449. DASSERT(F->f);
  1450. if (!skip)
  1451. return ERR_OK;
  1452. if ( F->is_seekable && !fseek(F->f,skip,SEEK_CUR) )
  1453. return ERR_OK;
  1454. char buf[0x8000];
  1455. while ( skip > 0 )
  1456. {
  1457. size_t max = skip < sizeof(buf) ? skip : sizeof(buf);
  1458. size_t stat = fread(buf,1,max,F->f);
  1459. if (!stat)
  1460. {
  1461. if (feof(F->f))
  1462. break;
  1463. return ERROR1(ERR_READ_FAILED,
  1464. "Reading %zu to skip failed: %s\n",
  1465. skip, F->fname );
  1466. }
  1467. skip -= stat;
  1468. }
  1469. return ERR_OK;
  1470. }
  1471. ///////////////////////////////////////////////////////////////////////////////
  1472. ///////////////////////////////////////////////////////////////////////////////
  1473. enumError RegisterFileError
  1474. (
  1475. File_t * f, // file structure
  1476. enumError new_error // new error code
  1477. )
  1478. {
  1479. DASSERT(f);
  1480. if ( f->max_err < new_error )
  1481. f->max_err = new_error;
  1482. return f->max_err;
  1483. }
  1484. //
  1485. ///////////////////////////////////////////////////////////////////////////////
  1486. ///////////////////////////////////////////////////////////////////////////////
  1487. s64 GetFileSize
  1488. (
  1489. ccp path1, // NULL or part 1 of path
  1490. ccp path2, // NULL or part 2 of path
  1491. s64 not_found_val, // return value if no regular file found
  1492. FileAttrib_t * fatt, // not NULL: store file attributes
  1493. bool fatt_max // true: store max values to 'fatt'
  1494. )
  1495. {
  1496. char pathbuf[PATH_MAX];
  1497. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  1498. TRACE("GetFileSize(%s,%lld)\n",path,not_found_val);
  1499. struct stat st;
  1500. if ( stat(path,&st) || !S_ISREG(st.st_mode) )
  1501. {
  1502. if ( fatt && !fatt_max )
  1503. ClearFileAttrib(fatt);
  1504. return not_found_val;
  1505. }
  1506. if (fatt)
  1507. {
  1508. if (fatt_max)
  1509. MaxFileAttrib(fatt,0,&st);
  1510. else
  1511. SetFileAttrib(fatt,0,&st);
  1512. }
  1513. return st.st_size;
  1514. }
  1515. ///////////////////////////////////////////////////////////////////////////////
  1516. ///////////////////////////////////////////////////////////////////////////////
  1517. enumError OpenReadFile
  1518. (
  1519. ccp path1, // NULL or part #1 of path
  1520. ccp path2, // NULL or part #2 of path
  1521. FileMode_t file_mode, // open modes
  1522. off_t limit, // >0: don't open, if file size > limit
  1523. ccp limit_message, // NULL or a with LF terminated text.
  1524. // It is printed after the message.
  1525. u8 ** res_data, // store alloced data here (always NULL terminated)
  1526. uint * res_size, // not NULL: store data size
  1527. ccp * res_fname, // not NULL: store alloced filename
  1528. FileAttrib_t * res_fatt // not NULL: store file attributes
  1529. )
  1530. {
  1531. DASSERT( path1 || path2 );
  1532. DASSERT(res_data);
  1533. *res_data = 0;
  1534. if (res_size)
  1535. *res_size = 0;
  1536. if (res_fname)
  1537. *res_fname = 0;
  1538. if (res_fatt)
  1539. memset(res_fatt,0,sizeof(*res_fatt));
  1540. char pathbuf[PATH_MAX];
  1541. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  1542. PRINT("OpenReadFile(%s,%s)\n",path,GetFileModeStatus(0,0,file_mode,0));
  1543. File_t F;
  1544. enumError err = OpenFile(&F,true,path,file_mode,limit,limit_message);
  1545. if (err)
  1546. return err;
  1547. uint data_size = F.st.st_size;
  1548. if (!F.is_seekable)
  1549. data_size = limit;
  1550. noPRINT("OpenReadFile() fsize=%u, seekable=%d\n",data_size,F.is_seekable);
  1551. u8 *data = MALLOC(data_size+1);
  1552. size_t read_size = fread(data,1,data_size,F.f);
  1553. if ( read_size && !F.is_seekable )
  1554. {
  1555. PRINT("non seekable read: %zu\n",read_size);
  1556. data_size = read_size;
  1557. data = REALLOC(data,data_size+1);
  1558. }
  1559. data[data_size] = 0; // termination for text files
  1560. if ( read_size != data_size )
  1561. {
  1562. if (!(file_mode&FM_SILENT))
  1563. FILEERROR1(&F,ERR_READ_FAILED,"Read failed: %s\n",path);
  1564. ResetFile(&F,false);
  1565. FREE(data);
  1566. return ERR_READ_FAILED;
  1567. }
  1568. *res_data = data;
  1569. if (res_size)
  1570. *res_size = data_size;
  1571. if (res_fname)
  1572. {
  1573. *res_fname = F.fname;
  1574. F.fname = EmptyString;
  1575. }
  1576. if (res_fatt)
  1577. SetFileAttrib(res_fatt,&F.fatt,0);
  1578. ResetFile(&F,false);
  1579. return ERR_OK;
  1580. }
  1581. ///////////////////////////////////////////////////////////////////////////////
  1582. enumError LoadFile
  1583. (
  1584. ccp path1, // NULL or part #1 of path
  1585. ccp path2, // NULL or part #2 of path
  1586. size_t skip, // skip num of bytes before reading
  1587. void * data, // destination buffer, size = 'size'
  1588. size_t size, // size to read
  1589. int silent, // 0: print all error messages
  1590. // 1: suppress file size warning
  1591. // 2: suppress all error messages
  1592. FileAttrib_t * fatt, // not NULL: store file attributes
  1593. bool fatt_max // true: store *max* values to 'fatt'
  1594. )
  1595. {
  1596. DASSERT(data);
  1597. if (!size)
  1598. return ERR_OK;
  1599. char pathbuf[PATH_MAX];
  1600. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  1601. TRACE("LoadFile(%s,%zu,%zu,%d)\n",path,skip,size,silent);
  1602. if (fatt)
  1603. {
  1604. struct stat st;
  1605. if (!stat(path,&st))
  1606. UseFileAttrib(fatt,0,&st,fatt_max);
  1607. }
  1608. FILE * f = fopen(path,"rb");
  1609. if (!f)
  1610. {
  1611. if (silent<2)
  1612. ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path);
  1613. return ERR_CANT_OPEN;
  1614. }
  1615. if ( skip > 0 )
  1616. fseek(f,skip,SEEK_SET);
  1617. size_t read_stat = fread(data,1,size,f);
  1618. fclose(f);
  1619. if ( read_stat == size )
  1620. return ERR_OK;
  1621. enumError err = ERR_READ_FAILED;
  1622. if ( silent == 1 && size > 0 )
  1623. err = ERR_WARNING;
  1624. else if ( silent < 2 )
  1625. ERROR1(ERR_READ_FAILED,"Can't read file: %s\n",path);
  1626. noPRINT("D=%p, s=%zu/%zu: %s\n",data,read_stat,size,path);
  1627. if ( read_stat >= 0 && read_stat < size )
  1628. memset((char*)data+read_stat,0,size-read_stat);
  1629. return err;
  1630. }
  1631. ///////////////////////////////////////////////////////////////////////////////
  1632. enumError LoadFileAlloc
  1633. (
  1634. ccp path1, // NULL or part #1 of path
  1635. ccp path2, // NULL or part #2 of path
  1636. size_t skip, // skip num of bytes before reading
  1637. u8 ** res_data, // result: free existing data, store ptr to alloc data
  1638. // always one more byte is alloced and set to NULL
  1639. size_t * res_size, // result: size of 'res_data'
  1640. size_t max_size, // >0: a file size limit
  1641. int silent, // 0: print all error messages
  1642. // 1: suppress file size warning
  1643. // 2: suppress all error messages
  1644. FileAttrib_t * fatt, // not NULL: store file attributes
  1645. bool fatt_max // true: store max values to 'fatt'
  1646. )
  1647. {
  1648. DASSERT(res_data);
  1649. DASSERT(res_size);
  1650. //--- clear return data
  1651. if (res_data)
  1652. *res_data = 0;
  1653. if (res_size)
  1654. *res_size = 0;
  1655. if ( fatt && !fatt_max )
  1656. memset(fatt,0,sizeof(*fatt));
  1657. //--- get file size
  1658. char pathbuf[PATH_MAX];
  1659. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  1660. const s64 size = GetFileSize(path,0,-1,0,0);
  1661. if ( size == -1 )
  1662. {
  1663. if ( silent < 2 )
  1664. ERROR0(ERR_CANT_OPEN,"File not found: %s\n",path);
  1665. return ERR_CANT_OPEN;
  1666. }
  1667. if ( max_size && size > max_size )
  1668. {
  1669. if ( silent < 2 )
  1670. ERROR0(ERR_INVALID_FILE,"File too large: %s\n",path);
  1671. return ERR_INVALID_FILE;
  1672. }
  1673. u8 *data = MALLOC(size+1);
  1674. enumError err = LoadFile(path,0,skip,data,size,silent,fatt,fatt_max);
  1675. if (err)
  1676. {
  1677. FREE(data);
  1678. return err;
  1679. }
  1680. if (res_data)
  1681. {
  1682. data[size] = 0;
  1683. *res_data = data;
  1684. }
  1685. else
  1686. FREE(data);
  1687. if (res_size)
  1688. *res_size = size;
  1689. return err;
  1690. }
  1691. ///////////////////////////////////////////////////////////////////////////////
  1692. enumError SaveFile
  1693. (
  1694. ccp path1, // NULL or part #1 of path
  1695. ccp path2, // NULL or part #2 of path
  1696. FileMode_t file_mode, // open modes
  1697. const void * data, // data to write
  1698. uint data_size, // size of 'data'
  1699. FileAttrib_t * fatt // not NULL: set timestamps using this attrib
  1700. )
  1701. {
  1702. DASSERT(data);
  1703. File_t F;
  1704. enumError err = OpenWriteFile(&F,true,path1,path2,file_mode,data,data_size);
  1705. if ( err == ERR_OK )
  1706. {
  1707. if (fatt)
  1708. memcpy(&F.fatt,fatt,sizeof(F.fatt));
  1709. err = ResetFile(&F,true);
  1710. }
  1711. return err;
  1712. }
  1713. ///////////////////////////////////////////////////////////////////////////////
  1714. enumError OpenWriteFile
  1715. (
  1716. File_t * f, // file structure
  1717. bool initialize, // true: initialize 'f'
  1718. ccp path1, // NULL or part #1 of path
  1719. ccp path2, // NULL or part #2 of path
  1720. FileMode_t file_mode, // open modes
  1721. const void * data, // data to write
  1722. uint data_size // size of 'data'
  1723. )
  1724. {
  1725. DASSERT(f);
  1726. DASSERT(data);
  1727. char pathbuf[PATH_MAX];
  1728. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  1729. TRACE("SaveFile(%s) %s\n",GetFileModeStatus(0,0,file_mode,0),path);
  1730. enumError err = CreateFile(f,true,path,file_mode);
  1731. if ( err || !f->f )
  1732. {
  1733. ResetFile(f,0);
  1734. return err;
  1735. }
  1736. const uint written = fwrite(data,1,data_size,f->f);
  1737. if ( written != data_size )
  1738. {
  1739. ERROR1(ERR_WRITE_FAILED,"Write to file failed: %s\n",path);
  1740. ResetFile(f,0);
  1741. return ERR_WRITE_FAILED;
  1742. }
  1743. return ERR_OK;
  1744. };
  1745. ///////////////////////////////////////////////////////////////////////////////
  1746. ///////////////////////////////////////////////////////////////////////////////
  1747. static int CompareInt ( const int * a, const int *b )
  1748. {
  1749. DASSERT( a && b );
  1750. return *a < *b ? -1 : *a > *b;
  1751. }
  1752. ///////////////////////////////////////////////////////////////////////////////
  1753. uint CloseAllExcept
  1754. (
  1755. int *list, // exception list of unordered FD, max contain -1
  1756. uint n_list, // number of elements in 'list'
  1757. int min, // close all until and including this fd
  1758. int add // if a FD was found, incremet 'min' to 'fd+add'
  1759. )
  1760. {
  1761. //--- use a private copy of the except list
  1762. enum { LOCAL_LIST_SIZE = 1000 };
  1763. int local_list[LOCAL_LIST_SIZE];
  1764. int *except_list = n_list < LOCAL_LIST_SIZE
  1765. ? local_list : MALLOC((n_list+1)*sizeof(*except_list));
  1766. int *ptr = except_list;
  1767. while ( n_list-- > 0 )
  1768. {
  1769. const int fd = *list++;
  1770. if ( fd != -1 )
  1771. *ptr++ = fd;
  1772. }
  1773. *ptr = -1;
  1774. //--- ... and sort it
  1775. n_list = ptr - except_list;
  1776. if ( n_list > 1 )
  1777. qsort(except_list,n_list,sizeof(*except_list),(qsort_func)CompareInt);
  1778. //--- adjust 'min' and 'add'
  1779. if ( add <= 0 )
  1780. add = 1;
  1781. const int temp = ( n_list ? except_list[n_list-1] : 0 ) + add;
  1782. if ( min < temp )
  1783. min = temp;
  1784. //--- close files
  1785. ptr = except_list;
  1786. int *end = ptr + n_list;
  1787. int i, count = 0;
  1788. for ( i = 0; i <= min; i++ )
  1789. {
  1790. while ( ptr < end && *ptr < i )
  1791. ptr++;
  1792. if ( i != *ptr && !close(i) )
  1793. {
  1794. count++;
  1795. const int temp = i + add;
  1796. if ( min < temp )
  1797. min = temp;
  1798. //printf("closed: %d/%d\n",i,min);
  1799. }
  1800. }
  1801. if ( except_list != local_list )
  1802. FREE(local_list);
  1803. return count;
  1804. }
  1805. //
  1806. ///////////////////////////////////////////////////////////////////////////////
  1807. /////////////// CopyFile*(), TransferFile() ///////////////
  1808. ///////////////////////////////////////////////////////////////////////////////
  1809. #undef LOGPRINT
  1810. //#define LOGPRINT PRINT
  1811. #define LOGPRINT noPRINT
  1812. ///////////////////////////////////////////////////////////////////////////////
  1813. bool IsSameFile ( ccp path1, ccp path2 )
  1814. {
  1815. if (!strcmp(path1,path2))
  1816. return true;
  1817. struct stat st1, st2;
  1818. if ( stat(path1,&st1) || stat(path2,&st2) )
  1819. return false;
  1820. return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
  1821. }
  1822. ///////////////////////////////////////////////////////////////////////////////
  1823. enumError CopyFileHelper
  1824. (
  1825. ccp src, // source path
  1826. ccp dest, // destination path
  1827. int open_flags, // flags for open()
  1828. mode_t open_mode, // mode for open()
  1829. uint temp_and_move // >0: ignore open_flags,
  1830. // create temp, then move temp to dest
  1831. )
  1832. {
  1833. LOGPRINT("CopyFileHelper(%x,%x,%d) %s -> %s\n",
  1834. open_flags, open_mode, temp_and_move>0, src, dest );
  1835. int fd_dest, fd_src;
  1836. char buf[16*1024], temp[PATH_MAX];
  1837. if ( !dest || !src || !*dest || !*src )
  1838. return ERR_MISSING_PARAM;
  1839. if (IsSameFile(src,dest))
  1840. return ERR_OK;
  1841. fd_src = open(src,O_RDONLY);
  1842. if ( fd_src < 0 )
  1843. return ERR_CANT_OPEN;
  1844. if (temp_and_move)
  1845. {
  1846. StringCat2S(temp,sizeof(temp),dest,"XXXXXX");
  1847. fd_dest = mkstemp(temp);
  1848. LOGPRINT("CopyFileHelper(TEMP) %s -> %d\n",temp,fd_dest);
  1849. }
  1850. else
  1851. fd_dest = open(dest,open_mode,open_mode);
  1852. if ( fd_dest < 0 )
  1853. goto error;
  1854. ssize_t nread;
  1855. while ( nread = read(fd_src,buf,sizeof(buf)), nread > 0 )
  1856. {
  1857. char *ptr = buf;
  1858. do
  1859. {
  1860. ssize_t nwritten = write(fd_dest,ptr,nread);
  1861. if ( nwritten >= 0 )
  1862. {
  1863. nread -= nwritten;
  1864. ptr += nwritten;
  1865. }
  1866. else if ( errno != EINTR )
  1867. goto error;
  1868. }
  1869. while (nread > 0);
  1870. }
  1871. if ( nread == 0 )
  1872. {
  1873. if ( close(fd_dest) < 0 )
  1874. {
  1875. fd_dest = -1;
  1876. goto error;
  1877. }
  1878. if (temp_and_move)
  1879. {
  1880. chmod(temp,open_mode);
  1881. if (rename(temp,dest))
  1882. {
  1883. unlink(temp);
  1884. goto error;
  1885. }
  1886. }
  1887. close(fd_src);
  1888. return ERR_OK;
  1889. }
  1890. error:;
  1891. int saved_errno = errno;
  1892. close(fd_src);
  1893. if ( fd_dest >= 0 )
  1894. {
  1895. close(fd_dest);
  1896. if (temp_and_move)
  1897. unlink(temp);
  1898. }
  1899. errno = saved_errno;
  1900. return ERR_WRITE_FAILED;
  1901. }
  1902. //-----------------------------------------------------------------------------
  1903. enumError CopyFile
  1904. (
  1905. ccp src, // source path
  1906. ccp dest, // destination path
  1907. mode_t open_mode // mode for open()
  1908. )
  1909. {
  1910. return CopyFileHelper(src,dest,O_WRONLY|O_TRUNC|O_EXCL,open_mode,false);
  1911. }
  1912. //-----------------------------------------------------------------------------
  1913. enumError CopyFileCreate
  1914. (
  1915. ccp src, // source path
  1916. ccp dest, // destination path
  1917. mode_t open_mode // mode for open()
  1918. )
  1919. {
  1920. return CopyFileHelper(src,dest,O_WRONLY|O_CREAT|O_EXCL,open_mode,false);
  1921. }
  1922. //-----------------------------------------------------------------------------
  1923. enumError CopyFileTemp
  1924. (
  1925. ccp src, // source path
  1926. ccp dest, // destination path
  1927. mode_t open_mode // mode for open()
  1928. )
  1929. {
  1930. return CopyFileHelper(src,dest,0,open_mode,true);
  1931. }
  1932. ///////////////////////////////////////////////////////////////////////////////
  1933. enumError TransferFile
  1934. (
  1935. LogFile_t *log, // not NULL: log activities
  1936. ccp dest, // destination path
  1937. ccp src, // source path
  1938. TransferMode_t tfer_mode, // transfer mode
  1939. mode_t open_mode // mode for CopyFile*() -> open()
  1940. )
  1941. {
  1942. if ( !dest || !*dest || !src || !*src )
  1943. return ERR_MISSING_PARAM;
  1944. if (IsSameFile(src,dest))
  1945. return ERR_NOTHING_TO_DO;
  1946. const bool testmode = ( tfer_mode & TFMD_F_TEST ) != 0;
  1947. if ( tfer_mode & TFMD_J_MOVE1 )
  1948. {
  1949. LOGPRINT("TransferFile(MOVE1) %s -> %s\n",src,dest);
  1950. struct stat st;
  1951. if ( !stat(src,&st) && st.st_nlink == 1
  1952. && ( testmode || !rename(src,dest) ))
  1953. {
  1954. if (log)
  1955. PrintLogFile(log, "%s:\n\t< %s\n\t> %s\n",
  1956. testmode ? "Would MOVE" : "MOVED", src, dest );
  1957. return ERR_OK;
  1958. }
  1959. }
  1960. else if ( tfer_mode & TFMD_J_MOVE )
  1961. {
  1962. LOGPRINT("TransferFile(MOVE) %s -> %s\n",src,dest);
  1963. if ( testmode || !rename(src,dest) )
  1964. {
  1965. if (log)
  1966. PrintLogFile(log, "%s:\n\t< %s\n\t> %s\n",
  1967. testmode ? "Would MOVE" : "MOVED", src, dest );
  1968. return ERR_OK;
  1969. }
  1970. }
  1971. if ( tfer_mode & TFMD_J_LINK )
  1972. {
  1973. LOGPRINT("TransferFile(RM_DEST) %s\n",dest);
  1974. if ( tfer_mode & TFMD_J_RM_DEST && !testmode )
  1975. unlink(dest);
  1976. LOGPRINT("TransferFile(LINK) %s -> %s\n",src,dest);
  1977. if ( testmode || !link(src,dest) )
  1978. {
  1979. if (log)
  1980. PrintLogFile(log, "%s:\n\t< %s\n\t> %s\n",
  1981. testmode ? "Would LINK" : "LINKED", src, dest );
  1982. return ERR_OK;
  1983. }
  1984. }
  1985. if ( tfer_mode & TFMD_J_COPY )
  1986. {
  1987. LOGPRINT("TransferFile(COPY) %s -> %s\n",src,dest);
  1988. if (testmode)
  1989. {
  1990. if (log)
  1991. {
  1992. if ( tfer_mode & TFMD_J_RM_DEST )
  1993. PrintLogFile(log, "Would REMOVE DEST: %s\n",dest);
  1994. PrintLogFile(log, "Would COPY:\n\t< %s\n\t> %s\n",src,dest);
  1995. if ( tfer_mode & TFMD_J_RM_SRC )
  1996. PrintLogFile(log, "Would REMOVE SRC: %s\n",src);
  1997. }
  1998. return ERR_OK;
  1999. }
  2000. else
  2001. {
  2002. enumError err = tfer_mode & TFMD_J_RM_DEST
  2003. ? CopyFileTemp(src,dest,open_mode)
  2004. : CopyFile(src,dest,open_mode);
  2005. if ( err == ERR_OK )
  2006. {
  2007. if (log)
  2008. {
  2009. if ( tfer_mode & TFMD_J_RM_DEST )
  2010. PrintLogFile(log, "REMOVE DEST: %s\n",dest);
  2011. PrintLogFile(log, "COPY:\n\t< %s\n\t> %s\n",src,dest);
  2012. }
  2013. if ( tfer_mode & TFMD_J_RM_SRC )
  2014. {
  2015. PRINT("TransferFile(RM_SRC) %s",src);
  2016. unlink(src);
  2017. if (log)
  2018. PrintLogFile(log, "REMOVE SRC: %s\n",src);
  2019. }
  2020. return ERR_OK;
  2021. }
  2022. }
  2023. }
  2024. return ERR_ERROR;
  2025. }
  2026. #undef LOGPRINT
  2027. //
  2028. ///////////////////////////////////////////////////////////////////////////////
  2029. /////////////// struct MemFile_t ///////////////
  2030. ///////////////////////////////////////////////////////////////////////////////
  2031. bool IsValidMemFile ( MemFile_t *mf )
  2032. {
  2033. return mf
  2034. && ( mf->data || !mf->size )
  2035. && mf->size >= mf->fend
  2036. && mf->fend >= mf->fpos;
  2037. }
  2038. ///////////////////////////////////////////////////////////////////////////////
  2039. void DumpMemFile
  2040. (
  2041. FILE *f, // valid output stream
  2042. int indent, // indent of output
  2043. MemFile_t *mf // valid MemFile_t data
  2044. )
  2045. {
  2046. DASSERT(f);
  2047. DASSERT(mf);
  2048. indent = NormalizeIndent(indent);
  2049. fprintf(f,"%*sfpos=0x%x/0x%x, size=0x%x/0x%x, zero=%d, valid=%d\n",
  2050. indent,"",
  2051. mf->fpos, mf->fend, mf->size, mf->max_size,
  2052. mf->zero_extend, IsValidMemFile(mf) );
  2053. }
  2054. ///////////////////////////////////////////////////////////////////////////////
  2055. bool AssertMemFile
  2056. (
  2057. MemFile_t *mf // valid MemFile_t data
  2058. )
  2059. {
  2060. if (IsValidMemFile(mf))
  2061. return true;
  2062. if (mf)
  2063. DumpMemFile(stderr,0,mf);
  2064. return false;
  2065. }
  2066. ///////////////////////////////////////////////////////////////////////////////
  2067. ///////////////////////////////////////////////////////////////////////////////
  2068. u8 * GetDataMemFile
  2069. (
  2070. // return NULL on err or a pointer to the data posisiton.
  2071. MemFile_t *mf, // valid MemFile_t data
  2072. uint fpos, // fpos to read from
  2073. uint size // needed data size
  2074. )
  2075. {
  2076. DASSERT(mf);
  2077. uint end = fpos + size;
  2078. if ( end < fpos || end < size )
  2079. return 0; // integer overflow
  2080. if ( end > mf->max_size )
  2081. return 0; // too large
  2082. if ( end > mf->size )
  2083. {
  2084. // grow buffer
  2085. uint new_size = ALIGN32( end + end/10 + 0x1000, 0x1000 );
  2086. if ( new_size > mf->max_size )
  2087. new_size = mf->max_size;
  2088. DASSERT( new_size >= end );
  2089. mf->data = REALLOC(mf->data,new_size);
  2090. DASSERT(mf->data);
  2091. memset(mf->data+mf->size,0,new_size-mf->size);
  2092. mf->size = new_size;
  2093. }
  2094. DASSERT( end <= mf->size );
  2095. mf->fpos = fpos;
  2096. if ( mf->fend < end )
  2097. mf->fend = end;
  2098. DASSERT(AssertMemFile(mf));
  2099. return mf->data + fpos;
  2100. }
  2101. ///////////////////////////////////////////////////////////////////////////////
  2102. ///////////////////////////////////////////////////////////////////////////////
  2103. void InitializeMemFile
  2104. (
  2105. MemFile_t *mf, // structure to initialize
  2106. uint start_size, // >0: alloc this for data
  2107. uint max_size // >0: file never grows above 'max_size'
  2108. )
  2109. {
  2110. DASSERT(mf);
  2111. memset(mf,0,sizeof(*mf));
  2112. mf->max_size = max_size ? max_size : M1(mf->max_size);
  2113. if ( start_size > 0 )
  2114. {
  2115. GetDataMemFile( mf, 0, start_size < mf->max_size ? start_size : mf->max_size );
  2116. mf->fend = 0;
  2117. }
  2118. }
  2119. ///////////////////////////////////////////////////////////////////////////////
  2120. void ResetMemFile
  2121. (
  2122. MemFile_t *mf // valid MemFile_t data
  2123. )
  2124. {
  2125. DASSERT(mf);
  2126. FREE(mf->data);
  2127. if (mf->err_name_alloced)
  2128. FreeString(mf->err_name);
  2129. InitializeMemFile(mf,0,mf->max_size);
  2130. }
  2131. ///////////////////////////////////////////////////////////////////////////////
  2132. ///////////////////////////////////////////////////////////////////////////////
  2133. enumError WriteMemFileAt
  2134. (
  2135. MemFile_t *mf, // valid MemFile_t data
  2136. uint fpos, // fpos to write
  2137. const void *data, // data to write
  2138. uint size // size to write
  2139. )
  2140. {
  2141. DASSERT(mf);
  2142. DASSERT(data||!size);
  2143. u8 *dest = GetDataMemFile(mf,fpos,size);
  2144. if (!dest)
  2145. {
  2146. if (mf->err_name)
  2147. ERROR0(ERR_WRITE_FAILED,"Memory file is full: %s\n",mf->err_name);
  2148. return ERR_WRITE_FAILED;
  2149. }
  2150. mf->eof = false;
  2151. if (size)
  2152. memcpy(dest,data,size);
  2153. mf->fpos = fpos + size;
  2154. if ( mf->fend < mf->fpos )
  2155. mf->fend = mf->fpos;
  2156. DASSERT(AssertMemFile(mf));
  2157. return ERR_OK;
  2158. }
  2159. ///////////////////////////////////////////////////////////////////////////////
  2160. uint ReadMemFileAt
  2161. (
  2162. MemFile_t *mf, // valid MemFile_t data
  2163. uint fpos, // fpos to read from
  2164. void *buf, // destination buffer
  2165. uint size // size to read
  2166. )
  2167. {
  2168. DASSERT(mf);
  2169. DASSERT(buf);
  2170. uint read_count = 0;
  2171. u8 *dest = buf;
  2172. if ( fpos < mf->fend )
  2173. {
  2174. read_count = mf->fend - fpos;
  2175. if ( read_count > size )
  2176. read_count = size;
  2177. memcpy(dest,mf->data+fpos,read_count);
  2178. mf->fpos = fpos + read_count;
  2179. dest += read_count;
  2180. size -= read_count;
  2181. }
  2182. mf->eof = size > 0;
  2183. if ( mf->eof && mf->zero_extend )
  2184. {
  2185. memset(dest,0,size);
  2186. mf->fpos += size;
  2187. read_count += size;
  2188. }
  2189. return read_count;
  2190. }
  2191. ///////////////////////////////////////////////////////////////////////////////
  2192. ///////////////////////////////////////////////////////////////////////////////
  2193. enumError LoadMemFile
  2194. (
  2195. MemFile_t *mf, // MemFile_t data
  2196. bool init_mf, // true: initialize 'mf' first
  2197. ccp path1, // NULL or part #1 of path
  2198. ccp path2, // NULL or part #2 of path
  2199. size_t skip, // >0: skip num of bytes before reading
  2200. size_t size, // >0: max size to read; 0: read all (remaining)
  2201. bool silent // true: suppress error messages
  2202. )
  2203. {
  2204. DASSERT(mf);
  2205. if (init_mf)
  2206. InitializeMemFile(mf,0,0);
  2207. else
  2208. ResetMemFile(mf);
  2209. char pathbuf[PATH_MAX];
  2210. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  2211. PRINT("LoadMemFile(silent=%d) %s\n",silent,path);
  2212. struct stat st;
  2213. if (stat(path,&st))
  2214. {
  2215. if (!silent)
  2216. ERROR1(ERR_CANT_OPEN,"Can't get file status: %s\n",path);
  2217. return ERR_CANT_OPEN;
  2218. }
  2219. UseFileAttrib(&mf->fatt,0,&st,0);
  2220. if ( skip >= st.st_size )
  2221. return ERR_OK;
  2222. const uint max_size = st.st_size - skip;
  2223. if ( !size || size > max_size )
  2224. size = max_size;
  2225. u8 *dest = GetDataMemFile(mf,0,size);
  2226. if (!dest)
  2227. {
  2228. if (!silent)
  2229. ERROR0(ERR_READ_FAILED,"Memory file is too small: %s\n",path);
  2230. return ERR_READ_FAILED;
  2231. }
  2232. FILE * f = fopen(path,"rb");
  2233. if (!f)
  2234. {
  2235. if (!silent)
  2236. ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path);
  2237. return ERR_CANT_OPEN;
  2238. }
  2239. if ( skip > 0 )
  2240. fseek(f,skip,SEEK_SET);
  2241. mf->fend = fread(dest,1,size,f);
  2242. fclose(f);
  2243. return ERR_OK;
  2244. }
  2245. ///////////////////////////////////////////////////////////////////////////////
  2246. enumError SaveMemFile
  2247. (
  2248. MemFile_t *mf, // MemFile_t data
  2249. ccp path1, // NULL or part #1 of path
  2250. ccp path2, // NULL or part #2 of path
  2251. FileMode_t fmode, // mode for file creation
  2252. bool sparse // true: enable sparse support
  2253. )
  2254. {
  2255. DASSERT(mf);
  2256. char pathbuf[PATH_MAX];
  2257. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  2258. PRINT("SaveMemFile(fmode=0x%x,sparse=%d) %u/%u/%u %s\n",
  2259. fmode, sparse, mf->fpos, mf->fend, mf->size, path );
  2260. File_t F;
  2261. enumError err = CreateFile(&F,true,path,fmode);
  2262. if ( err || !F.f )
  2263. {
  2264. ResetFile(&F,0);
  2265. return err;
  2266. }
  2267. if (!sparse)
  2268. {
  2269. const uint written = fwrite(mf->data,1,mf->fend,F.f);
  2270. if ( written != mf->fend )
  2271. {
  2272. if (!(fmode&FM_SILENT))
  2273. ERROR1(ERR_WRITE_FAILED,"Write to file failed: %s\n",path);
  2274. err_exit:
  2275. ResetFile(&F,0);
  2276. return ERR_WRITE_FAILED;
  2277. }
  2278. }
  2279. else
  2280. {
  2281. enum { BLOCK_SIZE = 512 };
  2282. char zero[BLOCK_SIZE];
  2283. memset(zero,0,sizeof(zero));
  2284. size_t cur_offset = 0;
  2285. uint off = 0;
  2286. while ( off < mf->fend )
  2287. {
  2288. //--- skip sparse blocks
  2289. while ( off < mf->fend )
  2290. {
  2291. uint max = mf->fend - off;
  2292. if ( max > BLOCK_SIZE )
  2293. max = BLOCK_SIZE;
  2294. if (memcmp(mf->data+off,zero,max))
  2295. break;
  2296. off += max;
  2297. }
  2298. //--- search next sparse block
  2299. uint start = off;
  2300. while ( off < mf->fend )
  2301. {
  2302. uint max = mf->fend - off;
  2303. if ( max > BLOCK_SIZE )
  2304. max = BLOCK_SIZE;
  2305. if (!memcmp(mf->data+off,zero,max))
  2306. break;
  2307. off += max;
  2308. }
  2309. err = WriteFileAt(&F,&cur_offset,start,mf->data+start,off-start);
  2310. if (err)
  2311. goto err_exit;
  2312. }
  2313. err = SetFileSize(&F,&cur_offset,mf->fend);
  2314. if (err)
  2315. goto err_exit;
  2316. }
  2317. return ResetFile(&F,true);
  2318. }
  2319. //
  2320. ///////////////////////////////////////////////////////////////////////////////
  2321. /////////////// TraceLog_t ///////////////
  2322. ///////////////////////////////////////////////////////////////////////////////
  2323. bool OpenTraceLog ( TraceLog_t *tl )
  2324. {
  2325. if ( !tl || tl->level < 0 )
  2326. return false;
  2327. if ( !tl->log && tl->fname )
  2328. {
  2329. tl->log = fopen(tl->fname,"wb");
  2330. if (tl->log)
  2331. {
  2332. fcntl(fileno(tl->log),F_SETFD,FD_CLOEXEC);
  2333. fprintf(tl->log,"# %s, pid=%d\n",
  2334. PrintTimeByFormat("%F %T %z",time(0)), getpid() );
  2335. }
  2336. }
  2337. if (!tl->log)
  2338. {
  2339. tl->level = -1;
  2340. return false;
  2341. }
  2342. return true;
  2343. }
  2344. ///////////////////////////////////////////////////////////////////////////////
  2345. bool TraceLogText ( TraceLog_t *tl, ccp text )
  2346. {
  2347. if (!OpenTraceLog(tl))
  2348. return false;
  2349. if (text)
  2350. {
  2351. fputs(text,tl->log);
  2352. fflush(tl->log);
  2353. }
  2354. return true;
  2355. }
  2356. ///////////////////////////////////////////////////////////////////////////////
  2357. bool TraceLogPrint ( TraceLog_t *tl, ccp format, ... )
  2358. {
  2359. if (!OpenTraceLog(tl))
  2360. return false;
  2361. if (format)
  2362. {
  2363. va_list arg;
  2364. va_start(arg,format);
  2365. vfprintf(tl->log,format,arg);
  2366. va_end(arg);
  2367. fflush(tl->log);
  2368. }
  2369. return true;
  2370. }
  2371. //
  2372. ///////////////////////////////////////////////////////////////////////////////
  2373. /////////////// struct LineBuffer_t ///////////////
  2374. ///////////////////////////////////////////////////////////////////////////////
  2375. #ifndef __APPLE__
  2376. ///////////////////////////////////////////////////////////////////////////////
  2377. void InitializeLineBuffer ( LineBuffer_t *lb, uint max_buf_size )
  2378. {
  2379. DASSERT(lb);
  2380. memset(lb,0,sizeof(*lb));
  2381. InitializeGrowBuffer(&lb->buf,max_buf_size);
  2382. }
  2383. ///////////////////////////////////////////////////////////////////////////////
  2384. void ResetLineBuffer ( LineBuffer_t *lb )
  2385. {
  2386. DASSERT(lb);
  2387. CloseLineBuffer(lb);
  2388. ResetGrowBuffer(&lb->buf);
  2389. FREE(lb->line);
  2390. lb->line = 0;
  2391. lb->seq_count++;
  2392. }
  2393. ///////////////////////////////////////////////////////////////////////////////
  2394. void ClearLineBuffer ( LineBuffer_t *lb )
  2395. {
  2396. DASSERT(lb);
  2397. ClearGrowBuffer(&lb->buf);
  2398. lb->used_lines = 0;
  2399. lb->seq_count++;
  2400. }
  2401. ///////////////////////////////////////////////////////////////////////////////
  2402. void RedirectLineBuffer
  2403. (
  2404. LineBuffer_t *lb, // valid line buffer
  2405. FILE *f, // output file, if NULL use 'lb->old_fd'
  2406. int max_lines // >=0: max number of redirected lines
  2407. )
  2408. {
  2409. DASSERT(lb);
  2410. if ( lb->buf.used && max_lines )
  2411. {
  2412. if (!f)
  2413. f = lb->old_fp;
  2414. if (f)
  2415. {
  2416. if ( max_lines < 0 )
  2417. fwrite(lb->buf.ptr,1,lb->buf.used,f);
  2418. else
  2419. {
  2420. mem_t *line;
  2421. uint n = GetMemListLineBuffer(lb,&line,-max_lines);
  2422. while ( n-- > 0 )
  2423. {
  2424. fprintf(f,"%.*s\n",line->len-1,line->ptr);
  2425. line++;
  2426. }
  2427. //DEL fprintf(f,"==> %zd %d\n",
  2428. //DEL line[-1].ptr + line[-1].len - (ccp)lb->buf.ptr,
  2429. //DEL lb->buf.used );
  2430. }
  2431. fflush(f);
  2432. if ( f == stdout )
  2433. stdout_seq_count++;
  2434. }
  2435. }
  2436. ClearGrowBuffer(&lb->buf);
  2437. lb->used_lines = 0;
  2438. lb->seq_count++;
  2439. }
  2440. ///////////////////////////////////////////////////////////////////////////////
  2441. ///////////////////////////////////////////////////////////////////////////////
  2442. LineBuffer_t * OpenLineBuffer
  2443. (
  2444. LineBuffer_t *lb, // line buffer; if NULL, malloc() one
  2445. bool init_lb, // true: initialize 'lb'
  2446. FILE **fp_pos, // NULL or external place of the file pointer
  2447. uint max_lines, // max numbers of lines
  2448. uint max_line_size, // >0: max size of a single line
  2449. uint max_buf_size // >0: max total buffer size
  2450. )
  2451. {
  2452. if (!lb)
  2453. lb = MALLOC(sizeof(*lb));
  2454. else if (!init_lb)
  2455. ResetLineBuffer(lb);
  2456. InitializeLineBuffer(lb,max_buf_size);
  2457. DASSERT(lb);
  2458. if (fp_pos)
  2459. {
  2460. lb->old_fp = *fp_pos;
  2461. lb->old_fp_pos = fp_pos;
  2462. }
  2463. lb->max_lines = max_lines > 1 ? max_lines : 1;
  2464. lb->max_line_size = max_line_size;
  2465. lb->line = MALLOC(lb->max_lines*sizeof(*lb->line));
  2466. static cookie_io_functions_t funcs =
  2467. {
  2468. 0, // read
  2469. (cookie_write_function_t*)WriteLineBuffer,
  2470. 0, // seek
  2471. (cookie_close_function_t*)CloseLineBuffer
  2472. };
  2473. lb->fp = fopencookie(lb,"wb",funcs);
  2474. if ( fp_pos && lb->fp )
  2475. *fp_pos = lb->fp;
  2476. return lb;
  2477. }
  2478. ///////////////////////////////////////////////////////////////////////////////
  2479. int CloseLineBuffer ( LineBuffer_t *lb )
  2480. {
  2481. DASSERT(lb);
  2482. if (lb->fp)
  2483. {
  2484. if ( lb->old_fp_pos && *lb->old_fp_pos == lb->fp )
  2485. *lb->old_fp_pos = lb->old_fp;
  2486. lb->fp = 0;
  2487. }
  2488. return 0;
  2489. }
  2490. ///////////////////////////////////////////////////////////////////////////////
  2491. ssize_t WriteLineBuffer ( LineBuffer_t *lb, ccp buf, size_t size )
  2492. {
  2493. DASSERT(lb);
  2494. if ( lb->redirect > 0 )
  2495. {
  2496. if (lb->old_fp)
  2497. {
  2498. ssize_t res = fwrite(buf,1,size,lb->old_fp);
  2499. fflush(lb->old_fp);
  2500. if ( lb->old_fp == stdout )
  2501. stdout_seq_count++;
  2502. return res;
  2503. }
  2504. }
  2505. else if (size)
  2506. {
  2507. if ( lb->open_line && lb->buf.used )
  2508. lb->buf.used--;
  2509. if ( lb->buf.max_size )
  2510. {
  2511. const int max_used = lb->buf.max_size - size - 1;
  2512. if ( max_used <= 0 )
  2513. {
  2514. ClearGrowBuffer(&lb->buf);
  2515. buf -= max_used;
  2516. size += max_used;
  2517. }
  2518. else if ( lb->buf.used > max_used )
  2519. {
  2520. u8 *ptr = lb->buf.ptr;
  2521. u8 *end = ptr + lb->buf.used;
  2522. ptr += lb->buf.used - max_used;
  2523. while ( ptr < end )
  2524. if ( *ptr++ == '\n' )
  2525. break;
  2526. DropGrowBuffer(&lb->buf,ptr-lb->buf.ptr);
  2527. }
  2528. }
  2529. uint new_lines = 0;
  2530. ccp ptr = buf, end = ptr + size;
  2531. while ( ptr < end )
  2532. if ( *ptr++ == '\n' )
  2533. new_lines++;
  2534. lb->line_count += new_lines;
  2535. InsertGrowBuffer(&lb->buf,buf,size);
  2536. lb->open_line = buf[size-1] != '\n';
  2537. if (lb->open_line)
  2538. InsertCharGrowBuffer(&lb->buf,'\n');
  2539. FixMemListLineBuffer(lb,true);
  2540. lb->seq_count++;
  2541. PRINT("WRITE %3zu bytes, is_open: %d, total lines: %d\n",
  2542. size, lb->open_line, lb->used_lines );
  2543. }
  2544. return size;
  2545. }
  2546. ///////////////////////////////////////////////////////////////////////////////
  2547. ///////////////////////////////////////////////////////////////////////////////
  2548. void FixMemListLineBuffer ( LineBuffer_t *lb, bool force )
  2549. {
  2550. DASSERT(lb);
  2551. if (!lb->buf.used)
  2552. lb->used_lines = 0;
  2553. else if ( force
  2554. || lb->prev_buf_ptr != lb->buf.ptr
  2555. || lb->prev_seq_count != lb->seq_count )
  2556. {
  2557. u8 *beg = lb->buf.ptr;
  2558. u8 *ptr = beg + lb->buf.used;
  2559. mem_t *dest = lb->line + lb->max_lines;
  2560. while ( beg < ptr && dest > lb->line )
  2561. {
  2562. u8* end = ptr--;
  2563. while ( beg < ptr && ptr[-1] != '\n' )
  2564. ptr--;
  2565. dest--;
  2566. dest->ptr = (ccp)ptr;
  2567. dest->len = end - ptr;
  2568. }
  2569. lb->used_lines = lb->line + lb->max_lines - dest;
  2570. if ( dest > lb->line )
  2571. memmove(lb->line,dest,lb->used_lines*sizeof(*dest));
  2572. if ( ptr > beg )
  2573. {
  2574. // don't use DropGrowBuffer() to hold buf stable
  2575. lb->buf.used -= ptr - beg;
  2576. lb->buf.ptr = ptr;
  2577. }
  2578. lb->prev_buf_ptr = lb->buf.ptr;
  2579. lb->prev_seq_count = lb->seq_count;
  2580. }
  2581. }
  2582. ///////////////////////////////////////////////////////////////////////////////
  2583. uint GetMemListLineBuffer
  2584. (
  2585. // returns the number of elements
  2586. LineBuffer_t *lb, // line buffer; if NULL, malloc() one
  2587. mem_t **res, // store pointer to first element here
  2588. int max_lines // !0: limit lines, <0: count from end
  2589. )
  2590. {
  2591. DASSERT(lb);
  2592. if ( lb->prev_buf_ptr != lb->buf.ptr )
  2593. FixMemListLineBuffer(lb,false);
  2594. uint pos = 0;
  2595. if (!max_lines)
  2596. max_lines = lb->used_lines;
  2597. else if ( max_lines < 0 )
  2598. {
  2599. max_lines = -max_lines;
  2600. if ( max_lines > lb->used_lines )
  2601. max_lines = lb->used_lines;
  2602. pos = lb->used_lines - max_lines;
  2603. }
  2604. else if ( max_lines > lb->used_lines )
  2605. max_lines = lb->used_lines;
  2606. if (res)
  2607. *res = lb->line + pos;
  2608. return max_lines;
  2609. }
  2610. ///////////////////////////////////////////////////////////////////////////////
  2611. uint PrintLineBuffer
  2612. (
  2613. // returns number of written lines
  2614. FILE *f, // output stream
  2615. int indent, // indention
  2616. LineBuffer_t *lb, // line buffer; if NULL, malloc() one
  2617. int max_lines, // !0: limit lines, <0: count from end
  2618. ccp open_line // not NULL: append this text for an open line
  2619. )
  2620. {
  2621. DASSERT(f);
  2622. DASSERT(lb);
  2623. indent = NormalizeIndent(indent);
  2624. mem_t *line;
  2625. uint i, n = GetMemListLineBuffer(lb,&line,max_lines);
  2626. for ( i = 1; i <= n; i++, line++ )
  2627. fprintf(f,"%*s%.*s%s\n",
  2628. indent,"",
  2629. line->len-1, line->ptr,
  2630. open_line && i == n && lb->open_line ? open_line : "" );
  2631. return n;
  2632. }
  2633. ///////////////////////////////////////////////////////////////////////////////
  2634. #endif // !__APPLE__
  2635. ///////////////////////////////////////////////////////////////////////////////
  2636. //
  2637. ///////////////////////////////////////////////////////////////////////////////
  2638. /////////////// file helpers ///////////////
  2639. ///////////////////////////////////////////////////////////////////////////////
  2640. const char inode_type_char[INTY__N+1] = "?-+SLFBCDR";
  2641. //-----------------------------------------------------------------------------
  2642. inode_type_t GetInodeType ( int ret_status, mode_t md )
  2643. {
  2644. return ret_status ? INTY_NOTFOUND
  2645. : S_ISREG(md) ? INTY_REG
  2646. : S_ISDIR(md) ? INTY_DIR
  2647. : S_ISCHR(md) ? INTY_CHAR
  2648. : S_ISBLK(md) ? INTY_BLOCK
  2649. : S_ISFIFO(md) ? INTY_FIFO
  2650. : S_ISLNK(md) ? INTY_LINK
  2651. : S_ISSOCK(md) ? INTY_SOCK
  2652. : INTY_AVAIL;
  2653. }
  2654. //-----------------------------------------------------------------------------
  2655. inode_type_t GetInodeTypeByPath ( ccp path, mode_t *mode )
  2656. {
  2657. struct stat st;
  2658. const int ret_status = stat(path,&st);
  2659. if (mode)
  2660. *mode = ret_status ? 0 : st.st_mode;
  2661. return GetInodeType(ret_status,st.st_mode);
  2662. }
  2663. //-----------------------------------------------------------------------------
  2664. inode_type_t GetInodeTypeByFD ( int fd, mode_t *mode )
  2665. {
  2666. struct stat st;
  2667. const int ret_status = fstat(fd,&st);
  2668. if (mode)
  2669. *mode = ret_status ? 0 : st.st_mode;
  2670. return GetInodeType(ret_status,st.st_mode);
  2671. }
  2672. ///////////////////////////////////////////////////////////////////////////////
  2673. int IsDirectory
  2674. (
  2675. // analyse filename (last char == '/') and stat(fname)
  2676. ccp fname, // NULL or path
  2677. int answer_if_empty // answer, if !fname || !*fname
  2678. )
  2679. {
  2680. if ( !fname || !*fname )
  2681. return answer_if_empty;
  2682. if ( *fname == '-' && !fname[1] )
  2683. return false;
  2684. if ( fname[strlen(fname)-1] == '/' )
  2685. return true;
  2686. struct stat st;
  2687. return !stat(fname,&st) && S_ISDIR(st.st_mode);
  2688. }
  2689. ///////////////////////////////////////////////////////////////////////////////
  2690. int ExistDirectory
  2691. (
  2692. // check only real file (stat(fname))
  2693. ccp fname, // NULL or path
  2694. int answer_if_empty // answer, if !fname || !*fname
  2695. )
  2696. {
  2697. if ( !fname || !*fname )
  2698. return answer_if_empty;
  2699. if ( *fname == '-' && !fname[1] )
  2700. return false;
  2701. struct stat st;
  2702. return !stat(fname,&st) && S_ISDIR(st.st_mode);
  2703. }
  2704. ///////////////////////////////////////////////////////////////////////////////
  2705. bool IsSameFilename ( ccp fn1, ccp fn2 )
  2706. {
  2707. if ( !fn1 || !fn2 )
  2708. return false;
  2709. while ( fn1[0] == '.' && fn1[1] == '/' )
  2710. fn1 += 2;
  2711. while ( fn2[0] == '.' && fn2[1] == '/' )
  2712. fn2 += 2;
  2713. return !strcmp(fn1,fn2);
  2714. }
  2715. ///////////////////////////////////////////////////////////////////////////////
  2716. // convert type get by readdir() to of struct stat.st_mode
  2717. uint ConvertDType2STMode ( uint d_type )
  2718. {
  2719. switch (d_type)
  2720. {
  2721. case DT_BLK: return S_IFBLK;
  2722. case DT_CHR: return S_IFCHR;
  2723. case DT_DIR: return S_IFDIR;
  2724. case DT_FIFO: return S_IFIFO;
  2725. case DT_LNK: return S_IFLNK;
  2726. case DT_REG: return S_IFREG;
  2727. case DT_SOCK: return S_IFSOCK;
  2728. }
  2729. return 0;
  2730. }
  2731. ///////////////////////////////////////////////////////////////////////////////
  2732. ///////////////////////////////////////////////////////////////////////////////
  2733. bool enable_config_search_log = false;
  2734. ccp FindConfigFile
  2735. (
  2736. // returns NULL or found file (alloced)
  2737. FindConfigFile_t mode, // bit field: select search destinations
  2738. ccp share_name, // NULL or <SHARE>
  2739. ccp file_name, // NULL or <FILENAME>
  2740. ccp prog_ext // <EXT>, if NULL use '.' + <FILENAME>
  2741. )
  2742. {
  2743. struct stat st;
  2744. char path[PATH_MAX];
  2745. //--- empty strings like NULL
  2746. if ( share_name && !*share_name ) share_name = ProgInfo.progname;
  2747. if ( file_name && !*file_name ) file_name = 0;
  2748. if ( prog_ext && !*prog_ext ) prog_ext = 0;
  2749. if ( file_name && share_name )
  2750. {
  2751. //--- the first attemps need FILENAME
  2752. if ( mode & FCF_HOME )
  2753. {
  2754. ccp home = getenv("HOME");
  2755. if ( home && *home )
  2756. {
  2757. snprintf(path,sizeof(path),"%s/.%s/%s",home,share_name,file_name);
  2758. if ( mode & FCF_F_DEBUG )
  2759. fprintf(stderr,"SEARCH: %s\n",path);
  2760. if ( !stat(path,&st) && S_ISREG(st.st_mode) )
  2761. return STRDUP(path);
  2762. }
  2763. }
  2764. if ( mode & FCF_REL_SHARE )
  2765. {
  2766. ccp pdir = ProgramDirectory();
  2767. if (pdir)
  2768. {
  2769. snprintf(path,sizeof(path),"%s/../share/%s/%s",pdir,share_name,file_name );
  2770. if ( mode & FCF_F_DEBUG )
  2771. fprintf(stderr,"SEARCH: %s\n",path);
  2772. if ( !stat(path,&st) && S_ISREG(st.st_mode) )
  2773. return STRDUP(path);
  2774. }
  2775. }
  2776. if ( mode & FCF_LOC_SHARE )
  2777. {
  2778. snprintf(path,sizeof(path),"/usr/local/share/%s/%s",share_name,file_name);
  2779. if ( mode & FCF_F_DEBUG )
  2780. fprintf(stderr,"SEARCH: %s\n",path);
  2781. if ( !stat(path,&st) && S_ISREG(st.st_mode) )
  2782. return STRDUP(path);
  2783. }
  2784. if ( mode & FCF_USR_SHARE )
  2785. {
  2786. snprintf(path,sizeof(path),"/usr/share/%s/%s",share_name,file_name);
  2787. if ( mode & FCF_F_DEBUG )
  2788. fprintf(stderr,"SEARCH: %s\n",path);
  2789. if ( !stat(path,&st) && S_ISREG(st.st_mode) )
  2790. return STRDUP(path);
  2791. }
  2792. }
  2793. if ( mode & (FCF_PROG_SHARE|FCF_PROG) && ( prog_ext || file_name ) )
  2794. {
  2795. ccp ppath = ProgramPathNoExt();
  2796. if (ppath)
  2797. {
  2798. if ( mode & FCF_PROG_SHARE )
  2799. {
  2800. ccp pdir = ProgramDirectory();
  2801. noPRINT("pdir=%s\n",pdir);
  2802. if (pdir)
  2803. {
  2804. ccp fname = strrchr(ppath,'/');
  2805. fname = fname ? fname+1 : ppath;
  2806. if (prog_ext)
  2807. snprintf(path,sizeof(path),"%s/share/%s%s",
  2808. pdir, fname, prog_ext );
  2809. else
  2810. snprintf(path,sizeof(path),"%s/share/%s.%s",
  2811. pdir, fname, file_name );
  2812. if ( mode & FCF_F_DEBUG )
  2813. fprintf(stderr,"SEARCH: %s\n",path);
  2814. if ( !stat(path,&st) && S_ISREG(st.st_mode) )
  2815. return STRDUP(path);
  2816. }
  2817. }
  2818. if ( mode & FCF_PROG )
  2819. {
  2820. if (prog_ext)
  2821. snprintf(path,sizeof(path),"%s%s",ppath,prog_ext);
  2822. else
  2823. snprintf(path,sizeof(path),"%s.%s",ppath,file_name);
  2824. if ( mode & FCF_F_DEBUG )
  2825. fprintf(stderr,"SEARCH: %s\n",path);
  2826. if ( !stat(path,&st) && S_ISREG(st.st_mode) )
  2827. return STRDUP(path);
  2828. }
  2829. }
  2830. }
  2831. return 0;
  2832. }
  2833. ///////////////////////////////////////////////////////////////////////////////
  2834. int SearchFiles
  2835. (
  2836. ccp path1, // not NULL: part #1 of base path
  2837. ccp path2, // not NULL: part #2 of base path
  2838. ccp match, // not NULL: filter files by MatchPattern()
  2839. // if empty: extract from combined path
  2840. SearchFilesFunc func, // callback function, never NULL
  2841. void *param // third parameter of func()
  2842. )
  2843. {
  2844. DASSERT(func);
  2845. char pathbuf[PATH_MAX];
  2846. ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
  2847. if ( match && !*match )
  2848. {
  2849. match = 0;
  2850. if ( path != pathbuf )
  2851. {
  2852. // path is modified!
  2853. StringCopyS(pathbuf,sizeof(pathbuf),path);
  2854. path = pathbuf;
  2855. }
  2856. char *found = strrchr(pathbuf,'/');
  2857. if (found)
  2858. {
  2859. if ( found == pathbuf )
  2860. path = "/";
  2861. *found++ = 0;
  2862. match = *found ? found : 0;
  2863. }
  2864. else
  2865. {
  2866. match = pathbuf;
  2867. path = ".";
  2868. }
  2869. }
  2870. if ( !path || !*path )
  2871. path = ".";
  2872. DIR *fdir = opendir(path);
  2873. int stat = errno;
  2874. if (fdir)
  2875. {
  2876. for(;;)
  2877. {
  2878. struct dirent *dent = readdir(fdir);
  2879. if (!dent)
  2880. break;
  2881. if ( !match || MatchPatternFull(match,dent->d_name) )
  2882. {
  2883. stat = func(path,dent,param);
  2884. if ( stat < 0 )
  2885. break;
  2886. }
  2887. }
  2888. closedir(fdir);
  2889. }
  2890. return stat;
  2891. }
  2892. ///////////////////////////////////////////////////////////////////////////////
  2893. ///////////////////////////////////////////////////////////////////////////////
  2894. enumError CreatePath
  2895. (
  2896. ccp path, // path to create
  2897. bool is_pure_dir // true: 'path' don't contains a filename part
  2898. )
  2899. {
  2900. TRACE("CreatePath(%s)\n",path);
  2901. char buf[PATH_MAX];
  2902. char *dest = StringCopyS(buf,sizeof(buf)-1,path);
  2903. if (is_pure_dir)
  2904. {
  2905. *dest++ = '/';
  2906. *dest = 0;
  2907. }
  2908. dest = buf;
  2909. for(;;)
  2910. {
  2911. // skip slashes
  2912. while ( *dest == '/' )
  2913. dest++;
  2914. // search end of current directory
  2915. while ( *dest && *dest != '/' )
  2916. dest++;
  2917. if (!*dest)
  2918. break;
  2919. *dest = 0;
  2920. if ( mkdir(buf,0777) && errno != EEXIST && !IsDirectory(buf,0) )
  2921. {
  2922. noTRACE("CREATE-DIR: %s -> err=%d (ENOTDIR=%d)\n",buf,errno,ENOTDIR);
  2923. if ( errno == ENOTDIR )
  2924. {
  2925. while ( dest > buf && *dest != '/' )
  2926. dest--;
  2927. if ( dest > buf )
  2928. *dest = 0;
  2929. }
  2930. return ERROR1( ERR_CANT_CREATE_DIR,
  2931. errno == ENOTDIR
  2932. ? "Not a directory: %s\n"
  2933. : "Can't create directory: %s\n", buf );
  2934. }
  2935. TRACE("CREATE-DIR: %s -> OK\n",buf);
  2936. *dest++ = '/';
  2937. }
  2938. return ERR_OK;
  2939. }
  2940. ///////////////////////////////////////////////////////////////////////////////
  2941. enumError RemoveSource
  2942. (
  2943. ccp fname, // file to remove
  2944. ccp dest_fname, // NULL or dest file name
  2945. // If real paths are same: don't remove
  2946. bool print_log, // true: print a log message
  2947. bool testmode // true: don't remove, log only
  2948. )
  2949. {
  2950. DASSERT(fname);
  2951. if (dest_fname)
  2952. {
  2953. if (!strcmp(fname,dest_fname))
  2954. return ERR_OK;
  2955. char p1[PATH_MAX], p2[PATH_MAX];
  2956. if ( realpath(fname,p1) && realpath(dest_fname,p2) && !strcmp(p1,p2) )
  2957. return ERR_OK;
  2958. }
  2959. if ( print_log || testmode )
  2960. fprintf( stdlog ? stdlog : stdout,
  2961. "%sREMOVE %s\n",
  2962. testmode ? "WOULD " : "", fname );
  2963. return !testmode && unlink(fname)
  2964. ? ERROR1( ERR_CANT_REMOVE, "Can't remove source file: %s\n", fname )
  2965. : ERR_OK;
  2966. }
  2967. ///////////////////////////////////////////////////////////////////////////////
  2968. char * FindFilename
  2969. (
  2970. // Find the last part of the path, trailing '/' are ignored
  2971. // return poiner to found string
  2972. ccp path, // path to analyze, valid string
  2973. uint * result_len // not NULL: return length of found string
  2974. )
  2975. {
  2976. DASSERT(path);
  2977. ccp end = path + strlen(path);
  2978. if ( end > path && end[-1] == '/' )
  2979. end--;
  2980. ccp start = end;
  2981. while ( start > path && start[-1] != '/' )
  2982. start--;
  2983. if (result_len)
  2984. *result_len = end - start;
  2985. return (char*)start;
  2986. }
  2987. ///////////////////////////////////////////////////////////////////////////////
  2988. uint NumberedFilename
  2989. (
  2990. // return used index number or 0, if not modified.
  2991. char * buf, // destination buffer
  2992. size_t bufsize, // size of 'buf'
  2993. ccp source, // source filename, may be part of 'buf'
  2994. // if NULL or empty:
  2995. ccp ext, // not NULL & not empty: file extension ('.ext')
  2996. uint ext_mode, // only relevant, if 'ext' is neither NULL not empty
  2997. // 0: use 'ext' only for detection
  2998. // 1: replace 'source' extension by 'ext'
  2999. // 2: append 'ext' if 'source' differ
  3000. // 3: append 'ext' always
  3001. bool detect_stdio // true: don't number "-"
  3002. )
  3003. {
  3004. //--- analyse stdin/stdout
  3005. char *end = StringCopyS(buf,bufsize,source);
  3006. if ( !source || !*source || detect_stdio && *source == '-' && !source[1] )
  3007. return 0;
  3008. //--- analyse parameter 'ext'
  3009. uint ext_len = 0;
  3010. if ( ext && *ext )
  3011. {
  3012. ext_len = strlen(ext);
  3013. if ( ext_mode >= 3 )
  3014. {
  3015. // append extension always
  3016. StringCopyE(end,buf+bufsize,ext);
  3017. }
  3018. else if ( end - buf <= ext_len || memcmp(end-ext_len,ext,ext_len) )
  3019. {
  3020. // extensions differ
  3021. if (!ext_mode)
  3022. ext_len = 0;
  3023. else if ( ext_mode == 2 )
  3024. StringCopyE(end,buf+bufsize,ext);
  3025. else
  3026. {
  3027. char *slash = strrchr(buf,'/');
  3028. char *point = strrchr(buf,'.');
  3029. if ( point && ( !slash || point > slash ) )
  3030. end = point;
  3031. StringCopyE(end,buf+bufsize,ext);
  3032. }
  3033. }
  3034. }
  3035. //--- does files exist? => if not: return without numbering
  3036. struct stat st;
  3037. if (stat(buf,&st))
  3038. return 0;
  3039. //--- split filename into dir + name + index + ext
  3040. char temp[PATH_MAX];
  3041. ccp dir = buf, search_dir;
  3042. uint dir_len;
  3043. ccp name = strrchr(buf,'/');
  3044. if (name)
  3045. {
  3046. dir_len = name++ - dir;
  3047. StringCopyS(temp,sizeof(temp),buf);
  3048. if ( dir_len < sizeof(temp) )
  3049. temp[dir_len] = 0;
  3050. search_dir = temp;
  3051. dir_len++;
  3052. }
  3053. else
  3054. {
  3055. name = buf;
  3056. dir_len = 0;
  3057. dir = "";
  3058. search_dir = ".";
  3059. }
  3060. uint name_len;
  3061. if (ext_len)
  3062. {
  3063. name_len = strlen(name);
  3064. if ( ext_len >= name_len )
  3065. goto find_ext;
  3066. name_len -= ext_len;
  3067. ext = name + name_len;
  3068. }
  3069. else
  3070. {
  3071. find_ext:
  3072. ext = strrchr(name,'.');
  3073. if ( ext && ext > name )
  3074. {
  3075. name_len = ext - name;
  3076. ext_len = strlen(ext);
  3077. }
  3078. else
  3079. {
  3080. name_len = strlen(name);
  3081. ext_len = 0;
  3082. ext = "";
  3083. }
  3084. }
  3085. ccp ptr = name + name_len - 1;
  3086. while ( ptr >= name && *ptr >= '0' && *ptr <= '9' )
  3087. ptr--;
  3088. name_len = ++ptr - name;
  3089. uint index = strtoul(ptr,0,10);
  3090. PRINT("|%s| : |%.*s|%.*s|#%u|%.*s|\n",
  3091. search_dir, dir_len, dir, name_len, name, index, ext_len, ext );
  3092. //--- iterate directory to find highest index
  3093. DIR *fdir = opendir(search_dir);
  3094. if (fdir)
  3095. {
  3096. for(;;)
  3097. {
  3098. struct dirent *dent = readdir(fdir);
  3099. if (!dent)
  3100. break;
  3101. ccp fname = dent->d_name;
  3102. const uint fname_len = strlen(fname);
  3103. if ( fname_len > name_len + ext_len
  3104. && !strncasecmp(name,fname,name_len) )
  3105. {
  3106. ccp fext = fname + fname_len - ext_len;
  3107. if (!strncasecmp(ext,fext,ext_len))
  3108. {
  3109. char *end;
  3110. uint idx = strtoul(fname+name_len,&end,10);
  3111. if ( end == fext && idx > index )
  3112. index = idx;
  3113. }
  3114. }
  3115. }
  3116. closedir(fdir);
  3117. }
  3118. //--- create new filename
  3119. index++;
  3120. snprintf(temp,sizeof(temp),"%.*s%u%s",name_len,name,index,ext);
  3121. StringCopyE(buf+dir_len,buf+bufsize,temp);
  3122. return index;
  3123. }
  3124. ///////////////////////////////////////////////////////////////////////////////
  3125. ///////////////////////////////////////////////////////////////////////////////
  3126. static char * GetBlockDeviceHolderHelper
  3127. ( char *buf, char *endbuf, ccp name, ccp sep, int *level )
  3128. {
  3129. DASSERT(buf);
  3130. DASSERT(endbuf);
  3131. DASSERT(level);
  3132. char path[200];
  3133. snprintf(path,sizeof(path),"/sys/class/block/%s/holders",name);
  3134. if (IsDirectory(path,0))
  3135. {
  3136. DIR *fdir = opendir(path);
  3137. if (fdir)
  3138. {
  3139. for(;;)
  3140. {
  3141. struct dirent *dent = readdir(fdir);
  3142. if (!dent)
  3143. break;
  3144. if ( dent->d_name[0] != '.' )
  3145. {
  3146. buf = GetBlockDeviceHolderHelper(buf,endbuf,dent->d_name,sep,level);
  3147. (*level)++;
  3148. break;
  3149. }
  3150. }
  3151. closedir(fdir);
  3152. }
  3153. }
  3154. return StringCat2E(buf,endbuf,sep,name);
  3155. }
  3156. //-----------------------------------------------------------------------------
  3157. ccp GetBlockDeviceHolder ( ccp name, ccp sep, int *ret_level )
  3158. {
  3159. static ParamField_t db = { .free_data = true };
  3160. bool old_found;
  3161. ParamFieldItem_t *item = FindInsertParamField(&db,name,false,0,&old_found);
  3162. DASSERT(item);
  3163. if (!old_found)
  3164. {
  3165. int level = 0;
  3166. char buf[1000];
  3167. ccp end = GetBlockDeviceHolderHelper(buf,buf+sizeof(buf)-strlen(sep)-1,name,sep,&level);
  3168. ccp beg = buf + strlen(sep);
  3169. if ( beg < end )
  3170. {
  3171. item->num = level;
  3172. item->data = MEMDUP(beg,end-beg+1);
  3173. }
  3174. }
  3175. if (ret_level)
  3176. *ret_level = item->num;
  3177. return item->data;
  3178. }
  3179. //
  3180. ///////////////////////////////////////////////////////////////////////////////
  3181. /////////////// SearchPaths() ///////////////
  3182. ///////////////////////////////////////////////////////////////////////////////
  3183. // [[search_paths_t]]
  3184. typedef struct search_paths_t
  3185. {
  3186. mem_t source; // path to analyse, any string outside
  3187. bool allow_hidden; // true: allow hidden dirs and files
  3188. char *path_buf; // pointer to complete path, work buffer
  3189. char *path_star; // begin of '**' for search_paths_dir()
  3190. char *path_ptr; // current end of path
  3191. char *path_end; // end of 'path_buf' -1
  3192. SearchPathsFunc func; // function to call, never NULL
  3193. void *param; // last param for func()
  3194. search_paths_stat_t status; // search status
  3195. }
  3196. search_paths_t;
  3197. ///////////////////////////////////////////////////////////////////////////////
  3198. static void search_paths_hit ( search_paths_t *sp, mem_t path, uint d_type )
  3199. {
  3200. DASSERT(sp);
  3201. enumError err = sp->func(path,d_type,sp->param);
  3202. if ( err < 0 )
  3203. {
  3204. err = -err;
  3205. sp->status.abort = true;
  3206. }
  3207. if ( err != ERR_JOB_IGNORED )
  3208. {
  3209. sp->status.hit_count++;
  3210. if ( sp->status.max_err < err )
  3211. sp->status.max_err = err;
  3212. }
  3213. sp->status.func_count++;
  3214. }
  3215. ///////////////////////////////////////////////////////////////////////////////
  3216. static void search_paths_dir ( search_paths_t *sp, ParamField_t *collect, int min, int max )
  3217. {
  3218. DASSERT(sp);
  3219. DASSERT(collect);
  3220. PRINT0("%s%s** %d - %d%s\n",colerr->debug,sp->path_buf,min,max,colerr->reset);
  3221. search_paths_t local = *sp;
  3222. char *path_ptr = local.path_ptr;
  3223. DIR *fdir = opendir( *local.path_buf ? local.path_buf: "." );
  3224. local.status.dir_count++;
  3225. if (fdir)
  3226. {
  3227. for(;;)
  3228. {
  3229. struct dirent *dent = readdir(fdir);
  3230. if (!dent)
  3231. break;
  3232. if ( dent->d_name[0] == '.'
  3233. && ( !local.allow_hidden
  3234. || !dent->d_name[1]
  3235. || dent->d_name[1] == '.' && !dent->d_name[2] ))
  3236. {
  3237. continue;
  3238. }
  3239. local.path_ptr = StringCopyE(path_ptr,sp->path_end,dent->d_name);
  3240. uint st_mode = ConvertDType2STMode(dent->d_type);
  3241. if (!st_mode)
  3242. {
  3243. struct stat st;
  3244. if (stat(local.path_buf,&st))
  3245. continue;
  3246. st_mode = st.st_mode;
  3247. }
  3248. if (!S_ISDIR(st_mode))
  3249. continue;
  3250. DASSERT(local.path_star);
  3251. DASSERT( local.path_star >= local.path_buf );
  3252. DASSERT( local.path_star <= local.path_ptr );
  3253. PRINT0("FOUND: [t=%d,m:%x,add=%d,r=%d] %s\n",
  3254. dent->d_type, st_mode, min<=0, max>0, local.path_star );
  3255. if ( min <= 0 )
  3256. InsertParamField(collect,local.path_star,false,st_mode,0);
  3257. if ( max > 0 )
  3258. {
  3259. *local.path_ptr++ = '/';
  3260. *local.path_ptr = 0;
  3261. search_paths_dir(&local,collect,min-1,max-1);
  3262. }
  3263. }
  3264. closedir(fdir);
  3265. }
  3266. sp->status = local.status;
  3267. }
  3268. ///////////////////////////////////////////////////////////////////////////////
  3269. static void search_paths_helper ( search_paths_t *sp )
  3270. {
  3271. ccp wc = FindFirstWildcard(sp->source);
  3272. if (!wc)
  3273. {
  3274. char *end = StringCopyEM(sp->path_ptr,sp->path_end,sp->source.ptr,sp->source.len);
  3275. struct stat st;
  3276. if (!stat(sp->path_buf,&st))
  3277. {
  3278. mem_t path = { .ptr = sp->path_buf, .len = end - sp->path_buf };
  3279. search_paths_hit(sp,path,st.st_mode);
  3280. }
  3281. return;
  3282. }
  3283. search_paths_t local = *sp;
  3284. ccp pat = memrchr(local.source.ptr,'/',wc-local.source.ptr);
  3285. pat = pat ? pat+1 : local.source.ptr;
  3286. ccp source_end = local.source.ptr + local.source.len;
  3287. ccp next = memchr(wc,'/',source_end-wc);
  3288. if (!next)
  3289. next = source_end;
  3290. mem_t pattern;
  3291. pattern.len = next - pat;
  3292. pattern.ptr = MEMDUP(pat,pattern.len);
  3293. const bool allow_hidden = local.allow_hidden || pattern.ptr[0] == '.';
  3294. char *path_ptr = StringCopyEM(local.path_ptr,local.path_end,local.source.ptr,pat-local.source.ptr);
  3295. local.path_ptr = path_ptr;
  3296. local.source.ptr = next;
  3297. local.source.len = source_end - next;
  3298. const bool want_dir = local.source.len && local.source.ptr[0] == '/';
  3299. PRINT0("SPLIT: %s %s%s%s %.*s [want_dir=%d]\n",
  3300. local.path_buf,
  3301. colerr->highlight, pattern.ptr, colerr->reset,
  3302. local.source.len, local.source.ptr, want_dir );
  3303. ParamField_t collect;
  3304. InitializeParamField(&collect);
  3305. collect.func_cmp = strcasecmp;
  3306. //--- check for **...
  3307. if ( pattern.ptr[0] == '*' && pattern.ptr[1] == '*' )
  3308. {
  3309. u32 n1 = 0, n2 = 100;
  3310. char *ptr = (char*)pattern.ptr+2;
  3311. if (*ptr)
  3312. {
  3313. if (isdigit(*ptr))
  3314. n1 = str2ul(ptr,&ptr,10);
  3315. if (*ptr)
  3316. {
  3317. if ( *ptr != '-' )
  3318. goto terminate;
  3319. if ( *++ptr )
  3320. {
  3321. n2 = str2ul(ptr,&ptr,10);
  3322. if (*ptr)
  3323. goto terminate;
  3324. }
  3325. }
  3326. else
  3327. n2 = n1;
  3328. if ( n2 > 100 )
  3329. n2 = 100;
  3330. if ( n1 > n2 )
  3331. goto terminate;
  3332. }
  3333. local.path_star = path_ptr;
  3334. search_paths_dir(&local,&collect,n1,n2);
  3335. PRINT0("%sDIR: %d records for **%s\n",colerr->highlight,collect.used,colerr->reset);
  3336. ParamFieldItem_t *end = collect.field + collect.used;
  3337. for ( ParamFieldItem_t *ptr = collect.field; !local.status.abort && ptr < end; ptr++ )
  3338. {
  3339. local.path_ptr = StringCopyE(path_ptr,sp->path_end,ptr->key);
  3340. PRINT0("%sDIR: %s%s\n",colerr->highlight,local.path_buf,colerr->reset);
  3341. search_paths_helper(&local);
  3342. if (local.status.abort)
  3343. break;
  3344. }
  3345. goto terminate;
  3346. }
  3347. //--- scan directory
  3348. DIR *fdir = opendir( *local.path_buf ? local.path_buf: "." );
  3349. local.status.dir_count++;
  3350. if (fdir)
  3351. {
  3352. for(;;)
  3353. {
  3354. struct dirent *dent = readdir(fdir);
  3355. if (!dent)
  3356. break;
  3357. if ( dent->d_name[0] == '.'
  3358. && ( !allow_hidden
  3359. || !dent->d_name[1]
  3360. || dent->d_name[1] == '.' && !dent->d_name[2] ))
  3361. {
  3362. continue;
  3363. }
  3364. uint st_mode = ConvertDType2STMode(dent->d_type);
  3365. if ( st_mode && want_dir && !S_ISDIR(st_mode) && !S_ISLNK(st_mode) )
  3366. continue;
  3367. if ( MatchPatternFull(pattern.ptr,dent->d_name) )
  3368. {
  3369. if (!st_mode)
  3370. {
  3371. local.path_ptr = StringCopyE(path_ptr,sp->path_end,dent->d_name);
  3372. struct stat st;
  3373. if (stat(local.path_buf,&st))
  3374. continue;
  3375. if ( want_dir && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) )
  3376. continue;
  3377. st_mode = st.st_mode;
  3378. }
  3379. PRINT0("BINGO: [%d,%x] %s\n",dent->d_type,st_mode,dent->d_name);
  3380. InsertParamField(&collect,dent->d_name,false,st_mode,0);
  3381. }
  3382. }
  3383. closedir(fdir);
  3384. }
  3385. if (!local.status.abort)
  3386. {
  3387. ParamFieldItem_t *end = collect.field + collect.used;
  3388. for ( ParamFieldItem_t *ptr = collect.field; !local.status.abort && ptr < end; ptr++ )
  3389. {
  3390. local.path_ptr = StringCopyE(path_ptr,sp->path_end,ptr->key);
  3391. if (local.source.len)
  3392. {
  3393. search_paths_helper(&local);
  3394. if (local.status.abort)
  3395. break;
  3396. }
  3397. else
  3398. {
  3399. mem_t path = { .ptr = local.path_buf, .len = local.path_ptr - local.path_buf };
  3400. search_paths_hit(&local,path,ptr->num);
  3401. }
  3402. }
  3403. }
  3404. terminate:;
  3405. ResetParamField(&collect);
  3406. FreeString(pattern.ptr);
  3407. sp->status = local.status;
  3408. }
  3409. ///////////////////////////////////////////////////////////////////////////////
  3410. search_paths_stat_t SearchPaths
  3411. (
  3412. ccp path1, // not NULL: part #1 of base path
  3413. ccp path2, // not NULL: part #2 of base path
  3414. bool allow_hidden, // allow hiddent directories and files
  3415. SearchPathsFunc func, // callback function, never NULL
  3416. void *param // last param for func()
  3417. )
  3418. {
  3419. if (!func)
  3420. {
  3421. search_paths_stat_t stat = { .max_err = ERR_MISSING_PARAM };
  3422. return stat;
  3423. }
  3424. char srcbuf[PATH_MAX+2];
  3425. ccp source = PathCatPP(srcbuf,sizeof(srcbuf),path1,path2);
  3426. char pathbuf[PATH_MAX];
  3427. search_paths_t sp =
  3428. {
  3429. .source.ptr = source,
  3430. .source.len = strlen(source),
  3431. .allow_hidden = allow_hidden,
  3432. .path_buf = pathbuf,
  3433. .path_ptr = pathbuf,
  3434. .path_end = pathbuf + sizeof(pathbuf) - 2,
  3435. .func = func,
  3436. .param = param,
  3437. };
  3438. search_paths_helper(&sp);
  3439. PRINT0("abort=%d, max_err = %d, %u dirs scanned, %u func calls, %u hits\n",
  3440. sp.abort, sp.max_err, sp.dir_count, sp.func_count, sp.hit_count );
  3441. return sp.status;
  3442. }
  3443. ///////////////////////////////////////////////////////////////////////////////
  3444. ///////////////////////////////////////////////////////////////////////////////
  3445. static enumError insert_string_field
  3446. (
  3447. mem_t path, // full path of existing file, never NULL
  3448. uint st_mode, // copy of struct stat.st_mode, see "man 2 stat"
  3449. void *param // user defined parameter
  3450. )
  3451. {
  3452. StringField_t * sf = param;
  3453. DASSERT(sf);
  3454. InsertStringField(sf,path.ptr,false);
  3455. return ERR_OK;
  3456. }
  3457. //-----------------------------------------------------------------------------
  3458. uint InsertStringFieldExpand
  3459. ( StringField_t * sf, ccp path1, ccp path2, bool allow_hidden )
  3460. {
  3461. DASSERT(sf);
  3462. search_paths_stat_t stat = SearchPaths(path1,path2,allow_hidden,insert_string_field,sf);
  3463. PRINT0("%d %d %d\n",stat.dir_count,stat.func_count,stat.hit_count);
  3464. if (!stat.hit_count)
  3465. {
  3466. char srcbuf[PATH_MAX+2];
  3467. ccp source = PathCatPP(srcbuf,sizeof(srcbuf),path1,path2);
  3468. InsertStringField(sf,source,false);
  3469. stat.hit_count++;
  3470. }
  3471. return stat.hit_count;
  3472. }
  3473. ///////////////////////////////////////////////////////////////////////////////
  3474. static enumError append_string_field
  3475. (
  3476. mem_t path, // full path of existing file, never NULL
  3477. uint st_mode, // copy of struct stat.st_mode, see "man 2 stat"
  3478. void *param // user defined parameter
  3479. )
  3480. {
  3481. StringField_t * sf = param;
  3482. DASSERT(sf);
  3483. AppendStringField(sf,path.ptr,false);
  3484. return ERR_OK;
  3485. }
  3486. //-----------------------------------------------------------------------------
  3487. uint AppendStringFieldExpand
  3488. ( StringField_t * sf, ccp path1, ccp path2, bool allow_hidden )
  3489. {
  3490. DASSERT(sf);
  3491. search_paths_stat_t stat = SearchPaths(path1,path2,allow_hidden,append_string_field,sf);
  3492. PRINT0("%d %d %d\n",stat.dir_count,stat.func_count,stat.hit_count);
  3493. if (!stat.hit_count)
  3494. {
  3495. char srcbuf[PATH_MAX+2];
  3496. ccp source = PathCatPP(srcbuf,sizeof(srcbuf),path1,path2);
  3497. AppendStringField(sf,source,false);
  3498. stat.hit_count++;
  3499. }
  3500. return stat.hit_count;
  3501. }
  3502. ///////////////////////////////////////////////////////////////////////////////
  3503. ///////////////////////////////////////////////////////////////////////////////
  3504. typedef struct search_param_field_t
  3505. {
  3506. ParamField_t *pf;
  3507. uint num;
  3508. cvp data;
  3509. }
  3510. search_param_field_t;
  3511. //-----------------------------------------------------------------------------
  3512. static enumError insert_param_field
  3513. (
  3514. mem_t path, // full path of existing file, never NULL
  3515. uint st_mode, // copy of struct stat.st_mode, see "man 2 stat"
  3516. void *param // user defined parameter
  3517. )
  3518. {
  3519. search_param_field_t *spf = param;
  3520. DASSERT(spf);
  3521. InsertParamField(spf->pf,path.ptr,false,spf->num,spf->data);
  3522. return ERR_OK;
  3523. }
  3524. //-----------------------------------------------------------------------------
  3525. uint InsertParamFieldExpand
  3526. ( ParamField_t * pf, ccp path1, ccp path2, bool allow_hidden, uint num, cvp data )
  3527. {
  3528. DASSERT(pf);
  3529. search_param_field_t spf = { .pf = pf, .num = num, .data = data };
  3530. search_paths_stat_t stat = SearchPaths(path1,path2,allow_hidden,insert_param_field,&spf);
  3531. PRINT0("%d %d %d\n",stat.dir_count,stat.func_count,stat.hit_count);
  3532. if (!stat.hit_count)
  3533. {
  3534. char srcbuf[PATH_MAX+2];
  3535. ccp source = PathCatPP(srcbuf,sizeof(srcbuf),path1,path2);
  3536. InsertParamField(pf,source,false,num,data);
  3537. stat.hit_count++;
  3538. }
  3539. return stat.hit_count;
  3540. }
  3541. ///////////////////////////////////////////////////////////////////////////////
  3542. static enumError append_param_field
  3543. (
  3544. mem_t path, // full path of existing file, never NULL
  3545. uint st_mode, // copy of struct stat.st_mode, see "man 2 stat"
  3546. void *param // user defined parameter
  3547. )
  3548. {
  3549. search_param_field_t *spf = param;
  3550. DASSERT(spf);
  3551. AppendParamField(spf->pf,path.ptr,false,spf->num,spf->data);
  3552. return ERR_OK;
  3553. }
  3554. //-----------------------------------------------------------------------------
  3555. uint AppendParamFieldExpand
  3556. ( ParamField_t * pf, ccp path1, ccp path2, bool allow_hidden, uint num, cvp data )
  3557. {
  3558. DASSERT(pf);
  3559. search_param_field_t spf = { .pf = pf, .num = num, .data = data };
  3560. search_paths_stat_t stat = SearchPaths(path1,path2,allow_hidden,append_param_field,&spf);
  3561. PRINT0("%d %d %d\n",stat.dir_count,stat.func_count,stat.hit_count);
  3562. if (!stat.hit_count)
  3563. {
  3564. char srcbuf[PATH_MAX+2];
  3565. ccp source = PathCatPP(srcbuf,sizeof(srcbuf),path1,path2);
  3566. AppendParamField(pf,source,false,num,data);
  3567. stat.hit_count++;
  3568. }
  3569. return stat.hit_count;
  3570. }
  3571. //
  3572. ///////////////////////////////////////////////////////////////////////////////
  3573. /////////////// normalize filenames ///////////////
  3574. ///////////////////////////////////////////////////////////////////////////////
  3575. char * NormalizeFileName
  3576. (
  3577. // returns a pointer to the NULL terminator within 'buf'
  3578. char * buf, // valid destination buffer
  3579. uint buf_size, // size of buf
  3580. ccp source, // NULL or source
  3581. bool allow_slash, // true: allow '/' in source
  3582. bool is_utf8, // true: enter UTF-8 mode
  3583. trailing_slash slash_mode // manipulate trailing slash
  3584. )
  3585. {
  3586. DASSERT(buf);
  3587. DASSERT(buf_size>1);
  3588. char *dest = buf, *end = buf + buf_size - 1;
  3589. TRACE("NormalizeFileName(%s,%d,%d,%d)\n",source,allow_slash,is_utf8,slash_mode);
  3590. if (source)
  3591. {
  3592. #ifdef __CYGWIN__
  3593. if (allow_slash)
  3594. {
  3595. const int drv_len = IsWindowsDriveSpec(source);
  3596. if (drv_len)
  3597. {
  3598. dest = StringCopyE(dest,end,"/cygdrive/c/");
  3599. if ( dest < end )
  3600. dest[-2] = tolower((int)*source);
  3601. source += drv_len;
  3602. }
  3603. }
  3604. #endif
  3605. bool skip_space = true;
  3606. while ( *source && dest < end )
  3607. {
  3608. unsigned char ch = *source++;
  3609. if ( ch == ':' )
  3610. {
  3611. if (!skip_space)
  3612. *dest++ = ' ';
  3613. if ( dest + 2 <= end )
  3614. {
  3615. *dest++ = '-';
  3616. *dest++ = ' ';
  3617. }
  3618. skip_space = true;
  3619. }
  3620. else
  3621. {
  3622. if ( isalnum(ch)
  3623. || ( is_utf8
  3624. ? ch >= 0x80
  3625. : ch == 0xe4 // ä
  3626. || ch == 0xf6 // ö
  3627. || ch == 0xfc // ü
  3628. || ch == 0xdf // ß
  3629. || ch == 0xc4 // Ä
  3630. || ch == 0xd6 // Ö
  3631. || ch == 0xdc // Ü
  3632. )
  3633. || strchr("_+-=%'\"$%&#,.!()[]{}<>",ch)
  3634. )
  3635. {
  3636. *dest++ = ch;
  3637. skip_space = false;
  3638. }
  3639. #ifdef __CYGWIN__
  3640. else if ( ( ch == '/' || ch == '\\' ) && allow_slash )
  3641. #else
  3642. else if ( ch == '/' && allow_slash )
  3643. #endif
  3644. {
  3645. if ( dest == buf || dest[-1] != '/' )
  3646. *dest++ = '/';
  3647. skip_space = false;
  3648. }
  3649. else if (!skip_space)
  3650. {
  3651. *dest++ = ' ';
  3652. skip_space = true;
  3653. }
  3654. }
  3655. }
  3656. }
  3657. if ( dest > buf && dest[-1] == ' ' )
  3658. dest--;
  3659. switch (slash_mode)
  3660. {
  3661. case TRSL_NONE: // do nothing special
  3662. break;
  3663. case TRSL_AUTO: // add trailing slash if it is a directory, remove otherwise
  3664. *dest = 0;
  3665. if (IsDirectory(buf,false))
  3666. goto add_slash;
  3667. // fall through
  3668. case TRSL_REMOVE: // remove trailing slash always
  3669. if ( dest > buf && dest[-1] == '/' )
  3670. dest--;
  3671. break;
  3672. case TRSL_ADD_AUTO: // add trailing slash if it is a directory
  3673. *dest = 0;
  3674. if (!IsDirectory(buf,false))
  3675. break;
  3676. // fall through
  3677. case TRSL_ADD_ALWAYS: // add trailing slash always
  3678. add_slash:
  3679. if ( dest > buf && dest < end && dest[-1] != '/' )
  3680. *dest++ = '/';
  3681. break;
  3682. }
  3683. DASSERT( dest <= end );
  3684. *dest = 0;
  3685. return dest;
  3686. }
  3687. ///////////////////////////////////////////////////////////////////////////////
  3688. ///////////////////////////////////////////////////////////////////////////////
  3689. uint IsWindowsDriveSpec
  3690. (
  3691. // returns the length of the found windows drive specification (0|2|3)
  3692. ccp src // NULL or valid string
  3693. )
  3694. {
  3695. if ( src
  3696. && src[1] == ':'
  3697. && ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' ) )
  3698. {
  3699. if (!src[2])
  3700. return 2;
  3701. if ( src[2] == '/' || src[2] == '\\' )
  3702. return 3;
  3703. }
  3704. return 0;
  3705. }
  3706. ///////////////////////////////////////////////////////////////////////////////
  3707. uint NormalizeFilenameCygwin
  3708. (
  3709. // returns a pointer to the NULL terminator within 'buf'
  3710. char * buf, // valid destination buffer
  3711. uint buf_size, // size of buf
  3712. ccp src // NULL or source
  3713. )
  3714. {
  3715. static char prefix[] = "/cygdrive/";
  3716. if ( buf_size < sizeof(prefix) + 5 || !src )
  3717. {
  3718. if ( buf && buf_size )
  3719. *buf = 0;
  3720. return 0;
  3721. }
  3722. char * end = buf + buf_size - 1;
  3723. char * dest = buf;
  3724. if ( ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' )
  3725. && src[1] == ':'
  3726. && ( src[2] == 0 || src[2] == '/' || src[2] == '\\' ))
  3727. {
  3728. memcpy(buf,prefix,sizeof(prefix));
  3729. dest = buf + sizeof(prefix)-1;
  3730. *dest++ = tolower((int)*src); // cygwin needs the '(int)'
  3731. #ifdef __CYGWIN__
  3732. // this can only be checked with real Cygwin.
  3733. *dest = 0;
  3734. if (IsDirectory(buf,false))
  3735. {
  3736. *dest++ = '/';
  3737. src += 2;
  3738. if (*src)
  3739. src++;
  3740. }
  3741. else
  3742. dest = buf;
  3743. #else //
  3744. *dest++ = '/';
  3745. src += 2;
  3746. if (*src)
  3747. src++;
  3748. #endif
  3749. }
  3750. DASSERT( dest < buf + buf_size );
  3751. while ( dest < end && *src )
  3752. if ( *src == '\\' )
  3753. {
  3754. *dest++ = '/';
  3755. src++;
  3756. }
  3757. else
  3758. *dest++ = *src++;
  3759. *dest = 0;
  3760. DASSERT( dest < buf + buf_size );
  3761. return dest - buf;
  3762. }
  3763. ///////////////////////////////////////////////////////////////////////////////
  3764. exmem_t GetNormalizeFilenameCygwin
  3765. (
  3766. // returns an object. Call FreeExMem(RESULT) to possible alloced memory.
  3767. ccp source, // NULL or source
  3768. bool try_circ // use circ-buffer, if result is small enough
  3769. )
  3770. {
  3771. char buf[PATH_MAX];
  3772. const uint len = NormalizeFilenameCygwin(buf,sizeof(buf),source);
  3773. return AllocExMemS(buf,len,try_circ,source,-1);
  3774. }
  3775. ///////////////////////////////////////////////////////////////////////////////
  3776. char * AllocNormalizedFilenameCygwin
  3777. (
  3778. // returns an alloced buffer with the normalized filename
  3779. ccp source, // NULL or source
  3780. bool try_circ // use circ-buffer, if result is small enough
  3781. )
  3782. {
  3783. char buf[PATH_MAX];
  3784. const uint len = NormalizeFilenameCygwin(buf,sizeof(buf),source);
  3785. return AllocCircBuf(buf,len,try_circ);
  3786. }
  3787. //
  3788. ///////////////////////////////////////////////////////////////////////////////
  3789. /////////////// search file & config ///////////////
  3790. ///////////////////////////////////////////////////////////////////////////////
  3791. void ResetSearchFile ( search_file_list_t *sfl )
  3792. {
  3793. if (sfl)
  3794. {
  3795. if (sfl->list)
  3796. {
  3797. uint i;
  3798. search_file_t *sf = sfl->list;
  3799. for ( i = 0; i < sfl->used; i++, sf++ )
  3800. if (sf->alloced)
  3801. FreeString(sf->fname);
  3802. FREE(sfl->list);
  3803. }
  3804. ResetEML(&sfl->symbols);
  3805. InitializeSearchFile(sfl);
  3806. }
  3807. }
  3808. ///////////////////////////////////////////////////////////////////////////////
  3809. search_file_t * AppendSearchFile
  3810. (
  3811. search_file_list_t *sfl, // valid search list, new files will be appended
  3812. ccp fname, // path+fname to add
  3813. CopyMode_t copy_mode, // copy mode for 'fname'
  3814. ccp append_if_dir // append this if 'fname' is a directory
  3815. )
  3816. {
  3817. search_file_t *res = 0;
  3818. if ( fname && *fname )
  3819. {
  3820. #if 0
  3821. ccp rpath = realpath(fname,0);
  3822. if (rpath)
  3823. {
  3824. if ( copy_mode == CPM_MOVE )
  3825. FreeString(fname);
  3826. fname = rpath;
  3827. copy_mode = CPM_MOVE;
  3828. }
  3829. #endif
  3830. uint i;
  3831. search_file_t *sf = sfl->list;
  3832. for ( i = 0; i < sfl->used; i++, sf++ )
  3833. {
  3834. if (!strcmp(sf->fname,fname))
  3835. {
  3836. res = sf;
  3837. goto end;
  3838. }
  3839. }
  3840. DASSERT( i == sfl->used );
  3841. //-- check for directory
  3842. const inode_type_t itype = GetInodeTypeByPath(fname,0);
  3843. if ( itype == INTY_DIR && append_if_dir )
  3844. {
  3845. char buf[PATH_MAX];
  3846. ccp fname2 = PathCatPP(buf,sizeof(buf),fname,append_if_dir);
  3847. res = AppendSearchFile(sfl,fname2,CPM_COPY,0);
  3848. }
  3849. else
  3850. {
  3851. if ( sfl->used == sfl->size )
  3852. {
  3853. sfl->size += sfl->size/8 + 20;
  3854. sfl->list = REALLOC(sfl->list,sfl->size*sizeof(*sfl->list));
  3855. }
  3856. res = sfl->list + sfl->used++;
  3857. memset(res,0,sizeof(*res));
  3858. res->itype = itype;
  3859. res->fname = CopyData(fname,strlen(fname)+1,copy_mode,&res->alloced);
  3860. fname = 0;
  3861. }
  3862. }
  3863. end:
  3864. if ( fname && copy_mode == CPM_MOVE )
  3865. FreeString(fname);
  3866. return res;
  3867. }
  3868. ///////////////////////////////////////////////////////////////////////////////
  3869. void DumpSearchFile ( FILE *f, int indent,
  3870. const search_file_list_t *sfl, bool show_symbols, ccp info )
  3871. {
  3872. if ( f && sfl && ( sfl->used || show_symbols && sfl->symbols.used ))
  3873. {
  3874. indent = NormalizeIndent(indent);
  3875. fprintf(f,"%*s%s%s" "SearchList %p, N=%u/%u\n",
  3876. indent,"", info ? info : "", info ? " : " : "",
  3877. sfl, sfl->used, sfl->size );
  3878. char buf[20];
  3879. const int fw_idx = indent
  3880. + snprintf(buf,sizeof(buf),"%d",sfl->used)
  3881. + ( show_symbols ? 3 : 1 );
  3882. uint idx = 0;
  3883. search_file_t *ptr, *end = sfl->list + sfl->used;
  3884. for ( ptr = sfl->list; ptr < end; ptr++, idx++ )
  3885. fprintf(f,"%*d [%c,%c:%02x] %s\n",
  3886. fw_idx, idx,
  3887. ptr->alloced ? 'A' : '-',
  3888. inode_type_char[ptr->itype], ptr->hint, ptr->fname );
  3889. if (show_symbols)
  3890. DumpEML(f,indent+2,&sfl->symbols,"Symbols");
  3891. }
  3892. }
  3893. ///////////////////////////////////////////////////////////////////////////////
  3894. ///////////////////////////////////////////////////////////////////////////////
  3895. static int add_search
  3896. (
  3897. // returns 0 if !stop_if_found, 2 if added+found, 1 if added, 0 else
  3898. search_file_list_t *sfl, // valid data
  3899. ccp config_fname, // default filename (without path) of config file
  3900. ccp *list, // NULL or list of filenames
  3901. int n_list, // num of 'list' elements, -1:null terminated list
  3902. ccp prefix, // prefix for relative filenames
  3903. uint hint, // attributes
  3904. int stop_if_found // >0: stop if file found
  3905. )
  3906. {
  3907. DASSERT(sfl);
  3908. if (!list)
  3909. return 0;
  3910. if ( n_list < 0 )
  3911. {
  3912. ccp *ptr;
  3913. for ( ptr = list; *ptr; ptr++ )
  3914. ;
  3915. n_list = ptr - list;
  3916. }
  3917. if (!n_list)
  3918. return 0;
  3919. int ret_val = 0;
  3920. char buf[PATH_MAX];
  3921. for ( ; n_list > 0; n_list--, list++ )
  3922. {
  3923. if (!*list)
  3924. continue;
  3925. ccp path = **list == '/' || !prefix || !*prefix
  3926. ? *list
  3927. : PathCatPP(buf,sizeof(buf),prefix,*list);
  3928. exmem_t res = ResolveSymbolsEML(&sfl->symbols,path);
  3929. search_file_t *sf = AppendSearchFile( sfl, res.data.ptr,
  3930. res.is_alloced ? CPM_MOVE : CPM_COPY, config_fname );
  3931. if (sf)
  3932. {
  3933. sf->hint |= hint;
  3934. if ( sf->itype == INTY_REG && stop_if_found > 0 )
  3935. return 2;
  3936. ret_val = 1;
  3937. }
  3938. }
  3939. return stop_if_found > 0 ? ret_val : 0;
  3940. }
  3941. //-----------------------------------------------------------------------------
  3942. bool SearchConfig
  3943. (
  3944. // for all paths:
  3945. // /... is an absolute path
  3946. // $(home)/... path relative to getenv("HOME")
  3947. // $(xdg_home)/... path relative to first path of getenv("XDG_CONFIG_HOME")
  3948. // $(xdg_etc)/... path relative to first path of getenv("XDG_CONFIG_DIRS")
  3949. // $(etc)/... path relative to /etc directory
  3950. // $(install)/... path relative to installation directory = ProgramDirectory()
  3951. // $(NAME)/... path relative to symbol in sfl->symbols
  3952. // xx relative paths otherwise
  3953. search_file_list_t *sfl,
  3954. // valid search list, new paths will be appended
  3955. // sfl->symbols: home, etc and install are added (not replaced)
  3956. // It is used to resolve all $(NAME) references.
  3957. ccp config_fname, // default filename (without path) of config file
  3958. ccp *option, // NULL or filenames by option => CONF_HINT_OPT
  3959. int n_option, // num of 'option' elements, -1:null terminated list
  3960. ccp *xdg_home, // NULL or $(xdg_home) based paths => CONF_HINT_HOME
  3961. int n_xdg_home, // num of 'home' elements, -1:null terminated list
  3962. ccp *home, // NULL or $(home) based paths => CONF_HINT_HOME
  3963. int n_home, // num of 'home' elements, -1:null terminated list
  3964. ccp *etc, // NULL or $(etc) based paths => CONF_HINT_ETC
  3965. int n_etc, // num of 'etc' elements, -1:null terminated list
  3966. ccp *install, // NULL or $(install) based paths => CONF_HINT_INST
  3967. int n_install, // num of 'install' elements, -1:null terminated list
  3968. ccp *misc, // NULL or absolute paths => CONF_HINT_MISC
  3969. int n_misc, // num of 'misc' elements, -1:null terminated list
  3970. int stop_mode // >0: stop if found, >1: stop on option
  3971. )
  3972. {
  3973. // ==> see: https://wiki.archlinux.org/index.php/XDG_Base_Directory
  3974. DASSERT(sfl);
  3975. AddStandardSymbolsEML(&sfl->symbols,false);
  3976. int stat = add_search(sfl,config_fname,option,n_option,0,CONF_HINT_OPT,stop_mode);
  3977. if ( stat > 1 )
  3978. return true;
  3979. if ( stop_mode > 1 && stat )
  3980. return false;
  3981. if (add_search(sfl,config_fname,xdg_home,n_xdg_home,"$(xdg_home)",CONF_HINT_HOME,stop_mode)>1)
  3982. return true;
  3983. if (add_search(sfl,config_fname,home,n_home,"$(home)",CONF_HINT_HOME,stop_mode)>1)
  3984. return true;
  3985. if (add_search(sfl,config_fname,etc,n_etc,"$(xdg_etc)",CONF_HINT_ETC,stop_mode)>1)
  3986. return true;
  3987. if (add_search(sfl,config_fname,etc,n_etc,"$(etc)",CONF_HINT_ETC,stop_mode)>1)
  3988. return true;
  3989. if (add_search(sfl,config_fname,install,n_install,"$(install)",CONF_HINT_INST,stop_mode)>1)
  3990. return true;
  3991. return add_search(sfl,config_fname,misc,n_misc,0,CONF_HINT_MISC,stop_mode)>1;
  3992. }
  3993. //
  3994. ///////////////////////////////////////////////////////////////////////////////
  3995. /////////////// FDList_t ///////////////
  3996. ///////////////////////////////////////////////////////////////////////////////
  3997. void ClearFDList ( FDList_t *fdl )
  3998. {
  3999. DASSERT(fdl);
  4000. fdl->max_fd = -1;
  4001. FD_ZERO(&fdl->readfds);
  4002. FD_ZERO(&fdl->writefds);
  4003. FD_ZERO(&fdl->exceptfds);
  4004. fdl->poll_used = 0;
  4005. fdl->n_sock = 0;
  4006. fdl->now_usec = GetTimeUSec(false);
  4007. fdl->timeout_usec = M1(fdl->timeout_usec);
  4008. fdl->timeout_nsec = M1(fdl->timeout_nsec);
  4009. }
  4010. ///////////////////////////////////////////////////////////////////////////////
  4011. void InitializeFDList ( FDList_t *fdl, bool use_poll )
  4012. {
  4013. DASSERT(fdl);
  4014. memset(fdl,0,sizeof(*fdl));
  4015. fdl->use_poll = use_poll;
  4016. ClearFDList(fdl);
  4017. }
  4018. ///////////////////////////////////////////////////////////////////////////////
  4019. void ResetFDList ( FDList_t *fdl )
  4020. {
  4021. DASSERT(fdl);
  4022. FREE(fdl->poll_list);
  4023. InitializeFDList(fdl,fdl->use_poll);
  4024. }
  4025. ///////////////////////////////////////////////////////////////////////////////
  4026. struct pollfd * AllocFDList ( FDList_t *fdl, uint n )
  4027. {
  4028. DASSERT(fdl);
  4029. uint min_size = fdl->poll_used + n;
  4030. if ( fdl->poll_size < min_size )
  4031. {
  4032. fdl->poll_size = ( min_size/0x18 + 2 ) * 0x20;
  4033. DASSERT( fdl->poll_size >= min_size );
  4034. fdl->poll_list = REALLOC(fdl->poll_list,fdl->poll_size*sizeof(*fdl->poll_list));
  4035. }
  4036. DASSERT( fdl->poll_used + n <= fdl->poll_size );
  4037. struct pollfd *ptr = fdl->poll_list + fdl->poll_used;
  4038. fdl->poll_used += n;
  4039. memset(ptr,0,n*sizeof(*ptr));
  4040. return ptr;
  4041. }
  4042. ///////////////////////////////////////////////////////////////////////////////
  4043. void AnnounceFDList ( FDList_t *fdl, uint n )
  4044. {
  4045. if (fdl->use_poll)
  4046. {
  4047. AllocFDList(fdl,n);
  4048. fdl->poll_used -= n;
  4049. }
  4050. }
  4051. ///////////////////////////////////////////////////////////////////////////////
  4052. uint AddFDList
  4053. (
  4054. // returns the pool-index if available, ~0 otherwise
  4055. FDList_t *fdl, // valid socket list
  4056. int sock, // socket to add
  4057. uint events // bit field: POLLIN|POLLPRI|POLLOUT|POLLRDHUP|...
  4058. )
  4059. {
  4060. events &= ~(POLLERR|POLLHUP|POLLNVAL); // only result!
  4061. if ( sock == -1 || !events )
  4062. return ~0;
  4063. fdl->n_sock++;
  4064. if ( fdl->max_fd < sock )
  4065. fdl->max_fd = sock;
  4066. DASSERT(fdl);
  4067. if (fdl->use_poll)
  4068. {
  4069. struct pollfd *pp = AllocFDList(fdl,1);
  4070. DASSERT(pp);
  4071. pp->fd = sock;
  4072. pp->events = events;
  4073. return pp - fdl->poll_list;
  4074. }
  4075. noPRINT("ADD-SOCK %d = %x\n",sock,events);
  4076. if ( events & POLLIN )
  4077. FD_SET(sock,&fdl->readfds);
  4078. if ( events & POLLOUT )
  4079. FD_SET(sock,&fdl->writefds);
  4080. if ( events & POLLPRI )
  4081. FD_SET(sock,&fdl->exceptfds);
  4082. return ~0;
  4083. }
  4084. ///////////////////////////////////////////////////////////////////////////////
  4085. uint GetEventFDList
  4086. (
  4087. // returns bit field: POLLIN|POLLOUT|POLLERR|...
  4088. FDList_t *fdl, // valid socket list
  4089. int sock, // socket to look for
  4090. uint poll_index // if use_poll: use the index for a fast search
  4091. )
  4092. {
  4093. if ( sock == -1 )
  4094. return 0;
  4095. DASSERT(fdl);
  4096. if (fdl->use_poll)
  4097. {
  4098. const struct pollfd *pp = fdl->poll_list;
  4099. if ( poll_index < fdl->poll_used && pp[poll_index].fd == sock )
  4100. return pp[poll_index].revents;
  4101. const struct pollfd *pend = pp + fdl->poll_used;
  4102. for ( ; pp < pend; pp++ )
  4103. if ( pp->fd == sock )
  4104. return pp->revents;
  4105. return 0;
  4106. }
  4107. uint revents = 0;
  4108. if (FD_ISSET(sock,&fdl->readfds))
  4109. revents |= POLLIN;
  4110. if (FD_ISSET(sock,&fdl->writefds))
  4111. revents |= POLLOUT;
  4112. if (FD_ISSET(sock,&fdl->exceptfds))
  4113. revents |= POLLPRI;
  4114. return revents;
  4115. }
  4116. ///////////////////////////////////////////////////////////////////////////////
  4117. ///////////////////////////////////////////////////////////////////////////////
  4118. static s_usec_t prepare_wait_fdl ( FDList_t *fdl )
  4119. {
  4120. DASSERT(fdl);
  4121. const s_usec_t now_usec = GetTimeUSec(false);
  4122. if (fdl->timeout_nsec)
  4123. {
  4124. const u_usec_t wait_until
  4125. = ( (s_usec_t)fdl->timeout_nsec - (s_usec_t)GetTimerNSec() )
  4126. / NSEC_PER_USEC + now_usec + 1;
  4127. if ( !fdl->timeout_usec || fdl->timeout_usec > wait_until )
  4128. fdl->timeout_usec = wait_until;
  4129. }
  4130. if ( fdl->min_wait_usec && fdl->timeout_usec )
  4131. {
  4132. const u_usec_t wait_until = now_usec + fdl->min_wait_usec;
  4133. if ( fdl->timeout_usec < wait_until )
  4134. fdl->timeout_usec = wait_until;
  4135. }
  4136. return now_usec;
  4137. }
  4138. //-----------------------------------------------------------------------------
  4139. static void finish_wait_fdl ( FDList_t *fdl, u_usec_t start_usec )
  4140. {
  4141. DASSERT(fdl);
  4142. UpdateCurrentTime();
  4143. fdl->now_usec = current_time.usec;
  4144. fdl->last_wait_usec = fdl->now_usec - start_usec;
  4145. fdl->wait_usec += fdl->last_wait_usec;
  4146. fdl->wait_count++;
  4147. UpdateCpuUsageIncrement();
  4148. }
  4149. ///////////////////////////////////////////////////////////////////////////////
  4150. int WaitFDList
  4151. (
  4152. FDList_t *fdl // valid socket list
  4153. )
  4154. {
  4155. DASSERT(fdl);
  4156. const s_usec_t now_usec = prepare_wait_fdl(fdl);
  4157. int stat;
  4158. if (fdl->use_poll)
  4159. {
  4160. int timeout;
  4161. if ( !fdl->timeout_usec )
  4162. timeout = -1;
  4163. else if ( fdl->timeout_usec > now_usec )
  4164. {
  4165. u_msec_t delta = ( fdl->timeout_usec - now_usec ) / USEC_PER_MSEC;
  4166. timeout = delta < 0x7fffffff ? delta : 0x7fffffff;
  4167. if (!timeout)
  4168. timeout = 1;
  4169. }
  4170. else
  4171. timeout = 0;
  4172. if (fdl->debug_file)
  4173. {
  4174. fprintf(fdl->debug_file,"POLL: timeout=%d\n",timeout);
  4175. fflush(fdl->debug_file);
  4176. }
  4177. stat = poll( fdl->poll_list, fdl->poll_used, timeout );
  4178. }
  4179. else
  4180. {
  4181. struct timeval tv, *ptv = &tv;
  4182. if ( !fdl->timeout_usec )
  4183. ptv = NULL;
  4184. else if ( fdl->timeout_usec > now_usec )
  4185. {
  4186. const u_usec_t delta = fdl->timeout_usec - now_usec;
  4187. if ( delta > USEC_PER_YEAR )
  4188. {
  4189. tv.tv_sec = SEC_PER_YEAR;
  4190. tv.tv_usec = 0;
  4191. }
  4192. else if ( delta > USEC_PER_MSEC )
  4193. {
  4194. tv.tv_sec = delta / USEC_PER_SEC;
  4195. tv.tv_usec = delta % USEC_PER_SEC;
  4196. }
  4197. else
  4198. {
  4199. tv.tv_sec = 0;
  4200. tv.tv_usec = USEC_PER_MSEC;
  4201. }
  4202. }
  4203. else
  4204. tv.tv_sec = tv.tv_usec = 0;
  4205. if (fdl->debug_file)
  4206. {
  4207. if (ptv)
  4208. fprintf(fdl->debug_file,"# SELECT: usec=%lld, Δnow=%lld, timeout=%llu.%06llu\n",
  4209. fdl->timeout_usec, fdl->timeout_usec - now_usec,
  4210. (u64)ptv->tv_sec, (u64)ptv->tv_usec );
  4211. else
  4212. fprintf(fdl->debug_file,"# SELECT: usec=%lld, Δnow=%lld, timeout=NULL\n",
  4213. fdl->timeout_usec, fdl->timeout_usec - now_usec );
  4214. fflush(fdl->debug_file);
  4215. }
  4216. stat = select( fdl->max_fd+1, &fdl->readfds, &fdl->writefds,
  4217. &fdl->exceptfds, ptv );
  4218. }
  4219. finish_wait_fdl(fdl,now_usec);
  4220. return stat;
  4221. }
  4222. ///////////////////////////////////////////////////////////////////////////////
  4223. int PWaitFDList
  4224. (
  4225. FDList_t *fdl, // valid socket list
  4226. const sigset_t *sigmask // NULL or signal mask
  4227. )
  4228. {
  4229. DASSERT(fdl);
  4230. #ifdef __APPLE__
  4231. // [[2do]] ??? untested work around
  4232. if (fdl->use_poll)
  4233. {
  4234. sigset_t origmask;
  4235. if (sigmask)
  4236. sigprocmask(SIG_SETMASK,sigmask,&origmask);
  4237. const int stat = WaitFDList(fdl);
  4238. if (sigmask)
  4239. sigprocmask(SIG_SETMASK,&origmask,NULL);
  4240. return stat;
  4241. }
  4242. #endif
  4243. const u_usec_t now_usec = prepare_wait_fdl(fdl);
  4244. struct timespec ts, *pts = &ts;
  4245. if ( fdl->timeout_usec > now_usec )
  4246. {
  4247. const u64 delta = fdl->timeout_usec - now_usec;
  4248. if ( delta > 1000000ull * 0x7fffffffull )
  4249. pts = NULL;
  4250. else if ( delta > USEC_PER_MSEC )
  4251. {
  4252. ts.tv_sec = delta / 1000000;
  4253. ts.tv_nsec = delta % 1000000 * 1000;
  4254. }
  4255. else
  4256. {
  4257. ts.tv_sec = 0;
  4258. ts.tv_nsec = NSEC_PER_MSEC;
  4259. }
  4260. }
  4261. else
  4262. ts.tv_sec = ts.tv_nsec = 0;
  4263. #ifdef __APPLE__
  4264. const int stat = pselect( fdl->max_fd+1, &fdl->readfds, &fdl->writefds,
  4265. &fdl->exceptfds, pts, sigmask );
  4266. #else
  4267. const int stat = fdl->use_poll
  4268. ? ppoll( fdl->poll_list, fdl->poll_used, pts, sigmask )
  4269. : pselect( fdl->max_fd+1, &fdl->readfds, &fdl->writefds,
  4270. &fdl->exceptfds, pts, sigmask );
  4271. #endif
  4272. finish_wait_fdl(fdl,now_usec);
  4273. return stat;
  4274. }
  4275. ///////////////////////////////////////////////////////////////////////////////
  4276. mem_t CheckUnixSocketPathMem
  4277. (
  4278. mem_t src, // NULL or source path to analyse
  4279. int tolerance // <1: 'unix:', 'file:', '/', './' and '../' detected
  4280. // 1: not 'NAME:' && at relast one '/'
  4281. // 2: not 'NAME:'
  4282. )
  4283. {
  4284. if ( src.ptr && src.len )
  4285. {
  4286. ccp ptr = src.ptr;
  4287. ccp end = ptr + src.len;
  4288. switch (*ptr)
  4289. {
  4290. case '/':
  4291. return src;
  4292. case '.':
  4293. {
  4294. ccp s = ptr+1;
  4295. if ( s < end && *s == '.' )
  4296. s++;
  4297. if ( s < end && *s == '/' )
  4298. return src;
  4299. }
  4300. break;
  4301. case 'f':
  4302. if ( end-ptr >= 5 && !memcmp(ptr,"file:",5) )
  4303. return RightMem(src,-5);
  4304. case 'u':
  4305. if ( end-ptr >= 5 && !memcmp(ptr,"unix:",5) )
  4306. return RightMem(src,-5);
  4307. break;
  4308. }
  4309. if ( tolerance > 0 )
  4310. {
  4311. while ( ptr < end && isalnum((int)*ptr) )
  4312. ptr++;
  4313. if ( ptr < end && *ptr != ':' && ( tolerance > 1 || strchr(ptr,'/')) )
  4314. return BehindMem(src,ptr);
  4315. }
  4316. }
  4317. return NullMem;
  4318. }
  4319. ///////////////////////////////////////////////////////////////////////////////
  4320. ccp CheckUnixSocketPath
  4321. (
  4322. ccp src, // NULL or source path to analyse
  4323. int tolerance // <1: 'unix:', 'file:', '/', './' and '../' detected
  4324. // 1: not 'NAME:' && at relast one '/'
  4325. // 2: not 'NAME:'
  4326. )
  4327. {
  4328. mem_t res = CheckUnixSocketPathMem(MemByString(src),tolerance);
  4329. return res.ptr;
  4330. }
  4331. //
  4332. ///////////////////////////////////////////////////////////////////////////////
  4333. /////////////// Catch Output ///////////////
  4334. ///////////////////////////////////////////////////////////////////////////////
  4335. int CatchIgnoreOutput
  4336. (
  4337. struct CatchOutput_t *ctrl, // control struct incl. data
  4338. int call_mode // 0:init, 1:new data, 2:term
  4339. )
  4340. {
  4341. DASSERT(ctrl);
  4342. ClearGrowBuffer(&ctrl->buf);
  4343. return 0;
  4344. }
  4345. ///////////////////////////////////////////////////////////////////////////////
  4346. void ResetCatchOutput ( CatchOutput_t *co, uint n )
  4347. {
  4348. while ( n-- > 0 )
  4349. {
  4350. if ( co->pipe_fd[0] != -1 )
  4351. {
  4352. close(co->pipe_fd[0]);
  4353. co->pipe_fd[0] = -1;
  4354. }
  4355. if ( co->pipe_fd[1] != -1 )
  4356. {
  4357. close(co->pipe_fd[1]);
  4358. co->pipe_fd[1] = -1;
  4359. }
  4360. ResetGrowBuffer(&co->buf);
  4361. co++;
  4362. }
  4363. }
  4364. ///////////////////////////////////////////////////////////////////////////////
  4365. enumError CatchOutput
  4366. (
  4367. ccp command, // command to execute
  4368. int argc, // num(arguments) in 'argv'; -1: argv is NULL terminated
  4369. char *const* argv, // list of arguments
  4370. CatchOutputFunc stdout_func, // NULL or function to catch stdout
  4371. CatchOutputFunc stderr_func, // NULL or function to catch stderr
  4372. void *user_ptr, // NULL or user defined parameter
  4373. bool silent // true: suppress error messages
  4374. )
  4375. {
  4376. if ( !command || !*command )
  4377. return ERR_NOTHING_TO_DO;
  4378. //--- setup streams & pipes
  4379. uint n_catch_out = 0;
  4380. CatchOutput_t catch_out[2];
  4381. memset(catch_out,0,sizeof(catch_out));
  4382. uint i;
  4383. for ( i = 1; i <= 2; i++ )
  4384. {
  4385. CatchOutputFunc func = i==1 ? stdout_func : stderr_func;
  4386. if (!func)
  4387. continue;
  4388. CatchOutput_t *co = catch_out + n_catch_out++;
  4389. co->mode = i;
  4390. co->func = func;
  4391. co->user_ptr = user_ptr;
  4392. InitializeGrowBuffer(&co->buf,16*KiB);
  4393. if ( pipe(co->pipe_fd) == -1 )
  4394. {
  4395. ResetCatchOutput(catch_out,n_catch_out);
  4396. if (!silent)
  4397. ERROR1(ERR_CANT_CREATE,"Can't create pipe\n");
  4398. return ERR_CANT_CREATE;
  4399. }
  4400. }
  4401. //--- setup arguments
  4402. if (!argv)
  4403. argc = 0;
  4404. else if ( argc < 0 )
  4405. for ( argc = 0; argv[argc]; argc++ )
  4406. ;
  4407. const uint local_args_size = 100;
  4408. char *local_args[local_args_size+2];
  4409. char **args = local_args;
  4410. if ( argc >= local_args_size )
  4411. args = MALLOC((argc+2)*sizeof(*args));
  4412. char **destarg = args;
  4413. *destarg++ = (char*)command;
  4414. for ( i = 0; i < argc; i++ )
  4415. *destarg++ = argv[i];
  4416. *destarg = 0;
  4417. #ifdef TEST
  4418. {
  4419. printf("EXEC '%s' [argc=%u]\n",command,argc);
  4420. uint i;
  4421. for ( i = 0; i < argc+2; i++ )
  4422. printf("%3d: %s\n",i,args[i]);
  4423. }
  4424. #endif
  4425. //--- fork
  4426. enumError err = ERR_OK;
  4427. int fork_stat = fork();
  4428. if ( fork_stat < 0 )
  4429. {
  4430. if (!silent)
  4431. ERROR1(ERR_ERROR,"Can't fork: %s\n",command);
  4432. err = ERR_ERROR;
  4433. }
  4434. else if (fork_stat)
  4435. {
  4436. PRINT("WAIT FOR %d\n",fork_stat);
  4437. CatchOutput_t *co, *co_end = catch_out + n_catch_out;
  4438. for ( co = catch_out; co < co_end; co++ )
  4439. {
  4440. DASSERT(co->func);
  4441. co->func(co,0);
  4442. close(co->pipe_fd[1]); // close childs (write) end
  4443. co->pipe_fd[1] = -1;
  4444. }
  4445. FDList_t fdl;
  4446. InitializeFDList(&fdl,false);
  4447. char buf[0x1000];
  4448. for(;;)
  4449. {
  4450. ClearFDList(&fdl);
  4451. fdl.timeout_usec = GetTimeUSec(false) + 60*USEC_PER_SEC;
  4452. for ( co = catch_out; co < co_end; co++ )
  4453. AddFDList(&fdl,co->pipe_fd[0],POLLIN);
  4454. if ( fdl.max_fd == -1 )
  4455. break;
  4456. const int stat = WaitFDList(&fdl);
  4457. if ( stat > 0 )
  4458. {
  4459. for ( co = catch_out; co < co_end; co++ )
  4460. {
  4461. int fd = co->pipe_fd[0];
  4462. uint event = GetEventFDList(&fdl,fd,0);
  4463. if ( event & POLLIN )
  4464. {
  4465. ssize_t read_size = read(fd,buf,sizeof(buf));
  4466. PRINT("READ %d[%d], %zd bytes\n",co->mode,fd,read_size);
  4467. if ( read_size > 0 )
  4468. {
  4469. PrepareGrowBuffer(&co->buf,read_size,true);
  4470. InsertGrowBuffer(&co->buf,buf,read_size);
  4471. if (co->func)
  4472. co->func(co,1);
  4473. }
  4474. else
  4475. {
  4476. close(fd);
  4477. co->pipe_fd[0] = -1;
  4478. }
  4479. }
  4480. }
  4481. }
  4482. }
  4483. ResetFDList(&fdl);
  4484. int wait_stat;
  4485. for(;;)
  4486. {
  4487. int pid = waitpid(fork_stat,&wait_stat,0);
  4488. PRINT("RESUME: %d = %x: exited=%d, wait_stat=%d [%s]\n",
  4489. wait_stat, wait_stat, WIFEXITED(wait_stat),
  4490. WEXITSTATUS(wait_stat), GetErrorName(WEXITSTATUS(wait_stat),"?") );
  4491. if ( pid == fork_stat )
  4492. break;
  4493. }
  4494. for ( co = catch_out; co < co_end; co++ )
  4495. if (co->func)
  4496. co->func(co,2);
  4497. ResetCatchOutput(catch_out,n_catch_out);
  4498. if (WIFEXITED(wait_stat))
  4499. return WEXITSTATUS(wait_stat);
  4500. if (!silent)
  4501. ERROR1(ERR_SYNTAX,"Child teminated with error: %s\n",command);
  4502. return ERR_SYNTAX;
  4503. }
  4504. else
  4505. {
  4506. PRINT("CHILD\n");
  4507. CatchOutput_t *co, *co_end = catch_out + n_catch_out;
  4508. for ( co = catch_out; co < co_end; co++ )
  4509. {
  4510. close(co->pipe_fd[0]); // close parents (read) end
  4511. co->pipe_fd[0] = -1;
  4512. if ( dup2(co->pipe_fd[1],co->mode) == -1 )
  4513. {
  4514. if (!silent)
  4515. ERROR1(ERR_CANT_CREATE,"dup2(,%d) failed: %s\n",
  4516. co->mode, command );
  4517. _exit(ERR_CANT_CREATE);
  4518. }
  4519. }
  4520. execvp(command,args);
  4521. if (!silent)
  4522. ERROR1(ERR_CANT_CREATE,"Can't create process: %s\n",command);
  4523. _exit(ERR_CANT_CREATE);
  4524. }
  4525. if ( args != local_args )
  4526. FREE(args);
  4527. return err;
  4528. }
  4529. ///////////////////////////////////////////////////////////////////////////////
  4530. enumError CatchOutputLine
  4531. (
  4532. ccp command_line, // command line to execute
  4533. CatchOutputFunc stdout_func, // NULL or function to catch stdout
  4534. CatchOutputFunc stderr_func, // NULL or function to catch stderr
  4535. void *user_ptr, // NULL or user defined parameter
  4536. bool silent // true: suppress error messages
  4537. )
  4538. {
  4539. SplitArg_t sa;
  4540. ScanSplitArg(&sa,true,command_line,0,0);
  4541. enumError err = ERR_SYNTAX;
  4542. if ( sa.argc > 1 )
  4543. err = CatchOutput( sa.argv[1], sa.argc-2, sa.argv+2,
  4544. stdout_func, stderr_func, user_ptr, silent );
  4545. ResetSplitArg(&sa);
  4546. return err;
  4547. }
  4548. //
  4549. ///////////////////////////////////////////////////////////////////////////////
  4550. /////////////// scan sections ///////////////
  4551. ///////////////////////////////////////////////////////////////////////////////
  4552. bool FindSection
  4553. (
  4554. // search file until next section found
  4555. // => read line and store data into 'si'
  4556. // => return TRUE, if section found
  4557. SectionInfo_t *si, // valid section info
  4558. char *buf, // use this buffer for line scanning
  4559. uint buf_size, // size of 'buf'
  4560. bool scan_buf // true: buffer contains already a valid
  4561. // and NULL terminated line
  4562. )
  4563. {
  4564. DASSERT(si);
  4565. DASSERT(si->f);
  4566. DASSERT(buf);
  4567. DASSERT( buf_size > 10 );
  4568. FreeString(si->section);
  4569. FreeString(si->path);
  4570. si->section = si->path = 0;
  4571. si->index = -1;
  4572. ResetParamField(&si->param);
  4573. buf_size--;
  4574. for(;;)
  4575. {
  4576. if ( !scan_buf && !fgets(buf,buf_size,si->f) )
  4577. break;
  4578. buf[buf_size] = 0;
  4579. scan_buf = false;
  4580. char *ptr = buf;
  4581. while ( *ptr > 0 && *ptr <= ' ' )
  4582. ptr++;
  4583. if ( *ptr != '[' )
  4584. continue;
  4585. ccp sect = ++ptr;
  4586. while ( isalnum((int)*ptr) || *ptr == '-' || *ptr == '_' )
  4587. ptr++;
  4588. ccp path = ptr;
  4589. if ( *ptr == '/' )
  4590. {
  4591. *ptr++ = 0;
  4592. path = ptr;
  4593. while ( *ptr > ' ' && *ptr != ':' && *ptr != ']' )
  4594. ptr++;
  4595. }
  4596. int index = -1;
  4597. if ( *ptr == ':' )
  4598. {
  4599. *ptr++ = 0;
  4600. index = str2ul(ptr,&ptr,10);
  4601. }
  4602. if ( *ptr != ']' )
  4603. continue;
  4604. *ptr = 0;
  4605. si->section = STRDUP(sect);
  4606. if (*path)
  4607. si->path = STRDUP(path);
  4608. si->index = index;
  4609. return true;
  4610. }
  4611. return false;
  4612. }
  4613. ///////////////////////////////////////////////////////////////////////////////
  4614. int ScanSections
  4615. (
  4616. // return the last returned value by On*()
  4617. FILE *f, // source file
  4618. SectionFunc OnSection, // not NULL: call this function for each section
  4619. // on result: <0: abort, 0:next section, 1:scan param
  4620. SectionFunc OnParams, // not NULL: call this function after param scan
  4621. // on result: <0: abort, continue
  4622. void *user_param // user defined parameter
  4623. )
  4624. {
  4625. DASSERT(f);
  4626. SectionInfo_t si;
  4627. memset(&si,0,sizeof(si));
  4628. si.f = f;
  4629. si.user_param = user_param;
  4630. InitializeParamField(&si.param);
  4631. si.param.free_data = true;
  4632. int stat = 0;
  4633. char buf[10000];
  4634. bool scan_buf = false;
  4635. while (FindSection(&si,buf,sizeof(buf),scan_buf))
  4636. {
  4637. scan_buf = false;
  4638. if (OnSection)
  4639. {
  4640. int stat = OnSection(&si);
  4641. if ( stat < 0 )
  4642. break;
  4643. if (!stat)
  4644. continue;
  4645. }
  4646. if (!OnParams)
  4647. continue;
  4648. // scan param
  4649. while (fgets(buf,sizeof(buf)-1,f))
  4650. {
  4651. buf[sizeof(buf)-1] = 0;
  4652. char *ptr = buf;
  4653. while ( *ptr > 0 && *ptr <= ' ' )
  4654. ptr++;
  4655. if ( *ptr == '[' )
  4656. {
  4657. scan_buf = true;
  4658. break;
  4659. }
  4660. //--- scan name
  4661. ccp name = ptr;
  4662. while ( *ptr > ' ' && *ptr != '=' )
  4663. ptr++;
  4664. char *name_end = ptr;
  4665. //--- scan param
  4666. while ( *ptr == ' ' || *ptr == '\t' )
  4667. ptr++;
  4668. if ( *ptr != '=' )
  4669. continue;
  4670. if ( *++ptr == ' ' ) // skip max 1 space
  4671. ptr++;
  4672. ccp value = ptr;
  4673. while ( *ptr && *ptr != '\n' && *ptr != '\r' )
  4674. ptr++;
  4675. *name_end = *ptr = 0;
  4676. InsertParamField(&si.param,name,false,0,STRDUP(value));
  4677. }
  4678. stat = OnParams(&si);
  4679. if ( stat < 0 )
  4680. break;
  4681. }
  4682. FreeString(si.section);
  4683. FreeString(si.path);
  4684. ResetParamField(&si.param);
  4685. return stat;
  4686. }
  4687. //
  4688. ///////////////////////////////////////////////////////////////////////////////
  4689. /////////////// scan configuration ///////////////
  4690. ///////////////////////////////////////////////////////////////////////////////
  4691. void ResetRestoreState ( RestoreState_t *rs )
  4692. {
  4693. if (rs)
  4694. {
  4695. ResetParamField(&rs->param);
  4696. InitializeRestoreState(rs);
  4697. }
  4698. }
  4699. ///////////////////////////////////////////////////////////////////////////////
  4700. enumError ScanRestoreState
  4701. (
  4702. RestoreState_t *rs, // valid control
  4703. void *data, // file data, modified, terminated by NULL or LF
  4704. uint size, // size of file data
  4705. void **end_data // not NULL: store end of analysed here
  4706. )
  4707. {
  4708. DASSERT(rs);
  4709. DASSERT(data||!size);
  4710. if (!size)
  4711. return ERR_OK;
  4712. char *ptr = data;
  4713. char *end = ptr + size;
  4714. if (!rs->param.field)
  4715. InitializeParamField(&rs->param);
  4716. //--- scan name=value
  4717. while ( ptr < end )
  4718. {
  4719. //--- skip lines and blanks
  4720. while ( ptr < end && (uchar)*ptr <= ' ' )
  4721. ptr++;
  4722. if ( *ptr == '[' )
  4723. break;
  4724. //--- scan name
  4725. ccp name = ptr;
  4726. while ( *ptr >= '!' && *ptr <= '~' && *ptr != '=' )
  4727. ptr++;
  4728. char *name_end = ptr;
  4729. //--- scan param
  4730. while ( *ptr == ' ' || *ptr == '\t' )
  4731. ptr++;
  4732. if ( *ptr != '=' )
  4733. continue;
  4734. if ( *++ptr == ' ' ) // skip max 1 space
  4735. ptr++;
  4736. ccp value = ptr;
  4737. while ( (uchar)*ptr >= ' ' || *ptr == '\t' )
  4738. ptr++;
  4739. *name_end = *ptr = 0;
  4740. InsertParamField(&rs->param,name,false,0,value);
  4741. //--- next line
  4742. while ( ptr < end && *ptr && *ptr != '\n' )
  4743. ptr++;
  4744. }
  4745. if (end_data)
  4746. *end_data = ptr;
  4747. return ERR_OK;
  4748. }
  4749. ///////////////////////////////////////////////////////////////////////////////
  4750. enumError RestoreState
  4751. (
  4752. const RestoreStateTab_t
  4753. *table, // section table, terminated by name==0
  4754. void *data, // file data, modified, terminated by NULL or LF
  4755. uint size, // size of file data
  4756. RestoreStateLog_t log_mode, // print warnings (bit field)
  4757. FILE *log_file, // error file; if NULL use stderr
  4758. cvp user_param // pointer provided by user
  4759. )
  4760. {
  4761. DASSERT(data||!size);
  4762. if (!size)
  4763. return ERR_OK;
  4764. if (!log_file)
  4765. log_file = stderr;
  4766. char *ptr = data;
  4767. char *end = ptr + size;
  4768. if ( !*end && *end != '\n' )
  4769. end[-1] = 0;
  4770. while ( ptr < end )
  4771. {
  4772. //--- search next section
  4773. while ( ptr < end && (uchar)*ptr <= ' ' )
  4774. ptr++;
  4775. if ( *ptr != '[' )
  4776. {
  4777. while ( ptr < end && *ptr && *ptr != '\n' )
  4778. ptr++;
  4779. if ( ptr < end )
  4780. ptr++;
  4781. continue;
  4782. }
  4783. ccp sect = ++ptr;
  4784. while ( isalnum((int)*ptr) || *ptr == '-' || *ptr == '_' )
  4785. ptr++;
  4786. ccp path = ptr;
  4787. if ( *ptr == '/' )
  4788. {
  4789. *ptr++ = 0;
  4790. path = ptr;
  4791. while ( isalnum((int)*ptr) || *ptr == '-' || *ptr == '_' || *ptr == '/' )
  4792. ptr++;
  4793. }
  4794. int index = -1;
  4795. if ( *ptr == ':' )
  4796. {
  4797. *ptr++ = 0;
  4798. index = strtoul(ptr,0,10);
  4799. }
  4800. *ptr = 0;
  4801. const RestoreStateTab_t *tab;
  4802. for ( tab = table; tab->name; tab++ )
  4803. if (!strcmp(tab->name,sect))
  4804. break;
  4805. if (!tab->name)
  4806. {
  4807. if ( log_mode & RSL_WARNINGS )
  4808. fprintf(log_file,"! #RESTORE: Unknown section: %s\n",sect);
  4809. continue;
  4810. }
  4811. TRACE("SECT: [%s/%s:%d]\n",sect,path,index);
  4812. if (!tab->func) // no need to analyse section
  4813. continue;
  4814. //--- scan name=value
  4815. RestoreState_t rs;
  4816. rs.sect = sect;
  4817. rs.path = path;
  4818. rs.index = index;
  4819. rs.user_param = user_param;
  4820. rs.user_info = 0;
  4821. rs.log_mode = log_mode;
  4822. rs.log_file = log_file;
  4823. InitializeParamField(&rs.param);
  4824. while ( ptr < end )
  4825. {
  4826. //--- next line
  4827. while ( ptr < end && *ptr && *ptr != '\n' )
  4828. ptr++;
  4829. //--- skip lines and blanks
  4830. while ( ptr < end && (uchar)*ptr <= ' ' )
  4831. ptr++;
  4832. if ( *ptr == '[' )
  4833. break;
  4834. //--- scan name
  4835. ccp name = ptr;
  4836. while ( *ptr >= '!' && *ptr <= '~' && *ptr != '=' )
  4837. ptr++;
  4838. char *name_end = ptr;
  4839. //--- scan param
  4840. while ( *ptr == ' ' || *ptr == '\t' )
  4841. ptr++;
  4842. if ( *ptr != '=' )
  4843. continue;
  4844. if ( *++ptr == ' ' ) // skip max 1 space
  4845. ptr++;
  4846. ccp value = ptr;
  4847. while ( (uchar)*ptr >= ' ' || *ptr == '\t' )
  4848. ptr++;
  4849. *name_end = *ptr = 0;
  4850. InsertParamField(&rs.param,name,false,0,value);
  4851. }
  4852. #ifdef TEST0
  4853. {
  4854. ParamFieldItem_t *ptr = rs.param.field, *end;
  4855. for ( end = ptr + rs.param.used; ptr < end; ptr++ )
  4856. printf("%4u: %20s = %s|\n",ptr->num,ptr->key,(ccp)ptr->data);
  4857. }
  4858. #endif
  4859. if (tab->func)
  4860. tab->func(&rs,tab->user_table);
  4861. if ( log_file && log_mode & RSL_UNUSED_NAMES )
  4862. {
  4863. ParamFieldItem_t *ptr = rs.param.field, *end;
  4864. for ( end = ptr + rs.param.used; ptr < end; ptr++ )
  4865. if (!ptr->num)
  4866. {
  4867. const uint len = strlen((ccp)ptr->data);
  4868. if ( len > 20 )
  4869. fprintf(log_file,"! #RESTORE[%s]: Unused: %s [%u] %.20s...\n",
  4870. PrintRestoreStateSection(&rs),
  4871. ptr->key, len, (ccp)ptr->data );
  4872. else
  4873. fprintf(log_file,"! #RESTORE[%s]: Unused: %s [%u] %s\n",
  4874. PrintRestoreStateSection(&rs),
  4875. ptr->key, len, (ccp)ptr->data );
  4876. }
  4877. }
  4878. ResetRestoreState(&rs);
  4879. }
  4880. return ERR_OK;
  4881. }
  4882. ///////////////////////////////////////////////////////////////////////////////
  4883. ccp PrintRestoreStateSection ( const RestoreState_t *rs )
  4884. {
  4885. // print into cyclic buffer
  4886. DASSERT(rs);
  4887. uint len = strlen(rs->sect) + strlen(rs->path) + 2;
  4888. char index[20];
  4889. *index = 0;
  4890. if ( rs->index >= 0 )
  4891. len += snprintf(index,sizeof(index),":%u",rs->index);
  4892. char *buf = GetCircBuf(len);
  4893. if (*rs->path)
  4894. snprintf(buf,len,"%s/%s%s",rs->sect,rs->path,index);
  4895. else
  4896. snprintf(buf,len,"%s%s",rs->sect,index);
  4897. return buf;
  4898. }
  4899. ///////////////////////////////////////////////////////////////////////////////
  4900. ///////////////////////////////////////////////////////////////////////////////
  4901. ParamFieldItem_t * GetParamField
  4902. (
  4903. const RestoreState_t *rs, // valid restore-state structure
  4904. ccp name // name of member
  4905. )
  4906. {
  4907. DASSERT(rs);
  4908. DASSERT(name);
  4909. ParamFieldItem_t *it = FindParamField(&rs->param,name);
  4910. if (it)
  4911. it->num++;
  4912. else if ( rs->log_mode & RSL_MISSED_NAMES )
  4913. fprintf(rs->log_file,"! #RESTORE[%s]: Missed: %s\n",
  4914. PrintRestoreStateSection(rs), name );
  4915. return it;
  4916. }
  4917. ///////////////////////////////////////////////////////////////////////////////
  4918. int GetParamFieldINT
  4919. (
  4920. const RestoreState_t *rs, // valid restore-state structure
  4921. ccp name, // name of member
  4922. int not_found // return this value if not found
  4923. )
  4924. {
  4925. DASSERT(rs);
  4926. DASSERT(name);
  4927. ParamFieldItem_t *it = GetParamField(rs,name);
  4928. return it ? str2l((ccp)it->data,0,10) : not_found;
  4929. }
  4930. ///////////////////////////////////////////////////////////////////////////////
  4931. uint GetParamFieldUINT
  4932. (
  4933. const RestoreState_t *rs, // valid restore-state structure
  4934. ccp name, // name of member
  4935. uint not_found // return this value if not found
  4936. )
  4937. {
  4938. DASSERT(rs);
  4939. DASSERT(name);
  4940. ParamFieldItem_t *it = GetParamField(rs,name);
  4941. return it ? str2ul((ccp)it->data,0,10) : not_found;
  4942. }
  4943. ///////////////////////////////////////////////////////////////////////////////
  4944. u64 GetParamFieldS64
  4945. (
  4946. const RestoreState_t *rs, // valid restore-state structure
  4947. ccp name, // name of member
  4948. s64 not_found // return this value if not found
  4949. )
  4950. {
  4951. DASSERT(rs);
  4952. DASSERT(name);
  4953. ParamFieldItem_t *it = GetParamField(rs,name);
  4954. return it ? str2ll((ccp)it->data,0,10) : not_found;
  4955. }
  4956. ///////////////////////////////////////////////////////////////////////////////
  4957. u64 GetParamFieldU64
  4958. (
  4959. const RestoreState_t *rs, // valid restore-state structure
  4960. ccp name, // name of member
  4961. u64 not_found // return this value if not found
  4962. )
  4963. {
  4964. DASSERT(rs);
  4965. DASSERT(name);
  4966. ParamFieldItem_t *it = GetParamField(rs,name);
  4967. return it ? str2ull((ccp)it->data,0,10) : not_found;
  4968. }
  4969. ///////////////////////////////////////////////////////////////////////////////
  4970. ///////////////////////////////////////////////////////////////////////////////
  4971. float GetParamFieldFLOAT
  4972. (
  4973. const RestoreState_t *rs, // valid restore-state structure
  4974. ccp name, // name of member
  4975. float not_found // return this value if not found
  4976. )
  4977. {
  4978. DASSERT(rs);
  4979. DASSERT(name);
  4980. ParamFieldItem_t *it = GetParamField(rs,name);
  4981. return it ? strtof((ccp)it->data,0) : not_found;
  4982. }
  4983. //-----------------------------------------------------------------------------
  4984. double GetParamFieldDBL
  4985. (
  4986. const RestoreState_t *rs, // valid restore-state structure
  4987. ccp name, // name of member
  4988. double not_found // return this value if not found
  4989. )
  4990. {
  4991. DASSERT(rs);
  4992. DASSERT(name);
  4993. ParamFieldItem_t *it = GetParamField(rs,name);
  4994. return it ? strtod((ccp)it->data,0) : not_found;
  4995. }
  4996. //-----------------------------------------------------------------------------
  4997. long double GetParamFieldLDBL
  4998. (
  4999. const RestoreState_t *rs, // valid restore-state structure
  5000. ccp name, // name of member
  5001. long double not_found // return this value if not found
  5002. )
  5003. {
  5004. DASSERT(rs);
  5005. DASSERT(name);
  5006. ParamFieldItem_t *it = GetParamField(rs,name);
  5007. return it ? strtold((ccp)it->data,0) : not_found;
  5008. }
  5009. ///////////////////////////////////////////////////////////////////////////////
  5010. ///////////////////////////////////////////////////////////////////////////////
  5011. int GetParamFieldBUF
  5012. (
  5013. // returns >=0: length of read data; -1:nothing done (not_found==NULL)
  5014. char *buf, // buffer to store result
  5015. uint buf_size, // size of buffer
  5016. const RestoreState_t *rs, // valid restore-state structure
  5017. ccp name, // name of member
  5018. EncodeMode_t decode, // decoding mode, fall back to OFF
  5019. // supported: STRING, UTF8, BASE64, BASE64X
  5020. ccp not_found // not NULL: store this value if not found
  5021. )
  5022. {
  5023. DASSERT(buf);
  5024. DASSERT(buf_size>1);
  5025. DASSERT(rs);
  5026. DASSERT(name);
  5027. ParamFieldItem_t *it = GetParamField(rs,name);
  5028. if (!it)
  5029. return not_found ? StringCopyS(buf,buf_size,not_found) - buf : -1;
  5030. return DecodeByMode(buf,buf_size,(ccp)it->data,-1,decode,0);
  5031. }
  5032. ///////////////////////////////////////////////////////////////////////////////
  5033. mem_t GetParamFieldMEM
  5034. (
  5035. // Returns the decoded 'source'. Result is NULL-terminated.
  5036. // It points either to 'buf' or is alloced (on buf==NULL or too less space)
  5037. // If alloced (mem.ptr!=buf) => call FreeMem(&mem)
  5038. char *buf, // buffer to store result
  5039. uint buf_size, // size of buffer
  5040. const RestoreState_t *rs, // valid restore-state structure
  5041. ccp name, // name of member
  5042. EncodeMode_t decode, // decoding mode, fall back to OFF
  5043. // supported: STRING, UTF8, BASE64, BASE64X
  5044. mem_t not_found // not NULL: return this value
  5045. )
  5046. {
  5047. DASSERT(rs);
  5048. DASSERT(name);
  5049. ParamFieldItem_t *it = GetParamField(rs,name);
  5050. return it
  5051. ? DecodeByModeMem(buf,buf_size,(ccp)it->data,-1,decode,0)
  5052. : not_found;
  5053. }
  5054. //
  5055. ///////////////////////////////////////////////////////////////////////////////
  5056. /////////////// save + restore config by table ///////////////
  5057. ///////////////////////////////////////////////////////////////////////////////
  5058. int srt_auto_dump = 0;
  5059. FILE *srt_auto_dump_file = 0;
  5060. ///////////////////////////////////////////////////////////////////////////////
  5061. void DumpStateTable
  5062. (
  5063. FILE *f, // valid output file
  5064. int indent,
  5065. const SaveRestoreTab_t
  5066. *srt // list of variables
  5067. )
  5068. {
  5069. if ( !f || !srt )
  5070. return;
  5071. indent = NormalizeIndent(indent);
  5072. printf(
  5073. "%*s off size *n(e) ty em name\n"
  5074. "%*s----------------------------------\n",
  5075. indent,"",
  5076. indent,"" );
  5077. for ( ; srt->type != SRT__TERM; srt++ )
  5078. {
  5079. if ( srt->type == SRT__IS_LIST )
  5080. {
  5081. if ( srt->name )
  5082. fprintf(f,"%*s# %s\n",indent,"",srt->name);
  5083. else
  5084. putc('\n',f);
  5085. }
  5086. else
  5087. fprintf(f,"%*s%4u %4u *%-4d %2u %2u %s\n",
  5088. indent,"",
  5089. srt->offset, srt->size, srt->n_elem, srt->type, srt->emode,
  5090. srt->name ? srt->name
  5091. : srt->type == SRT_DEF_ARRAY ? " >ARRAY" : "-" );
  5092. }
  5093. }
  5094. ///////////////////////////////////////////////////////////////////////////////
  5095. static void print_scs_name ( FILE *f, int fw, int use_tab, ccp format, ... )
  5096. __attribute__ ((__format__(__printf__,4,5)));
  5097. static void print_scs_name ( FILE *f, int fw, int use_tab, ccp format, ... )
  5098. {
  5099. va_list arg;
  5100. va_start(arg,format);
  5101. const int len = vfprintf(f,format,arg);
  5102. va_end(arg);
  5103. if (use_tab)
  5104. {
  5105. const int tablen = ( use_tab - len ) / 8;
  5106. fprintf(f,"%.*s= ", tablen<0 ? 0 : tablen, Tabs20 );
  5107. }
  5108. else
  5109. fprintf(f,"%*s= ", len < fw ? fw-len : 0, "" );
  5110. }
  5111. //-----------------------------------------------------------------------------
  5112. static u64 SCS_get_uint ( const u8* data, int size )
  5113. {
  5114. switch(size)
  5115. {
  5116. case 1: return *(u8*) data;
  5117. case 2: return *(u16*)data;
  5118. case 4: return *(u32*)data;
  5119. case 8: return *(u64*)data;
  5120. }
  5121. return 0;
  5122. }
  5123. //-----------------------------------------------------------------------------
  5124. static u64 SCS_get_int ( const u8* data, int size )
  5125. {
  5126. switch(size)
  5127. {
  5128. case 1: return *(s8*) data;
  5129. case 2: return *(s16*)data;
  5130. case 4: return *(s32*)data;
  5131. case 8: return *(s64*)data;
  5132. }
  5133. return 0;
  5134. }
  5135. //-----------------------------------------------------------------------------
  5136. static void SCS_save_string ( FILE *f, ccp src, int slen, EncodeMode_t emode )
  5137. {
  5138. if (!src)
  5139. fputc('%',f);
  5140. else
  5141. {
  5142. char buf[4000];
  5143. mem_t res = EncodeByModeMem(buf,sizeof(buf),src,slen,emode);
  5144. if (res.len)
  5145. {
  5146. const bool quote = NeedsQuotesByEncoding(emode);
  5147. if (quote)
  5148. fputc('"',f);
  5149. fwrite(res.ptr,1,res.len,f);
  5150. if (quote)
  5151. fputc('"',f);
  5152. }
  5153. if ( res.ptr != buf )
  5154. FreeMem(&res);
  5155. }
  5156. }
  5157. //
  5158. //-----------------------------------------------------------------------------
  5159. void SaveCurrentStateByTable
  5160. (
  5161. FILE *f, // valid output file
  5162. cvp data0, // valid pointer to source struct
  5163. const SaveRestoreTab_t
  5164. *srt, // list of variables
  5165. ccp prefix, // NULL or prefix for names
  5166. uint fw_name // field width of name, 0=AUTO
  5167. // tabs are used for multiple of 8 and for AUTO
  5168. )
  5169. {
  5170. DASSERT(data0);
  5171. DASSERT(srt);
  5172. if ( !f || !data0 || !srt )
  5173. return;
  5174. if ( srt_auto_dump && srt_auto_dump_file )
  5175. {
  5176. if ( srt_auto_dump > 0 )
  5177. srt_auto_dump--;
  5178. DumpStateTable(srt_auto_dump_file,0,srt);
  5179. }
  5180. if (!prefix)
  5181. prefix = EmptyString;
  5182. const uint prelen = strlen(prefix);
  5183. if (!fw_name)
  5184. {
  5185. const SaveRestoreTab_t *ptr;
  5186. for ( ptr = srt; ptr->type != SRT__TERM; ptr++ )
  5187. if (ptr->name)
  5188. {
  5189. int slen = strlen(ptr->name);
  5190. switch(srt->type)
  5191. {
  5192. case SRT_STRING_FIELD:
  5193. case SRT_PARAM_FIELD:
  5194. slen += 3;
  5195. break;
  5196. }
  5197. if ( fw_name < slen )
  5198. fw_name = slen;
  5199. }
  5200. fw_name += prelen;
  5201. fw_name = (fw_name+7)/8*8;
  5202. }
  5203. const int use_tab = (fw_name&7) ? 0 : (fw_name/8)*8+7;
  5204. const u8 *data = data0;
  5205. uint last_count = 0;
  5206. uint aelem_count = 0;
  5207. uint aelem_offset = 0;
  5208. for ( ; srt->type != SRT__TERM; srt++ )
  5209. {
  5210. if ( srt->type == SRT_DEF_ARRAY )
  5211. {
  5212. aelem_offset = srt->size;
  5213. if ( srt->n_elem < 0 )
  5214. {
  5215. aelem_count = -srt->n_elem;
  5216. if ( aelem_count > last_count )
  5217. aelem_count = last_count;
  5218. }
  5219. else
  5220. aelem_count = srt->n_elem;
  5221. continue;
  5222. }
  5223. if ( srt->type == SRT__IS_LIST )
  5224. {
  5225. if ( srt->name )
  5226. fprintf(f,"# %s\n",srt->name);
  5227. else
  5228. putc('\n',f);
  5229. continue;
  5230. }
  5231. if (!srt->name)
  5232. continue;
  5233. if ( srt->type < SRT__IS_LIST )
  5234. print_scs_name(f,fw_name,use_tab,"%s%s",prefix,srt->name);
  5235. int n_elem, elem_off;
  5236. bool have_array = aelem_count && aelem_offset;
  5237. if (have_array)
  5238. {
  5239. elem_off = aelem_offset;
  5240. n_elem = aelem_count;
  5241. }
  5242. else
  5243. {
  5244. elem_off = srt->size;
  5245. n_elem = srt->n_elem;
  5246. if ( n_elem < 0 )
  5247. {
  5248. have_array = true;
  5249. n_elem = -n_elem;
  5250. if ( n_elem > last_count )
  5251. n_elem = last_count;
  5252. }
  5253. else if ( n_elem > 1 )
  5254. have_array = true;
  5255. }
  5256. uint i, offset;
  5257. switch(srt->type)
  5258. {
  5259. case SRT_BOOL:
  5260. if (have_array)
  5261. {
  5262. const u8 *d = data + srt->offset;
  5263. for ( i = 0; i < n_elem; i++, d += elem_off )
  5264. fprintf(f,"%s%u", i ? "," : "", SCS_get_uint(d,srt->size)!=0 );
  5265. fputc('\n',f);
  5266. }
  5267. else
  5268. fprintf(f,"%u\n",SCS_get_uint(data+srt->offset,srt->size)!=0);
  5269. break;
  5270. case SRT_COUNT:
  5271. last_count = 0;
  5272. // fall through
  5273. case SRT_UINT:
  5274. if (have_array)
  5275. {
  5276. const u8 *d = data + srt->offset;
  5277. for ( i = 0; i < n_elem; i++, d += elem_off )
  5278. fprintf(f,"%s%llu", i ? "," : "", SCS_get_uint(d,srt->size) );
  5279. fputc('\n',f);
  5280. }
  5281. else
  5282. {
  5283. const u64 num = SCS_get_uint(data+srt->offset,srt->size);
  5284. if ( srt->type == SRT_COUNT && num <= UINT_MAX )
  5285. last_count = num;
  5286. fprintf(f,"%llu\n",num);
  5287. }
  5288. break;
  5289. case SRT_HEX:
  5290. if (have_array)
  5291. {
  5292. const u8 *d = data + srt->offset;
  5293. for ( i = 0; i < n_elem; i++, d += elem_off )
  5294. fprintf(f,"%s%#llx", i ? "," : "", SCS_get_uint(d,srt->size) );
  5295. fputc('\n',f);
  5296. }
  5297. else
  5298. fprintf(f,"%#llx\n",SCS_get_uint(data+srt->offset,srt->size));
  5299. break;
  5300. case SRT_INT:
  5301. if (have_array)
  5302. {
  5303. const u8 *d = data + srt->offset;
  5304. for ( i = 0; i < n_elem; i++, d += elem_off )
  5305. fprintf(f,"%s%lld", i ? "," : "", SCS_get_int(d,srt->size) );
  5306. fputc('\n',f);
  5307. }
  5308. else
  5309. fprintf(f,"%lld\n",SCS_get_int(data+srt->offset,srt->size));
  5310. break;
  5311. case SRT_FLOAT:
  5312. if ( srt->size == sizeof(float) )
  5313. {
  5314. const u8 *d = data + srt->offset;
  5315. for ( i = 0; i < n_elem; i++, d += elem_off )
  5316. fprintf(f, "%s%.8g", i ? "," : "", *(float*)d );
  5317. }
  5318. else if ( srt->size == sizeof(double) )
  5319. {
  5320. const u8 *d = data + srt->offset;
  5321. for ( i = 0; i < n_elem; i++, d += elem_off )
  5322. fprintf(f, "%s%.16g", i ? "," : "", *(double*)d );
  5323. }
  5324. else if ( srt->size == sizeof(long double) )
  5325. {
  5326. const u8 *d = data + srt->offset;
  5327. for ( i = 0; i < n_elem; i++, d += elem_off )
  5328. fprintf(f, "%s%.20Lg", i ? "," : "", *(long double*)d );
  5329. }
  5330. else
  5331. fputs("0.0",f);
  5332. fputc('\n',f);
  5333. break;
  5334. case SRT_XFLOAT:
  5335. if ( srt->size == sizeof(float) )
  5336. {
  5337. const u8 *d = data + srt->offset;
  5338. for ( i = 0; i < n_elem; i++, d += elem_off )
  5339. fprintf(f, "%s%a", i ? "," : "", *(float*)d );
  5340. }
  5341. else if ( srt->size == sizeof(double) )
  5342. {
  5343. const u8 *d = data + srt->offset;
  5344. for ( i = 0; i < n_elem; i++, d += elem_off )
  5345. fprintf(f, "%s%a", i ? "," : "", *(double*)d );
  5346. }
  5347. else if ( srt->size == sizeof(long double) )
  5348. {
  5349. const u8 *d = data + srt->offset;
  5350. for ( i = 0; i < n_elem; i++, d += elem_off )
  5351. fprintf(f, "%s%La", i ? "," : "", *(long double*)d );
  5352. }
  5353. else
  5354. fputs("0.0",f);
  5355. fputc('\n',f);
  5356. break;
  5357. //-----
  5358. case SRT_STRING_SIZE:
  5359. if ( n_elem > 0 )
  5360. for ( i = 0, offset = srt->offset;; offset += elem_off )
  5361. {
  5362. SCS_save_string(f,(char*)(data+offset),-1,srt->emode);
  5363. if ( ++i == n_elem )
  5364. break;
  5365. fputc(',',f);
  5366. }
  5367. fputc('\n',f);
  5368. break;
  5369. case SRT_STRING_ALLOC:
  5370. if ( n_elem > 0 )
  5371. for ( i = 0, offset = srt->offset;; offset += elem_off )
  5372. {
  5373. SCS_save_string(f,*(ccp*)(data+offset),-1,srt->emode);
  5374. if ( ++i == n_elem )
  5375. break;
  5376. fputc(',',f);
  5377. }
  5378. fputc('\n',f);
  5379. break;
  5380. case SRT_MEM:
  5381. {
  5382. if ( n_elem > 0 )
  5383. for ( i = 0, offset = srt->offset;; offset += elem_off )
  5384. {
  5385. mem_t *mem = (mem_t*)(data+offset);
  5386. SCS_save_string(f,mem->ptr,mem->len,srt->emode);
  5387. if ( ++i == n_elem )
  5388. break;
  5389. fputc(',',f);
  5390. }
  5391. fputc('\n',f);
  5392. break;
  5393. }
  5394. //-----
  5395. case SRT_STRING_FIELD:
  5396. {
  5397. print_scs_name(f,fw_name,use_tab,"%s%s@n",prefix,srt->name);
  5398. const StringField_t *sf = (StringField_t*)(data+srt->offset);
  5399. fprintf(f,"%u\n",sf->used);
  5400. int i;
  5401. ccp *ptr = sf->field;
  5402. for ( i = 0; i < sf->used; i++, ptr++ )
  5403. {
  5404. print_scs_name(f,fw_name,use_tab,"%s%s@%u",prefix,srt->name,i);
  5405. SCS_save_string(f,*ptr,-1,srt->emode);
  5406. fputc('\n',f);
  5407. }
  5408. break;
  5409. }
  5410. case SRT_PARAM_FIELD:
  5411. {
  5412. print_scs_name(f,fw_name,use_tab,"%s%s@n",prefix,srt->name);
  5413. const ParamField_t *pf = (ParamField_t*)(data+srt->offset);
  5414. fprintf(f,"%u\n",pf->used);
  5415. int i;
  5416. ParamFieldItem_t *ptr = pf->field;
  5417. for ( i = 0; i < pf->used; i++, ptr++ )
  5418. {
  5419. print_scs_name(f,fw_name,use_tab,"%s%s@%u",prefix,srt->name,i);
  5420. fprintf(f,"%d ",ptr->num);
  5421. SCS_save_string(f,ptr->key,-1,srt->emode);
  5422. fputc('\n',f);
  5423. }
  5424. break;
  5425. }
  5426. }
  5427. }
  5428. }
  5429. //
  5430. ///////////////////////////////////////////////////////////////////////////////
  5431. ///////////////////////////////////////////////////////////////////////////////
  5432. void RestoreStateByTable
  5433. (
  5434. RestoreState_t *rs, // info data, can be modified (cleaned after call)
  5435. void *data0, // valid pointer to destination struct
  5436. const SaveRestoreTab_t *srt, // list of variables
  5437. ccp prefix // NULL or prefix for names
  5438. )
  5439. {
  5440. DASSERT(rs);
  5441. DASSERT(srt);
  5442. DASSERT(data0);
  5443. if ( !rs || !data0 || !srt )
  5444. return;
  5445. if ( srt_auto_dump && srt_auto_dump_file )
  5446. {
  5447. if ( srt_auto_dump > 0 )
  5448. srt_auto_dump--;
  5449. DumpStateTable(srt_auto_dump_file,0,srt);
  5450. }
  5451. char buf[4000];
  5452. if (!prefix)
  5453. prefix = EmptyString;
  5454. uint last_count = 0;
  5455. uint aelem_count = 0;
  5456. uint aelem_offset = 0;
  5457. bool mode_add = false;
  5458. for ( ; srt->type != SRT__TERM; srt++ )
  5459. {
  5460. if ( srt->type == SRT_DEF_ARRAY )
  5461. {
  5462. aelem_offset = srt->size;
  5463. if ( srt->n_elem < 0 )
  5464. {
  5465. aelem_count = -srt->n_elem;
  5466. if ( aelem_count > last_count )
  5467. aelem_count = last_count;
  5468. }
  5469. else
  5470. aelem_count = srt->n_elem;
  5471. continue;
  5472. }
  5473. if ( srt->type >= SRT_MODE__BEGIN && srt->type <= SRT_MODE__END )
  5474. {
  5475. switch (srt->type)
  5476. {
  5477. case SRT_MODE_ASSIGN: mode_add = false; break;
  5478. case SRT_MODE_ADD: mode_add = true; break;
  5479. }
  5480. continue;
  5481. }
  5482. if ( srt->type == SRT__IS_LIST || !srt->name )
  5483. continue;
  5484. const u8 *data = data0;
  5485. if ( srt->type < SRT__IS_LIST )
  5486. StringCat2S(buf,sizeof(buf),prefix,srt->name);
  5487. int n_elem, elem_off;
  5488. if ( aelem_count && aelem_offset )
  5489. {
  5490. elem_off = aelem_offset;
  5491. n_elem = aelem_count;
  5492. }
  5493. else
  5494. {
  5495. elem_off = srt->size;
  5496. n_elem = srt->n_elem;
  5497. if ( n_elem < 0 )
  5498. {
  5499. n_elem = -n_elem;
  5500. if ( n_elem > last_count )
  5501. n_elem = last_count;
  5502. }
  5503. }
  5504. switch(srt->type)
  5505. {
  5506. case SRT_COUNT:
  5507. last_count = 0;
  5508. // fall through
  5509. case SRT_BOOL:
  5510. case SRT_UINT:
  5511. case SRT_HEX:
  5512. if (n_elem)
  5513. {
  5514. ParamFieldItem_t *it = GetParamField(rs,buf);
  5515. if (!it)
  5516. break;
  5517. char *src = (char*)it->data;
  5518. u8 *val = (u8*)(data+srt->offset);
  5519. uint i = 0;
  5520. while (*src)
  5521. {
  5522. u64 num = str2ull(src,&src,10);
  5523. if ( !i && srt->type == SRT_COUNT && num <= UINT_MAX )
  5524. last_count = num;
  5525. if (mode_add)
  5526. switch(srt->size)
  5527. {
  5528. case 1: *(u8*) val += num; break;
  5529. case 2: *(u16*)val += num; break;
  5530. case 4: *(u32*)val += num; break;
  5531. case 8: *(u64*)val += num; break;
  5532. }
  5533. else
  5534. switch(srt->size)
  5535. {
  5536. case 1: *(u8*) val = num; break;
  5537. case 2: *(u16*)val = num; break;
  5538. case 4: *(u32*)val = num; break;
  5539. case 8: *(u64*)val = num; break;
  5540. }
  5541. if ( ++i == n_elem )
  5542. break;
  5543. val += elem_off;
  5544. if ( *src == ',' )
  5545. src++;
  5546. }
  5547. }
  5548. break;
  5549. case SRT_INT:
  5550. if (n_elem)
  5551. {
  5552. ParamFieldItem_t *it = GetParamField(rs,buf);
  5553. if (!it)
  5554. break;
  5555. char *src = (char*)it->data;
  5556. u8 *val = (u8*)(data+srt->offset);
  5557. uint i = 0;
  5558. while (*src)
  5559. {
  5560. s64 num = str2ll(src,&src,10);
  5561. if (mode_add)
  5562. switch(srt->size)
  5563. {
  5564. case 1: *(s8*) val += num; break;
  5565. case 2: *(s16*)val += num; break;
  5566. case 4: *(s32*)val += num; break;
  5567. case 8: *(s64*)val += num; break;
  5568. }
  5569. else
  5570. switch(srt->size)
  5571. {
  5572. case 1: *(s8*) val = num; break;
  5573. case 2: *(s16*)val = num; break;
  5574. case 4: *(s32*)val = num; break;
  5575. case 8: *(s64*)val = num; break;
  5576. }
  5577. if ( ++i == n_elem )
  5578. break;
  5579. val += elem_off;
  5580. if ( *src == ',' )
  5581. src++;
  5582. }
  5583. }
  5584. break;
  5585. case SRT_FLOAT:
  5586. case SRT_XFLOAT:
  5587. if (n_elem)
  5588. {
  5589. ParamFieldItem_t *it = GetParamField(rs,buf);
  5590. if (!it)
  5591. break;
  5592. char *src = (char*)it->data;
  5593. u8 *val = (u8*)(data+srt->offset);
  5594. uint i = 0;
  5595. while (*src)
  5596. {
  5597. if (mode_add)
  5598. {
  5599. if ( srt->size == sizeof(float) )
  5600. *(float*)val += strtof(src,&src);
  5601. else if ( srt->size == sizeof(double) )
  5602. *(double*)val += strtod(src,&src);
  5603. else if ( srt->size == sizeof(long double) )
  5604. *(long double*)val += strtold(src,&src);
  5605. }
  5606. else
  5607. {
  5608. if ( srt->size == sizeof(float) )
  5609. *(float*)val = strtof(src,&src);
  5610. else if ( srt->size == sizeof(double) )
  5611. *(double*)val = strtod(src,&src);
  5612. else if ( srt->size == sizeof(long double) )
  5613. *(long double*)val = strtold(src,&src);
  5614. }
  5615. if ( ++i == n_elem )
  5616. break;
  5617. val += elem_off;
  5618. if ( *src == ',' )
  5619. src++;
  5620. }
  5621. }
  5622. break;
  5623. //-----
  5624. case SRT_STRING_SIZE:
  5625. if (n_elem)
  5626. {
  5627. ParamFieldItem_t *it = GetParamField(rs,buf);
  5628. mem_list_t ml;
  5629. DecodeByModeMemList(&ml,2,it?(ccp)it->data:0,-1,srt->emode,0);
  5630. char *val = (char*)(data+srt->offset);
  5631. uint i;
  5632. for ( i = 0; i < n_elem; i++, val += elem_off )
  5633. StringCopyS( val, srt->size, i<ml.used ? ml.list[i].ptr : 0 );
  5634. ResetMemList(&ml);
  5635. }
  5636. break;
  5637. case SRT_STRING_ALLOC:
  5638. if (n_elem)
  5639. {
  5640. ParamFieldItem_t *it = GetParamField(rs,buf);
  5641. mem_list_t ml;
  5642. DecodeByModeMemList(&ml,2,it?(ccp)it->data:0,-1,srt->emode,0);
  5643. ccp *val = (ccp*)(data+srt->offset);
  5644. uint i;
  5645. for ( i = 0; i < n_elem; i++ )
  5646. {
  5647. FreeString(*val);
  5648. *val = 0;
  5649. if ( i < ml.used )
  5650. {
  5651. mem_t *m = ml.list+i;
  5652. if ( m->ptr )
  5653. *val = m->ptr == EmptyString ? EmptyString : MEMDUP(m->ptr,m->len);
  5654. }
  5655. val = (ccp*)( (u8*)val + elem_off );
  5656. }
  5657. ResetMemList(&ml);
  5658. }
  5659. break;
  5660. case SRT_MEM:
  5661. if (n_elem)
  5662. {
  5663. ParamFieldItem_t *it = GetParamField(rs,buf);
  5664. mem_list_t ml;
  5665. DecodeByModeMemList(&ml,2,it?(ccp)it->data:0,-1,srt->emode,0);
  5666. mem_t *val = (mem_t*)(data+srt->offset);
  5667. uint i;
  5668. for ( i = 0; i < n_elem; i++ )
  5669. {
  5670. FreeString(val->ptr);
  5671. val->ptr = 0;
  5672. val->len = 0;
  5673. if ( i < ml.used )
  5674. {
  5675. mem_t *m = ml.list+i;
  5676. if ( m->ptr )
  5677. *val = m->ptr == EmptyString ? EmptyMem : DupMem(*m);
  5678. }
  5679. val = (mem_t*)( (u8*)val + elem_off );
  5680. }
  5681. ResetMemList(&ml);
  5682. }
  5683. break;
  5684. //-----
  5685. case SRT_STRING_FIELD:
  5686. {
  5687. StringField_t *sf = (StringField_t*)(data+srt->offset);
  5688. ResetStringField(sf);
  5689. snprintf(buf,sizeof(buf),"%s%s@n",prefix,srt->name);
  5690. const int n = GetParamFieldINT(rs,buf,0);
  5691. int i;
  5692. for ( i = 0; i < n; i++ )
  5693. {
  5694. snprintf(buf,sizeof(buf),"%s%s@%u",prefix,srt->name,i);
  5695. mem_t res = GetParamFieldMEM(buf,sizeof(buf),rs,buf,srt->emode,NullMem);
  5696. InsertStringField(sf,res.ptr,res.ptr!=buf);
  5697. }
  5698. break;
  5699. }
  5700. case SRT_PARAM_FIELD:
  5701. {
  5702. ParamField_t *sf = (ParamField_t*)(data+srt->offset);
  5703. ResetParamField(sf);
  5704. snprintf(buf,sizeof(buf),"%s%s@n",prefix,srt->name);
  5705. const int n = GetParamFieldINT(rs,buf,0);
  5706. int i;
  5707. for ( i = 0; i < n; i++ )
  5708. {
  5709. snprintf(buf,sizeof(buf),"%s%s@%u",prefix,srt->name,i);
  5710. ParamFieldItem_t *it = GetParamField(rs,buf);
  5711. if (it)
  5712. {
  5713. char *str;
  5714. const int num = strtol((ccp)it->data,&str,10);
  5715. if ( *str == ' ' ) // space should always exist
  5716. str++;
  5717. mem_t res = DecodeByModeMem(buf,sizeof(buf),str,-1,srt->emode,0);
  5718. InsertParamField(sf,res.ptr,false,num,0);
  5719. if ( res.ptr != buf )
  5720. FreeString(res.ptr);
  5721. }
  5722. }
  5723. break;
  5724. }
  5725. }
  5726. }
  5727. }
  5728. //
  5729. ///////////////////////////////////////////////////////////////////////////////
  5730. /////////////// scan socket type ///////////////
  5731. ///////////////////////////////////////////////////////////////////////////////
  5732. bool ScanSocketInfo
  5733. (
  5734. // Syntax: [ PREFIX ',' ]... [ PREFIX ':' ] address
  5735. // returns TRUE, if a protocol or a type is found
  5736. socket_info_t *si, // result, not NULL, will be initialized
  5737. ccp address, // address to analyze
  5738. uint tolerance // tolerace:
  5739. // 0: invalid without prefix
  5740. // 1: analyse beginning of address part:
  5741. // '/' or './' or '../' -> AF_UNIX
  5742. // 2: estimate type
  5743. // '/' before first ':' -> AF_UNIX
  5744. // 1.2 | 1.2.3 | 1.2.3.4 -> AF_INET
  5745. // a HEX:IP combi -> AF_INET6
  5746. // domain name -> AF_INET_ANY
  5747. )
  5748. {
  5749. DASSERT(si);
  5750. InitializeSocketInfo(si);
  5751. if (!address)
  5752. return false;
  5753. while ( *address && (uchar)*address <= ' ' )
  5754. address++;
  5755. static const KeywordTab_t keytab[] =
  5756. {
  5757. { -1, "STREAM", 0, SOCK_STREAM },
  5758. { -1, "DGRAM", "DATAGRAM", SOCK_DGRAM },
  5759. { AF_UNIX, "UNIX", "FILE", -1 },
  5760. { AF_UNIX, "UNIX-TCP", "FILE-TCP", SOCK_STREAM },
  5761. { AF_UNIX, "UNIX-STREAM", "FILE-STREAM", SOCK_STREAM },
  5762. { AF_UNIX, "UNIX-UDP", "FILE-UDP", SOCK_DGRAM },
  5763. { AF_UNIX, "UNIX-DGRAM", "FILE-DGRAM", SOCK_DGRAM },
  5764. { AF_UNIX, "UNIX-DATAGRAM","FILE-DATAGRAM",SOCK_DGRAM },
  5765. { AF_UNIX, "UNIX-SEQ", "FILE-SEQ", SOCK_SEQPACKET },
  5766. { AF_UNIX, "SEQ", 0, SOCK_SEQPACKET },
  5767. { AF_INET_ANY, "IP", 0, -1 },
  5768. { AF_INET_ANY, "TCP", 0, SOCK_STREAM },
  5769. { AF_INET_ANY, "UDP", 0, SOCK_DGRAM },
  5770. { AF_INET_ANY, "RAW", 0, SOCK_RAW },
  5771. { AF_INET, "IP4", "IPV4", -1 },
  5772. { AF_INET, "TCP4", "TCPV4", SOCK_STREAM },
  5773. { AF_INET, "UDP4", "UDPV4", SOCK_DGRAM },
  5774. { AF_INET, "RAW4", "RAWV4", SOCK_RAW },
  5775. { AF_INET6, "IP6", "IPV6", -1 },
  5776. { AF_INET6, "TCP6", "TCPV6", SOCK_STREAM },
  5777. { AF_INET6, "UDP6", "UDPV6", SOCK_DGRAM },
  5778. { AF_INET6, "RAW6", "RAWV6", SOCK_RAW },
  5779. {0,0,0,0}
  5780. };
  5781. bool ok = false;
  5782. int proto = -1;
  5783. int type = -1;
  5784. ccp start = address;
  5785. for(;;)
  5786. {
  5787. char namebuf[10];
  5788. char *dest = namebuf;
  5789. ccp ptr = start;
  5790. while ( dest < namebuf + sizeof(namebuf) - 1
  5791. && isalnum((int)*ptr) || *ptr == '-' )
  5792. {
  5793. *dest++ = *ptr++;
  5794. }
  5795. if ( ptr == start || *ptr != ',' && *ptr != ':' )
  5796. break;
  5797. *dest = 0;
  5798. int abbrev;
  5799. const KeywordTab_t *k = ScanKeyword(&abbrev,namebuf,keytab);
  5800. if ( !k || abbrev )
  5801. break;
  5802. if ( k->id == AF_INET_ANY ? ( proto == -1 ) : ( k->id >= 0 ) )
  5803. proto = k->id;
  5804. if ( k->opt >= 0 )
  5805. type = k->opt;
  5806. noPRINT("%3lld %3lld |%s|%s| -> %d,%d\n",
  5807. k->id, k->opt, k->name1, k->name2 ? k->name2 : "",
  5808. proto, type );
  5809. start = ptr;
  5810. if ( *start++ == ':' )
  5811. {
  5812. ok = true;
  5813. break;
  5814. }
  5815. }
  5816. if (!ok)
  5817. {
  5818. start = address;
  5819. proto = -1;
  5820. type = -1;
  5821. }
  5822. if ( proto < 0 && tolerance > 0 )
  5823. {
  5824. if ( *start == '/' || !memcmp(start,"./",2) || !memcmp(start,"../",3) )
  5825. {
  5826. proto = AF_UNIX;
  5827. goto proto_found;
  5828. }
  5829. if ( tolerance > 1 )
  5830. {
  5831. ccp colon = strchr(start,':');
  5832. if (!colon)
  5833. colon = start + strlen(start);
  5834. //--- test for UNIX
  5835. ccp slash = strchr(start,'/');
  5836. if ( slash && slash < colon )
  5837. {
  5838. proto = AF_UNIX;
  5839. goto proto_found;
  5840. }
  5841. //--- test for IPv4
  5842. ccp ptr = start;
  5843. uint count = 0;
  5844. while ( ptr < colon )
  5845. {
  5846. const char ch = *ptr;
  5847. if ( ch == '.' )
  5848. count++;
  5849. else if ( ch < '0' || ch > '9' )
  5850. break;
  5851. ptr++;
  5852. }
  5853. if ( ptr == colon && count < 4 )
  5854. {
  5855. proto = AF_INET;
  5856. if ( count > 0 )
  5857. goto proto_found;
  5858. }
  5859. //--- test for IPv6
  5860. ptr = start;
  5861. const bool have_bracket = *ptr == '[';
  5862. if (have_bracket)
  5863. ptr++;
  5864. count = 0;
  5865. while (*ptr)
  5866. {
  5867. const char ch = *ptr;
  5868. if ( ch == ':' )
  5869. count++;
  5870. else if ( ch != '.'
  5871. && !( ch >= '0' && ch <= '9' )
  5872. && !( ch >= 'A' && ch <= 'F' )
  5873. && !( ch >= 'a' && ch <= 'f' )
  5874. )
  5875. {
  5876. break;
  5877. }
  5878. ptr++;
  5879. }
  5880. if ( count > 1 && count < 8 && ( !have_bracket || *ptr == ']' ))
  5881. {
  5882. proto = AF_INET6;
  5883. goto proto_found;
  5884. }
  5885. //--- test for a domain name
  5886. ptr = start;
  5887. count = 0;
  5888. ccp beg_name = ptr;
  5889. while ( ptr < colon )
  5890. {
  5891. const int ch = *ptr;
  5892. if ( ptr > beg_name && ch == '.' )
  5893. {
  5894. count++;
  5895. beg_name = ptr+1;
  5896. }
  5897. else if ( ptr == beg_name ? !isalpha(ch) : !isalnum(ch) )
  5898. break;
  5899. ptr++;
  5900. }
  5901. if ( count > 0 && ptr == colon )
  5902. {
  5903. proto = AF_INET_ANY;
  5904. goto proto_found;
  5905. }
  5906. }
  5907. }
  5908. if ( proto || type )
  5909. {
  5910. proto_found:
  5911. si->is_valid = true;
  5912. si->protocol = proto;
  5913. si->type = type;
  5914. si->address = start;
  5915. }
  5916. //printf(">>> v=%d {%d,%d}: %s\n",ok,proto,type,start);
  5917. return si->is_valid;
  5918. }
  5919. ///////////////////////////////////////////////////////////////////////////////
  5920. ccp PrintSocketInfo ( int protocol, int type )
  5921. {
  5922. switch (protocol)
  5923. {
  5924. case AF_UNIX:
  5925. switch(type)
  5926. {
  5927. case SOCK_STREAM: return "unix-tcp:";
  5928. case SOCK_DGRAM: return "unix-udp:";
  5929. case SOCK_SEQPACKET: return "unix-seq:";
  5930. default: return "unix:";
  5931. }
  5932. break;
  5933. case AF_INET:
  5934. switch(type)
  5935. {
  5936. case SOCK_STREAM: return "tcp4:";
  5937. case SOCK_DGRAM: return "udp4:";
  5938. case SOCK_RAW: return "raw4:";
  5939. default: return "ipv4:";
  5940. }
  5941. break;
  5942. case AF_INET6:
  5943. switch(type)
  5944. {
  5945. case SOCK_STREAM: return "tcp6:";
  5946. case SOCK_DGRAM: return "udp6:";
  5947. case SOCK_RAW: return "raw6:";
  5948. default: return "ipv6:";
  5949. }
  5950. break;
  5951. case AF_INET_ANY:
  5952. switch(type)
  5953. {
  5954. case SOCK_STREAM: return "tcp:";
  5955. case SOCK_DGRAM: return "udp:";
  5956. case SOCK_RAW: return "raw:";
  5957. default: return "ip:";
  5958. }
  5959. break;
  5960. };
  5961. return "";
  5962. }
  5963. //
  5964. ///////////////////////////////////////////////////////////////////////////////
  5965. /////////////// stat_file_count_t ///////////////
  5966. ///////////////////////////////////////////////////////////////////////////////
  5967. stat_file_count_t stat_file_count = {0};
  5968. ///////////////////////////////////////////////////////////////////////////////
  5969. uint CountOpenFiles()
  5970. {
  5971. DIR *fdir = opendir("/proc/self/fd");
  5972. if (fdir)
  5973. {
  5974. uint count = 0;
  5975. for(;;)
  5976. {
  5977. struct dirent *dent = readdir(fdir);
  5978. if (!dent)
  5979. break;
  5980. if (dent->d_name[0] != '.' )
  5981. count++;
  5982. }
  5983. closedir(fdir);
  5984. stat_file_count.cur_files = count;
  5985. if ( stat_file_count.max_files < count )
  5986. stat_file_count.max_files = count;
  5987. }
  5988. return stat_file_count.cur_files;
  5989. }
  5990. ///////////////////////////////////////////////////////////////////////////////
  5991. void RegisterFileId ( int fd )
  5992. {
  5993. if ( (int)stat_file_count.max_files <= fd )
  5994. stat_file_count.max_files = fd+1;
  5995. }
  5996. ///////////////////////////////////////////////////////////////////////////////
  5997. void UpdateOpenFiles ( bool count_current )
  5998. {
  5999. struct rlimit rlim;
  6000. if (!getrlimit(RLIMIT_NOFILE,&rlim))
  6001. {
  6002. stat_file_count.cur_limit = rlim.rlim_cur;
  6003. stat_file_count.max_limit = rlim.rlim_max;
  6004. }
  6005. if ( count_current || !stat_file_count.max_files )
  6006. CountOpenFiles();
  6007. }
  6008. ///////////////////////////////////////////////////////////////////////////////
  6009. uint SetOpenFilesLimit ( uint limit )
  6010. {
  6011. struct rlimit rlim;
  6012. if (!getrlimit(RLIMIT_NOFILE,&rlim))
  6013. {
  6014. rlim.rlim_cur = limit;
  6015. setrlimit(RLIMIT_NOFILE,&rlim);
  6016. }
  6017. UpdateOpenFiles(false);
  6018. return stat_file_count.cur_limit;
  6019. }
  6020. ///////////////////////////////////////////////////////////////////////////////
  6021. ccp PrintOpenFiles ( bool count_current )
  6022. {
  6023. if (!stat_file_count.cur_limit)
  6024. UpdateOpenFiles(count_current);
  6025. else if (count_current)
  6026. CountOpenFiles();
  6027. return PrintCircBuf("cur=%u, max=%u, limit=%u/%u",
  6028. stat_file_count.cur_files,
  6029. stat_file_count.max_files,
  6030. stat_file_count.cur_limit,
  6031. stat_file_count.max_limit );
  6032. };
  6033. //
  6034. ///////////////////////////////////////////////////////////////////////////////
  6035. /////////////// PrintScript ///////////////
  6036. ///////////////////////////////////////////////////////////////////////////////
  6037. ccp GetNamePSFF ( PrintScriptFF fform )
  6038. {
  6039. switch(fform)
  6040. {
  6041. case PSFF_UNKNOWN: return "TEXT";
  6042. case PSFF_ASSIGN: return "ASSIGN";
  6043. case PSFF_CONFIG: return "CONFIG";
  6044. case PSFF_JSON: return "JSON";
  6045. case PSFF_BASH: return "BASH";
  6046. case PSFF_SH: return "SH";
  6047. case PSFF_PHP: return "PHP";
  6048. case PSFF_MAKEDOC: return "MDOC";
  6049. case PSFF_C: return "C";
  6050. }
  6051. return "???";
  6052. }
  6053. ///////////////////////////////////////////////////////////////////////////////
  6054. void PrintScriptHeader ( PrintScript_t *ps )
  6055. {
  6056. DASSERT(ps);
  6057. ps->index = 0;
  6058. ps->add_index = false;
  6059. ps->sep[0] = ps->sep[1] = 0;
  6060. if ( !ps->var_name || !*ps->var_name )
  6061. ps->var_name = "res";
  6062. if ( !ps->var_prefix )
  6063. ps->var_prefix = "";
  6064. if ( ps->f )
  6065. {
  6066. switch (ps->fform)
  6067. {
  6068. case PSFF_JSON:
  6069. if (ps->create_array)
  6070. fputs("[",ps->f);
  6071. ps->boc = 0;
  6072. break;
  6073. case PSFF_SH:
  6074. ps->add_index =ps->create_array;
  6075. ps->boc = "#";
  6076. break;
  6077. case PSFF_PHP:
  6078. if (ps->create_array)
  6079. fprintf(ps->f,"$%s = array();\n\n",ps->var_name);
  6080. ps->boc = "#";
  6081. break;
  6082. case PSFF_MAKEDOC:
  6083. if (ps->create_array)
  6084. fprintf(ps->f,"%s = @LIST\n\n",ps->var_name);
  6085. ps->boc = "#!";
  6086. break;
  6087. case PSFF_C:
  6088. if (ps->create_array)
  6089. fprintf(ps->f,"%s = @LIST\n\n",ps->var_name);
  6090. ps->boc = "//";
  6091. break;
  6092. default:
  6093. //fputc('\n',ps->f);
  6094. ps->boc = "#";
  6095. break;
  6096. }
  6097. }
  6098. }
  6099. ///////////////////////////////////////////////////////////////////////////////
  6100. void PrintScriptFooter ( PrintScript_t *ps )
  6101. {
  6102. DASSERT(ps);
  6103. if (ps->f)
  6104. {
  6105. switch (ps->fform)
  6106. {
  6107. case PSFF_JSON:
  6108. if (ps->create_array)
  6109. fputs("]\n",ps->f);
  6110. break;
  6111. case PSFF_SH:
  6112. if (ps->create_array)
  6113. fprintf(ps->f,"%sN=%u\n\n",ps->var_prefix,ps->index);
  6114. break;
  6115. case PSFF_PHP:
  6116. fputs("?>\n",ps->f);
  6117. break;
  6118. default:
  6119. break;
  6120. }
  6121. }
  6122. }
  6123. ///////////////////////////////////////////////////////////////////////////////
  6124. int PutScriptVars
  6125. (
  6126. PrintScript_t *ps, // valid control struct
  6127. uint mode, // bit field: 1=open var, 2:close var
  6128. ccp text // text with lines of format NAME=VALUE
  6129. )
  6130. {
  6131. FILE * f = ps ? ps->f : 0;
  6132. if (!f)
  6133. return 0;
  6134. DASSERT(ps);
  6135. //--- begin of output
  6136. if ( mode & 1 )
  6137. {
  6138. ps->count = 0;
  6139. if ( ps->add_index )
  6140. snprintf( ps->prefix, sizeof(ps->prefix),
  6141. "%s%u", ps->var_prefix, ps->index );
  6142. else
  6143. StringCopyS( ps->prefix, sizeof(ps->prefix), ps->var_prefix );
  6144. switch (ps->fform)
  6145. {
  6146. case PSFF_JSON:
  6147. fprintf(f,"%s{",ps->sep);
  6148. ps->sep[0] = ',';
  6149. break;
  6150. case PSFF_PHP:
  6151. fputs("$d = new \\stdClass;\n",f);
  6152. break;
  6153. case PSFF_MAKEDOC:
  6154. fprintf(f,"d = @MAP\n");
  6155. break;
  6156. default:
  6157. break;
  6158. }
  6159. }
  6160. //--- print var lines
  6161. ccp ptr = text ? text : "";
  6162. char varbuf[200];
  6163. for(;;)
  6164. {
  6165. if ( ps->ena_empty && ps->boc )
  6166. {
  6167. while ( *ptr == ' ' || *ptr == '\t' )
  6168. ptr++;
  6169. if ( *ptr == '\n' )
  6170. {
  6171. ptr++;
  6172. fputc('\n',f);
  6173. continue;
  6174. }
  6175. }
  6176. else
  6177. while ( *ptr && (uchar)*ptr <= ' ' )
  6178. ptr++;
  6179. if ( *ptr == '#' )
  6180. {
  6181. ccp comment = ++ptr;
  6182. while ( (uchar)*ptr >= ' ' )
  6183. ptr++;
  6184. if ( ps->ena_comments && ps->boc )
  6185. fprintf(f,"%s%.*s\n",ps->boc,(int)(ptr-comment),comment);
  6186. goto next_line;
  6187. }
  6188. ccp varname = ptr;
  6189. while ( *ptr && *ptr != '=' )
  6190. ptr++;
  6191. if ( *ptr != '=' || ptr == varname )
  6192. break;
  6193. const int varlen = ptr - varname;
  6194. if ( ps->force_case <= LOUP_LOWER )
  6195. {
  6196. MemLowerS(varbuf,sizeof(varbuf),MemByS(varname,varlen));
  6197. varname = varbuf;
  6198. }
  6199. else if ( ps->force_case >= LOUP_UPPER )
  6200. {
  6201. varname = MemUpperS(varbuf,sizeof(varbuf),MemByS(varname,varlen));
  6202. varname = varbuf;
  6203. }
  6204. ccp quote, param;
  6205. uint plen, free_param = 0;
  6206. if ( *++ptr == '"' )
  6207. {
  6208. ptr++;
  6209. param = ptr;
  6210. if (ps->auto_quote)
  6211. {
  6212. quote = EmptyString;
  6213. while ( *ptr && *ptr != '"' )
  6214. {
  6215. if ( *ptr == '\\' && ptr[1] )
  6216. ptr++;
  6217. ptr++;
  6218. }
  6219. param = EscapeString(param,ptr-param,EmptyString,EmptyString,CHMD__MODERN,
  6220. ps->fform == PSFF_BASH ? '$' : '"', true, &plen );
  6221. free_param++;
  6222. }
  6223. else
  6224. {
  6225. quote = "\"";
  6226. while ( *ptr && !( ptr[0] == '"' && ptr[1] == '\n' ))
  6227. ptr++;
  6228. plen = ptr - param;
  6229. }
  6230. if ( *ptr == '"' )
  6231. ptr++;
  6232. }
  6233. else
  6234. {
  6235. param = ptr;
  6236. quote = EmptyString;
  6237. while ( *ptr && *ptr != '\n' )
  6238. ptr++;
  6239. plen = ptr - param;
  6240. }
  6241. switch (ps->fform)
  6242. {
  6243. case PSFF_ASSIGN:
  6244. fprintf(f, "%.*s = %s%.*s%s\n",
  6245. varlen, varname, quote, plen, param, quote );
  6246. break;
  6247. case PSFF_CONFIG:
  6248. if ( ps->eq_tabstop > 0 )
  6249. {
  6250. int tabs = ps->eq_tabstop - varlen/8;
  6251. if ( tabs < 0 )
  6252. tabs = 0;
  6253. fprintf(f, "%.*s%.*s= %.*s\n",
  6254. varlen, varname, tabs, Tabs20, plen, param );
  6255. }
  6256. else
  6257. fprintf(f, "%.*s = %.*s\n",
  6258. varlen, varname, plen, param );
  6259. break;
  6260. case PSFF_JSON:
  6261. fprintf(f, "%s\"%.*s\":%s%.*s%s",
  6262. ps->count ? "," : "",
  6263. varlen, varname, quote, plen, param, quote );
  6264. break;
  6265. case PSFF_BASH:
  6266. if (ps->create_array)
  6267. {
  6268. fprintf(f, "%s%.*s%s=(%s%.*s%s)\n",
  6269. ps->prefix,
  6270. varlen, varname, ps->index ? "+" : "", quote, plen, param, quote );
  6271. break;
  6272. }
  6273. // fall through
  6274. case PSFF_SH:
  6275. fprintf(f, "%s%.*s=%s%.*s%s\n",
  6276. ps->prefix, varlen, varname, quote, plen, param, quote );
  6277. break;
  6278. case PSFF_PHP:
  6279. case PSFF_C:
  6280. fprintf(f, "$d->%.*s = %s%.*s%s;\n",
  6281. varlen, varname, quote, plen, param, quote );
  6282. break;
  6283. case PSFF_MAKEDOC:
  6284. fprintf(f, "d[\"%.*s\"] = %s%.*s%s\n",
  6285. varlen, varname, quote, plen, param, quote );
  6286. break;
  6287. default:
  6288. fprintf(f, "%*.*s = %s%.*s%s\n",
  6289. ps->var_size > 0 ? ps->var_size : 20,
  6290. varlen, varname, quote, plen, param, quote );
  6291. break;
  6292. }
  6293. ps->count++;
  6294. if (free_param)
  6295. FreeString(param);
  6296. //-- skip to next line
  6297. next_line:
  6298. while ( *ptr && (uchar)*ptr <= ' ' && *ptr != '\n' )
  6299. ptr++;
  6300. if ( *ptr == '\n' )
  6301. ptr++;
  6302. }
  6303. //--- end of output
  6304. if ( mode & 2 )
  6305. {
  6306. switch (ps->fform)
  6307. {
  6308. case PSFF_JSON:
  6309. fputs("}\n",f);
  6310. break;
  6311. case PSFF_PHP:
  6312. fprintf(f, "$%s%s = $d;\n\n",
  6313. ps->var_name, ps->create_array ? "[]" : "" );
  6314. break;
  6315. case PSFF_MAKEDOC:
  6316. fprintf(f, "%s %s= move(d);\n\n",
  6317. ps->var_name, ps->create_array ? "#" : "" );
  6318. break;
  6319. default:
  6320. fputc('\n',f);
  6321. break;
  6322. }
  6323. fflush(f);
  6324. }
  6325. ps->index++;
  6326. return ps->count;
  6327. }
  6328. ///////////////////////////////////////////////////////////////////////////////
  6329. int PrintScriptVars
  6330. (
  6331. PrintScript_t *ps, // valid control struct
  6332. uint mode, // bit field: 1=open var, 2:close var
  6333. ccp format, // format of message
  6334. ... // arguments
  6335. )
  6336. {
  6337. DASSERT(ps);
  6338. char buf[5000];
  6339. if (format)
  6340. {
  6341. va_list arg;
  6342. va_start(arg,format);
  6343. vsnprintf(buf,sizeof(buf),format,arg);
  6344. va_end(arg);
  6345. }
  6346. else
  6347. *buf = 0;
  6348. return PutScriptVars(ps,mode,buf);
  6349. }
  6350. //
  6351. ///////////////////////////////////////////////////////////////////////////////
  6352. /////////////// CpuStatus_t ///////////////
  6353. ///////////////////////////////////////////////////////////////////////////////
  6354. CpuStatus_t GetCpuStatus ( CpuStatus_t * prev_cpuinfo )
  6355. {
  6356. //--- retrieve static data
  6357. static int n_cpu = -1;
  6358. static int clock_ticks = 100;
  6359. char buf[1000];
  6360. if ( n_cpu < 0 )
  6361. {
  6362. n_cpu = 0;
  6363. FILE *f = fopen("/proc/cpuinfo","r");
  6364. if (f)
  6365. {
  6366. while (fgets(buf,sizeof(buf),f))
  6367. if ( !memcmp(buf,"processor",9) && buf[9] <= ' ' )
  6368. n_cpu++;
  6369. fclose(f);
  6370. }
  6371. clock_ticks = sysconf(_SC_CLK_TCK);
  6372. if ( clock_ticks <= 0 )
  6373. clock_ticks = 100;
  6374. }
  6375. //--- retrieve cpu data
  6376. CpuStatus_t current =
  6377. {
  6378. .valid = true,
  6379. .nsec = GetTimerNSec(),
  6380. .n_cpu = n_cpu,
  6381. .clock_ticks = clock_ticks
  6382. };
  6383. // man 5 proc | oo +/proc/loadavg
  6384. FILE *f_load = fopen("/proc/loadavg","r");
  6385. if (f_load)
  6386. {
  6387. if (fgets(buf,sizeof(buf),f_load))
  6388. {
  6389. char *ptr = buf;
  6390. current.loadavg_1m = strtod(ptr,&ptr);
  6391. current.loadavg_5m = strtod(ptr,&ptr);
  6392. current.loadavg_15m = strtod(ptr,&ptr);
  6393. current.running_proc = strtol(ptr,&ptr,10);
  6394. current.total_proc = strtol(ptr+1,&ptr,10);
  6395. }
  6396. fclose(f_load);
  6397. }
  6398. // man 5 proc | oo +/proc/stat
  6399. FILE * f_stat = fopen("/proc/stat","r");
  6400. if (f_stat)
  6401. {
  6402. while (fgets(buf,sizeof(buf),f_stat))
  6403. if ( !memcmp(buf,"cpu ",4) )
  6404. {
  6405. char *ptr = buf;
  6406. current.user = strtol(ptr+4,&ptr,10) * 10000 / clock_ticks;
  6407. current.nice = strtol(ptr,&ptr,10) * 10000 / clock_ticks;
  6408. current.system = strtol(ptr,&ptr,10) * 10000 / clock_ticks;
  6409. current.idle = strtol(ptr,&ptr,10) * 10000 / clock_ticks;
  6410. break;
  6411. }
  6412. fclose(f_stat);
  6413. }
  6414. if (!prev_cpuinfo)
  6415. return current;
  6416. //--- get delta for result
  6417. if (!prev_cpuinfo->valid)
  6418. *prev_cpuinfo = current;
  6419. CpuStatus_t res = current;
  6420. res.nsec -= prev_cpuinfo->nsec;
  6421. res.user -= prev_cpuinfo->user;
  6422. res.nice -= prev_cpuinfo->nice;
  6423. res.system -= prev_cpuinfo->system;
  6424. res.idle -= prev_cpuinfo->idle;
  6425. *prev_cpuinfo = current;
  6426. return res;
  6427. }
  6428. ///////////////////////////////////////////////////////////////////////////////
  6429. ccp PrintCpuStatus ( const CpuStatus_t * cpuinfo )
  6430. {
  6431. if ( !cpuinfo || !cpuinfo->valid )
  6432. return "INVALID";
  6433. return PrintCircBuf(
  6434. "ticks=%d, cpus=%d, times=%lld+%lld+%lld+%lld=%lld, avg=%4.2f,%4.2f,%4.2f %u/%u, nsec=%llu",
  6435. cpuinfo->clock_ticks,
  6436. cpuinfo->n_cpu,
  6437. cpuinfo->user, cpuinfo->nice, cpuinfo->system, cpuinfo->idle,
  6438. cpuinfo->user + cpuinfo->nice + cpuinfo->system + cpuinfo->idle,
  6439. cpuinfo->loadavg[0], cpuinfo->loadavg[1], cpuinfo->loadavg[2],
  6440. cpuinfo->running_proc, cpuinfo->total_proc,
  6441. cpuinfo->nsec );
  6442. }
  6443. //
  6444. ///////////////////////////////////////////////////////////////////////////////
  6445. /////////////// MemoryStatus_t ///////////////
  6446. ///////////////////////////////////////////////////////////////////////////////
  6447. MemoryStatus_t GetMemoryStatus(void)
  6448. {
  6449. MemoryStatus_t mem = {0};
  6450. FILE *f = fopen("/proc/meminfo","r");
  6451. if (f)
  6452. {
  6453. char buf[200];
  6454. int count = 5; // abort, if all 5 params scanned
  6455. while ( count > 0 && fgets(buf,sizeof(buf),f) )
  6456. {
  6457. ccp colon = strchr(buf,':');
  6458. if (colon)
  6459. {
  6460. u64 num = strtoul(colon+1,0,10) * 1024;
  6461. switch (colon-buf)
  6462. {
  6463. case 6:
  6464. if (!memcmp(buf,"Cached",6)) { count--; mem.cached = num; break; }
  6465. break;
  6466. case 7:
  6467. if (!memcmp(buf,"MemFree",7)) { count--; mem.free = num; break; }
  6468. if (!memcmp(buf,"Buffers",7)) { count--; mem.buffers = num; break; }
  6469. break;
  6470. case 8:
  6471. if (!memcmp(buf,"MemTotal",8)) { count--; mem.total = num; break; }
  6472. break;
  6473. case 12:
  6474. if (!memcmp(buf,"MemAvailable",12)) { count--; mem.avail = num; break; }
  6475. break;
  6476. }
  6477. }
  6478. }
  6479. fclose(f);
  6480. mem.used = mem.total - mem.free - mem.buffers - mem.cached;
  6481. }
  6482. return mem;
  6483. }
  6484. ///////////////////////////////////////////////////////////////////////////////
  6485. ccp PrintMemoryStatus ( const MemoryStatus_t * ms )
  6486. {
  6487. MemoryStatus_t temp;
  6488. if (!ms)
  6489. {
  6490. temp = GetMemoryStatus();
  6491. ms = &temp;
  6492. }
  6493. const sizeform_mode_t mode = DC_SFORM_NARROW;
  6494. return PrintCircBuf(
  6495. "tot=%s, free=%s, avail=%s, used=%s, buf=%s, cache=%s\n",
  6496. PrintNumberU7(0,0,ms->total,mode),
  6497. PrintNumberU7(0,0,ms->free,mode),
  6498. PrintNumberU7(0,0,ms->avail,mode),
  6499. PrintNumberU7(0,0,ms->used,mode),
  6500. PrintNumberU7(0,0,ms->buffers,mode),
  6501. PrintNumberU7(0,0,ms->cached,mode) );
  6502. }
  6503. //
  6504. ///////////////////////////////////////////////////////////////////////////////
  6505. /////////////// END ///////////////
  6506. ///////////////////////////////////////////////////////////////////////////////