123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736 |
- /*
- * xpath.c: XML Path Language implementation
- * XPath is a language for addressing parts of an XML document,
- * designed to be used by both XSLT and XPointer
- *
- * Reference: W3C Recommendation 16 November 1999
- * http://www.w3.org/TR/1999/REC-xpath-19991116
- * Public reference:
- * http://www.w3.org/TR/xpath
- *
- * See Copyright for the status of this software
- *
- * Author: daniel@veillard.com
- *
- */
- /* To avoid EBCDIC trouble when parsing on zOS */
- #if defined(__MVS__)
- #pragma convert("ISO8859-1")
- #endif
- #define IN_LIBXML
- #include "libxml.h"
- #include <limits.h>
- #include <string.h>
- #include <stddef.h>
- #ifdef HAVE_SYS_TYPES_H
- #include <sys/types.h>
- #endif
- #ifdef HAVE_MATH_H
- #include <math.h>
- #endif
- #ifdef HAVE_FLOAT_H
- #include <float.h>
- #endif
- #ifdef HAVE_CTYPE_H
- #include <ctype.h>
- #endif
- #ifdef HAVE_SIGNAL_H
- #include <signal.h>
- #endif
- #include <libxml/xmlmemory.h>
- #include <libxml/tree.h>
- #include <libxml/valid.h>
- #include <libxml/xpath.h>
- #include <libxml/xpathInternals.h>
- #include <libxml/parserInternals.h>
- #include <libxml/hash.h>
- #ifdef LIBXML_XPTR_ENABLED
- #include <libxml/xpointer.h>
- #endif
- #ifdef LIBXML_DEBUG_ENABLED
- #include <libxml/debugXML.h>
- #endif
- #include <libxml/xmlerror.h>
- #include <libxml/threads.h>
- #include <libxml/globals.h>
- #ifdef LIBXML_PATTERN_ENABLED
- #include <libxml/pattern.h>
- #endif
- #include "buf.h"
- #ifdef LIBXML_PATTERN_ENABLED
- #define XPATH_STREAMING
- #endif
- #define TODO \
- xmlGenericError(xmlGenericErrorContext, \
- "Unimplemented block at %s:%d\n", \
- __FILE__, __LINE__);
- /**
- * WITH_TIM_SORT:
- *
- * Use the Timsort algorithm provided in timsort.h to sort
- * nodeset as this is a great improvement over the old Shell sort
- * used in xmlXPathNodeSetSort()
- */
- #define WITH_TIM_SORT
- /*
- * XP_OPTIMIZED_NON_ELEM_COMPARISON:
- * If defined, this will use xmlXPathCmpNodesExt() instead of
- * xmlXPathCmpNodes(). The new function is optimized comparison of
- * non-element nodes; actually it will speed up comparison only if
- * xmlXPathOrderDocElems() was called in order to index the elements of
- * a tree in document order; Libxslt does such an indexing, thus it will
- * benefit from this optimization.
- */
- #define XP_OPTIMIZED_NON_ELEM_COMPARISON
- /*
- * XP_OPTIMIZED_FILTER_FIRST:
- * If defined, this will optimize expressions like "key('foo', 'val')[b][1]"
- * in a way, that it stop evaluation at the first node.
- */
- #define XP_OPTIMIZED_FILTER_FIRST
- /*
- * XP_DEBUG_OBJ_USAGE:
- * Internal flag to enable tracking of how much XPath objects have been
- * created.
- */
- /* #define XP_DEBUG_OBJ_USAGE */
- /*
- * XPATH_MAX_STEPS:
- * when compiling an XPath expression we arbitrary limit the maximum
- * number of step operation in the compiled expression. 1000000 is
- * an insanely large value which should never be reached under normal
- * circumstances
- */
- #define XPATH_MAX_STEPS 1000000
- /*
- * XPATH_MAX_STACK_DEPTH:
- * when evaluating an XPath expression we arbitrary limit the maximum
- * number of object allowed to be pushed on the stack. 1000000 is
- * an insanely large value which should never be reached under normal
- * circumstances
- */
- #define XPATH_MAX_STACK_DEPTH 1000000
- /*
- * XPATH_MAX_NODESET_LENGTH:
- * when evaluating an XPath expression nodesets are created and we
- * arbitrary limit the maximum length of those node set. 10000000 is
- * an insanely large value which should never be reached under normal
- * circumstances, one would first need to construct an in memory tree
- * with more than 10 millions nodes.
- */
- #define XPATH_MAX_NODESET_LENGTH 10000000
- /*
- * XPATH_MAX_RECRUSION_DEPTH:
- * Maximum amount of nested functions calls when parsing or evaluating
- * expressions
- */
- #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- #define XPATH_MAX_RECURSION_DEPTH 500
- #else
- #define XPATH_MAX_RECURSION_DEPTH 5000
- #endif
- /*
- * TODO:
- * There are a few spots where some tests are done which depend upon ascii
- * data. These should be enhanced for full UTF8 support (see particularly
- * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT)
- */
- #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
- /**
- * xmlXPathCmpNodesExt:
- * @node1: the first node
- * @node2: the second node
- *
- * Compare two nodes w.r.t document order.
- * This one is optimized for handling of non-element nodes.
- *
- * Returns -2 in case of error 1 if first point < second point, 0 if
- * it's the same node, -1 otherwise
- */
- static int
- xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) {
- int depth1, depth2;
- int misc = 0, precedence1 = 0, precedence2 = 0;
- xmlNodePtr miscNode1 = NULL, miscNode2 = NULL;
- xmlNodePtr cur, root;
- ptrdiff_t l1, l2;
- if ((node1 == NULL) || (node2 == NULL))
- return(-2);
- if (node1 == node2)
- return(0);
- /*
- * a couple of optimizations which will avoid computations in most cases
- */
- switch (node1->type) {
- case XML_ELEMENT_NODE:
- if (node2->type == XML_ELEMENT_NODE) {
- if ((0 > (ptrdiff_t) node1->content) &&
- (0 > (ptrdiff_t) node2->content) &&
- (node1->doc == node2->doc))
- {
- l1 = -((ptrdiff_t) node1->content);
- l2 = -((ptrdiff_t) node2->content);
- if (l1 < l2)
- return(1);
- if (l1 > l2)
- return(-1);
- } else
- goto turtle_comparison;
- }
- break;
- case XML_ATTRIBUTE_NODE:
- precedence1 = 1; /* element is owner */
- miscNode1 = node1;
- node1 = node1->parent;
- misc = 1;
- break;
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_COMMENT_NODE:
- case XML_PI_NODE: {
- miscNode1 = node1;
- /*
- * Find nearest element node.
- */
- if (node1->prev != NULL) {
- do {
- node1 = node1->prev;
- if (node1->type == XML_ELEMENT_NODE) {
- precedence1 = 3; /* element in prev-sibl axis */
- break;
- }
- if (node1->prev == NULL) {
- precedence1 = 2; /* element is parent */
- /*
- * URGENT TODO: Are there any cases, where the
- * parent of such a node is not an element node?
- */
- node1 = node1->parent;
- break;
- }
- } while (1);
- } else {
- precedence1 = 2; /* element is parent */
- node1 = node1->parent;
- }
- if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) ||
- (0 <= (ptrdiff_t) node1->content)) {
- /*
- * Fallback for whatever case.
- */
- node1 = miscNode1;
- precedence1 = 0;
- } else
- misc = 1;
- }
- break;
- case XML_NAMESPACE_DECL:
- /*
- * TODO: why do we return 1 for namespace nodes?
- */
- return(1);
- default:
- break;
- }
- switch (node2->type) {
- case XML_ELEMENT_NODE:
- break;
- case XML_ATTRIBUTE_NODE:
- precedence2 = 1; /* element is owner */
- miscNode2 = node2;
- node2 = node2->parent;
- misc = 1;
- break;
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_COMMENT_NODE:
- case XML_PI_NODE: {
- miscNode2 = node2;
- if (node2->prev != NULL) {
- do {
- node2 = node2->prev;
- if (node2->type == XML_ELEMENT_NODE) {
- precedence2 = 3; /* element in prev-sibl axis */
- break;
- }
- if (node2->prev == NULL) {
- precedence2 = 2; /* element is parent */
- node2 = node2->parent;
- break;
- }
- } while (1);
- } else {
- precedence2 = 2; /* element is parent */
- node2 = node2->parent;
- }
- if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
- (0 <= (ptrdiff_t) node2->content))
- {
- node2 = miscNode2;
- precedence2 = 0;
- } else
- misc = 1;
- }
- break;
- case XML_NAMESPACE_DECL:
- return(1);
- default:
- break;
- }
- if (misc) {
- if (node1 == node2) {
- if (precedence1 == precedence2) {
- /*
- * The ugly case; but normally there aren't many
- * adjacent non-element nodes around.
- */
- cur = miscNode2->prev;
- while (cur != NULL) {
- if (cur == miscNode1)
- return(1);
- if (cur->type == XML_ELEMENT_NODE)
- return(-1);
- cur = cur->prev;
- }
- return (-1);
- } else {
- /*
- * Evaluate based on higher precedence wrt to the element.
- * TODO: This assumes attributes are sorted before content.
- * Is this 100% correct?
- */
- if (precedence1 < precedence2)
- return(1);
- else
- return(-1);
- }
- }
- /*
- * Special case: One of the helper-elements is contained by the other.
- * <foo>
- * <node2>
- * <node1>Text-1(precedence1 == 2)</node1>
- * </node2>
- * Text-6(precedence2 == 3)
- * </foo>
- */
- if ((precedence2 == 3) && (precedence1 > 1)) {
- cur = node1->parent;
- while (cur) {
- if (cur == node2)
- return(1);
- cur = cur->parent;
- }
- }
- if ((precedence1 == 3) && (precedence2 > 1)) {
- cur = node2->parent;
- while (cur) {
- if (cur == node1)
- return(-1);
- cur = cur->parent;
- }
- }
- }
- /*
- * Speedup using document order if available.
- */
- if ((node1->type == XML_ELEMENT_NODE) &&
- (node2->type == XML_ELEMENT_NODE) &&
- (0 > (ptrdiff_t) node1->content) &&
- (0 > (ptrdiff_t) node2->content) &&
- (node1->doc == node2->doc)) {
- l1 = -((ptrdiff_t) node1->content);
- l2 = -((ptrdiff_t) node2->content);
- if (l1 < l2)
- return(1);
- if (l1 > l2)
- return(-1);
- }
- turtle_comparison:
- if (node1 == node2->prev)
- return(1);
- if (node1 == node2->next)
- return(-1);
- /*
- * compute depth to root
- */
- for (depth2 = 0, cur = node2; cur->parent != NULL; cur = cur->parent) {
- if (cur->parent == node1)
- return(1);
- depth2++;
- }
- root = cur;
- for (depth1 = 0, cur = node1; cur->parent != NULL; cur = cur->parent) {
- if (cur->parent == node2)
- return(-1);
- depth1++;
- }
- /*
- * Distinct document (or distinct entities :-( ) case.
- */
- if (root != cur) {
- return(-2);
- }
- /*
- * get the nearest common ancestor.
- */
- while (depth1 > depth2) {
- depth1--;
- node1 = node1->parent;
- }
- while (depth2 > depth1) {
- depth2--;
- node2 = node2->parent;
- }
- while (node1->parent != node2->parent) {
- node1 = node1->parent;
- node2 = node2->parent;
- /* should not happen but just in case ... */
- if ((node1 == NULL) || (node2 == NULL))
- return(-2);
- }
- /*
- * Find who's first.
- */
- if (node1 == node2->prev)
- return(1);
- if (node1 == node2->next)
- return(-1);
- /*
- * Speedup using document order if available.
- */
- if ((node1->type == XML_ELEMENT_NODE) &&
- (node2->type == XML_ELEMENT_NODE) &&
- (0 > (ptrdiff_t) node1->content) &&
- (0 > (ptrdiff_t) node2->content) &&
- (node1->doc == node2->doc)) {
- l1 = -((ptrdiff_t) node1->content);
- l2 = -((ptrdiff_t) node2->content);
- if (l1 < l2)
- return(1);
- if (l1 > l2)
- return(-1);
- }
- for (cur = node1->next;cur != NULL;cur = cur->next)
- if (cur == node2)
- return(1);
- return(-1); /* assume there is no sibling list corruption */
- }
- #endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
- /*
- * Wrapper for the Timsort algorithm from timsort.h
- */
- #ifdef WITH_TIM_SORT
- #define SORT_NAME libxml_domnode
- #define SORT_TYPE xmlNodePtr
- /**
- * wrap_cmp:
- * @x: a node
- * @y: another node
- *
- * Comparison function for the Timsort implementation
- *
- * Returns -2 in case of error -1 if first point < second point, 0 if
- * it's the same node, +1 otherwise
- */
- static
- int wrap_cmp( xmlNodePtr x, xmlNodePtr y );
- #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
- static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
- {
- int res = xmlXPathCmpNodesExt(x, y);
- return res == -2 ? res : -res;
- }
- #else
- static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
- {
- int res = xmlXPathCmpNodes(x, y);
- return res == -2 ? res : -res;
- }
- #endif
- #define SORT_CMP(x, y) (wrap_cmp(x, y))
- #include "timsort.h"
- #endif /* WITH_TIM_SORT */
- #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
- /************************************************************************
- * *
- * Floating point stuff *
- * *
- ************************************************************************/
- double xmlXPathNAN;
- double xmlXPathPINF;
- double xmlXPathNINF;
- /**
- * xmlXPathInit:
- *
- * Initialize the XPath environment
- */
- ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
- void
- xmlXPathInit(void) {
- /* MSVC doesn't allow division by zero in constant expressions. */
- double zero = 0.0;
- xmlXPathNAN = 0.0 / zero;
- xmlXPathPINF = 1.0 / zero;
- xmlXPathNINF = -xmlXPathPINF;
- }
- /**
- * xmlXPathIsNaN:
- * @val: a double value
- *
- * Returns 1 if the value is a NaN, 0 otherwise
- */
- int
- xmlXPathIsNaN(double val) {
- #ifdef isnan
- return isnan(val);
- #else
- return !(val == val);
- #endif
- }
- /**
- * xmlXPathIsInf:
- * @val: a double value
- *
- * Returns 1 if the value is +Infinite, -1 if -Infinite, 0 otherwise
- */
- int
- xmlXPathIsInf(double val) {
- #ifdef isinf
- return isinf(val) ? (val > 0 ? 1 : -1) : 0;
- #else
- if (val >= xmlXPathPINF)
- return 1;
- if (val <= -xmlXPathPINF)
- return -1;
- return 0;
- #endif
- }
- #endif /* SCHEMAS or XPATH */
- #ifdef LIBXML_XPATH_ENABLED
- /*
- * TODO: when compatibility allows remove all "fake node libxslt" strings
- * the test should just be name[0] = ' '
- */
- #ifdef DEBUG_XPATH_EXPRESSION
- #define DEBUG_STEP
- #define DEBUG_EXPR
- #define DEBUG_EVAL_COUNTS
- #endif
- static xmlNs xmlXPathXMLNamespaceStruct = {
- NULL,
- XML_NAMESPACE_DECL,
- XML_XML_NAMESPACE,
- BAD_CAST "xml",
- NULL,
- NULL
- };
- static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
- #ifndef LIBXML_THREAD_ENABLED
- /*
- * Optimizer is disabled only when threaded apps are detected while
- * the library ain't compiled for thread safety.
- */
- static int xmlXPathDisableOptimizer = 0;
- #endif
- /************************************************************************
- * *
- * Error handling routines *
- * *
- ************************************************************************/
- /**
- * XP_ERRORNULL:
- * @X: the error code
- *
- * Macro to raise an XPath error and return NULL.
- */
- #define XP_ERRORNULL(X) \
- { xmlXPathErr(ctxt, X); return(NULL); }
- /*
- * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
- */
- static const char *xmlXPathErrorMessages[] = {
- "Ok\n",
- "Number encoding\n",
- "Unfinished literal\n",
- "Start of literal\n",
- "Expected $ for variable reference\n",
- "Undefined variable\n",
- "Invalid predicate\n",
- "Invalid expression\n",
- "Missing closing curly brace\n",
- "Unregistered function\n",
- "Invalid operand\n",
- "Invalid type\n",
- "Invalid number of arguments\n",
- "Invalid context size\n",
- "Invalid context position\n",
- "Memory allocation error\n",
- "Syntax error\n",
- "Resource error\n",
- "Sub resource error\n",
- "Undefined namespace prefix\n",
- "Encoding error\n",
- "Char out of XML range\n",
- "Invalid or incomplete context\n",
- "Stack usage error\n",
- "Forbidden variable\n",
- "Operation limit exceeded\n",
- "Recursion limit exceeded\n",
- "?? Unknown error ??\n" /* Must be last in the list! */
- };
- #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
- sizeof(xmlXPathErrorMessages[0])) - 1)
- /**
- * xmlXPathErrMemory:
- * @ctxt: an XPath context
- * @extra: extra information
- *
- * Handle a redefinition of attribute error
- */
- static void
- xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
- {
- if (ctxt != NULL) {
- xmlResetError(&ctxt->lastError);
- if (extra) {
- xmlChar buf[200];
- xmlStrPrintf(buf, 200,
- "Memory allocation failed : %s\n",
- extra);
- ctxt->lastError.message = (char *) xmlStrdup(buf);
- } else {
- ctxt->lastError.message = (char *)
- xmlStrdup(BAD_CAST "Memory allocation failed\n");
- }
- ctxt->lastError.domain = XML_FROM_XPATH;
- ctxt->lastError.code = XML_ERR_NO_MEMORY;
- if (ctxt->error != NULL)
- ctxt->error(ctxt->userData, &ctxt->lastError);
- } else {
- if (extra)
- __xmlRaiseError(NULL, NULL, NULL,
- NULL, NULL, XML_FROM_XPATH,
- XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
- extra, NULL, NULL, 0, 0,
- "Memory allocation failed : %s\n", extra);
- else
- __xmlRaiseError(NULL, NULL, NULL,
- NULL, NULL, XML_FROM_XPATH,
- XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
- NULL, NULL, NULL, 0, 0,
- "Memory allocation failed\n");
- }
- }
- /**
- * xmlXPathPErrMemory:
- * @ctxt: an XPath parser context
- * @extra: extra information
- *
- * Handle a redefinition of attribute error
- */
- static void
- xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
- {
- if (ctxt == NULL)
- xmlXPathErrMemory(NULL, extra);
- else {
- ctxt->error = XPATH_MEMORY_ERROR;
- xmlXPathErrMemory(ctxt->context, extra);
- }
- }
- /**
- * xmlXPathErr:
- * @ctxt: a XPath parser context
- * @error: the error code
- *
- * Handle an XPath error
- */
- void
- xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
- {
- if ((error < 0) || (error > MAXERRNO))
- error = MAXERRNO;
- if (ctxt == NULL) {
- __xmlRaiseError(NULL, NULL, NULL,
- NULL, NULL, XML_FROM_XPATH,
- error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
- XML_ERR_ERROR, NULL, 0,
- NULL, NULL, NULL, 0, 0,
- "%s", xmlXPathErrorMessages[error]);
- return;
- }
- ctxt->error = error;
- if (ctxt->context == NULL) {
- __xmlRaiseError(NULL, NULL, NULL,
- NULL, NULL, XML_FROM_XPATH,
- error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
- XML_ERR_ERROR, NULL, 0,
- (const char *) ctxt->base, NULL, NULL,
- ctxt->cur - ctxt->base, 0,
- "%s", xmlXPathErrorMessages[error]);
- return;
- }
- /* cleanup current last error */
- xmlResetError(&ctxt->context->lastError);
- ctxt->context->lastError.domain = XML_FROM_XPATH;
- ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
- XPATH_EXPRESSION_OK;
- ctxt->context->lastError.level = XML_ERR_ERROR;
- ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
- ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
- ctxt->context->lastError.node = ctxt->context->debugNode;
- if (ctxt->context->error != NULL) {
- ctxt->context->error(ctxt->context->userData,
- &ctxt->context->lastError);
- } else {
- __xmlRaiseError(NULL, NULL, NULL,
- NULL, ctxt->context->debugNode, XML_FROM_XPATH,
- error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
- XML_ERR_ERROR, NULL, 0,
- (const char *) ctxt->base, NULL, NULL,
- ctxt->cur - ctxt->base, 0,
- "%s", xmlXPathErrorMessages[error]);
- }
- }
- /**
- * xmlXPatherror:
- * @ctxt: the XPath Parser context
- * @file: the file name
- * @line: the line number
- * @no: the error number
- *
- * Formats an error message.
- */
- void
- xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
- int line ATTRIBUTE_UNUSED, int no) {
- xmlXPathErr(ctxt, no);
- }
- /**
- * xmlXPathCheckOpLimit:
- * @ctxt: the XPath Parser context
- * @opCount: the number of operations to be added
- *
- * Adds opCount to the running total of operations and returns -1 if the
- * operation limit is exceeded. Returns 0 otherwise.
- */
- static int
- xmlXPathCheckOpLimit(xmlXPathParserContextPtr ctxt, unsigned long opCount) {
- xmlXPathContextPtr xpctxt = ctxt->context;
- if ((opCount > xpctxt->opLimit) ||
- (xpctxt->opCount > xpctxt->opLimit - opCount)) {
- xpctxt->opCount = xpctxt->opLimit;
- xmlXPathErr(ctxt, XPATH_OP_LIMIT_EXCEEDED);
- return(-1);
- }
- xpctxt->opCount += opCount;
- return(0);
- }
- #define OP_LIMIT_EXCEEDED(ctxt, n) \
- ((ctxt->context->opLimit != 0) && (xmlXPathCheckOpLimit(ctxt, n) < 0))
- /************************************************************************
- * *
- * Utilities *
- * *
- ************************************************************************/
- /**
- * xsltPointerList:
- *
- * Pointer-list for various purposes.
- */
- typedef struct _xmlPointerList xmlPointerList;
- typedef xmlPointerList *xmlPointerListPtr;
- struct _xmlPointerList {
- void **items;
- int number;
- int size;
- };
- /*
- * TODO: Since such a list-handling is used in xmlschemas.c and libxslt
- * and here, we should make the functions public.
- */
- static int
- xmlPointerListAddSize(xmlPointerListPtr list,
- void *item,
- int initialSize)
- {
- if (list->items == NULL) {
- if (initialSize <= 0)
- initialSize = 1;
- list->items = (void **) xmlMalloc(initialSize * sizeof(void *));
- if (list->items == NULL) {
- xmlXPathErrMemory(NULL,
- "xmlPointerListCreate: allocating item\n");
- return(-1);
- }
- list->number = 0;
- list->size = initialSize;
- } else if (list->size <= list->number) {
- if (list->size > 50000000) {
- xmlXPathErrMemory(NULL,
- "xmlPointerListAddSize: re-allocating item\n");
- return(-1);
- }
- list->size *= 2;
- list->items = (void **) xmlRealloc(list->items,
- list->size * sizeof(void *));
- if (list->items == NULL) {
- xmlXPathErrMemory(NULL,
- "xmlPointerListAddSize: re-allocating item\n");
- list->size = 0;
- return(-1);
- }
- }
- list->items[list->number++] = item;
- return(0);
- }
- /**
- * xsltPointerListCreate:
- *
- * Creates an xsltPointerList structure.
- *
- * Returns a xsltPointerList structure or NULL in case of an error.
- */
- static xmlPointerListPtr
- xmlPointerListCreate(int initialSize)
- {
- xmlPointerListPtr ret;
- ret = xmlMalloc(sizeof(xmlPointerList));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL,
- "xmlPointerListCreate: allocating item\n");
- return (NULL);
- }
- memset(ret, 0, sizeof(xmlPointerList));
- if (initialSize > 0) {
- xmlPointerListAddSize(ret, NULL, initialSize);
- ret->number = 0;
- }
- return (ret);
- }
- /**
- * xsltPointerListFree:
- *
- * Frees the xsltPointerList structure. This does not free
- * the content of the list.
- */
- static void
- xmlPointerListFree(xmlPointerListPtr list)
- {
- if (list == NULL)
- return;
- if (list->items != NULL)
- xmlFree(list->items);
- xmlFree(list);
- }
- /************************************************************************
- * *
- * Parser Types *
- * *
- ************************************************************************/
- /*
- * Types are private:
- */
- typedef enum {
- XPATH_OP_END=0,
- XPATH_OP_AND,
- XPATH_OP_OR,
- XPATH_OP_EQUAL,
- XPATH_OP_CMP,
- XPATH_OP_PLUS,
- XPATH_OP_MULT,
- XPATH_OP_UNION,
- XPATH_OP_ROOT,
- XPATH_OP_NODE,
- XPATH_OP_COLLECT,
- XPATH_OP_VALUE, /* 11 */
- XPATH_OP_VARIABLE,
- XPATH_OP_FUNCTION,
- XPATH_OP_ARG,
- XPATH_OP_PREDICATE,
- XPATH_OP_FILTER, /* 16 */
- XPATH_OP_SORT /* 17 */
- #ifdef LIBXML_XPTR_ENABLED
- ,XPATH_OP_RANGETO
- #endif
- } xmlXPathOp;
- typedef enum {
- AXIS_ANCESTOR = 1,
- AXIS_ANCESTOR_OR_SELF,
- AXIS_ATTRIBUTE,
- AXIS_CHILD,
- AXIS_DESCENDANT,
- AXIS_DESCENDANT_OR_SELF,
- AXIS_FOLLOWING,
- AXIS_FOLLOWING_SIBLING,
- AXIS_NAMESPACE,
- AXIS_PARENT,
- AXIS_PRECEDING,
- AXIS_PRECEDING_SIBLING,
- AXIS_SELF
- } xmlXPathAxisVal;
- typedef enum {
- NODE_TEST_NONE = 0,
- NODE_TEST_TYPE = 1,
- NODE_TEST_PI = 2,
- NODE_TEST_ALL = 3,
- NODE_TEST_NS = 4,
- NODE_TEST_NAME = 5
- } xmlXPathTestVal;
- typedef enum {
- NODE_TYPE_NODE = 0,
- NODE_TYPE_COMMENT = XML_COMMENT_NODE,
- NODE_TYPE_TEXT = XML_TEXT_NODE,
- NODE_TYPE_PI = XML_PI_NODE
- } xmlXPathTypeVal;
- typedef struct _xmlXPathStepOp xmlXPathStepOp;
- typedef xmlXPathStepOp *xmlXPathStepOpPtr;
- struct _xmlXPathStepOp {
- xmlXPathOp op; /* The identifier of the operation */
- int ch1; /* First child */
- int ch2; /* Second child */
- int value;
- int value2;
- int value3;
- void *value4;
- void *value5;
- xmlXPathFunction cache;
- void *cacheURI;
- };
- struct _xmlXPathCompExpr {
- int nbStep; /* Number of steps in this expression */
- int maxStep; /* Maximum number of steps allocated */
- xmlXPathStepOp *steps; /* ops for computation of this expression */
- int last; /* index of last step in expression */
- xmlChar *expr; /* the expression being computed */
- xmlDictPtr dict; /* the dictionary to use if any */
- #ifdef DEBUG_EVAL_COUNTS
- int nb;
- xmlChar *string;
- #endif
- #ifdef XPATH_STREAMING
- xmlPatternPtr stream;
- #endif
- };
- /************************************************************************
- * *
- * Forward declarations *
- * *
- ************************************************************************/
- static void
- xmlXPathFreeValueTree(xmlNodeSetPtr obj);
- static void
- xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj);
- static int
- xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op, xmlNodePtr *first);
- static int
- xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op,
- int isPredicate);
- static void
- xmlXPathFreeObjectEntry(void *obj, const xmlChar *name);
- /************************************************************************
- * *
- * Parser Type functions *
- * *
- ************************************************************************/
- /**
- * xmlXPathNewCompExpr:
- *
- * Create a new Xpath component
- *
- * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
- */
- static xmlXPathCompExprPtr
- xmlXPathNewCompExpr(void) {
- xmlXPathCompExprPtr cur;
- cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
- if (cur == NULL) {
- xmlXPathErrMemory(NULL, "allocating component\n");
- return(NULL);
- }
- memset(cur, 0, sizeof(xmlXPathCompExpr));
- cur->maxStep = 10;
- cur->nbStep = 0;
- cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
- sizeof(xmlXPathStepOp));
- if (cur->steps == NULL) {
- xmlXPathErrMemory(NULL, "allocating steps\n");
- xmlFree(cur);
- return(NULL);
- }
- memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
- cur->last = -1;
- #ifdef DEBUG_EVAL_COUNTS
- cur->nb = 0;
- #endif
- return(cur);
- }
- /**
- * xmlXPathFreeCompExpr:
- * @comp: an XPATH comp
- *
- * Free up the memory allocated by @comp
- */
- void
- xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
- {
- xmlXPathStepOpPtr op;
- int i;
- if (comp == NULL)
- return;
- if (comp->dict == NULL) {
- for (i = 0; i < comp->nbStep; i++) {
- op = &comp->steps[i];
- if (op->value4 != NULL) {
- if (op->op == XPATH_OP_VALUE)
- xmlXPathFreeObject(op->value4);
- else
- xmlFree(op->value4);
- }
- if (op->value5 != NULL)
- xmlFree(op->value5);
- }
- } else {
- for (i = 0; i < comp->nbStep; i++) {
- op = &comp->steps[i];
- if (op->value4 != NULL) {
- if (op->op == XPATH_OP_VALUE)
- xmlXPathFreeObject(op->value4);
- }
- }
- xmlDictFree(comp->dict);
- }
- if (comp->steps != NULL) {
- xmlFree(comp->steps);
- }
- #ifdef DEBUG_EVAL_COUNTS
- if (comp->string != NULL) {
- xmlFree(comp->string);
- }
- #endif
- #ifdef XPATH_STREAMING
- if (comp->stream != NULL) {
- xmlFreePatternList(comp->stream);
- }
- #endif
- if (comp->expr != NULL) {
- xmlFree(comp->expr);
- }
- xmlFree(comp);
- }
- /**
- * xmlXPathCompExprAdd:
- * @comp: the compiled expression
- * @ch1: first child index
- * @ch2: second child index
- * @op: an op
- * @value: the first int value
- * @value2: the second int value
- * @value3: the third int value
- * @value4: the first string value
- * @value5: the second string value
- *
- * Add a step to an XPath Compiled Expression
- *
- * Returns -1 in case of failure, the index otherwise
- */
- static int
- xmlXPathCompExprAdd(xmlXPathParserContextPtr ctxt, int ch1, int ch2,
- xmlXPathOp op, int value,
- int value2, int value3, void *value4, void *value5) {
- xmlXPathCompExprPtr comp = ctxt->comp;
- if (comp->nbStep >= comp->maxStep) {
- xmlXPathStepOp *real;
- if (comp->maxStep >= XPATH_MAX_STEPS) {
- xmlXPathPErrMemory(ctxt, "adding step\n");
- return(-1);
- }
- comp->maxStep *= 2;
- real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
- comp->maxStep * sizeof(xmlXPathStepOp));
- if (real == NULL) {
- comp->maxStep /= 2;
- xmlXPathPErrMemory(ctxt, "adding step\n");
- return(-1);
- }
- comp->steps = real;
- }
- comp->last = comp->nbStep;
- comp->steps[comp->nbStep].ch1 = ch1;
- comp->steps[comp->nbStep].ch2 = ch2;
- comp->steps[comp->nbStep].op = op;
- comp->steps[comp->nbStep].value = value;
- comp->steps[comp->nbStep].value2 = value2;
- comp->steps[comp->nbStep].value3 = value3;
- if ((comp->dict != NULL) &&
- ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
- (op == XPATH_OP_COLLECT))) {
- if (value4 != NULL) {
- comp->steps[comp->nbStep].value4 = (xmlChar *)
- (void *)xmlDictLookup(comp->dict, value4, -1);
- xmlFree(value4);
- } else
- comp->steps[comp->nbStep].value4 = NULL;
- if (value5 != NULL) {
- comp->steps[comp->nbStep].value5 = (xmlChar *)
- (void *)xmlDictLookup(comp->dict, value5, -1);
- xmlFree(value5);
- } else
- comp->steps[comp->nbStep].value5 = NULL;
- } else {
- comp->steps[comp->nbStep].value4 = value4;
- comp->steps[comp->nbStep].value5 = value5;
- }
- comp->steps[comp->nbStep].cache = NULL;
- return(comp->nbStep++);
- }
- /**
- * xmlXPathCompSwap:
- * @comp: the compiled expression
- * @op: operation index
- *
- * Swaps 2 operations in the compiled expression
- */
- static void
- xmlXPathCompSwap(xmlXPathStepOpPtr op) {
- int tmp;
- #ifndef LIBXML_THREAD_ENABLED
- /*
- * Since this manipulates possibly shared variables, this is
- * disabled if one detects that the library is used in a multithreaded
- * application
- */
- if (xmlXPathDisableOptimizer)
- return;
- #endif
- tmp = op->ch1;
- op->ch1 = op->ch2;
- op->ch2 = tmp;
- }
- #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
- xmlXPathCompExprAdd(ctxt, (op1), (op2), \
- (op), (val), (val2), (val3), (val4), (val5))
- #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
- xmlXPathCompExprAdd(ctxt, ctxt->comp->last, -1, \
- (op), (val), (val2), (val3), (val4), (val5))
- #define PUSH_LEAVE_EXPR(op, val, val2) \
- xmlXPathCompExprAdd(ctxt, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
- #define PUSH_UNARY_EXPR(op, ch, val, val2) \
- xmlXPathCompExprAdd(ctxt, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
- #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
- xmlXPathCompExprAdd(ctxt, (ch1), (ch2), (op), \
- (val), (val2), 0 ,NULL ,NULL)
- /************************************************************************
- * *
- * XPath object cache structures *
- * *
- ************************************************************************/
- /* #define XP_DEFAULT_CACHE_ON */
- #define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL))
- typedef struct _xmlXPathContextCache xmlXPathContextCache;
- typedef xmlXPathContextCache *xmlXPathContextCachePtr;
- struct _xmlXPathContextCache {
- xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */
- xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */
- xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */
- xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */
- xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */
- int maxNodeset;
- int maxString;
- int maxBoolean;
- int maxNumber;
- int maxMisc;
- #ifdef XP_DEBUG_OBJ_USAGE
- int dbgCachedAll;
- int dbgCachedNodeset;
- int dbgCachedString;
- int dbgCachedBool;
- int dbgCachedNumber;
- int dbgCachedPoint;
- int dbgCachedRange;
- int dbgCachedLocset;
- int dbgCachedUsers;
- int dbgCachedXSLTTree;
- int dbgCachedUndefined;
- int dbgReusedAll;
- int dbgReusedNodeset;
- int dbgReusedString;
- int dbgReusedBool;
- int dbgReusedNumber;
- int dbgReusedPoint;
- int dbgReusedRange;
- int dbgReusedLocset;
- int dbgReusedUsers;
- int dbgReusedXSLTTree;
- int dbgReusedUndefined;
- #endif
- };
- /************************************************************************
- * *
- * Debugging related functions *
- * *
- ************************************************************************/
- #define STRANGE \
- xmlGenericError(xmlGenericErrorContext, \
- "Internal error at %s:%d\n", \
- __FILE__, __LINE__);
- #ifdef LIBXML_DEBUG_ENABLED
- static void
- xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
- int i;
- char shift[100];
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- if (cur == NULL) {
- fprintf(output, "%s", shift);
- fprintf(output, "Node is NULL !\n");
- return;
- }
- if ((cur->type == XML_DOCUMENT_NODE) ||
- (cur->type == XML_HTML_DOCUMENT_NODE)) {
- fprintf(output, "%s", shift);
- fprintf(output, " /\n");
- } else if (cur->type == XML_ATTRIBUTE_NODE)
- xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
- else
- xmlDebugDumpOneNode(output, cur, depth);
- }
- static void
- xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
- xmlNodePtr tmp;
- int i;
- char shift[100];
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- if (cur == NULL) {
- fprintf(output, "%s", shift);
- fprintf(output, "Node is NULL !\n");
- return;
- }
- while (cur != NULL) {
- tmp = cur;
- cur = cur->next;
- xmlDebugDumpOneNode(output, tmp, depth);
- }
- }
- static void
- xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
- int i;
- char shift[100];
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- if (cur == NULL) {
- fprintf(output, "%s", shift);
- fprintf(output, "NodeSet is NULL !\n");
- return;
- }
- if (cur != NULL) {
- fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
- for (i = 0;i < cur->nodeNr;i++) {
- fprintf(output, "%s", shift);
- fprintf(output, "%d", i + 1);
- xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
- }
- }
- }
- static void
- xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
- int i;
- char shift[100];
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
- fprintf(output, "%s", shift);
- fprintf(output, "Value Tree is NULL !\n");
- return;
- }
- fprintf(output, "%s", shift);
- fprintf(output, "%d", i + 1);
- xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
- }
- #if defined(LIBXML_XPTR_ENABLED)
- static void
- xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
- int i;
- char shift[100];
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- if (cur == NULL) {
- fprintf(output, "%s", shift);
- fprintf(output, "LocationSet is NULL !\n");
- return;
- }
- for (i = 0;i < cur->locNr;i++) {
- fprintf(output, "%s", shift);
- fprintf(output, "%d : ", i + 1);
- xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
- }
- }
- #endif /* LIBXML_XPTR_ENABLED */
- /**
- * xmlXPathDebugDumpObject:
- * @output: the FILE * to dump the output
- * @cur: the object to inspect
- * @depth: indentation level
- *
- * Dump the content of the object for debugging purposes
- */
- void
- xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
- int i;
- char shift[100];
- if (output == NULL) return;
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- fprintf(output, "%s", shift);
- if (cur == NULL) {
- fprintf(output, "Object is empty (NULL)\n");
- return;
- }
- switch(cur->type) {
- case XPATH_UNDEFINED:
- fprintf(output, "Object is uninitialized\n");
- break;
- case XPATH_NODESET:
- fprintf(output, "Object is a Node Set :\n");
- xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
- break;
- case XPATH_XSLT_TREE:
- fprintf(output, "Object is an XSLT value tree :\n");
- xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
- break;
- case XPATH_BOOLEAN:
- fprintf(output, "Object is a Boolean : ");
- if (cur->boolval) fprintf(output, "true\n");
- else fprintf(output, "false\n");
- break;
- case XPATH_NUMBER:
- switch (xmlXPathIsInf(cur->floatval)) {
- case 1:
- fprintf(output, "Object is a number : Infinity\n");
- break;
- case -1:
- fprintf(output, "Object is a number : -Infinity\n");
- break;
- default:
- if (xmlXPathIsNaN(cur->floatval)) {
- fprintf(output, "Object is a number : NaN\n");
- } else if (cur->floatval == 0) {
- /* Omit sign for negative zero. */
- fprintf(output, "Object is a number : 0\n");
- } else {
- fprintf(output, "Object is a number : %0g\n", cur->floatval);
- }
- }
- break;
- case XPATH_STRING:
- fprintf(output, "Object is a string : ");
- xmlDebugDumpString(output, cur->stringval);
- fprintf(output, "\n");
- break;
- case XPATH_POINT:
- fprintf(output, "Object is a point : index %d in node", cur->index);
- xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
- fprintf(output, "\n");
- break;
- case XPATH_RANGE:
- if ((cur->user2 == NULL) ||
- ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
- fprintf(output, "Object is a collapsed range :\n");
- fprintf(output, "%s", shift);
- if (cur->index >= 0)
- fprintf(output, "index %d in ", cur->index);
- fprintf(output, "node\n");
- xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
- depth + 1);
- } else {
- fprintf(output, "Object is a range :\n");
- fprintf(output, "%s", shift);
- fprintf(output, "From ");
- if (cur->index >= 0)
- fprintf(output, "index %d in ", cur->index);
- fprintf(output, "node\n");
- xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
- depth + 1);
- fprintf(output, "%s", shift);
- fprintf(output, "To ");
- if (cur->index2 >= 0)
- fprintf(output, "index %d in ", cur->index2);
- fprintf(output, "node\n");
- xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
- depth + 1);
- fprintf(output, "\n");
- }
- break;
- case XPATH_LOCATIONSET:
- #if defined(LIBXML_XPTR_ENABLED)
- fprintf(output, "Object is a Location Set:\n");
- xmlXPathDebugDumpLocationSet(output,
- (xmlLocationSetPtr) cur->user, depth);
- #endif
- break;
- case XPATH_USERS:
- fprintf(output, "Object is user defined\n");
- break;
- }
- }
- static void
- xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
- xmlXPathStepOpPtr op, int depth) {
- int i;
- char shift[100];
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- fprintf(output, "%s", shift);
- if (op == NULL) {
- fprintf(output, "Step is NULL\n");
- return;
- }
- switch (op->op) {
- case XPATH_OP_END:
- fprintf(output, "END"); break;
- case XPATH_OP_AND:
- fprintf(output, "AND"); break;
- case XPATH_OP_OR:
- fprintf(output, "OR"); break;
- case XPATH_OP_EQUAL:
- if (op->value)
- fprintf(output, "EQUAL =");
- else
- fprintf(output, "EQUAL !=");
- break;
- case XPATH_OP_CMP:
- if (op->value)
- fprintf(output, "CMP <");
- else
- fprintf(output, "CMP >");
- if (!op->value2)
- fprintf(output, "=");
- break;
- case XPATH_OP_PLUS:
- if (op->value == 0)
- fprintf(output, "PLUS -");
- else if (op->value == 1)
- fprintf(output, "PLUS +");
- else if (op->value == 2)
- fprintf(output, "PLUS unary -");
- else if (op->value == 3)
- fprintf(output, "PLUS unary - -");
- break;
- case XPATH_OP_MULT:
- if (op->value == 0)
- fprintf(output, "MULT *");
- else if (op->value == 1)
- fprintf(output, "MULT div");
- else
- fprintf(output, "MULT mod");
- break;
- case XPATH_OP_UNION:
- fprintf(output, "UNION"); break;
- case XPATH_OP_ROOT:
- fprintf(output, "ROOT"); break;
- case XPATH_OP_NODE:
- fprintf(output, "NODE"); break;
- case XPATH_OP_SORT:
- fprintf(output, "SORT"); break;
- case XPATH_OP_COLLECT: {
- xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value;
- xmlXPathTestVal test = (xmlXPathTestVal)op->value2;
- xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3;
- const xmlChar *prefix = op->value4;
- const xmlChar *name = op->value5;
- fprintf(output, "COLLECT ");
- switch (axis) {
- case AXIS_ANCESTOR:
- fprintf(output, " 'ancestors' "); break;
- case AXIS_ANCESTOR_OR_SELF:
- fprintf(output, " 'ancestors-or-self' "); break;
- case AXIS_ATTRIBUTE:
- fprintf(output, " 'attributes' "); break;
- case AXIS_CHILD:
- fprintf(output, " 'child' "); break;
- case AXIS_DESCENDANT:
- fprintf(output, " 'descendant' "); break;
- case AXIS_DESCENDANT_OR_SELF:
- fprintf(output, " 'descendant-or-self' "); break;
- case AXIS_FOLLOWING:
- fprintf(output, " 'following' "); break;
- case AXIS_FOLLOWING_SIBLING:
- fprintf(output, " 'following-siblings' "); break;
- case AXIS_NAMESPACE:
- fprintf(output, " 'namespace' "); break;
- case AXIS_PARENT:
- fprintf(output, " 'parent' "); break;
- case AXIS_PRECEDING:
- fprintf(output, " 'preceding' "); break;
- case AXIS_PRECEDING_SIBLING:
- fprintf(output, " 'preceding-sibling' "); break;
- case AXIS_SELF:
- fprintf(output, " 'self' "); break;
- }
- switch (test) {
- case NODE_TEST_NONE:
- fprintf(output, "'none' "); break;
- case NODE_TEST_TYPE:
- fprintf(output, "'type' "); break;
- case NODE_TEST_PI:
- fprintf(output, "'PI' "); break;
- case NODE_TEST_ALL:
- fprintf(output, "'all' "); break;
- case NODE_TEST_NS:
- fprintf(output, "'namespace' "); break;
- case NODE_TEST_NAME:
- fprintf(output, "'name' "); break;
- }
- switch (type) {
- case NODE_TYPE_NODE:
- fprintf(output, "'node' "); break;
- case NODE_TYPE_COMMENT:
- fprintf(output, "'comment' "); break;
- case NODE_TYPE_TEXT:
- fprintf(output, "'text' "); break;
- case NODE_TYPE_PI:
- fprintf(output, "'PI' "); break;
- }
- if (prefix != NULL)
- fprintf(output, "%s:", prefix);
- if (name != NULL)
- fprintf(output, "%s", (const char *) name);
- break;
- }
- case XPATH_OP_VALUE: {
- xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
- fprintf(output, "ELEM ");
- xmlXPathDebugDumpObject(output, object, 0);
- goto finish;
- }
- case XPATH_OP_VARIABLE: {
- const xmlChar *prefix = op->value5;
- const xmlChar *name = op->value4;
- if (prefix != NULL)
- fprintf(output, "VARIABLE %s:%s", prefix, name);
- else
- fprintf(output, "VARIABLE %s", name);
- break;
- }
- case XPATH_OP_FUNCTION: {
- int nbargs = op->value;
- const xmlChar *prefix = op->value5;
- const xmlChar *name = op->value4;
- if (prefix != NULL)
- fprintf(output, "FUNCTION %s:%s(%d args)",
- prefix, name, nbargs);
- else
- fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
- break;
- }
- case XPATH_OP_ARG: fprintf(output, "ARG"); break;
- case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
- case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
- #ifdef LIBXML_XPTR_ENABLED
- case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
- #endif
- default:
- fprintf(output, "UNKNOWN %d\n", op->op); return;
- }
- fprintf(output, "\n");
- finish:
- if (op->ch1 >= 0)
- xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
- if (op->ch2 >= 0)
- xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
- }
- /**
- * xmlXPathDebugDumpCompExpr:
- * @output: the FILE * for the output
- * @comp: the precompiled XPath expression
- * @depth: the indentation level.
- *
- * Dumps the tree of the compiled XPath expression.
- */
- void
- xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
- int depth) {
- int i;
- char shift[100];
- if ((output == NULL) || (comp == NULL)) return;
- for (i = 0;((i < depth) && (i < 25));i++)
- shift[2 * i] = shift[2 * i + 1] = ' ';
- shift[2 * i] = shift[2 * i + 1] = 0;
- fprintf(output, "%s", shift);
- #ifdef XPATH_STREAMING
- if (comp->stream) {
- fprintf(output, "Streaming Expression\n");
- } else
- #endif
- {
- fprintf(output, "Compiled Expression : %d elements\n",
- comp->nbStep);
- i = comp->last;
- xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
- }
- }
- #ifdef XP_DEBUG_OBJ_USAGE
- /*
- * XPath object usage related debugging variables.
- */
- static int xmlXPathDebugObjCounterUndefined = 0;
- static int xmlXPathDebugObjCounterNodeset = 0;
- static int xmlXPathDebugObjCounterBool = 0;
- static int xmlXPathDebugObjCounterNumber = 0;
- static int xmlXPathDebugObjCounterString = 0;
- static int xmlXPathDebugObjCounterPoint = 0;
- static int xmlXPathDebugObjCounterRange = 0;
- static int xmlXPathDebugObjCounterLocset = 0;
- static int xmlXPathDebugObjCounterUsers = 0;
- static int xmlXPathDebugObjCounterXSLTTree = 0;
- static int xmlXPathDebugObjCounterAll = 0;
- static int xmlXPathDebugObjTotalUndefined = 0;
- static int xmlXPathDebugObjTotalNodeset = 0;
- static int xmlXPathDebugObjTotalBool = 0;
- static int xmlXPathDebugObjTotalNumber = 0;
- static int xmlXPathDebugObjTotalString = 0;
- static int xmlXPathDebugObjTotalPoint = 0;
- static int xmlXPathDebugObjTotalRange = 0;
- static int xmlXPathDebugObjTotalLocset = 0;
- static int xmlXPathDebugObjTotalUsers = 0;
- static int xmlXPathDebugObjTotalXSLTTree = 0;
- static int xmlXPathDebugObjTotalAll = 0;
- static int xmlXPathDebugObjMaxUndefined = 0;
- static int xmlXPathDebugObjMaxNodeset = 0;
- static int xmlXPathDebugObjMaxBool = 0;
- static int xmlXPathDebugObjMaxNumber = 0;
- static int xmlXPathDebugObjMaxString = 0;
- static int xmlXPathDebugObjMaxPoint = 0;
- static int xmlXPathDebugObjMaxRange = 0;
- static int xmlXPathDebugObjMaxLocset = 0;
- static int xmlXPathDebugObjMaxUsers = 0;
- static int xmlXPathDebugObjMaxXSLTTree = 0;
- static int xmlXPathDebugObjMaxAll = 0;
- static void
- xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
- {
- if (ctxt != NULL) {
- if (ctxt->cache != NULL) {
- xmlXPathContextCachePtr cache =
- (xmlXPathContextCachePtr) ctxt->cache;
- cache->dbgCachedAll = 0;
- cache->dbgCachedNodeset = 0;
- cache->dbgCachedString = 0;
- cache->dbgCachedBool = 0;
- cache->dbgCachedNumber = 0;
- cache->dbgCachedPoint = 0;
- cache->dbgCachedRange = 0;
- cache->dbgCachedLocset = 0;
- cache->dbgCachedUsers = 0;
- cache->dbgCachedXSLTTree = 0;
- cache->dbgCachedUndefined = 0;
- cache->dbgReusedAll = 0;
- cache->dbgReusedNodeset = 0;
- cache->dbgReusedString = 0;
- cache->dbgReusedBool = 0;
- cache->dbgReusedNumber = 0;
- cache->dbgReusedPoint = 0;
- cache->dbgReusedRange = 0;
- cache->dbgReusedLocset = 0;
- cache->dbgReusedUsers = 0;
- cache->dbgReusedXSLTTree = 0;
- cache->dbgReusedUndefined = 0;
- }
- }
- xmlXPathDebugObjCounterUndefined = 0;
- xmlXPathDebugObjCounterNodeset = 0;
- xmlXPathDebugObjCounterBool = 0;
- xmlXPathDebugObjCounterNumber = 0;
- xmlXPathDebugObjCounterString = 0;
- xmlXPathDebugObjCounterPoint = 0;
- xmlXPathDebugObjCounterRange = 0;
- xmlXPathDebugObjCounterLocset = 0;
- xmlXPathDebugObjCounterUsers = 0;
- xmlXPathDebugObjCounterXSLTTree = 0;
- xmlXPathDebugObjCounterAll = 0;
- xmlXPathDebugObjTotalUndefined = 0;
- xmlXPathDebugObjTotalNodeset = 0;
- xmlXPathDebugObjTotalBool = 0;
- xmlXPathDebugObjTotalNumber = 0;
- xmlXPathDebugObjTotalString = 0;
- xmlXPathDebugObjTotalPoint = 0;
- xmlXPathDebugObjTotalRange = 0;
- xmlXPathDebugObjTotalLocset = 0;
- xmlXPathDebugObjTotalUsers = 0;
- xmlXPathDebugObjTotalXSLTTree = 0;
- xmlXPathDebugObjTotalAll = 0;
- xmlXPathDebugObjMaxUndefined = 0;
- xmlXPathDebugObjMaxNodeset = 0;
- xmlXPathDebugObjMaxBool = 0;
- xmlXPathDebugObjMaxNumber = 0;
- xmlXPathDebugObjMaxString = 0;
- xmlXPathDebugObjMaxPoint = 0;
- xmlXPathDebugObjMaxRange = 0;
- xmlXPathDebugObjMaxLocset = 0;
- xmlXPathDebugObjMaxUsers = 0;
- xmlXPathDebugObjMaxXSLTTree = 0;
- xmlXPathDebugObjMaxAll = 0;
- }
- static void
- xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
- xmlXPathObjectType objType)
- {
- int isCached = 0;
- if (ctxt != NULL) {
- if (ctxt->cache != NULL) {
- xmlXPathContextCachePtr cache =
- (xmlXPathContextCachePtr) ctxt->cache;
- isCached = 1;
- cache->dbgReusedAll++;
- switch (objType) {
- case XPATH_UNDEFINED:
- cache->dbgReusedUndefined++;
- break;
- case XPATH_NODESET:
- cache->dbgReusedNodeset++;
- break;
- case XPATH_BOOLEAN:
- cache->dbgReusedBool++;
- break;
- case XPATH_NUMBER:
- cache->dbgReusedNumber++;
- break;
- case XPATH_STRING:
- cache->dbgReusedString++;
- break;
- case XPATH_POINT:
- cache->dbgReusedPoint++;
- break;
- case XPATH_RANGE:
- cache->dbgReusedRange++;
- break;
- case XPATH_LOCATIONSET:
- cache->dbgReusedLocset++;
- break;
- case XPATH_USERS:
- cache->dbgReusedUsers++;
- break;
- case XPATH_XSLT_TREE:
- cache->dbgReusedXSLTTree++;
- break;
- default:
- break;
- }
- }
- }
- switch (objType) {
- case XPATH_UNDEFINED:
- if (! isCached)
- xmlXPathDebugObjTotalUndefined++;
- xmlXPathDebugObjCounterUndefined++;
- if (xmlXPathDebugObjCounterUndefined >
- xmlXPathDebugObjMaxUndefined)
- xmlXPathDebugObjMaxUndefined =
- xmlXPathDebugObjCounterUndefined;
- break;
- case XPATH_NODESET:
- if (! isCached)
- xmlXPathDebugObjTotalNodeset++;
- xmlXPathDebugObjCounterNodeset++;
- if (xmlXPathDebugObjCounterNodeset >
- xmlXPathDebugObjMaxNodeset)
- xmlXPathDebugObjMaxNodeset =
- xmlXPathDebugObjCounterNodeset;
- break;
- case XPATH_BOOLEAN:
- if (! isCached)
- xmlXPathDebugObjTotalBool++;
- xmlXPathDebugObjCounterBool++;
- if (xmlXPathDebugObjCounterBool >
- xmlXPathDebugObjMaxBool)
- xmlXPathDebugObjMaxBool =
- xmlXPathDebugObjCounterBool;
- break;
- case XPATH_NUMBER:
- if (! isCached)
- xmlXPathDebugObjTotalNumber++;
- xmlXPathDebugObjCounterNumber++;
- if (xmlXPathDebugObjCounterNumber >
- xmlXPathDebugObjMaxNumber)
- xmlXPathDebugObjMaxNumber =
- xmlXPathDebugObjCounterNumber;
- break;
- case XPATH_STRING:
- if (! isCached)
- xmlXPathDebugObjTotalString++;
- xmlXPathDebugObjCounterString++;
- if (xmlXPathDebugObjCounterString >
- xmlXPathDebugObjMaxString)
- xmlXPathDebugObjMaxString =
- xmlXPathDebugObjCounterString;
- break;
- case XPATH_POINT:
- if (! isCached)
- xmlXPathDebugObjTotalPoint++;
- xmlXPathDebugObjCounterPoint++;
- if (xmlXPathDebugObjCounterPoint >
- xmlXPathDebugObjMaxPoint)
- xmlXPathDebugObjMaxPoint =
- xmlXPathDebugObjCounterPoint;
- break;
- case XPATH_RANGE:
- if (! isCached)
- xmlXPathDebugObjTotalRange++;
- xmlXPathDebugObjCounterRange++;
- if (xmlXPathDebugObjCounterRange >
- xmlXPathDebugObjMaxRange)
- xmlXPathDebugObjMaxRange =
- xmlXPathDebugObjCounterRange;
- break;
- case XPATH_LOCATIONSET:
- if (! isCached)
- xmlXPathDebugObjTotalLocset++;
- xmlXPathDebugObjCounterLocset++;
- if (xmlXPathDebugObjCounterLocset >
- xmlXPathDebugObjMaxLocset)
- xmlXPathDebugObjMaxLocset =
- xmlXPathDebugObjCounterLocset;
- break;
- case XPATH_USERS:
- if (! isCached)
- xmlXPathDebugObjTotalUsers++;
- xmlXPathDebugObjCounterUsers++;
- if (xmlXPathDebugObjCounterUsers >
- xmlXPathDebugObjMaxUsers)
- xmlXPathDebugObjMaxUsers =
- xmlXPathDebugObjCounterUsers;
- break;
- case XPATH_XSLT_TREE:
- if (! isCached)
- xmlXPathDebugObjTotalXSLTTree++;
- xmlXPathDebugObjCounterXSLTTree++;
- if (xmlXPathDebugObjCounterXSLTTree >
- xmlXPathDebugObjMaxXSLTTree)
- xmlXPathDebugObjMaxXSLTTree =
- xmlXPathDebugObjCounterXSLTTree;
- break;
- default:
- break;
- }
- if (! isCached)
- xmlXPathDebugObjTotalAll++;
- xmlXPathDebugObjCounterAll++;
- if (xmlXPathDebugObjCounterAll >
- xmlXPathDebugObjMaxAll)
- xmlXPathDebugObjMaxAll =
- xmlXPathDebugObjCounterAll;
- }
- static void
- xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
- xmlXPathObjectType objType)
- {
- int isCached = 0;
- if (ctxt != NULL) {
- if (ctxt->cache != NULL) {
- xmlXPathContextCachePtr cache =
- (xmlXPathContextCachePtr) ctxt->cache;
- isCached = 1;
- cache->dbgCachedAll++;
- switch (objType) {
- case XPATH_UNDEFINED:
- cache->dbgCachedUndefined++;
- break;
- case XPATH_NODESET:
- cache->dbgCachedNodeset++;
- break;
- case XPATH_BOOLEAN:
- cache->dbgCachedBool++;
- break;
- case XPATH_NUMBER:
- cache->dbgCachedNumber++;
- break;
- case XPATH_STRING:
- cache->dbgCachedString++;
- break;
- case XPATH_POINT:
- cache->dbgCachedPoint++;
- break;
- case XPATH_RANGE:
- cache->dbgCachedRange++;
- break;
- case XPATH_LOCATIONSET:
- cache->dbgCachedLocset++;
- break;
- case XPATH_USERS:
- cache->dbgCachedUsers++;
- break;
- case XPATH_XSLT_TREE:
- cache->dbgCachedXSLTTree++;
- break;
- default:
- break;
- }
- }
- }
- switch (objType) {
- case XPATH_UNDEFINED:
- xmlXPathDebugObjCounterUndefined--;
- break;
- case XPATH_NODESET:
- xmlXPathDebugObjCounterNodeset--;
- break;
- case XPATH_BOOLEAN:
- xmlXPathDebugObjCounterBool--;
- break;
- case XPATH_NUMBER:
- xmlXPathDebugObjCounterNumber--;
- break;
- case XPATH_STRING:
- xmlXPathDebugObjCounterString--;
- break;
- case XPATH_POINT:
- xmlXPathDebugObjCounterPoint--;
- break;
- case XPATH_RANGE:
- xmlXPathDebugObjCounterRange--;
- break;
- case XPATH_LOCATIONSET:
- xmlXPathDebugObjCounterLocset--;
- break;
- case XPATH_USERS:
- xmlXPathDebugObjCounterUsers--;
- break;
- case XPATH_XSLT_TREE:
- xmlXPathDebugObjCounterXSLTTree--;
- break;
- default:
- break;
- }
- xmlXPathDebugObjCounterAll--;
- }
- static void
- xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
- {
- int reqAll, reqNodeset, reqString, reqBool, reqNumber,
- reqXSLTTree, reqUndefined;
- int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
- caNumber = 0, caXSLTTree = 0, caUndefined = 0;
- int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
- reNumber = 0, reXSLTTree = 0, reUndefined = 0;
- int leftObjs = xmlXPathDebugObjCounterAll;
- reqAll = xmlXPathDebugObjTotalAll;
- reqNodeset = xmlXPathDebugObjTotalNodeset;
- reqString = xmlXPathDebugObjTotalString;
- reqBool = xmlXPathDebugObjTotalBool;
- reqNumber = xmlXPathDebugObjTotalNumber;
- reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
- reqUndefined = xmlXPathDebugObjTotalUndefined;
- printf("# XPath object usage:\n");
- if (ctxt != NULL) {
- if (ctxt->cache != NULL) {
- xmlXPathContextCachePtr cache =
- (xmlXPathContextCachePtr) ctxt->cache;
- reAll = cache->dbgReusedAll;
- reqAll += reAll;
- reNodeset = cache->dbgReusedNodeset;
- reqNodeset += reNodeset;
- reString = cache->dbgReusedString;
- reqString += reString;
- reBool = cache->dbgReusedBool;
- reqBool += reBool;
- reNumber = cache->dbgReusedNumber;
- reqNumber += reNumber;
- reXSLTTree = cache->dbgReusedXSLTTree;
- reqXSLTTree += reXSLTTree;
- reUndefined = cache->dbgReusedUndefined;
- reqUndefined += reUndefined;
- caAll = cache->dbgCachedAll;
- caBool = cache->dbgCachedBool;
- caNodeset = cache->dbgCachedNodeset;
- caString = cache->dbgCachedString;
- caNumber = cache->dbgCachedNumber;
- caXSLTTree = cache->dbgCachedXSLTTree;
- caUndefined = cache->dbgCachedUndefined;
- if (cache->nodesetObjs)
- leftObjs -= cache->nodesetObjs->number;
- if (cache->stringObjs)
- leftObjs -= cache->stringObjs->number;
- if (cache->booleanObjs)
- leftObjs -= cache->booleanObjs->number;
- if (cache->numberObjs)
- leftObjs -= cache->numberObjs->number;
- if (cache->miscObjs)
- leftObjs -= cache->miscObjs->number;
- }
- }
- printf("# all\n");
- printf("# total : %d\n", reqAll);
- printf("# left : %d\n", leftObjs);
- printf("# created: %d\n", xmlXPathDebugObjTotalAll);
- printf("# reused : %d\n", reAll);
- printf("# max : %d\n", xmlXPathDebugObjMaxAll);
- printf("# node-sets\n");
- printf("# total : %d\n", reqNodeset);
- printf("# created: %d\n", xmlXPathDebugObjTotalNodeset);
- printf("# reused : %d\n", reNodeset);
- printf("# max : %d\n", xmlXPathDebugObjMaxNodeset);
- printf("# strings\n");
- printf("# total : %d\n", reqString);
- printf("# created: %d\n", xmlXPathDebugObjTotalString);
- printf("# reused : %d\n", reString);
- printf("# max : %d\n", xmlXPathDebugObjMaxString);
- printf("# booleans\n");
- printf("# total : %d\n", reqBool);
- printf("# created: %d\n", xmlXPathDebugObjTotalBool);
- printf("# reused : %d\n", reBool);
- printf("# max : %d\n", xmlXPathDebugObjMaxBool);
- printf("# numbers\n");
- printf("# total : %d\n", reqNumber);
- printf("# created: %d\n", xmlXPathDebugObjTotalNumber);
- printf("# reused : %d\n", reNumber);
- printf("# max : %d\n", xmlXPathDebugObjMaxNumber);
- printf("# XSLT result tree fragments\n");
- printf("# total : %d\n", reqXSLTTree);
- printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree);
- printf("# reused : %d\n", reXSLTTree);
- printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree);
- printf("# undefined\n");
- printf("# total : %d\n", reqUndefined);
- printf("# created: %d\n", xmlXPathDebugObjTotalUndefined);
- printf("# reused : %d\n", reUndefined);
- printf("# max : %d\n", xmlXPathDebugObjMaxUndefined);
- }
- #endif /* XP_DEBUG_OBJ_USAGE */
- #endif /* LIBXML_DEBUG_ENABLED */
- /************************************************************************
- * *
- * XPath object caching *
- * *
- ************************************************************************/
- /**
- * xmlXPathNewCache:
- *
- * Create a new object cache
- *
- * Returns the xmlXPathCache just allocated.
- */
- static xmlXPathContextCachePtr
- xmlXPathNewCache(void)
- {
- xmlXPathContextCachePtr ret;
- ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating object cache\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache));
- ret->maxNodeset = 100;
- ret->maxString = 100;
- ret->maxBoolean = 100;
- ret->maxNumber = 100;
- ret->maxMisc = 100;
- return(ret);
- }
- static void
- xmlXPathCacheFreeObjectList(xmlPointerListPtr list)
- {
- int i;
- xmlXPathObjectPtr obj;
- if (list == NULL)
- return;
- for (i = 0; i < list->number; i++) {
- obj = list->items[i];
- /*
- * Note that it is already assured that we don't need to
- * look out for namespace nodes in the node-set.
- */
- if (obj->nodesetval != NULL) {
- if (obj->nodesetval->nodeTab != NULL)
- xmlFree(obj->nodesetval->nodeTab);
- xmlFree(obj->nodesetval);
- }
- xmlFree(obj);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjCounterAll--;
- #endif
- }
- xmlPointerListFree(list);
- }
- static void
- xmlXPathFreeCache(xmlXPathContextCachePtr cache)
- {
- if (cache == NULL)
- return;
- if (cache->nodesetObjs)
- xmlXPathCacheFreeObjectList(cache->nodesetObjs);
- if (cache->stringObjs)
- xmlXPathCacheFreeObjectList(cache->stringObjs);
- if (cache->booleanObjs)
- xmlXPathCacheFreeObjectList(cache->booleanObjs);
- if (cache->numberObjs)
- xmlXPathCacheFreeObjectList(cache->numberObjs);
- if (cache->miscObjs)
- xmlXPathCacheFreeObjectList(cache->miscObjs);
- xmlFree(cache);
- }
- /**
- * xmlXPathContextSetCache:
- *
- * @ctxt: the XPath context
- * @active: enables/disables (creates/frees) the cache
- * @value: a value with semantics dependent on @options
- * @options: options (currently only the value 0 is used)
- *
- * Creates/frees an object cache on the XPath context.
- * If activates XPath objects (xmlXPathObject) will be cached internally
- * to be reused.
- * @options:
- * 0: This will set the XPath object caching:
- * @value:
- * This will set the maximum number of XPath objects
- * to be cached per slot
- * There are 5 slots for: node-set, string, number, boolean, and
- * misc objects. Use <0 for the default number (100).
- * Other values for @options have currently no effect.
- *
- * Returns 0 if the setting succeeded, and -1 on API or internal errors.
- */
- int
- xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
- int active,
- int value,
- int options)
- {
- if (ctxt == NULL)
- return(-1);
- if (active) {
- xmlXPathContextCachePtr cache;
- if (ctxt->cache == NULL) {
- ctxt->cache = xmlXPathNewCache();
- if (ctxt->cache == NULL)
- return(-1);
- }
- cache = (xmlXPathContextCachePtr) ctxt->cache;
- if (options == 0) {
- if (value < 0)
- value = 100;
- cache->maxNodeset = value;
- cache->maxString = value;
- cache->maxNumber = value;
- cache->maxBoolean = value;
- cache->maxMisc = value;
- }
- } else if (ctxt->cache != NULL) {
- xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
- ctxt->cache = NULL;
- }
- return(0);
- }
- /**
- * xmlXPathCacheWrapNodeSet:
- * @ctxt: the XPath context
- * @val: the NodePtr value
- *
- * This is the cached version of xmlXPathWrapNodeSet().
- * Wrap the Nodeset @val in a new xmlXPathObjectPtr
- *
- * Returns the created or reused object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val)
- {
- if ((ctxt != NULL) && (ctxt->cache != NULL)) {
- xmlXPathContextCachePtr cache =
- (xmlXPathContextCachePtr) ctxt->cache;
- if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_NODESET;
- ret->nodesetval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
- #endif
- return(ret);
- }
- }
- return(xmlXPathWrapNodeSet(val));
- }
- /**
- * xmlXPathCacheWrapString:
- * @ctxt: the XPath context
- * @val: the xmlChar * value
- *
- * This is the cached version of xmlXPathWrapString().
- * Wraps the @val string into an XPath object.
- *
- * Returns the created or reused object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val)
- {
- if ((ctxt != NULL) && (ctxt->cache != NULL)) {
- xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
- if ((cache->stringObjs != NULL) &&
- (cache->stringObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->stringObjs->items[--cache->stringObjs->number];
- ret->type = XPATH_STRING;
- ret->stringval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
- #endif
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- /*
- * Fallback to misc-cache.
- */
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_STRING;
- ret->stringval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
- #endif
- return(ret);
- }
- }
- return(xmlXPathWrapString(val));
- }
- /**
- * xmlXPathCacheNewNodeSet:
- * @ctxt: the XPath context
- * @val: the NodePtr value
- *
- * This is the cached version of xmlXPathNewNodeSet().
- * Acquire an xmlXPathObjectPtr of type NodeSet and initialize
- * it with the single Node @val
- *
- * Returns the created or reused object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val)
- {
- if ((ctxt != NULL) && (ctxt->cache)) {
- xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
- if ((cache->nodesetObjs != NULL) &&
- (cache->nodesetObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- /*
- * Use the nodeset-cache.
- */
- ret = (xmlXPathObjectPtr)
- cache->nodesetObjs->items[--cache->nodesetObjs->number];
- ret->type = XPATH_NODESET;
- ret->boolval = 0;
- if (val) {
- if ((ret->nodesetval->nodeMax == 0) ||
- (val->type == XML_NAMESPACE_DECL))
- {
- /* TODO: Check memory error. */
- xmlXPathNodeSetAddUnique(ret->nodesetval, val);
- } else {
- ret->nodesetval->nodeTab[0] = val;
- ret->nodesetval->nodeNr = 1;
- }
- }
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
- #endif
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- /*
- * Fallback to misc-cache.
- */
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_NODESET;
- ret->boolval = 0;
- ret->nodesetval = xmlXPathNodeSetCreate(val);
- if (ret->nodesetval == NULL) {
- ctxt->lastError.domain = XML_FROM_XPATH;
- ctxt->lastError.code = XML_ERR_NO_MEMORY;
- return(NULL);
- }
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
- #endif
- return(ret);
- }
- }
- return(xmlXPathNewNodeSet(val));
- }
- /**
- * xmlXPathCacheNewCString:
- * @ctxt: the XPath context
- * @val: the char * value
- *
- * This is the cached version of xmlXPathNewCString().
- * Acquire an xmlXPathObjectPtr of type string and of value @val
- *
- * Returns the created or reused object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val)
- {
- if ((ctxt != NULL) && (ctxt->cache)) {
- xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
- if ((cache->stringObjs != NULL) &&
- (cache->stringObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->stringObjs->items[--cache->stringObjs->number];
- ret->type = XPATH_STRING;
- ret->stringval = xmlStrdup(BAD_CAST val);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
- #endif
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_STRING;
- ret->stringval = xmlStrdup(BAD_CAST val);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
- #endif
- return(ret);
- }
- }
- return(xmlXPathNewCString(val));
- }
- /**
- * xmlXPathCacheNewString:
- * @ctxt: the XPath context
- * @val: the xmlChar * value
- *
- * This is the cached version of xmlXPathNewString().
- * Acquire an xmlXPathObjectPtr of type string and of value @val
- *
- * Returns the created or reused object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val)
- {
- if ((ctxt != NULL) && (ctxt->cache)) {
- xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
- if ((cache->stringObjs != NULL) &&
- (cache->stringObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->stringObjs->items[--cache->stringObjs->number];
- ret->type = XPATH_STRING;
- if (val != NULL)
- ret->stringval = xmlStrdup(val);
- else
- ret->stringval = xmlStrdup((const xmlChar *)"");
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
- #endif
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_STRING;
- if (val != NULL)
- ret->stringval = xmlStrdup(val);
- else
- ret->stringval = xmlStrdup((const xmlChar *)"");
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
- #endif
- return(ret);
- }
- }
- return(xmlXPathNewString(val));
- }
- /**
- * xmlXPathCacheNewBoolean:
- * @ctxt: the XPath context
- * @val: the boolean value
- *
- * This is the cached version of xmlXPathNewBoolean().
- * Acquires an xmlXPathObjectPtr of type boolean and of value @val
- *
- * Returns the created or reused object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val)
- {
- if ((ctxt != NULL) && (ctxt->cache)) {
- xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
- if ((cache->booleanObjs != NULL) &&
- (cache->booleanObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->booleanObjs->items[--cache->booleanObjs->number];
- ret->type = XPATH_BOOLEAN;
- ret->boolval = (val != 0);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
- #endif
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_BOOLEAN;
- ret->boolval = (val != 0);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
- #endif
- return(ret);
- }
- }
- return(xmlXPathNewBoolean(val));
- }
- /**
- * xmlXPathCacheNewFloat:
- * @ctxt: the XPath context
- * @val: the double value
- *
- * This is the cached version of xmlXPathNewFloat().
- * Acquires an xmlXPathObjectPtr of type double and of value @val
- *
- * Returns the created or reused object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val)
- {
- if ((ctxt != NULL) && (ctxt->cache)) {
- xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
- if ((cache->numberObjs != NULL) &&
- (cache->numberObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->numberObjs->items[--cache->numberObjs->number];
- ret->type = XPATH_NUMBER;
- ret->floatval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
- #endif
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_NUMBER;
- ret->floatval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
- #endif
- return(ret);
- }
- }
- return(xmlXPathNewFloat(val));
- }
- /**
- * xmlXPathCacheConvertString:
- * @ctxt: the XPath context
- * @val: an XPath object
- *
- * This is the cached version of xmlXPathConvertString().
- * Converts an existing object to its string() equivalent
- *
- * Returns a created or reused object, the old one is freed (cached)
- * (or the operation is done directly on @val)
- */
- static xmlXPathObjectPtr
- xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
- xmlChar *res = NULL;
- if (val == NULL)
- return(xmlXPathCacheNewCString(ctxt, ""));
- switch (val->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
- #endif
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- res = xmlXPathCastNodeSetToString(val->nodesetval);
- break;
- case XPATH_STRING:
- return(val);
- case XPATH_BOOLEAN:
- res = xmlXPathCastBooleanToString(val->boolval);
- break;
- case XPATH_NUMBER:
- res = xmlXPathCastNumberToString(val->floatval);
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO;
- break;
- }
- xmlXPathReleaseObject(ctxt, val);
- if (res == NULL)
- return(xmlXPathCacheNewCString(ctxt, ""));
- return(xmlXPathCacheWrapString(ctxt, res));
- }
- /**
- * xmlXPathCacheObjectCopy:
- * @ctxt: the XPath context
- * @val: the original object
- *
- * This is the cached version of xmlXPathObjectCopy().
- * Acquire a copy of a given object
- *
- * Returns a created or reused created object.
- */
- static xmlXPathObjectPtr
- xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val)
- {
- if (val == NULL)
- return(NULL);
- if (XP_HAS_CACHE(ctxt)) {
- switch (val->type) {
- case XPATH_NODESET:
- return(xmlXPathCacheWrapNodeSet(ctxt,
- xmlXPathNodeSetMerge(NULL, val->nodesetval)));
- case XPATH_STRING:
- return(xmlXPathCacheNewString(ctxt, val->stringval));
- case XPATH_BOOLEAN:
- return(xmlXPathCacheNewBoolean(ctxt, val->boolval));
- case XPATH_NUMBER:
- return(xmlXPathCacheNewFloat(ctxt, val->floatval));
- default:
- break;
- }
- }
- return(xmlXPathObjectCopy(val));
- }
- /**
- * xmlXPathCacheConvertBoolean:
- * @ctxt: the XPath context
- * @val: an XPath object
- *
- * This is the cached version of xmlXPathConvertBoolean().
- * Converts an existing object to its boolean() equivalent
- *
- * Returns a created or reused object, the old one is freed (or the operation
- * is done directly on @val)
- */
- static xmlXPathObjectPtr
- xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
- xmlXPathObjectPtr ret;
- if (val == NULL)
- return(xmlXPathCacheNewBoolean(ctxt, 0));
- if (val->type == XPATH_BOOLEAN)
- return(val);
- ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val));
- xmlXPathReleaseObject(ctxt, val);
- return(ret);
- }
- /**
- * xmlXPathCacheConvertNumber:
- * @ctxt: the XPath context
- * @val: an XPath object
- *
- * This is the cached version of xmlXPathConvertNumber().
- * Converts an existing object to its number() equivalent
- *
- * Returns a created or reused object, the old one is freed (or the operation
- * is done directly on @val)
- */
- static xmlXPathObjectPtr
- xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
- xmlXPathObjectPtr ret;
- if (val == NULL)
- return(xmlXPathCacheNewFloat(ctxt, 0.0));
- if (val->type == XPATH_NUMBER)
- return(val);
- ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val));
- xmlXPathReleaseObject(ctxt, val);
- return(ret);
- }
- /************************************************************************
- * *
- * Parser stacks related functions and macros *
- * *
- ************************************************************************/
- /**
- * xmlXPathSetFrame:
- * @ctxt: an XPath parser context
- *
- * Set the callee evaluation frame
- *
- * Returns the previous frame value to be restored once done
- */
- static int
- xmlXPathSetFrame(xmlXPathParserContextPtr ctxt) {
- int ret;
- if (ctxt == NULL)
- return(0);
- ret = ctxt->valueFrame;
- ctxt->valueFrame = ctxt->valueNr;
- return(ret);
- }
- /**
- * xmlXPathPopFrame:
- * @ctxt: an XPath parser context
- * @frame: the previous frame value
- *
- * Remove the callee evaluation frame
- */
- static void
- xmlXPathPopFrame(xmlXPathParserContextPtr ctxt, int frame) {
- if (ctxt == NULL)
- return;
- if (ctxt->valueNr < ctxt->valueFrame) {
- xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR);
- }
- ctxt->valueFrame = frame;
- }
- /**
- * valuePop:
- * @ctxt: an XPath evaluation context
- *
- * Pops the top XPath object from the value stack
- *
- * Returns the XPath object just removed
- */
- xmlXPathObjectPtr
- valuePop(xmlXPathParserContextPtr ctxt)
- {
- xmlXPathObjectPtr ret;
- if ((ctxt == NULL) || (ctxt->valueNr <= 0))
- return (NULL);
- if (ctxt->valueNr <= ctxt->valueFrame) {
- xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR);
- return (NULL);
- }
- ctxt->valueNr--;
- if (ctxt->valueNr > 0)
- ctxt->value = ctxt->valueTab[ctxt->valueNr - 1];
- else
- ctxt->value = NULL;
- ret = ctxt->valueTab[ctxt->valueNr];
- ctxt->valueTab[ctxt->valueNr] = NULL;
- return (ret);
- }
- /**
- * valuePush:
- * @ctxt: an XPath evaluation context
- * @value: the XPath object
- *
- * Pushes a new XPath object on top of the value stack. If value is NULL,
- * a memory error is recorded in the parser context.
- *
- * Returns the number of items on the value stack, or -1 in case of error.
- */
- int
- valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
- {
- if (ctxt == NULL) return(-1);
- if (value == NULL) {
- /*
- * A NULL value typically indicates that a memory allocation failed,
- * so we set ctxt->error here to propagate the error.
- */
- ctxt->error = XPATH_MEMORY_ERROR;
- return(-1);
- }
- if (ctxt->valueNr >= ctxt->valueMax) {
- xmlXPathObjectPtr *tmp;
- if (ctxt->valueMax >= XPATH_MAX_STACK_DEPTH) {
- xmlXPathPErrMemory(ctxt, "XPath stack depth limit reached\n");
- return (-1);
- }
- tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
- 2 * ctxt->valueMax *
- sizeof(ctxt->valueTab[0]));
- if (tmp == NULL) {
- xmlXPathPErrMemory(ctxt, "pushing value\n");
- return (-1);
- }
- ctxt->valueMax *= 2;
- ctxt->valueTab = tmp;
- }
- ctxt->valueTab[ctxt->valueNr] = value;
- ctxt->value = value;
- return (ctxt->valueNr++);
- }
- /**
- * xmlXPathPopBoolean:
- * @ctxt: an XPath parser context
- *
- * Pops a boolean from the stack, handling conversion if needed.
- * Check error with #xmlXPathCheckError.
- *
- * Returns the boolean
- */
- int
- xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr obj;
- int ret;
- obj = valuePop(ctxt);
- if (obj == NULL) {
- xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
- return(0);
- }
- if (obj->type != XPATH_BOOLEAN)
- ret = xmlXPathCastToBoolean(obj);
- else
- ret = obj->boolval;
- xmlXPathReleaseObject(ctxt->context, obj);
- return(ret);
- }
- /**
- * xmlXPathPopNumber:
- * @ctxt: an XPath parser context
- *
- * Pops a number from the stack, handling conversion if needed.
- * Check error with #xmlXPathCheckError.
- *
- * Returns the number
- */
- double
- xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr obj;
- double ret;
- obj = valuePop(ctxt);
- if (obj == NULL) {
- xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
- return(0);
- }
- if (obj->type != XPATH_NUMBER)
- ret = xmlXPathCastToNumber(obj);
- else
- ret = obj->floatval;
- xmlXPathReleaseObject(ctxt->context, obj);
- return(ret);
- }
- /**
- * xmlXPathPopString:
- * @ctxt: an XPath parser context
- *
- * Pops a string from the stack, handling conversion if needed.
- * Check error with #xmlXPathCheckError.
- *
- * Returns the string
- */
- xmlChar *
- xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr obj;
- xmlChar * ret;
- obj = valuePop(ctxt);
- if (obj == NULL) {
- xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
- return(NULL);
- }
- ret = xmlXPathCastToString(obj); /* this does required strdup */
- /* TODO: needs refactoring somewhere else */
- if (obj->stringval == ret)
- obj->stringval = NULL;
- xmlXPathReleaseObject(ctxt->context, obj);
- return(ret);
- }
- /**
- * xmlXPathPopNodeSet:
- * @ctxt: an XPath parser context
- *
- * Pops a node-set from the stack, handling conversion if needed.
- * Check error with #xmlXPathCheckError.
- *
- * Returns the node-set
- */
- xmlNodeSetPtr
- xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr obj;
- xmlNodeSetPtr ret;
- if (ctxt == NULL) return(NULL);
- if (ctxt->value == NULL) {
- xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
- return(NULL);
- }
- if (!xmlXPathStackIsNodeSet(ctxt)) {
- xmlXPathSetTypeError(ctxt);
- return(NULL);
- }
- obj = valuePop(ctxt);
- ret = obj->nodesetval;
- #if 0
- /* to fix memory leak of not clearing obj->user */
- if (obj->boolval && obj->user != NULL)
- xmlFreeNodeList((xmlNodePtr) obj->user);
- #endif
- obj->nodesetval = NULL;
- xmlXPathReleaseObject(ctxt->context, obj);
- return(ret);
- }
- /**
- * xmlXPathPopExternal:
- * @ctxt: an XPath parser context
- *
- * Pops an external object from the stack, handling conversion if needed.
- * Check error with #xmlXPathCheckError.
- *
- * Returns the object
- */
- void *
- xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr obj;
- void * ret;
- if ((ctxt == NULL) || (ctxt->value == NULL)) {
- xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
- return(NULL);
- }
- if (ctxt->value->type != XPATH_USERS) {
- xmlXPathSetTypeError(ctxt);
- return(NULL);
- }
- obj = valuePop(ctxt);
- ret = obj->user;
- obj->user = NULL;
- xmlXPathReleaseObject(ctxt->context, obj);
- return(ret);
- }
- /*
- * Macros for accessing the content. Those should be used only by the parser,
- * and not exported.
- *
- * Dirty macros, i.e. one need to make assumption on the context to use them
- *
- * CUR_PTR return the current pointer to the xmlChar to be parsed.
- * CUR returns the current xmlChar value, i.e. a 8 bit value
- * in ISO-Latin or UTF-8.
- * This should be used internally by the parser
- * only to compare to ASCII values otherwise it would break when
- * running with UTF-8 encoding.
- * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
- * to compare on ASCII based substring.
- * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
- * strings within the parser.
- * CURRENT Returns the current char value, with the full decoding of
- * UTF-8 if we are using this mode. It returns an int.
- * NEXT Skip to the next character, this does the proper decoding
- * in UTF-8 mode. It also pop-up unfinished entities on the fly.
- * It returns the pointer to the current xmlChar.
- */
- #define CUR (*ctxt->cur)
- #define SKIP(val) ctxt->cur += (val)
- #define NXT(val) ctxt->cur[(val)]
- #define CUR_PTR ctxt->cur
- #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
- #define COPY_BUF(l,b,i,v) \
- if (l == 1) b[i++] = (xmlChar) v; \
- else i += xmlCopyChar(l,&b[i],v)
- #define NEXTL(l) ctxt->cur += l
- #define SKIP_BLANKS \
- while (IS_BLANK_CH(*(ctxt->cur))) NEXT
- #define CURRENT (*ctxt->cur)
- #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
- #ifndef DBL_DIG
- #define DBL_DIG 16
- #endif
- #ifndef DBL_EPSILON
- #define DBL_EPSILON 1E-9
- #endif
- #define UPPER_DOUBLE 1E9
- #define LOWER_DOUBLE 1E-5
- #define LOWER_DOUBLE_EXP 5
- #define INTEGER_DIGITS DBL_DIG
- #define FRACTION_DIGITS (DBL_DIG + 1 + (LOWER_DOUBLE_EXP))
- #define EXPONENT_DIGITS (3 + 2)
- /**
- * xmlXPathFormatNumber:
- * @number: number to format
- * @buffer: output buffer
- * @buffersize: size of output buffer
- *
- * Convert the number into a string representation.
- */
- static void
- xmlXPathFormatNumber(double number, char buffer[], int buffersize)
- {
- switch (xmlXPathIsInf(number)) {
- case 1:
- if (buffersize > (int)sizeof("Infinity"))
- snprintf(buffer, buffersize, "Infinity");
- break;
- case -1:
- if (buffersize > (int)sizeof("-Infinity"))
- snprintf(buffer, buffersize, "-Infinity");
- break;
- default:
- if (xmlXPathIsNaN(number)) {
- if (buffersize > (int)sizeof("NaN"))
- snprintf(buffer, buffersize, "NaN");
- } else if (number == 0) {
- /* Omit sign for negative zero. */
- snprintf(buffer, buffersize, "0");
- } else if ((number > INT_MIN) && (number < INT_MAX) &&
- (number == (int) number)) {
- char work[30];
- char *ptr, *cur;
- int value = (int) number;
- ptr = &buffer[0];
- if (value == 0) {
- *ptr++ = '0';
- } else {
- snprintf(work, 29, "%d", value);
- cur = &work[0];
- while ((*cur) && (ptr - buffer < buffersize)) {
- *ptr++ = *cur++;
- }
- }
- if (ptr - buffer < buffersize) {
- *ptr = 0;
- } else if (buffersize > 0) {
- ptr--;
- *ptr = 0;
- }
- } else {
- /*
- For the dimension of work,
- DBL_DIG is number of significant digits
- EXPONENT is only needed for "scientific notation"
- 3 is sign, decimal point, and terminating zero
- LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
- Note that this dimension is slightly (a few characters)
- larger than actually necessary.
- */
- char work[DBL_DIG + EXPONENT_DIGITS + 3 + LOWER_DOUBLE_EXP];
- int integer_place, fraction_place;
- char *ptr;
- char *after_fraction;
- double absolute_value;
- int size;
- absolute_value = fabs(number);
- /*
- * First choose format - scientific or regular floating point.
- * In either case, result is in work, and after_fraction points
- * just past the fractional part.
- */
- if ( ((absolute_value > UPPER_DOUBLE) ||
- (absolute_value < LOWER_DOUBLE)) &&
- (absolute_value != 0.0) ) {
- /* Use scientific notation */
- integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
- fraction_place = DBL_DIG - 1;
- size = snprintf(work, sizeof(work),"%*.*e",
- integer_place, fraction_place, number);
- while ((size > 0) && (work[size] != 'e')) size--;
- }
- else {
- /* Use regular notation */
- if (absolute_value > 0.0) {
- integer_place = (int)log10(absolute_value);
- if (integer_place > 0)
- fraction_place = DBL_DIG - integer_place - 1;
- else
- fraction_place = DBL_DIG - integer_place;
- } else {
- fraction_place = 1;
- }
- size = snprintf(work, sizeof(work), "%0.*f",
- fraction_place, number);
- }
- /* Remove leading spaces sometimes inserted by snprintf */
- while (work[0] == ' ') {
- for (ptr = &work[0];(ptr[0] = ptr[1]);ptr++);
- size--;
- }
- /* Remove fractional trailing zeroes */
- after_fraction = work + size;
- ptr = after_fraction;
- while (*(--ptr) == '0')
- ;
- if (*ptr != '.')
- ptr++;
- while ((*ptr++ = *after_fraction++) != 0);
- /* Finally copy result back to caller */
- size = strlen(work) + 1;
- if (size > buffersize) {
- work[buffersize - 1] = 0;
- size = buffersize;
- }
- memmove(buffer, work, size);
- }
- break;
- }
- }
- /************************************************************************
- * *
- * Routines to handle NodeSets *
- * *
- ************************************************************************/
- /**
- * xmlXPathOrderDocElems:
- * @doc: an input document
- *
- * Call this routine to speed up XPath computation on static documents.
- * This stamps all the element nodes with the document order
- * Like for line information, the order is kept in the element->content
- * field, the value stored is actually - the node number (starting at -1)
- * to be able to differentiate from line numbers.
- *
- * Returns the number of elements found in the document or -1 in case
- * of error.
- */
- long
- xmlXPathOrderDocElems(xmlDocPtr doc) {
- ptrdiff_t count = 0;
- xmlNodePtr cur;
- if (doc == NULL)
- return(-1);
- cur = doc->children;
- while (cur != NULL) {
- if (cur->type == XML_ELEMENT_NODE) {
- cur->content = (void *) (-(++count));
- if (cur->children != NULL) {
- cur = cur->children;
- continue;
- }
- }
- if (cur->next != NULL) {
- cur = cur->next;
- continue;
- }
- do {
- cur = cur->parent;
- if (cur == NULL)
- break;
- if (cur == (xmlNodePtr) doc) {
- cur = NULL;
- break;
- }
- if (cur->next != NULL) {
- cur = cur->next;
- break;
- }
- } while (cur != NULL);
- }
- return((long) count);
- }
- /**
- * xmlXPathCmpNodes:
- * @node1: the first node
- * @node2: the second node
- *
- * Compare two nodes w.r.t document order
- *
- * Returns -2 in case of error 1 if first point < second point, 0 if
- * it's the same node, -1 otherwise
- */
- int
- xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
- int depth1, depth2;
- int attr1 = 0, attr2 = 0;
- xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
- xmlNodePtr cur, root;
- if ((node1 == NULL) || (node2 == NULL))
- return(-2);
- /*
- * a couple of optimizations which will avoid computations in most cases
- */
- if (node1 == node2) /* trivial case */
- return(0);
- if (node1->type == XML_ATTRIBUTE_NODE) {
- attr1 = 1;
- attrNode1 = node1;
- node1 = node1->parent;
- }
- if (node2->type == XML_ATTRIBUTE_NODE) {
- attr2 = 1;
- attrNode2 = node2;
- node2 = node2->parent;
- }
- if (node1 == node2) {
- if (attr1 == attr2) {
- /* not required, but we keep attributes in order */
- if (attr1 != 0) {
- cur = attrNode2->prev;
- while (cur != NULL) {
- if (cur == attrNode1)
- return (1);
- cur = cur->prev;
- }
- return (-1);
- }
- return(0);
- }
- if (attr2 == 1)
- return(1);
- return(-1);
- }
- if ((node1->type == XML_NAMESPACE_DECL) ||
- (node2->type == XML_NAMESPACE_DECL))
- return(1);
- if (node1 == node2->prev)
- return(1);
- if (node1 == node2->next)
- return(-1);
- /*
- * Speedup using document order if available.
- */
- if ((node1->type == XML_ELEMENT_NODE) &&
- (node2->type == XML_ELEMENT_NODE) &&
- (0 > (ptrdiff_t) node1->content) &&
- (0 > (ptrdiff_t) node2->content) &&
- (node1->doc == node2->doc)) {
- ptrdiff_t l1, l2;
- l1 = -((ptrdiff_t) node1->content);
- l2 = -((ptrdiff_t) node2->content);
- if (l1 < l2)
- return(1);
- if (l1 > l2)
- return(-1);
- }
- /*
- * compute depth to root
- */
- for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
- if (cur->parent == node1)
- return(1);
- depth2++;
- }
- root = cur;
- for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
- if (cur->parent == node2)
- return(-1);
- depth1++;
- }
- /*
- * Distinct document (or distinct entities :-( ) case.
- */
- if (root != cur) {
- return(-2);
- }
- /*
- * get the nearest common ancestor.
- */
- while (depth1 > depth2) {
- depth1--;
- node1 = node1->parent;
- }
- while (depth2 > depth1) {
- depth2--;
- node2 = node2->parent;
- }
- while (node1->parent != node2->parent) {
- node1 = node1->parent;
- node2 = node2->parent;
- /* should not happen but just in case ... */
- if ((node1 == NULL) || (node2 == NULL))
- return(-2);
- }
- /*
- * Find who's first.
- */
- if (node1 == node2->prev)
- return(1);
- if (node1 == node2->next)
- return(-1);
- /*
- * Speedup using document order if available.
- */
- if ((node1->type == XML_ELEMENT_NODE) &&
- (node2->type == XML_ELEMENT_NODE) &&
- (0 > (ptrdiff_t) node1->content) &&
- (0 > (ptrdiff_t) node2->content) &&
- (node1->doc == node2->doc)) {
- ptrdiff_t l1, l2;
- l1 = -((ptrdiff_t) node1->content);
- l2 = -((ptrdiff_t) node2->content);
- if (l1 < l2)
- return(1);
- if (l1 > l2)
- return(-1);
- }
- for (cur = node1->next;cur != NULL;cur = cur->next)
- if (cur == node2)
- return(1);
- return(-1); /* assume there is no sibling list corruption */
- }
- /**
- * xmlXPathNodeSetSort:
- * @set: the node set
- *
- * Sort the node set in document order
- */
- void
- xmlXPathNodeSetSort(xmlNodeSetPtr set) {
- #ifndef WITH_TIM_SORT
- int i, j, incr, len;
- xmlNodePtr tmp;
- #endif
- if (set == NULL)
- return;
- #ifndef WITH_TIM_SORT
- /*
- * Use the old Shell's sort implementation to sort the node-set
- * Timsort ought to be quite faster
- */
- len = set->nodeNr;
- for (incr = len / 2; incr > 0; incr /= 2) {
- for (i = incr; i < len; i++) {
- j = i - incr;
- while (j >= 0) {
- #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
- if (xmlXPathCmpNodesExt(set->nodeTab[j],
- set->nodeTab[j + incr]) == -1)
- #else
- if (xmlXPathCmpNodes(set->nodeTab[j],
- set->nodeTab[j + incr]) == -1)
- #endif
- {
- tmp = set->nodeTab[j];
- set->nodeTab[j] = set->nodeTab[j + incr];
- set->nodeTab[j + incr] = tmp;
- j -= incr;
- } else
- break;
- }
- }
- }
- #else /* WITH_TIM_SORT */
- libxml_domnode_tim_sort(set->nodeTab, set->nodeNr);
- #endif /* WITH_TIM_SORT */
- }
- #define XML_NODESET_DEFAULT 10
- /**
- * xmlXPathNodeSetDupNs:
- * @node: the parent node of the namespace XPath node
- * @ns: the libxml namespace declaration node.
- *
- * Namespace node in libxml don't match the XPath semantic. In a node set
- * the namespace nodes are duplicated and the next pointer is set to the
- * parent node in the XPath semantic.
- *
- * Returns the newly created object.
- */
- static xmlNodePtr
- xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
- xmlNsPtr cur;
- if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
- return(NULL);
- if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
- return((xmlNodePtr) ns);
- /*
- * Allocate a new Namespace and fill the fields.
- */
- cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
- if (cur == NULL) {
- xmlXPathErrMemory(NULL, "duplicating namespace\n");
- return(NULL);
- }
- memset(cur, 0, sizeof(xmlNs));
- cur->type = XML_NAMESPACE_DECL;
- if (ns->href != NULL)
- cur->href = xmlStrdup(ns->href);
- if (ns->prefix != NULL)
- cur->prefix = xmlStrdup(ns->prefix);
- cur->next = (xmlNsPtr) node;
- return((xmlNodePtr) cur);
- }
- /**
- * xmlXPathNodeSetFreeNs:
- * @ns: the XPath namespace node found in a nodeset.
- *
- * Namespace nodes in libxml don't match the XPath semantic. In a node set
- * the namespace nodes are duplicated and the next pointer is set to the
- * parent node in the XPath semantic. Check if such a node needs to be freed
- */
- void
- xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
- if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
- return;
- if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) {
- if (ns->href != NULL)
- xmlFree((xmlChar *)ns->href);
- if (ns->prefix != NULL)
- xmlFree((xmlChar *)ns->prefix);
- xmlFree(ns);
- }
- }
- /**
- * xmlXPathNodeSetCreate:
- * @val: an initial xmlNodePtr, or NULL
- *
- * Create a new xmlNodeSetPtr of type double and of value @val
- *
- * Returns the newly created object.
- */
- xmlNodeSetPtr
- xmlXPathNodeSetCreate(xmlNodePtr val) {
- xmlNodeSetPtr ret;
- ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating nodeset\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
- if (val != NULL) {
- ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
- sizeof(xmlNodePtr));
- if (ret->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "creating nodeset\n");
- xmlFree(ret);
- return(NULL);
- }
- memset(ret->nodeTab, 0 ,
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
- ret->nodeMax = XML_NODESET_DEFAULT;
- if (val->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) val;
- /* TODO: Check memory error. */
- ret->nodeTab[ret->nodeNr++] =
- xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- } else
- ret->nodeTab[ret->nodeNr++] = val;
- }
- return(ret);
- }
- /**
- * xmlXPathNodeSetContains:
- * @cur: the node-set
- * @val: the node
- *
- * checks whether @cur contains @val
- *
- * Returns true (1) if @cur contains @val, false (0) otherwise
- */
- int
- xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
- int i;
- if ((cur == NULL) || (val == NULL)) return(0);
- if (val->type == XML_NAMESPACE_DECL) {
- for (i = 0; i < cur->nodeNr; i++) {
- if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns1, ns2;
- ns1 = (xmlNsPtr) val;
- ns2 = (xmlNsPtr) cur->nodeTab[i];
- if (ns1 == ns2)
- return(1);
- if ((ns1->next != NULL) && (ns2->next == ns1->next) &&
- (xmlStrEqual(ns1->prefix, ns2->prefix)))
- return(1);
- }
- }
- } else {
- for (i = 0; i < cur->nodeNr; i++) {
- if (cur->nodeTab[i] == val)
- return(1);
- }
- }
- return(0);
- }
- /**
- * xmlXPathNodeSetAddNs:
- * @cur: the initial node set
- * @node: the hosting node
- * @ns: a the namespace node
- *
- * add a new namespace node to an existing NodeSet
- *
- * Returns 0 in case of success and -1 in case of error
- */
- int
- xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) {
- int i;
- if ((cur == NULL) || (ns == NULL) || (node == NULL) ||
- (ns->type != XML_NAMESPACE_DECL) ||
- (node->type != XML_ELEMENT_NODE))
- return(-1);
- /* @@ with_ns to check whether namespace nodes should be looked at @@ */
- /*
- * prevent duplicates
- */
- for (i = 0;i < cur->nodeNr;i++) {
- if ((cur->nodeTab[i] != NULL) &&
- (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
- (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
- (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
- return(0);
- }
- /*
- * grow the nodeTab if needed
- */
- if (cur->nodeMax == 0) {
- cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
- sizeof(xmlNodePtr));
- if (cur->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "growing nodeset\n");
- return(-1);
- }
- memset(cur->nodeTab, 0 ,
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
- cur->nodeMax = XML_NODESET_DEFAULT;
- } else if (cur->nodeNr == cur->nodeMax) {
- xmlNodePtr *temp;
- if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
- xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
- return(-1);
- }
- temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
- sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "growing nodeset\n");
- return(-1);
- }
- cur->nodeMax *= 2;
- cur->nodeTab = temp;
- }
- /* TODO: Check memory error. */
- cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);
- return(0);
- }
- /**
- * xmlXPathNodeSetAdd:
- * @cur: the initial node set
- * @val: a new xmlNodePtr
- *
- * add a new xmlNodePtr to an existing NodeSet
- *
- * Returns 0 in case of success, and -1 in case of error
- */
- int
- xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
- int i;
- if ((cur == NULL) || (val == NULL)) return(-1);
- /* @@ with_ns to check whether namespace nodes should be looked at @@ */
- /*
- * prevent duplicates
- */
- for (i = 0;i < cur->nodeNr;i++)
- if (cur->nodeTab[i] == val) return(0);
- /*
- * grow the nodeTab if needed
- */
- if (cur->nodeMax == 0) {
- cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
- sizeof(xmlNodePtr));
- if (cur->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "growing nodeset\n");
- return(-1);
- }
- memset(cur->nodeTab, 0 ,
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
- cur->nodeMax = XML_NODESET_DEFAULT;
- } else if (cur->nodeNr == cur->nodeMax) {
- xmlNodePtr *temp;
- if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
- xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
- return(-1);
- }
- temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
- sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "growing nodeset\n");
- return(-1);
- }
- cur->nodeMax *= 2;
- cur->nodeTab = temp;
- }
- if (val->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) val;
- /* TODO: Check memory error. */
- cur->nodeTab[cur->nodeNr++] =
- xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- } else
- cur->nodeTab[cur->nodeNr++] = val;
- return(0);
- }
- /**
- * xmlXPathNodeSetAddUnique:
- * @cur: the initial node set
- * @val: a new xmlNodePtr
- *
- * add a new xmlNodePtr to an existing NodeSet, optimized version
- * when we are sure the node is not already in the set.
- *
- * Returns 0 in case of success and -1 in case of failure
- */
- int
- xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
- if ((cur == NULL) || (val == NULL)) return(-1);
- /* @@ with_ns to check whether namespace nodes should be looked at @@ */
- /*
- * grow the nodeTab if needed
- */
- if (cur->nodeMax == 0) {
- cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
- sizeof(xmlNodePtr));
- if (cur->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "growing nodeset\n");
- return(-1);
- }
- memset(cur->nodeTab, 0 ,
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
- cur->nodeMax = XML_NODESET_DEFAULT;
- } else if (cur->nodeNr == cur->nodeMax) {
- xmlNodePtr *temp;
- if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
- xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
- return(-1);
- }
- temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
- sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "growing nodeset\n");
- return(-1);
- }
- cur->nodeTab = temp;
- cur->nodeMax *= 2;
- }
- if (val->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) val;
- /* TODO: Check memory error. */
- cur->nodeTab[cur->nodeNr++] =
- xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- } else
- cur->nodeTab[cur->nodeNr++] = val;
- return(0);
- }
- /**
- * xmlXPathNodeSetMerge:
- * @val1: the first NodeSet or NULL
- * @val2: the second NodeSet
- *
- * Merges two nodesets, all nodes from @val2 are added to @val1
- * if @val1 is NULL, a new set is created and copied from @val2
- *
- * Returns @val1 once extended or NULL in case of error.
- */
- xmlNodeSetPtr
- xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
- int i, j, initNr, skip;
- xmlNodePtr n1, n2;
- if (val2 == NULL) return(val1);
- if (val1 == NULL) {
- val1 = xmlXPathNodeSetCreate(NULL);
- if (val1 == NULL)
- return (NULL);
- #if 0
- /*
- * TODO: The optimization won't work in every case, since
- * those nasty namespace nodes need to be added with
- * xmlXPathNodeSetDupNs() to the set; thus a pure
- * memcpy is not possible.
- * If there was a flag on the nodesetval, indicating that
- * some temporary nodes are in, that would be helpful.
- */
- /*
- * Optimization: Create an equally sized node-set
- * and memcpy the content.
- */
- val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
- if (val1 == NULL)
- return(NULL);
- if (val2->nodeNr != 0) {
- if (val2->nodeNr == 1)
- *(val1->nodeTab) = *(val2->nodeTab);
- else {
- memcpy(val1->nodeTab, val2->nodeTab,
- val2->nodeNr * sizeof(xmlNodePtr));
- }
- val1->nodeNr = val2->nodeNr;
- }
- return(val1);
- #endif
- }
- /* @@ with_ns to check whether namespace nodes should be looked at @@ */
- initNr = val1->nodeNr;
- for (i = 0;i < val2->nodeNr;i++) {
- n2 = val2->nodeTab[i];
- /*
- * check against duplicates
- */
- skip = 0;
- for (j = 0; j < initNr; j++) {
- n1 = val1->nodeTab[j];
- if (n1 == n2) {
- skip = 1;
- break;
- } else if ((n1->type == XML_NAMESPACE_DECL) &&
- (n2->type == XML_NAMESPACE_DECL)) {
- if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
- (xmlStrEqual(((xmlNsPtr) n1)->prefix,
- ((xmlNsPtr) n2)->prefix)))
- {
- skip = 1;
- break;
- }
- }
- }
- if (skip)
- continue;
- /*
- * grow the nodeTab if needed
- */
- if (val1->nodeMax == 0) {
- val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
- sizeof(xmlNodePtr));
- if (val1->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
- }
- memset(val1->nodeTab, 0 ,
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
- val1->nodeMax = XML_NODESET_DEFAULT;
- } else if (val1->nodeNr == val1->nodeMax) {
- xmlNodePtr *temp;
- if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
- xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
- return(NULL);
- }
- temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
- sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
- }
- val1->nodeTab = temp;
- val1->nodeMax *= 2;
- }
- if (n2->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) n2;
- /* TODO: Check memory error. */
- val1->nodeTab[val1->nodeNr++] =
- xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- } else
- val1->nodeTab[val1->nodeNr++] = n2;
- }
- return(val1);
- }
- /**
- * xmlXPathNodeSetMergeAndClear:
- * @set1: the first NodeSet or NULL
- * @set2: the second NodeSet
- *
- * Merges two nodesets, all nodes from @set2 are added to @set1.
- * Checks for duplicate nodes. Clears set2.
- *
- * Returns @set1 once extended or NULL in case of error.
- */
- static xmlNodeSetPtr
- xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
- {
- {
- int i, j, initNbSet1;
- xmlNodePtr n1, n2;
- initNbSet1 = set1->nodeNr;
- for (i = 0;i < set2->nodeNr;i++) {
- n2 = set2->nodeTab[i];
- /*
- * Skip duplicates.
- */
- for (j = 0; j < initNbSet1; j++) {
- n1 = set1->nodeTab[j];
- if (n1 == n2) {
- goto skip_node;
- } else if ((n1->type == XML_NAMESPACE_DECL) &&
- (n2->type == XML_NAMESPACE_DECL))
- {
- if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
- (xmlStrEqual(((xmlNsPtr) n1)->prefix,
- ((xmlNsPtr) n2)->prefix)))
- {
- /*
- * Free the namespace node.
- */
- set2->nodeTab[i] = NULL;
- xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
- goto skip_node;
- }
- }
- }
- /*
- * grow the nodeTab if needed
- */
- if (set1->nodeMax == 0) {
- set1->nodeTab = (xmlNodePtr *) xmlMalloc(
- XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
- if (set1->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
- }
- memset(set1->nodeTab, 0,
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
- set1->nodeMax = XML_NODESET_DEFAULT;
- } else if (set1->nodeNr >= set1->nodeMax) {
- xmlNodePtr *temp;
- if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
- xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
- return(NULL);
- }
- temp = (xmlNodePtr *) xmlRealloc(
- set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
- }
- set1->nodeTab = temp;
- set1->nodeMax *= 2;
- }
- set1->nodeTab[set1->nodeNr++] = n2;
- skip_node:
- {}
- }
- }
- set2->nodeNr = 0;
- return(set1);
- }
- /**
- * xmlXPathNodeSetMergeAndClearNoDupls:
- * @set1: the first NodeSet or NULL
- * @set2: the second NodeSet
- *
- * Merges two nodesets, all nodes from @set2 are added to @set1.
- * Doesn't check for duplicate nodes. Clears set2.
- *
- * Returns @set1 once extended or NULL in case of error.
- */
- static xmlNodeSetPtr
- xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
- {
- {
- int i;
- xmlNodePtr n2;
- for (i = 0;i < set2->nodeNr;i++) {
- n2 = set2->nodeTab[i];
- if (set1->nodeMax == 0) {
- set1->nodeTab = (xmlNodePtr *) xmlMalloc(
- XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
- if (set1->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
- }
- memset(set1->nodeTab, 0,
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
- set1->nodeMax = XML_NODESET_DEFAULT;
- } else if (set1->nodeNr >= set1->nodeMax) {
- xmlNodePtr *temp;
- if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
- xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
- return(NULL);
- }
- temp = (xmlNodePtr *) xmlRealloc(
- set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
- }
- set1->nodeTab = temp;
- set1->nodeMax *= 2;
- }
- set1->nodeTab[set1->nodeNr++] = n2;
- }
- }
- set2->nodeNr = 0;
- return(set1);
- }
- /**
- * xmlXPathNodeSetDel:
- * @cur: the initial node set
- * @val: an xmlNodePtr
- *
- * Removes an xmlNodePtr from an existing NodeSet
- */
- void
- xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
- int i;
- if (cur == NULL) return;
- if (val == NULL) return;
- /*
- * find node in nodeTab
- */
- for (i = 0;i < cur->nodeNr;i++)
- if (cur->nodeTab[i] == val) break;
- if (i >= cur->nodeNr) { /* not found */
- #ifdef DEBUG
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
- val->name);
- #endif
- return;
- }
- if ((cur->nodeTab[i] != NULL) &&
- (cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
- xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
- cur->nodeNr--;
- for (;i < cur->nodeNr;i++)
- cur->nodeTab[i] = cur->nodeTab[i + 1];
- cur->nodeTab[cur->nodeNr] = NULL;
- }
- /**
- * xmlXPathNodeSetRemove:
- * @cur: the initial node set
- * @val: the index to remove
- *
- * Removes an entry from an existing NodeSet list.
- */
- void
- xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
- if (cur == NULL) return;
- if (val >= cur->nodeNr) return;
- if ((cur->nodeTab[val] != NULL) &&
- (cur->nodeTab[val]->type == XML_NAMESPACE_DECL))
- xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
- cur->nodeNr--;
- for (;val < cur->nodeNr;val++)
- cur->nodeTab[val] = cur->nodeTab[val + 1];
- cur->nodeTab[cur->nodeNr] = NULL;
- }
- /**
- * xmlXPathFreeNodeSet:
- * @obj: the xmlNodeSetPtr to free
- *
- * Free the NodeSet compound (not the actual nodes !).
- */
- void
- xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
- if (obj == NULL) return;
- if (obj->nodeTab != NULL) {
- int i;
- /* @@ with_ns to check whether namespace nodes should be looked at @@ */
- for (i = 0;i < obj->nodeNr;i++)
- if ((obj->nodeTab[i] != NULL) &&
- (obj->nodeTab[i]->type == XML_NAMESPACE_DECL))
- xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
- xmlFree(obj->nodeTab);
- }
- xmlFree(obj);
- }
- /**
- * xmlXPathNodeSetClearFromPos:
- * @set: the node set to be cleared
- * @pos: the start position to clear from
- *
- * Clears the list from temporary XPath objects (e.g. namespace nodes
- * are feed) starting with the entry at @pos, but does *not* free the list
- * itself. Sets the length of the list to @pos.
- */
- static void
- xmlXPathNodeSetClearFromPos(xmlNodeSetPtr set, int pos, int hasNsNodes)
- {
- if ((set == NULL) || (pos >= set->nodeNr))
- return;
- else if ((hasNsNodes)) {
- int i;
- xmlNodePtr node;
- for (i = pos; i < set->nodeNr; i++) {
- node = set->nodeTab[i];
- if ((node != NULL) &&
- (node->type == XML_NAMESPACE_DECL))
- xmlXPathNodeSetFreeNs((xmlNsPtr) node);
- }
- }
- set->nodeNr = pos;
- }
- /**
- * xmlXPathNodeSetClear:
- * @set: the node set to clear
- *
- * Clears the list from all temporary XPath objects (e.g. namespace nodes
- * are feed), but does *not* free the list itself. Sets the length of the
- * list to 0.
- */
- static void
- xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes)
- {
- xmlXPathNodeSetClearFromPos(set, 0, hasNsNodes);
- }
- /**
- * xmlXPathNodeSetKeepLast:
- * @set: the node set to be cleared
- *
- * Move the last node to the first position and clear temporary XPath objects
- * (e.g. namespace nodes) from all other nodes. Sets the length of the list
- * to 1.
- */
- static void
- xmlXPathNodeSetKeepLast(xmlNodeSetPtr set)
- {
- int i;
- xmlNodePtr node;
- if ((set == NULL) || (set->nodeNr <= 1))
- return;
- for (i = 0; i < set->nodeNr - 1; i++) {
- node = set->nodeTab[i];
- if ((node != NULL) &&
- (node->type == XML_NAMESPACE_DECL))
- xmlXPathNodeSetFreeNs((xmlNsPtr) node);
- }
- set->nodeTab[0] = set->nodeTab[set->nodeNr-1];
- set->nodeNr = 1;
- }
- /**
- * xmlXPathFreeValueTree:
- * @obj: the xmlNodeSetPtr to free
- *
- * Free the NodeSet compound and the actual tree, this is different
- * from xmlXPathFreeNodeSet()
- */
- static void
- xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
- int i;
- if (obj == NULL) return;
- if (obj->nodeTab != NULL) {
- for (i = 0;i < obj->nodeNr;i++) {
- if (obj->nodeTab[i] != NULL) {
- if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
- xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
- } else {
- xmlFreeNodeList(obj->nodeTab[i]);
- }
- }
- }
- xmlFree(obj->nodeTab);
- }
- xmlFree(obj);
- }
- #if defined(DEBUG) || defined(DEBUG_STEP)
- /**
- * xmlGenericErrorContextNodeSet:
- * @output: a FILE * for the output
- * @obj: the xmlNodeSetPtr to display
- *
- * Quick display of a NodeSet
- */
- void
- xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
- int i;
- if (output == NULL) output = xmlGenericErrorContext;
- if (obj == NULL) {
- fprintf(output, "NodeSet == NULL !\n");
- return;
- }
- if (obj->nodeNr == 0) {
- fprintf(output, "NodeSet is empty\n");
- return;
- }
- if (obj->nodeTab == NULL) {
- fprintf(output, " nodeTab == NULL !\n");
- return;
- }
- for (i = 0; i < obj->nodeNr; i++) {
- if (obj->nodeTab[i] == NULL) {
- fprintf(output, " NULL !\n");
- return;
- }
- if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
- (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
- fprintf(output, " /");
- else if (obj->nodeTab[i]->name == NULL)
- fprintf(output, " noname!");
- else fprintf(output, " %s", obj->nodeTab[i]->name);
- }
- fprintf(output, "\n");
- }
- #endif
- /**
- * xmlXPathNewNodeSet:
- * @val: the NodePtr value
- *
- * Create a new xmlXPathObjectPtr of type NodeSet and initialize
- * it with the single Node @val
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathNewNodeSet(xmlNodePtr val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating nodeset\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_NODESET;
- ret->boolval = 0;
- /* TODO: Check memory error. */
- ret->nodesetval = xmlXPathNodeSetCreate(val);
- /* @@ with_ns to check whether namespace nodes should be looked at @@ */
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
- #endif
- return(ret);
- }
- /**
- * xmlXPathNewValueTree:
- * @val: the NodePtr value
- *
- * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
- * it with the tree root @val
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathNewValueTree(xmlNodePtr val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating result value tree\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_XSLT_TREE;
- ret->boolval = 1;
- ret->user = (void *) val;
- ret->nodesetval = xmlXPathNodeSetCreate(val);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
- #endif
- return(ret);
- }
- /**
- * xmlXPathNewNodeSetList:
- * @val: an existing NodeSet
- *
- * Create a new xmlXPathObjectPtr of type NodeSet and initialize
- * it with the Nodeset @val
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathNewNodeSetList(xmlNodeSetPtr val)
- {
- xmlXPathObjectPtr ret;
- int i;
- if (val == NULL)
- ret = NULL;
- else if (val->nodeTab == NULL)
- ret = xmlXPathNewNodeSet(NULL);
- else {
- ret = xmlXPathNewNodeSet(val->nodeTab[0]);
- if (ret) {
- for (i = 1; i < val->nodeNr; ++i) {
- /* TODO: Propagate memory error. */
- if (xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i])
- < 0) break;
- }
- }
- }
- return (ret);
- }
- /**
- * xmlXPathWrapNodeSet:
- * @val: the NodePtr value
- *
- * Wrap the Nodeset @val in a new xmlXPathObjectPtr
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating node set object\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_NODESET;
- ret->nodesetval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
- #endif
- return(ret);
- }
- /**
- * xmlXPathFreeNodeSetList:
- * @obj: an existing NodeSetList object
- *
- * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
- * the list contrary to xmlXPathFreeObject().
- */
- void
- xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
- if (obj == NULL) return;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageReleased(NULL, obj->type);
- #endif
- xmlFree(obj);
- }
- /**
- * xmlXPathDifference:
- * @nodes1: a node-set
- * @nodes2: a node-set
- *
- * Implements the EXSLT - Sets difference() function:
- * node-set set:difference (node-set, node-set)
- *
- * Returns the difference between the two node sets, or nodes1 if
- * nodes2 is empty
- */
- xmlNodeSetPtr
- xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
- xmlNodeSetPtr ret;
- int i, l1;
- xmlNodePtr cur;
- if (xmlXPathNodeSetIsEmpty(nodes2))
- return(nodes1);
- /* TODO: Check memory error. */
- ret = xmlXPathNodeSetCreate(NULL);
- if (xmlXPathNodeSetIsEmpty(nodes1))
- return(ret);
- l1 = xmlXPathNodeSetGetLength(nodes1);
- for (i = 0; i < l1; i++) {
- cur = xmlXPathNodeSetItem(nodes1, i);
- if (!xmlXPathNodeSetContains(nodes2, cur)) {
- /* TODO: Propagate memory error. */
- if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
- break;
- }
- }
- return(ret);
- }
- /**
- * xmlXPathIntersection:
- * @nodes1: a node-set
- * @nodes2: a node-set
- *
- * Implements the EXSLT - Sets intersection() function:
- * node-set set:intersection (node-set, node-set)
- *
- * Returns a node set comprising the nodes that are within both the
- * node sets passed as arguments
- */
- xmlNodeSetPtr
- xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
- xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
- int i, l1;
- xmlNodePtr cur;
- if (ret == NULL)
- return(ret);
- if (xmlXPathNodeSetIsEmpty(nodes1))
- return(ret);
- if (xmlXPathNodeSetIsEmpty(nodes2))
- return(ret);
- l1 = xmlXPathNodeSetGetLength(nodes1);
- for (i = 0; i < l1; i++) {
- cur = xmlXPathNodeSetItem(nodes1, i);
- if (xmlXPathNodeSetContains(nodes2, cur)) {
- /* TODO: Propagate memory error. */
- if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
- break;
- }
- }
- return(ret);
- }
- /**
- * xmlXPathDistinctSorted:
- * @nodes: a node-set, sorted by document order
- *
- * Implements the EXSLT - Sets distinct() function:
- * node-set set:distinct (node-set)
- *
- * Returns a subset of the nodes contained in @nodes, or @nodes if
- * it is empty
- */
- xmlNodeSetPtr
- xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
- xmlNodeSetPtr ret;
- xmlHashTablePtr hash;
- int i, l;
- xmlChar * strval;
- xmlNodePtr cur;
- if (xmlXPathNodeSetIsEmpty(nodes))
- return(nodes);
- ret = xmlXPathNodeSetCreate(NULL);
- if (ret == NULL)
- return(ret);
- l = xmlXPathNodeSetGetLength(nodes);
- hash = xmlHashCreate (l);
- for (i = 0; i < l; i++) {
- cur = xmlXPathNodeSetItem(nodes, i);
- strval = xmlXPathCastNodeToString(cur);
- if (xmlHashLookup(hash, strval) == NULL) {
- xmlHashAddEntry(hash, strval, strval);
- /* TODO: Propagate memory error. */
- if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
- break;
- } else {
- xmlFree(strval);
- }
- }
- xmlHashFree(hash, xmlHashDefaultDeallocator);
- return(ret);
- }
- /**
- * xmlXPathDistinct:
- * @nodes: a node-set
- *
- * Implements the EXSLT - Sets distinct() function:
- * node-set set:distinct (node-set)
- * @nodes is sorted by document order, then #exslSetsDistinctSorted
- * is called with the sorted node-set
- *
- * Returns a subset of the nodes contained in @nodes, or @nodes if
- * it is empty
- */
- xmlNodeSetPtr
- xmlXPathDistinct (xmlNodeSetPtr nodes) {
- if (xmlXPathNodeSetIsEmpty(nodes))
- return(nodes);
- xmlXPathNodeSetSort(nodes);
- return(xmlXPathDistinctSorted(nodes));
- }
- /**
- * xmlXPathHasSameNodes:
- * @nodes1: a node-set
- * @nodes2: a node-set
- *
- * Implements the EXSLT - Sets has-same-nodes function:
- * boolean set:has-same-node(node-set, node-set)
- *
- * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
- * otherwise
- */
- int
- xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
- int i, l;
- xmlNodePtr cur;
- if (xmlXPathNodeSetIsEmpty(nodes1) ||
- xmlXPathNodeSetIsEmpty(nodes2))
- return(0);
- l = xmlXPathNodeSetGetLength(nodes1);
- for (i = 0; i < l; i++) {
- cur = xmlXPathNodeSetItem(nodes1, i);
- if (xmlXPathNodeSetContains(nodes2, cur))
- return(1);
- }
- return(0);
- }
- /**
- * xmlXPathNodeLeadingSorted:
- * @nodes: a node-set, sorted by document order
- * @node: a node
- *
- * Implements the EXSLT - Sets leading() function:
- * node-set set:leading (node-set, node-set)
- *
- * Returns the nodes in @nodes that precede @node in document order,
- * @nodes if @node is NULL or an empty node-set if @nodes
- * doesn't contain @node
- */
- xmlNodeSetPtr
- xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
- int i, l;
- xmlNodePtr cur;
- xmlNodeSetPtr ret;
- if (node == NULL)
- return(nodes);
- ret = xmlXPathNodeSetCreate(NULL);
- if (ret == NULL)
- return(ret);
- if (xmlXPathNodeSetIsEmpty(nodes) ||
- (!xmlXPathNodeSetContains(nodes, node)))
- return(ret);
- l = xmlXPathNodeSetGetLength(nodes);
- for (i = 0; i < l; i++) {
- cur = xmlXPathNodeSetItem(nodes, i);
- if (cur == node)
- break;
- /* TODO: Propagate memory error. */
- if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
- break;
- }
- return(ret);
- }
- /**
- * xmlXPathNodeLeading:
- * @nodes: a node-set
- * @node: a node
- *
- * Implements the EXSLT - Sets leading() function:
- * node-set set:leading (node-set, node-set)
- * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
- * is called.
- *
- * Returns the nodes in @nodes that precede @node in document order,
- * @nodes if @node is NULL or an empty node-set if @nodes
- * doesn't contain @node
- */
- xmlNodeSetPtr
- xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
- xmlXPathNodeSetSort(nodes);
- return(xmlXPathNodeLeadingSorted(nodes, node));
- }
- /**
- * xmlXPathLeadingSorted:
- * @nodes1: a node-set, sorted by document order
- * @nodes2: a node-set, sorted by document order
- *
- * Implements the EXSLT - Sets leading() function:
- * node-set set:leading (node-set, node-set)
- *
- * Returns the nodes in @nodes1 that precede the first node in @nodes2
- * in document order, @nodes1 if @nodes2 is NULL or empty or
- * an empty node-set if @nodes1 doesn't contain @nodes2
- */
- xmlNodeSetPtr
- xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
- if (xmlXPathNodeSetIsEmpty(nodes2))
- return(nodes1);
- return(xmlXPathNodeLeadingSorted(nodes1,
- xmlXPathNodeSetItem(nodes2, 1)));
- }
- /**
- * xmlXPathLeading:
- * @nodes1: a node-set
- * @nodes2: a node-set
- *
- * Implements the EXSLT - Sets leading() function:
- * node-set set:leading (node-set, node-set)
- * @nodes1 and @nodes2 are sorted by document order, then
- * #exslSetsLeadingSorted is called.
- *
- * Returns the nodes in @nodes1 that precede the first node in @nodes2
- * in document order, @nodes1 if @nodes2 is NULL or empty or
- * an empty node-set if @nodes1 doesn't contain @nodes2
- */
- xmlNodeSetPtr
- xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
- if (xmlXPathNodeSetIsEmpty(nodes2))
- return(nodes1);
- if (xmlXPathNodeSetIsEmpty(nodes1))
- return(xmlXPathNodeSetCreate(NULL));
- xmlXPathNodeSetSort(nodes1);
- xmlXPathNodeSetSort(nodes2);
- return(xmlXPathNodeLeadingSorted(nodes1,
- xmlXPathNodeSetItem(nodes2, 1)));
- }
- /**
- * xmlXPathNodeTrailingSorted:
- * @nodes: a node-set, sorted by document order
- * @node: a node
- *
- * Implements the EXSLT - Sets trailing() function:
- * node-set set:trailing (node-set, node-set)
- *
- * Returns the nodes in @nodes that follow @node in document order,
- * @nodes if @node is NULL or an empty node-set if @nodes
- * doesn't contain @node
- */
- xmlNodeSetPtr
- xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
- int i, l;
- xmlNodePtr cur;
- xmlNodeSetPtr ret;
- if (node == NULL)
- return(nodes);
- ret = xmlXPathNodeSetCreate(NULL);
- if (ret == NULL)
- return(ret);
- if (xmlXPathNodeSetIsEmpty(nodes) ||
- (!xmlXPathNodeSetContains(nodes, node)))
- return(ret);
- l = xmlXPathNodeSetGetLength(nodes);
- for (i = l - 1; i >= 0; i--) {
- cur = xmlXPathNodeSetItem(nodes, i);
- if (cur == node)
- break;
- /* TODO: Propagate memory error. */
- if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
- break;
- }
- xmlXPathNodeSetSort(ret); /* bug 413451 */
- return(ret);
- }
- /**
- * xmlXPathNodeTrailing:
- * @nodes: a node-set
- * @node: a node
- *
- * Implements the EXSLT - Sets trailing() function:
- * node-set set:trailing (node-set, node-set)
- * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
- * is called.
- *
- * Returns the nodes in @nodes that follow @node in document order,
- * @nodes if @node is NULL or an empty node-set if @nodes
- * doesn't contain @node
- */
- xmlNodeSetPtr
- xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
- xmlXPathNodeSetSort(nodes);
- return(xmlXPathNodeTrailingSorted(nodes, node));
- }
- /**
- * xmlXPathTrailingSorted:
- * @nodes1: a node-set, sorted by document order
- * @nodes2: a node-set, sorted by document order
- *
- * Implements the EXSLT - Sets trailing() function:
- * node-set set:trailing (node-set, node-set)
- *
- * Returns the nodes in @nodes1 that follow the first node in @nodes2
- * in document order, @nodes1 if @nodes2 is NULL or empty or
- * an empty node-set if @nodes1 doesn't contain @nodes2
- */
- xmlNodeSetPtr
- xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
- if (xmlXPathNodeSetIsEmpty(nodes2))
- return(nodes1);
- return(xmlXPathNodeTrailingSorted(nodes1,
- xmlXPathNodeSetItem(nodes2, 0)));
- }
- /**
- * xmlXPathTrailing:
- * @nodes1: a node-set
- * @nodes2: a node-set
- *
- * Implements the EXSLT - Sets trailing() function:
- * node-set set:trailing (node-set, node-set)
- * @nodes1 and @nodes2 are sorted by document order, then
- * #xmlXPathTrailingSorted is called.
- *
- * Returns the nodes in @nodes1 that follow the first node in @nodes2
- * in document order, @nodes1 if @nodes2 is NULL or empty or
- * an empty node-set if @nodes1 doesn't contain @nodes2
- */
- xmlNodeSetPtr
- xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
- if (xmlXPathNodeSetIsEmpty(nodes2))
- return(nodes1);
- if (xmlXPathNodeSetIsEmpty(nodes1))
- return(xmlXPathNodeSetCreate(NULL));
- xmlXPathNodeSetSort(nodes1);
- xmlXPathNodeSetSort(nodes2);
- return(xmlXPathNodeTrailingSorted(nodes1,
- xmlXPathNodeSetItem(nodes2, 0)));
- }
- /************************************************************************
- * *
- * Routines to handle extra functions *
- * *
- ************************************************************************/
- /**
- * xmlXPathRegisterFunc:
- * @ctxt: the XPath context
- * @name: the function name
- * @f: the function implementation or NULL
- *
- * Register a new function. If @f is NULL it unregisters the function
- *
- * Returns 0 in case of success, -1 in case of error
- */
- int
- xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
- xmlXPathFunction f) {
- return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
- }
- /**
- * xmlXPathRegisterFuncNS:
- * @ctxt: the XPath context
- * @name: the function name
- * @ns_uri: the function namespace URI
- * @f: the function implementation or NULL
- *
- * Register a new function. If @f is NULL it unregisters the function
- *
- * Returns 0 in case of success, -1 in case of error
- */
- int
- xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
- const xmlChar *ns_uri, xmlXPathFunction f) {
- if (ctxt == NULL)
- return(-1);
- if (name == NULL)
- return(-1);
- if (ctxt->funcHash == NULL)
- ctxt->funcHash = xmlHashCreate(0);
- if (ctxt->funcHash == NULL)
- return(-1);
- if (f == NULL)
- return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL));
- XML_IGNORE_PEDANTIC_WARNINGS
- return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
- XML_POP_WARNINGS
- }
- /**
- * xmlXPathRegisterFuncLookup:
- * @ctxt: the XPath context
- * @f: the lookup function
- * @funcCtxt: the lookup data
- *
- * Registers an external mechanism to do function lookup.
- */
- void
- xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
- xmlXPathFuncLookupFunc f,
- void *funcCtxt) {
- if (ctxt == NULL)
- return;
- ctxt->funcLookupFunc = f;
- ctxt->funcLookupData = funcCtxt;
- }
- /**
- * xmlXPathFunctionLookup:
- * @ctxt: the XPath context
- * @name: the function name
- *
- * Search in the Function array of the context for the given
- * function.
- *
- * Returns the xmlXPathFunction or NULL if not found
- */
- xmlXPathFunction
- xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
- if (ctxt == NULL)
- return (NULL);
- if (ctxt->funcLookupFunc != NULL) {
- xmlXPathFunction ret;
- xmlXPathFuncLookupFunc f;
- f = ctxt->funcLookupFunc;
- ret = f(ctxt->funcLookupData, name, NULL);
- if (ret != NULL)
- return(ret);
- }
- return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
- }
- /**
- * xmlXPathFunctionLookupNS:
- * @ctxt: the XPath context
- * @name: the function name
- * @ns_uri: the function namespace URI
- *
- * Search in the Function array of the context for the given
- * function.
- *
- * Returns the xmlXPathFunction or NULL if not found
- */
- xmlXPathFunction
- xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
- const xmlChar *ns_uri) {
- xmlXPathFunction ret;
- if (ctxt == NULL)
- return(NULL);
- if (name == NULL)
- return(NULL);
- if (ctxt->funcLookupFunc != NULL) {
- xmlXPathFuncLookupFunc f;
- f = ctxt->funcLookupFunc;
- ret = f(ctxt->funcLookupData, name, ns_uri);
- if (ret != NULL)
- return(ret);
- }
- if (ctxt->funcHash == NULL)
- return(NULL);
- XML_IGNORE_PEDANTIC_WARNINGS
- ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri);
- XML_POP_WARNINGS
- return(ret);
- }
- /**
- * xmlXPathRegisteredFuncsCleanup:
- * @ctxt: the XPath context
- *
- * Cleanup the XPath context data associated to registered functions
- */
- void
- xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
- if (ctxt == NULL)
- return;
- xmlHashFree(ctxt->funcHash, NULL);
- ctxt->funcHash = NULL;
- }
- /************************************************************************
- * *
- * Routines to handle Variables *
- * *
- ************************************************************************/
- /**
- * xmlXPathRegisterVariable:
- * @ctxt: the XPath context
- * @name: the variable name
- * @value: the variable value or NULL
- *
- * Register a new variable value. If @value is NULL it unregisters
- * the variable
- *
- * Returns 0 in case of success, -1 in case of error
- */
- int
- xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
- xmlXPathObjectPtr value) {
- return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
- }
- /**
- * xmlXPathRegisterVariableNS:
- * @ctxt: the XPath context
- * @name: the variable name
- * @ns_uri: the variable namespace URI
- * @value: the variable value or NULL
- *
- * Register a new variable value. If @value is NULL it unregisters
- * the variable
- *
- * Returns 0 in case of success, -1 in case of error
- */
- int
- xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
- const xmlChar *ns_uri,
- xmlXPathObjectPtr value) {
- if (ctxt == NULL)
- return(-1);
- if (name == NULL)
- return(-1);
- if (ctxt->varHash == NULL)
- ctxt->varHash = xmlHashCreate(0);
- if (ctxt->varHash == NULL)
- return(-1);
- if (value == NULL)
- return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
- xmlXPathFreeObjectEntry));
- return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
- (void *) value, xmlXPathFreeObjectEntry));
- }
- /**
- * xmlXPathRegisterVariableLookup:
- * @ctxt: the XPath context
- * @f: the lookup function
- * @data: the lookup data
- *
- * register an external mechanism to do variable lookup
- */
- void
- xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
- xmlXPathVariableLookupFunc f, void *data) {
- if (ctxt == NULL)
- return;
- ctxt->varLookupFunc = f;
- ctxt->varLookupData = data;
- }
- /**
- * xmlXPathVariableLookup:
- * @ctxt: the XPath context
- * @name: the variable name
- *
- * Search in the Variable array of the context for the given
- * variable value.
- *
- * Returns a copy of the value or NULL if not found
- */
- xmlXPathObjectPtr
- xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
- if (ctxt == NULL)
- return(NULL);
- if (ctxt->varLookupFunc != NULL) {
- xmlXPathObjectPtr ret;
- ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
- (ctxt->varLookupData, name, NULL);
- return(ret);
- }
- return(xmlXPathVariableLookupNS(ctxt, name, NULL));
- }
- /**
- * xmlXPathVariableLookupNS:
- * @ctxt: the XPath context
- * @name: the variable name
- * @ns_uri: the variable namespace URI
- *
- * Search in the Variable array of the context for the given
- * variable value.
- *
- * Returns the a copy of the value or NULL if not found
- */
- xmlXPathObjectPtr
- xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
- const xmlChar *ns_uri) {
- if (ctxt == NULL)
- return(NULL);
- if (ctxt->varLookupFunc != NULL) {
- xmlXPathObjectPtr ret;
- ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
- (ctxt->varLookupData, name, ns_uri);
- if (ret != NULL) return(ret);
- }
- if (ctxt->varHash == NULL)
- return(NULL);
- if (name == NULL)
- return(NULL);
- return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr)
- xmlHashLookup2(ctxt->varHash, name, ns_uri)));
- }
- /**
- * xmlXPathRegisteredVariablesCleanup:
- * @ctxt: the XPath context
- *
- * Cleanup the XPath context data associated to registered variables
- */
- void
- xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
- if (ctxt == NULL)
- return;
- xmlHashFree(ctxt->varHash, xmlXPathFreeObjectEntry);
- ctxt->varHash = NULL;
- }
- /**
- * xmlXPathRegisterNs:
- * @ctxt: the XPath context
- * @prefix: the namespace prefix cannot be NULL or empty string
- * @ns_uri: the namespace name
- *
- * Register a new namespace. If @ns_uri is NULL it unregisters
- * the namespace
- *
- * Returns 0 in case of success, -1 in case of error
- */
- int
- xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
- const xmlChar *ns_uri) {
- if (ctxt == NULL)
- return(-1);
- if (prefix == NULL)
- return(-1);
- if (prefix[0] == 0)
- return(-1);
- if (ctxt->nsHash == NULL)
- ctxt->nsHash = xmlHashCreate(10);
- if (ctxt->nsHash == NULL)
- return(-1);
- if (ns_uri == NULL)
- return(xmlHashRemoveEntry(ctxt->nsHash, prefix,
- xmlHashDefaultDeallocator));
- return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri),
- xmlHashDefaultDeallocator));
- }
- /**
- * xmlXPathNsLookup:
- * @ctxt: the XPath context
- * @prefix: the namespace prefix value
- *
- * Search in the namespace declaration array of the context for the given
- * namespace name associated to the given prefix
- *
- * Returns the value or NULL if not found
- */
- const xmlChar *
- xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
- if (ctxt == NULL)
- return(NULL);
- if (prefix == NULL)
- return(NULL);
- #ifdef XML_XML_NAMESPACE
- if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
- return(XML_XML_NAMESPACE);
- #endif
- if (ctxt->namespaces != NULL) {
- int i;
- for (i = 0;i < ctxt->nsNr;i++) {
- if ((ctxt->namespaces[i] != NULL) &&
- (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
- return(ctxt->namespaces[i]->href);
- }
- }
- return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
- }
- /**
- * xmlXPathRegisteredNsCleanup:
- * @ctxt: the XPath context
- *
- * Cleanup the XPath context data associated to registered variables
- */
- void
- xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
- if (ctxt == NULL)
- return;
- xmlHashFree(ctxt->nsHash, xmlHashDefaultDeallocator);
- ctxt->nsHash = NULL;
- }
- /************************************************************************
- * *
- * Routines to handle Values *
- * *
- ************************************************************************/
- /* Allocations are terrible, one needs to optimize all this !!! */
- /**
- * xmlXPathNewFloat:
- * @val: the double value
- *
- * Create a new xmlXPathObjectPtr of type double and of value @val
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathNewFloat(double val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating float object\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_NUMBER;
- ret->floatval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
- #endif
- return(ret);
- }
- /**
- * xmlXPathNewBoolean:
- * @val: the boolean value
- *
- * Create a new xmlXPathObjectPtr of type boolean and of value @val
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathNewBoolean(int val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating boolean object\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_BOOLEAN;
- ret->boolval = (val != 0);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
- #endif
- return(ret);
- }
- /**
- * xmlXPathNewString:
- * @val: the xmlChar * value
- *
- * Create a new xmlXPathObjectPtr of type string and of value @val
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathNewString(const xmlChar *val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating string object\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_STRING;
- if (val != NULL)
- ret->stringval = xmlStrdup(val);
- else
- ret->stringval = xmlStrdup((const xmlChar *)"");
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
- #endif
- return(ret);
- }
- /**
- * xmlXPathWrapString:
- * @val: the xmlChar * value
- *
- * Wraps the @val string into an XPath object.
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathWrapString (xmlChar *val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating string object\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_STRING;
- ret->stringval = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
- #endif
- return(ret);
- }
- /**
- * xmlXPathNewCString:
- * @val: the char * value
- *
- * Create a new xmlXPathObjectPtr of type string and of value @val
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathNewCString(const char *val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating string object\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_STRING;
- ret->stringval = xmlStrdup(BAD_CAST val);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
- #endif
- return(ret);
- }
- /**
- * xmlXPathWrapCString:
- * @val: the char * value
- *
- * Wraps a string into an XPath object.
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathWrapCString (char * val) {
- return(xmlXPathWrapString((xmlChar *)(val)));
- }
- /**
- * xmlXPathWrapExternal:
- * @val: the user data
- *
- * Wraps the @val data into an XPath object.
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathWrapExternal (void *val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating user object\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
- ret->type = XPATH_USERS;
- ret->user = val;
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
- #endif
- return(ret);
- }
- /**
- * xmlXPathObjectCopy:
- * @val: the original object
- *
- * allocate a new copy of a given object
- *
- * Returns the newly created object.
- */
- xmlXPathObjectPtr
- xmlXPathObjectCopy(xmlXPathObjectPtr val) {
- xmlXPathObjectPtr ret;
- if (val == NULL)
- return(NULL);
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "copying object\n");
- return(NULL);
- }
- memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageRequested(NULL, val->type);
- #endif
- switch (val->type) {
- case XPATH_BOOLEAN:
- case XPATH_NUMBER:
- case XPATH_POINT:
- case XPATH_RANGE:
- break;
- case XPATH_STRING:
- ret->stringval = xmlStrdup(val->stringval);
- break;
- case XPATH_XSLT_TREE:
- #if 0
- /*
- Removed 11 July 2004 - the current handling of xslt tmpRVT nodes means that
- this previous handling is no longer correct, and can cause some serious
- problems (ref. bug 145547)
- */
- if ((val->nodesetval != NULL) &&
- (val->nodesetval->nodeTab != NULL)) {
- xmlNodePtr cur, tmp;
- xmlDocPtr top;
- ret->boolval = 1;
- top = xmlNewDoc(NULL);
- top->name = (char *)
- xmlStrdup(val->nodesetval->nodeTab[0]->name);
- ret->user = top;
- if (top != NULL) {
- top->doc = top;
- cur = val->nodesetval->nodeTab[0]->children;
- while (cur != NULL) {
- tmp = xmlDocCopyNode(cur, top, 1);
- xmlAddChild((xmlNodePtr) top, tmp);
- cur = cur->next;
- }
- }
- ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
- } else
- ret->nodesetval = xmlXPathNodeSetCreate(NULL);
- /* Deallocate the copied tree value */
- break;
- #endif
- case XPATH_NODESET:
- /* TODO: Check memory error. */
- ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
- /* Do not deallocate the copied tree value */
- ret->boolval = 0;
- break;
- case XPATH_LOCATIONSET:
- #ifdef LIBXML_XPTR_ENABLED
- {
- xmlLocationSetPtr loc = val->user;
- ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
- break;
- }
- #endif
- case XPATH_USERS:
- ret->user = val->user;
- break;
- case XPATH_UNDEFINED:
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathObjectCopy: unsupported type %d\n",
- val->type);
- break;
- }
- return(ret);
- }
- /**
- * xmlXPathFreeObject:
- * @obj: the object to free
- *
- * Free up an xmlXPathObjectPtr object.
- */
- void
- xmlXPathFreeObject(xmlXPathObjectPtr obj) {
- if (obj == NULL) return;
- if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
- if (obj->boolval) {
- #if 0
- if (obj->user != NULL) {
- xmlXPathFreeNodeSet(obj->nodesetval);
- xmlFreeNodeList((xmlNodePtr) obj->user);
- } else
- #endif
- obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */
- if (obj->nodesetval != NULL)
- xmlXPathFreeValueTree(obj->nodesetval);
- } else {
- if (obj->nodesetval != NULL)
- xmlXPathFreeNodeSet(obj->nodesetval);
- }
- #ifdef LIBXML_XPTR_ENABLED
- } else if (obj->type == XPATH_LOCATIONSET) {
- if (obj->user != NULL)
- xmlXPtrFreeLocationSet(obj->user);
- #endif
- } else if (obj->type == XPATH_STRING) {
- if (obj->stringval != NULL)
- xmlFree(obj->stringval);
- }
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageReleased(NULL, obj->type);
- #endif
- xmlFree(obj);
- }
- static void
- xmlXPathFreeObjectEntry(void *obj, const xmlChar *name ATTRIBUTE_UNUSED) {
- xmlXPathFreeObject((xmlXPathObjectPtr) obj);
- }
- /**
- * xmlXPathReleaseObject:
- * @obj: the xmlXPathObjectPtr to free or to cache
- *
- * Depending on the state of the cache this frees the given
- * XPath object or stores it in the cache.
- */
- static void
- xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj)
- {
- #define XP_CACHE_ADD(sl, o) if (sl == NULL) { \
- sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \
- if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj;
- #define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n))
- if (obj == NULL)
- return;
- if ((ctxt == NULL) || (ctxt->cache == NULL)) {
- xmlXPathFreeObject(obj);
- } else {
- xmlXPathContextCachePtr cache =
- (xmlXPathContextCachePtr) ctxt->cache;
- switch (obj->type) {
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- if (obj->nodesetval != NULL) {
- if (obj->boolval) {
- /*
- * It looks like the @boolval is used for
- * evaluation if this an XSLT Result Tree Fragment.
- * TODO: Check if this assumption is correct.
- */
- obj->type = XPATH_XSLT_TREE; /* just for debugging */
- xmlXPathFreeValueTree(obj->nodesetval);
- obj->nodesetval = NULL;
- } else if ((obj->nodesetval->nodeMax <= 40) &&
- (XP_CACHE_WANTS(cache->nodesetObjs,
- cache->maxNodeset)))
- {
- XP_CACHE_ADD(cache->nodesetObjs, obj);
- goto obj_cached;
- } else {
- xmlXPathFreeNodeSet(obj->nodesetval);
- obj->nodesetval = NULL;
- }
- }
- break;
- case XPATH_STRING:
- if (obj->stringval != NULL)
- xmlFree(obj->stringval);
- if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) {
- XP_CACHE_ADD(cache->stringObjs, obj);
- goto obj_cached;
- }
- break;
- case XPATH_BOOLEAN:
- if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) {
- XP_CACHE_ADD(cache->booleanObjs, obj);
- goto obj_cached;
- }
- break;
- case XPATH_NUMBER:
- if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) {
- XP_CACHE_ADD(cache->numberObjs, obj);
- goto obj_cached;
- }
- break;
- #ifdef LIBXML_XPTR_ENABLED
- case XPATH_LOCATIONSET:
- if (obj->user != NULL) {
- xmlXPtrFreeLocationSet(obj->user);
- }
- goto free_obj;
- #endif
- default:
- goto free_obj;
- }
- /*
- * Fallback to adding to the misc-objects slot.
- */
- if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) {
- XP_CACHE_ADD(cache->miscObjs, obj);
- } else
- goto free_obj;
- obj_cached:
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageReleased(ctxt, obj->type);
- #endif
- if (obj->nodesetval != NULL) {
- xmlNodeSetPtr tmpset = obj->nodesetval;
- /*
- * TODO: Due to those nasty ns-nodes, we need to traverse
- * the list and free the ns-nodes.
- * URGENT TODO: Check if it's actually slowing things down.
- * Maybe we shouldn't try to preserve the list.
- */
- if (tmpset->nodeNr > 1) {
- int i;
- xmlNodePtr node;
- for (i = 0; i < tmpset->nodeNr; i++) {
- node = tmpset->nodeTab[i];
- if ((node != NULL) &&
- (node->type == XML_NAMESPACE_DECL))
- {
- xmlXPathNodeSetFreeNs((xmlNsPtr) node);
- }
- }
- } else if (tmpset->nodeNr == 1) {
- if ((tmpset->nodeTab[0] != NULL) &&
- (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL))
- xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]);
- }
- tmpset->nodeNr = 0;
- memset(obj, 0, sizeof(xmlXPathObject));
- obj->nodesetval = tmpset;
- } else
- memset(obj, 0, sizeof(xmlXPathObject));
- return;
- free_obj:
- /*
- * Cache is full; free the object.
- */
- if (obj->nodesetval != NULL)
- xmlXPathFreeNodeSet(obj->nodesetval);
- #ifdef XP_DEBUG_OBJ_USAGE
- xmlXPathDebugObjUsageReleased(NULL, obj->type);
- #endif
- xmlFree(obj);
- }
- return;
- }
- /************************************************************************
- * *
- * Type Casting Routines *
- * *
- ************************************************************************/
- /**
- * xmlXPathCastBooleanToString:
- * @val: a boolean
- *
- * Converts a boolean to its string value.
- *
- * Returns a newly allocated string.
- */
- xmlChar *
- xmlXPathCastBooleanToString (int val) {
- xmlChar *ret;
- if (val)
- ret = xmlStrdup((const xmlChar *) "true");
- else
- ret = xmlStrdup((const xmlChar *) "false");
- return(ret);
- }
- /**
- * xmlXPathCastNumberToString:
- * @val: a number
- *
- * Converts a number to its string value.
- *
- * Returns a newly allocated string.
- */
- xmlChar *
- xmlXPathCastNumberToString (double val) {
- xmlChar *ret;
- switch (xmlXPathIsInf(val)) {
- case 1:
- ret = xmlStrdup((const xmlChar *) "Infinity");
- break;
- case -1:
- ret = xmlStrdup((const xmlChar *) "-Infinity");
- break;
- default:
- if (xmlXPathIsNaN(val)) {
- ret = xmlStrdup((const xmlChar *) "NaN");
- } else if (val == 0) {
- /* Omit sign for negative zero. */
- ret = xmlStrdup((const xmlChar *) "0");
- } else {
- /* could be improved */
- char buf[100];
- xmlXPathFormatNumber(val, buf, 99);
- buf[99] = 0;
- ret = xmlStrdup((const xmlChar *) buf);
- }
- }
- return(ret);
- }
- /**
- * xmlXPathCastNodeToString:
- * @node: a node
- *
- * Converts a node to its string value.
- *
- * Returns a newly allocated string.
- */
- xmlChar *
- xmlXPathCastNodeToString (xmlNodePtr node) {
- xmlChar *ret;
- if ((ret = xmlNodeGetContent(node)) == NULL)
- ret = xmlStrdup((const xmlChar *) "");
- return(ret);
- }
- /**
- * xmlXPathCastNodeSetToString:
- * @ns: a node-set
- *
- * Converts a node-set to its string value.
- *
- * Returns a newly allocated string.
- */
- xmlChar *
- xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
- if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
- return(xmlStrdup((const xmlChar *) ""));
- if (ns->nodeNr > 1)
- xmlXPathNodeSetSort(ns);
- return(xmlXPathCastNodeToString(ns->nodeTab[0]));
- }
- /**
- * xmlXPathCastToString:
- * @val: an XPath object
- *
- * Converts an existing object to its string() equivalent
- *
- * Returns the allocated string value of the object, NULL in case of error.
- * It's up to the caller to free the string memory with xmlFree().
- */
- xmlChar *
- xmlXPathCastToString(xmlXPathObjectPtr val) {
- xmlChar *ret = NULL;
- if (val == NULL)
- return(xmlStrdup((const xmlChar *) ""));
- switch (val->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
- #endif
- ret = xmlStrdup((const xmlChar *) "");
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- ret = xmlXPathCastNodeSetToString(val->nodesetval);
- break;
- case XPATH_STRING:
- return(xmlStrdup(val->stringval));
- case XPATH_BOOLEAN:
- ret = xmlXPathCastBooleanToString(val->boolval);
- break;
- case XPATH_NUMBER: {
- ret = xmlXPathCastNumberToString(val->floatval);
- break;
- }
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO
- ret = xmlStrdup((const xmlChar *) "");
- break;
- }
- return(ret);
- }
- /**
- * xmlXPathConvertString:
- * @val: an XPath object
- *
- * Converts an existing object to its string() equivalent
- *
- * Returns the new object, the old one is freed (or the operation
- * is done directly on @val)
- */
- xmlXPathObjectPtr
- xmlXPathConvertString(xmlXPathObjectPtr val) {
- xmlChar *res = NULL;
- if (val == NULL)
- return(xmlXPathNewCString(""));
- switch (val->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
- #endif
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- res = xmlXPathCastNodeSetToString(val->nodesetval);
- break;
- case XPATH_STRING:
- return(val);
- case XPATH_BOOLEAN:
- res = xmlXPathCastBooleanToString(val->boolval);
- break;
- case XPATH_NUMBER:
- res = xmlXPathCastNumberToString(val->floatval);
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO;
- break;
- }
- xmlXPathFreeObject(val);
- if (res == NULL)
- return(xmlXPathNewCString(""));
- return(xmlXPathWrapString(res));
- }
- /**
- * xmlXPathCastBooleanToNumber:
- * @val: a boolean
- *
- * Converts a boolean to its number value
- *
- * Returns the number value
- */
- double
- xmlXPathCastBooleanToNumber(int val) {
- if (val)
- return(1.0);
- return(0.0);
- }
- /**
- * xmlXPathCastStringToNumber:
- * @val: a string
- *
- * Converts a string to its number value
- *
- * Returns the number value
- */
- double
- xmlXPathCastStringToNumber(const xmlChar * val) {
- return(xmlXPathStringEvalNumber(val));
- }
- /**
- * xmlXPathCastNodeToNumber:
- * @node: a node
- *
- * Converts a node to its number value
- *
- * Returns the number value
- */
- double
- xmlXPathCastNodeToNumber (xmlNodePtr node) {
- xmlChar *strval;
- double ret;
- if (node == NULL)
- return(xmlXPathNAN);
- strval = xmlXPathCastNodeToString(node);
- if (strval == NULL)
- return(xmlXPathNAN);
- ret = xmlXPathCastStringToNumber(strval);
- xmlFree(strval);
- return(ret);
- }
- /**
- * xmlXPathCastNodeSetToNumber:
- * @ns: a node-set
- *
- * Converts a node-set to its number value
- *
- * Returns the number value
- */
- double
- xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
- xmlChar *str;
- double ret;
- if (ns == NULL)
- return(xmlXPathNAN);
- str = xmlXPathCastNodeSetToString(ns);
- ret = xmlXPathCastStringToNumber(str);
- xmlFree(str);
- return(ret);
- }
- /**
- * xmlXPathCastToNumber:
- * @val: an XPath object
- *
- * Converts an XPath object to its number value
- *
- * Returns the number value
- */
- double
- xmlXPathCastToNumber(xmlXPathObjectPtr val) {
- double ret = 0.0;
- if (val == NULL)
- return(xmlXPathNAN);
- switch (val->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
- #endif
- ret = xmlXPathNAN;
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
- break;
- case XPATH_STRING:
- ret = xmlXPathCastStringToNumber(val->stringval);
- break;
- case XPATH_NUMBER:
- ret = val->floatval;
- break;
- case XPATH_BOOLEAN:
- ret = xmlXPathCastBooleanToNumber(val->boolval);
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO;
- ret = xmlXPathNAN;
- break;
- }
- return(ret);
- }
- /**
- * xmlXPathConvertNumber:
- * @val: an XPath object
- *
- * Converts an existing object to its number() equivalent
- *
- * Returns the new object, the old one is freed (or the operation
- * is done directly on @val)
- */
- xmlXPathObjectPtr
- xmlXPathConvertNumber(xmlXPathObjectPtr val) {
- xmlXPathObjectPtr ret;
- if (val == NULL)
- return(xmlXPathNewFloat(0.0));
- if (val->type == XPATH_NUMBER)
- return(val);
- ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
- xmlXPathFreeObject(val);
- return(ret);
- }
- /**
- * xmlXPathCastNumberToBoolean:
- * @val: a number
- *
- * Converts a number to its boolean value
- *
- * Returns the boolean value
- */
- int
- xmlXPathCastNumberToBoolean (double val) {
- if (xmlXPathIsNaN(val) || (val == 0.0))
- return(0);
- return(1);
- }
- /**
- * xmlXPathCastStringToBoolean:
- * @val: a string
- *
- * Converts a string to its boolean value
- *
- * Returns the boolean value
- */
- int
- xmlXPathCastStringToBoolean (const xmlChar *val) {
- if ((val == NULL) || (xmlStrlen(val) == 0))
- return(0);
- return(1);
- }
- /**
- * xmlXPathCastNodeSetToBoolean:
- * @ns: a node-set
- *
- * Converts a node-set to its boolean value
- *
- * Returns the boolean value
- */
- int
- xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
- if ((ns == NULL) || (ns->nodeNr == 0))
- return(0);
- return(1);
- }
- /**
- * xmlXPathCastToBoolean:
- * @val: an XPath object
- *
- * Converts an XPath object to its boolean value
- *
- * Returns the boolean value
- */
- int
- xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
- int ret = 0;
- if (val == NULL)
- return(0);
- switch (val->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
- #endif
- ret = 0;
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
- break;
- case XPATH_STRING:
- ret = xmlXPathCastStringToBoolean(val->stringval);
- break;
- case XPATH_NUMBER:
- ret = xmlXPathCastNumberToBoolean(val->floatval);
- break;
- case XPATH_BOOLEAN:
- ret = val->boolval;
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO;
- ret = 0;
- break;
- }
- return(ret);
- }
- /**
- * xmlXPathConvertBoolean:
- * @val: an XPath object
- *
- * Converts an existing object to its boolean() equivalent
- *
- * Returns the new object, the old one is freed (or the operation
- * is done directly on @val)
- */
- xmlXPathObjectPtr
- xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
- xmlXPathObjectPtr ret;
- if (val == NULL)
- return(xmlXPathNewBoolean(0));
- if (val->type == XPATH_BOOLEAN)
- return(val);
- ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
- xmlXPathFreeObject(val);
- return(ret);
- }
- /************************************************************************
- * *
- * Routines to handle XPath contexts *
- * *
- ************************************************************************/
- /**
- * xmlXPathNewContext:
- * @doc: the XML document
- *
- * Create a new xmlXPathContext
- *
- * Returns the xmlXPathContext just allocated. The caller will need to free it.
- */
- xmlXPathContextPtr
- xmlXPathNewContext(xmlDocPtr doc) {
- xmlXPathContextPtr ret;
- ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating context\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
- ret->doc = doc;
- ret->node = NULL;
- ret->varHash = NULL;
- ret->nb_types = 0;
- ret->max_types = 0;
- ret->types = NULL;
- ret->funcHash = xmlHashCreate(0);
- ret->nb_axis = 0;
- ret->max_axis = 0;
- ret->axis = NULL;
- ret->nsHash = NULL;
- ret->user = NULL;
- ret->contextSize = -1;
- ret->proximityPosition = -1;
- #ifdef XP_DEFAULT_CACHE_ON
- if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) {
- xmlXPathFreeContext(ret);
- return(NULL);
- }
- #endif
- xmlXPathRegisterAllFunctions(ret);
- return(ret);
- }
- /**
- * xmlXPathFreeContext:
- * @ctxt: the context to free
- *
- * Free up an xmlXPathContext
- */
- void
- xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
- if (ctxt == NULL) return;
- if (ctxt->cache != NULL)
- xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
- xmlXPathRegisteredNsCleanup(ctxt);
- xmlXPathRegisteredFuncsCleanup(ctxt);
- xmlXPathRegisteredVariablesCleanup(ctxt);
- xmlResetError(&ctxt->lastError);
- xmlFree(ctxt);
- }
- /************************************************************************
- * *
- * Routines to handle XPath parser contexts *
- * *
- ************************************************************************/
- #define CHECK_CTXT(ctxt) \
- if (ctxt == NULL) { \
- __xmlRaiseError(NULL, NULL, NULL, \
- NULL, NULL, XML_FROM_XPATH, \
- XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
- __FILE__, __LINE__, \
- NULL, NULL, NULL, 0, 0, \
- "NULL context pointer\n"); \
- return(NULL); \
- } \
- #define CHECK_CTXT_NEG(ctxt) \
- if (ctxt == NULL) { \
- __xmlRaiseError(NULL, NULL, NULL, \
- NULL, NULL, XML_FROM_XPATH, \
- XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
- __FILE__, __LINE__, \
- NULL, NULL, NULL, 0, 0, \
- "NULL context pointer\n"); \
- return(-1); \
- } \
- #define CHECK_CONTEXT(ctxt) \
- if ((ctxt == NULL) || (ctxt->doc == NULL) || \
- (ctxt->doc->children == NULL)) { \
- xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_INVALID_CTXT); \
- return(NULL); \
- }
- /**
- * xmlXPathNewParserContext:
- * @str: the XPath expression
- * @ctxt: the XPath context
- *
- * Create a new xmlXPathParserContext
- *
- * Returns the xmlXPathParserContext just allocated.
- */
- xmlXPathParserContextPtr
- xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
- xmlXPathParserContextPtr ret;
- ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
- if (ret == NULL) {
- xmlXPathErrMemory(ctxt, "creating parser context\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
- ret->cur = ret->base = str;
- ret->context = ctxt;
- ret->comp = xmlXPathNewCompExpr();
- if (ret->comp == NULL) {
- xmlFree(ret->valueTab);
- xmlFree(ret);
- return(NULL);
- }
- if ((ctxt != NULL) && (ctxt->dict != NULL)) {
- ret->comp->dict = ctxt->dict;
- xmlDictReference(ret->comp->dict);
- }
- return(ret);
- }
- /**
- * xmlXPathCompParserContext:
- * @comp: the XPath compiled expression
- * @ctxt: the XPath context
- *
- * Create a new xmlXPathParserContext when processing a compiled expression
- *
- * Returns the xmlXPathParserContext just allocated.
- */
- static xmlXPathParserContextPtr
- xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
- xmlXPathParserContextPtr ret;
- ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
- if (ret == NULL) {
- xmlXPathErrMemory(ctxt, "creating evaluation context\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
- /* Allocate the value stack */
- ret->valueTab = (xmlXPathObjectPtr *)
- xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
- if (ret->valueTab == NULL) {
- xmlFree(ret);
- xmlXPathErrMemory(ctxt, "creating evaluation context\n");
- return(NULL);
- }
- ret->valueNr = 0;
- ret->valueMax = 10;
- ret->value = NULL;
- ret->valueFrame = 0;
- ret->context = ctxt;
- ret->comp = comp;
- return(ret);
- }
- /**
- * xmlXPathFreeParserContext:
- * @ctxt: the context to free
- *
- * Free up an xmlXPathParserContext
- */
- void
- xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
- int i;
- if (ctxt->valueTab != NULL) {
- for (i = 0; i < ctxt->valueNr; i++) {
- if (ctxt->context)
- xmlXPathReleaseObject(ctxt->context, ctxt->valueTab[i]);
- else
- xmlXPathFreeObject(ctxt->valueTab[i]);
- }
- xmlFree(ctxt->valueTab);
- }
- if (ctxt->comp != NULL) {
- #ifdef XPATH_STREAMING
- if (ctxt->comp->stream != NULL) {
- xmlFreePatternList(ctxt->comp->stream);
- ctxt->comp->stream = NULL;
- }
- #endif
- xmlXPathFreeCompExpr(ctxt->comp);
- }
- xmlFree(ctxt);
- }
- /************************************************************************
- * *
- * The implicit core function library *
- * *
- ************************************************************************/
- /**
- * xmlXPathNodeValHash:
- * @node: a node pointer
- *
- * Function computing the beginning of the string value of the node,
- * used to speed up comparisons
- *
- * Returns an int usable as a hash
- */
- static unsigned int
- xmlXPathNodeValHash(xmlNodePtr node) {
- int len = 2;
- const xmlChar * string = NULL;
- xmlNodePtr tmp = NULL;
- unsigned int ret = 0;
- if (node == NULL)
- return(0);
- if (node->type == XML_DOCUMENT_NODE) {
- tmp = xmlDocGetRootElement((xmlDocPtr) node);
- if (tmp == NULL)
- node = node->children;
- else
- node = tmp;
- if (node == NULL)
- return(0);
- }
- switch (node->type) {
- case XML_COMMENT_NODE:
- case XML_PI_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_TEXT_NODE:
- string = node->content;
- if (string == NULL)
- return(0);
- if (string[0] == 0)
- return(0);
- return(((unsigned int) string[0]) +
- (((unsigned int) string[1]) << 8));
- case XML_NAMESPACE_DECL:
- string = ((xmlNsPtr)node)->href;
- if (string == NULL)
- return(0);
- if (string[0] == 0)
- return(0);
- return(((unsigned int) string[0]) +
- (((unsigned int) string[1]) << 8));
- case XML_ATTRIBUTE_NODE:
- tmp = ((xmlAttrPtr) node)->children;
- break;
- case XML_ELEMENT_NODE:
- tmp = node->children;
- break;
- default:
- return(0);
- }
- while (tmp != NULL) {
- switch (tmp->type) {
- case XML_CDATA_SECTION_NODE:
- case XML_TEXT_NODE:
- string = tmp->content;
- break;
- default:
- string = NULL;
- break;
- }
- if ((string != NULL) && (string[0] != 0)) {
- if (len == 1) {
- return(ret + (((unsigned int) string[0]) << 8));
- }
- if (string[1] == 0) {
- len = 1;
- ret = (unsigned int) string[0];
- } else {
- return(((unsigned int) string[0]) +
- (((unsigned int) string[1]) << 8));
- }
- }
- /*
- * Skip to next node
- */
- if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
- if (tmp->children->type != XML_ENTITY_DECL) {
- tmp = tmp->children;
- continue;
- }
- }
- if (tmp == node)
- break;
- if (tmp->next != NULL) {
- tmp = tmp->next;
- continue;
- }
- do {
- tmp = tmp->parent;
- if (tmp == NULL)
- break;
- if (tmp == node) {
- tmp = NULL;
- break;
- }
- if (tmp->next != NULL) {
- tmp = tmp->next;
- break;
- }
- } while (tmp != NULL);
- }
- return(ret);
- }
- /**
- * xmlXPathStringHash:
- * @string: a string
- *
- * Function computing the beginning of the string value of the node,
- * used to speed up comparisons
- *
- * Returns an int usable as a hash
- */
- static unsigned int
- xmlXPathStringHash(const xmlChar * string) {
- if (string == NULL)
- return((unsigned int) 0);
- if (string[0] == 0)
- return(0);
- return(((unsigned int) string[0]) +
- (((unsigned int) string[1]) << 8));
- }
- /**
- * xmlXPathCompareNodeSetFloat:
- * @ctxt: the XPath Parser context
- * @inf: less than (1) or greater than (0)
- * @strict: is the comparison strict
- * @arg: the node set
- * @f: the value
- *
- * Implement the compare operation between a nodeset and a number
- * @ns < @val (1, 1, ...
- * @ns <= @val (1, 0, ...
- * @ns > @val (0, 1, ...
- * @ns >= @val (0, 0, ...
- *
- * If one object to be compared is a node-set and the other is a number,
- * then the comparison will be true if and only if there is a node in the
- * node-set such that the result of performing the comparison on the number
- * to be compared and on the result of converting the string-value of that
- * node to a number using the number function is true.
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- static int
- xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
- xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
- int i, ret = 0;
- xmlNodeSetPtr ns;
- xmlChar *str2;
- if ((f == NULL) || (arg == NULL) ||
- ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
- xmlXPathReleaseObject(ctxt->context, arg);
- xmlXPathReleaseObject(ctxt->context, f);
- return(0);
- }
- ns = arg->nodesetval;
- if (ns != NULL) {
- for (i = 0;i < ns->nodeNr;i++) {
- str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
- if (str2 != NULL) {
- valuePush(ctxt,
- xmlXPathCacheNewString(ctxt->context, str2));
- xmlFree(str2);
- xmlXPathNumberFunction(ctxt, 1);
- valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f));
- ret = xmlXPathCompareValues(ctxt, inf, strict);
- if (ret)
- break;
- }
- }
- }
- xmlXPathReleaseObject(ctxt->context, arg);
- xmlXPathReleaseObject(ctxt->context, f);
- return(ret);
- }
- /**
- * xmlXPathCompareNodeSetString:
- * @ctxt: the XPath Parser context
- * @inf: less than (1) or greater than (0)
- * @strict: is the comparison strict
- * @arg: the node set
- * @s: the value
- *
- * Implement the compare operation between a nodeset and a string
- * @ns < @val (1, 1, ...
- * @ns <= @val (1, 0, ...
- * @ns > @val (0, 1, ...
- * @ns >= @val (0, 0, ...
- *
- * If one object to be compared is a node-set and the other is a string,
- * then the comparison will be true if and only if there is a node in
- * the node-set such that the result of performing the comparison on the
- * string-value of the node and the other string is true.
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- static int
- xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
- xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
- int i, ret = 0;
- xmlNodeSetPtr ns;
- xmlChar *str2;
- if ((s == NULL) || (arg == NULL) ||
- ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
- xmlXPathReleaseObject(ctxt->context, arg);
- xmlXPathReleaseObject(ctxt->context, s);
- return(0);
- }
- ns = arg->nodesetval;
- if (ns != NULL) {
- for (i = 0;i < ns->nodeNr;i++) {
- str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
- if (str2 != NULL) {
- valuePush(ctxt,
- xmlXPathCacheNewString(ctxt->context, str2));
- xmlFree(str2);
- valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s));
- ret = xmlXPathCompareValues(ctxt, inf, strict);
- if (ret)
- break;
- }
- }
- }
- xmlXPathReleaseObject(ctxt->context, arg);
- xmlXPathReleaseObject(ctxt->context, s);
- return(ret);
- }
- /**
- * xmlXPathCompareNodeSets:
- * @inf: less than (1) or greater than (0)
- * @strict: is the comparison strict
- * @arg1: the first node set object
- * @arg2: the second node set object
- *
- * Implement the compare operation on nodesets:
- *
- * If both objects to be compared are node-sets, then the comparison
- * will be true if and only if there is a node in the first node-set
- * and a node in the second node-set such that the result of performing
- * the comparison on the string-values of the two nodes is true.
- * ....
- * When neither object to be compared is a node-set and the operator
- * is <=, <, >= or >, then the objects are compared by converting both
- * objects to numbers and comparing the numbers according to IEEE 754.
- * ....
- * The number function converts its argument to a number as follows:
- * - a string that consists of optional whitespace followed by an
- * optional minus sign followed by a Number followed by whitespace
- * is converted to the IEEE 754 number that is nearest (according
- * to the IEEE 754 round-to-nearest rule) to the mathematical value
- * represented by the string; any other string is converted to NaN
- *
- * Conclusion all nodes need to be converted first to their string value
- * and then the comparison must be done when possible
- */
- static int
- xmlXPathCompareNodeSets(int inf, int strict,
- xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
- int i, j, init = 0;
- double val1;
- double *values2;
- int ret = 0;
- xmlNodeSetPtr ns1;
- xmlNodeSetPtr ns2;
- if ((arg1 == NULL) ||
- ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
- xmlXPathFreeObject(arg2);
- return(0);
- }
- if ((arg2 == NULL) ||
- ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
- return(0);
- }
- ns1 = arg1->nodesetval;
- ns2 = arg2->nodesetval;
- if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
- return(0);
- }
- if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
- return(0);
- }
- values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
- if (values2 == NULL) {
- /* TODO: Propagate memory error. */
- xmlXPathErrMemory(NULL, "comparing nodesets\n");
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
- return(0);
- }
- for (i = 0;i < ns1->nodeNr;i++) {
- val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
- if (xmlXPathIsNaN(val1))
- continue;
- for (j = 0;j < ns2->nodeNr;j++) {
- if (init == 0) {
- values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
- }
- if (xmlXPathIsNaN(values2[j]))
- continue;
- if (inf && strict)
- ret = (val1 < values2[j]);
- else if (inf && !strict)
- ret = (val1 <= values2[j]);
- else if (!inf && strict)
- ret = (val1 > values2[j]);
- else if (!inf && !strict)
- ret = (val1 >= values2[j]);
- if (ret)
- break;
- }
- if (ret)
- break;
- init = 1;
- }
- xmlFree(values2);
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
- return(ret);
- }
- /**
- * xmlXPathCompareNodeSetValue:
- * @ctxt: the XPath Parser context
- * @inf: less than (1) or greater than (0)
- * @strict: is the comparison strict
- * @arg: the node set
- * @val: the value
- *
- * Implement the compare operation between a nodeset and a value
- * @ns < @val (1, 1, ...
- * @ns <= @val (1, 0, ...
- * @ns > @val (0, 1, ...
- * @ns >= @val (0, 0, ...
- *
- * If one object to be compared is a node-set and the other is a boolean,
- * then the comparison will be true if and only if the result of performing
- * the comparison on the boolean and on the result of converting
- * the node-set to a boolean using the boolean function is true.
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- static int
- xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
- xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
- if ((val == NULL) || (arg == NULL) ||
- ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
- return(0);
- switch(val->type) {
- case XPATH_NUMBER:
- return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- return(xmlXPathCompareNodeSets(inf, strict, arg, val));
- case XPATH_STRING:
- return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
- case XPATH_BOOLEAN:
- valuePush(ctxt, arg);
- xmlXPathBooleanFunction(ctxt, 1);
- valuePush(ctxt, val);
- return(xmlXPathCompareValues(ctxt, inf, strict));
- default:
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompareNodeSetValue: Can't compare node set "
- "and object of type %d\n",
- val->type);
- xmlXPathReleaseObject(ctxt->context, arg);
- xmlXPathReleaseObject(ctxt->context, val);
- XP_ERROR0(XPATH_INVALID_TYPE);
- }
- return(0);
- }
- /**
- * xmlXPathEqualNodeSetString:
- * @arg: the nodeset object argument
- * @str: the string to compare to.
- * @neq: flag to show whether for '=' (0) or '!=' (1)
- *
- * Implement the equal operation on XPath objects content: @arg1 == @arg2
- * If one object to be compared is a node-set and the other is a string,
- * then the comparison will be true if and only if there is a node in
- * the node-set such that the result of performing the comparison on the
- * string-value of the node and the other string is true.
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- static int
- xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq)
- {
- int i;
- xmlNodeSetPtr ns;
- xmlChar *str2;
- unsigned int hash;
- if ((str == NULL) || (arg == NULL) ||
- ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
- return (0);
- ns = arg->nodesetval;
- /*
- * A NULL nodeset compared with a string is always false
- * (since there is no node equal, and no node not equal)
- */
- if ((ns == NULL) || (ns->nodeNr <= 0) )
- return (0);
- hash = xmlXPathStringHash(str);
- for (i = 0; i < ns->nodeNr; i++) {
- if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
- str2 = xmlNodeGetContent(ns->nodeTab[i]);
- if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
- xmlFree(str2);
- if (neq)
- continue;
- return (1);
- } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) {
- if (neq)
- continue;
- return (1);
- } else if (neq) {
- if (str2 != NULL)
- xmlFree(str2);
- return (1);
- }
- if (str2 != NULL)
- xmlFree(str2);
- } else if (neq)
- return (1);
- }
- return (0);
- }
- /**
- * xmlXPathEqualNodeSetFloat:
- * @arg: the nodeset object argument
- * @f: the float to compare to
- * @neq: flag to show whether to compare '=' (0) or '!=' (1)
- *
- * Implement the equal operation on XPath objects content: @arg1 == @arg2
- * If one object to be compared is a node-set and the other is a number,
- * then the comparison will be true if and only if there is a node in
- * the node-set such that the result of performing the comparison on the
- * number to be compared and on the result of converting the string-value
- * of that node to a number using the number function is true.
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- static int
- xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt,
- xmlXPathObjectPtr arg, double f, int neq) {
- int i, ret=0;
- xmlNodeSetPtr ns;
- xmlChar *str2;
- xmlXPathObjectPtr val;
- double v;
- if ((arg == NULL) ||
- ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
- return(0);
- ns = arg->nodesetval;
- if (ns != NULL) {
- for (i=0;i<ns->nodeNr;i++) {
- str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
- if (str2 != NULL) {
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2));
- xmlFree(str2);
- xmlXPathNumberFunction(ctxt, 1);
- val = valuePop(ctxt);
- v = val->floatval;
- xmlXPathReleaseObject(ctxt->context, val);
- if (!xmlXPathIsNaN(v)) {
- if ((!neq) && (v==f)) {
- ret = 1;
- break;
- } else if ((neq) && (v!=f)) {
- ret = 1;
- break;
- }
- } else { /* NaN is unequal to any value */
- if (neq)
- ret = 1;
- }
- }
- }
- }
- return(ret);
- }
- /**
- * xmlXPathEqualNodeSets:
- * @arg1: first nodeset object argument
- * @arg2: second nodeset object argument
- * @neq: flag to show whether to test '=' (0) or '!=' (1)
- *
- * Implement the equal / not equal operation on XPath nodesets:
- * @arg1 == @arg2 or @arg1 != @arg2
- * If both objects to be compared are node-sets, then the comparison
- * will be true if and only if there is a node in the first node-set and
- * a node in the second node-set such that the result of performing the
- * comparison on the string-values of the two nodes is true.
- *
- * (needless to say, this is a costly operation)
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- static int
- xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) {
- int i, j;
- unsigned int *hashs1;
- unsigned int *hashs2;
- xmlChar **values1;
- xmlChar **values2;
- int ret = 0;
- xmlNodeSetPtr ns1;
- xmlNodeSetPtr ns2;
- if ((arg1 == NULL) ||
- ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
- return(0);
- if ((arg2 == NULL) ||
- ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
- return(0);
- ns1 = arg1->nodesetval;
- ns2 = arg2->nodesetval;
- if ((ns1 == NULL) || (ns1->nodeNr <= 0))
- return(0);
- if ((ns2 == NULL) || (ns2->nodeNr <= 0))
- return(0);
- /*
- * for equal, check if there is a node pertaining to both sets
- */
- if (neq == 0)
- for (i = 0;i < ns1->nodeNr;i++)
- for (j = 0;j < ns2->nodeNr;j++)
- if (ns1->nodeTab[i] == ns2->nodeTab[j])
- return(1);
- values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
- if (values1 == NULL) {
- /* TODO: Propagate memory error. */
- xmlXPathErrMemory(NULL, "comparing nodesets\n");
- return(0);
- }
- hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
- if (hashs1 == NULL) {
- /* TODO: Propagate memory error. */
- xmlXPathErrMemory(NULL, "comparing nodesets\n");
- xmlFree(values1);
- return(0);
- }
- memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
- values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
- if (values2 == NULL) {
- /* TODO: Propagate memory error. */
- xmlXPathErrMemory(NULL, "comparing nodesets\n");
- xmlFree(hashs1);
- xmlFree(values1);
- return(0);
- }
- hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
- if (hashs2 == NULL) {
- /* TODO: Propagate memory error. */
- xmlXPathErrMemory(NULL, "comparing nodesets\n");
- xmlFree(hashs1);
- xmlFree(values1);
- xmlFree(values2);
- return(0);
- }
- memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
- for (i = 0;i < ns1->nodeNr;i++) {
- hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
- for (j = 0;j < ns2->nodeNr;j++) {
- if (i == 0)
- hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
- if (hashs1[i] != hashs2[j]) {
- if (neq) {
- ret = 1;
- break;
- }
- }
- else {
- if (values1[i] == NULL)
- values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
- if (values2[j] == NULL)
- values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
- ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
- if (ret)
- break;
- }
- }
- if (ret)
- break;
- }
- for (i = 0;i < ns1->nodeNr;i++)
- if (values1[i] != NULL)
- xmlFree(values1[i]);
- for (j = 0;j < ns2->nodeNr;j++)
- if (values2[j] != NULL)
- xmlFree(values2[j]);
- xmlFree(values1);
- xmlFree(values2);
- xmlFree(hashs1);
- xmlFree(hashs2);
- return(ret);
- }
- static int
- xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
- xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
- int ret = 0;
- /*
- *At this point we are assured neither arg1 nor arg2
- *is a nodeset, so we can just pick the appropriate routine.
- */
- switch (arg1->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "Equal: undefined\n");
- #endif
- break;
- case XPATH_BOOLEAN:
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "Equal: undefined\n");
- #endif
- break;
- case XPATH_BOOLEAN:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "Equal: %d boolean %d \n",
- arg1->boolval, arg2->boolval);
- #endif
- ret = (arg1->boolval == arg2->boolval);
- break;
- case XPATH_NUMBER:
- ret = (arg1->boolval ==
- xmlXPathCastNumberToBoolean(arg2->floatval));
- break;
- case XPATH_STRING:
- if ((arg2->stringval == NULL) ||
- (arg2->stringval[0] == 0)) ret = 0;
- else
- ret = 1;
- ret = (arg1->boolval == ret);
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- break;
- }
- break;
- case XPATH_NUMBER:
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "Equal: undefined\n");
- #endif
- break;
- case XPATH_BOOLEAN:
- ret = (arg2->boolval==
- xmlXPathCastNumberToBoolean(arg1->floatval));
- break;
- case XPATH_STRING:
- valuePush(ctxt, arg2);
- xmlXPathNumberFunction(ctxt, 1);
- arg2 = valuePop(ctxt);
- /* Falls through. */
- case XPATH_NUMBER:
- /* Hand check NaN and Infinity equalities */
- if (xmlXPathIsNaN(arg1->floatval) ||
- xmlXPathIsNaN(arg2->floatval)) {
- ret = 0;
- } else if (xmlXPathIsInf(arg1->floatval) == 1) {
- if (xmlXPathIsInf(arg2->floatval) == 1)
- ret = 1;
- else
- ret = 0;
- } else if (xmlXPathIsInf(arg1->floatval) == -1) {
- if (xmlXPathIsInf(arg2->floatval) == -1)
- ret = 1;
- else
- ret = 0;
- } else if (xmlXPathIsInf(arg2->floatval) == 1) {
- if (xmlXPathIsInf(arg1->floatval) == 1)
- ret = 1;
- else
- ret = 0;
- } else if (xmlXPathIsInf(arg2->floatval) == -1) {
- if (xmlXPathIsInf(arg1->floatval) == -1)
- ret = 1;
- else
- ret = 0;
- } else {
- ret = (arg1->floatval == arg2->floatval);
- }
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- break;
- }
- break;
- case XPATH_STRING:
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "Equal: undefined\n");
- #endif
- break;
- case XPATH_BOOLEAN:
- if ((arg1->stringval == NULL) ||
- (arg1->stringval[0] == 0)) ret = 0;
- else
- ret = 1;
- ret = (arg2->boolval == ret);
- break;
- case XPATH_STRING:
- ret = xmlStrEqual(arg1->stringval, arg2->stringval);
- break;
- case XPATH_NUMBER:
- valuePush(ctxt, arg1);
- xmlXPathNumberFunction(ctxt, 1);
- arg1 = valuePop(ctxt);
- /* Hand check NaN and Infinity equalities */
- if (xmlXPathIsNaN(arg1->floatval) ||
- xmlXPathIsNaN(arg2->floatval)) {
- ret = 0;
- } else if (xmlXPathIsInf(arg1->floatval) == 1) {
- if (xmlXPathIsInf(arg2->floatval) == 1)
- ret = 1;
- else
- ret = 0;
- } else if (xmlXPathIsInf(arg1->floatval) == -1) {
- if (xmlXPathIsInf(arg2->floatval) == -1)
- ret = 1;
- else
- ret = 0;
- } else if (xmlXPathIsInf(arg2->floatval) == 1) {
- if (xmlXPathIsInf(arg1->floatval) == 1)
- ret = 1;
- else
- ret = 0;
- } else if (xmlXPathIsInf(arg2->floatval) == -1) {
- if (xmlXPathIsInf(arg1->floatval) == -1)
- ret = 1;
- else
- ret = 0;
- } else {
- ret = (arg1->floatval == arg2->floatval);
- }
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- break;
- }
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- break;
- }
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- return(ret);
- }
- /**
- * xmlXPathEqualValues:
- * @ctxt: the XPath Parser context
- *
- * Implement the equal operation on XPath objects content: @arg1 == @arg2
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- int
- xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr arg1, arg2, argtmp;
- int ret = 0;
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
- arg2 = valuePop(ctxt);
- arg1 = valuePop(ctxt);
- if ((arg1 == NULL) || (arg2 == NULL)) {
- if (arg1 != NULL)
- xmlXPathReleaseObject(ctxt->context, arg1);
- else
- xmlXPathReleaseObject(ctxt->context, arg2);
- XP_ERROR0(XPATH_INVALID_OPERAND);
- }
- if (arg1 == arg2) {
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "Equal: by pointer\n");
- #endif
- xmlXPathFreeObject(arg1);
- return(1);
- }
- /*
- *If either argument is a nodeset, it's a 'special case'
- */
- if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
- (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
- /*
- *Hack it to assure arg1 is the nodeset
- */
- if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
- argtmp = arg2;
- arg2 = arg1;
- arg1 = argtmp;
- }
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "Equal: undefined\n");
- #endif
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
- break;
- case XPATH_BOOLEAN:
- if ((arg1->nodesetval == NULL) ||
- (arg1->nodesetval->nodeNr == 0)) ret = 0;
- else
- ret = 1;
- ret = (ret == arg2->boolval);
- break;
- case XPATH_NUMBER:
- ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0);
- break;
- case XPATH_STRING:
- ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO
- break;
- }
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- return(ret);
- }
- return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
- }
- /**
- * xmlXPathNotEqualValues:
- * @ctxt: the XPath Parser context
- *
- * Implement the equal operation on XPath objects content: @arg1 == @arg2
- *
- * Returns 0 or 1 depending on the results of the test.
- */
- int
- xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr arg1, arg2, argtmp;
- int ret = 0;
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
- arg2 = valuePop(ctxt);
- arg1 = valuePop(ctxt);
- if ((arg1 == NULL) || (arg2 == NULL)) {
- if (arg1 != NULL)
- xmlXPathReleaseObject(ctxt->context, arg1);
- else
- xmlXPathReleaseObject(ctxt->context, arg2);
- XP_ERROR0(XPATH_INVALID_OPERAND);
- }
- if (arg1 == arg2) {
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "NotEqual: by pointer\n");
- #endif
- xmlXPathReleaseObject(ctxt->context, arg1);
- return(0);
- }
- /*
- *If either argument is a nodeset, it's a 'special case'
- */
- if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
- (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
- /*
- *Hack it to assure arg1 is the nodeset
- */
- if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
- argtmp = arg2;
- arg2 = arg1;
- arg1 = argtmp;
- }
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "NotEqual: undefined\n");
- #endif
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
- break;
- case XPATH_BOOLEAN:
- if ((arg1->nodesetval == NULL) ||
- (arg1->nodesetval->nodeNr == 0)) ret = 0;
- else
- ret = 1;
- ret = (ret != arg2->boolval);
- break;
- case XPATH_NUMBER:
- ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1);
- break;
- case XPATH_STRING:
- ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
- break;
- case XPATH_USERS:
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- TODO
- break;
- }
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- return(ret);
- }
- return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
- }
- /**
- * xmlXPathCompareValues:
- * @ctxt: the XPath Parser context
- * @inf: less than (1) or greater than (0)
- * @strict: is the comparison strict
- *
- * Implement the compare operation on XPath objects:
- * @arg1 < @arg2 (1, 1, ...
- * @arg1 <= @arg2 (1, 0, ...
- * @arg1 > @arg2 (0, 1, ...
- * @arg1 >= @arg2 (0, 0, ...
- *
- * When neither object to be compared is a node-set and the operator is
- * <=, <, >=, >, then the objects are compared by converted both objects
- * to numbers and comparing the numbers according to IEEE 754. The <
- * comparison will be true if and only if the first number is less than the
- * second number. The <= comparison will be true if and only if the first
- * number is less than or equal to the second number. The > comparison
- * will be true if and only if the first number is greater than the second
- * number. The >= comparison will be true if and only if the first number
- * is greater than or equal to the second number.
- *
- * Returns 1 if the comparison succeeded, 0 if it failed
- */
- int
- xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
- int ret = 0, arg1i = 0, arg2i = 0;
- xmlXPathObjectPtr arg1, arg2;
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
- arg2 = valuePop(ctxt);
- arg1 = valuePop(ctxt);
- if ((arg1 == NULL) || (arg2 == NULL)) {
- if (arg1 != NULL)
- xmlXPathReleaseObject(ctxt->context, arg1);
- else
- xmlXPathReleaseObject(ctxt->context, arg2);
- XP_ERROR0(XPATH_INVALID_OPERAND);
- }
- if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
- (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
- /*
- * If either argument is a XPATH_NODESET or XPATH_XSLT_TREE the two arguments
- * are not freed from within this routine; they will be freed from the
- * called routine, e.g. xmlXPathCompareNodeSets or xmlXPathCompareNodeSetValue
- */
- if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
- ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
- ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
- } else {
- if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
- ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
- arg1, arg2);
- } else {
- ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
- arg2, arg1);
- }
- }
- return(ret);
- }
- if (arg1->type != XPATH_NUMBER) {
- valuePush(ctxt, arg1);
- xmlXPathNumberFunction(ctxt, 1);
- arg1 = valuePop(ctxt);
- }
- if (arg1->type != XPATH_NUMBER) {
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
- XP_ERROR0(XPATH_INVALID_OPERAND);
- }
- if (arg2->type != XPATH_NUMBER) {
- valuePush(ctxt, arg2);
- xmlXPathNumberFunction(ctxt, 1);
- arg2 = valuePop(ctxt);
- }
- if (arg2->type != XPATH_NUMBER) {
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- XP_ERROR0(XPATH_INVALID_OPERAND);
- }
- /*
- * Add tests for infinity and nan
- * => feedback on 3.4 for Inf and NaN
- */
- /* Hand check NaN and Infinity comparisons */
- if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
- ret=0;
- } else {
- arg1i=xmlXPathIsInf(arg1->floatval);
- arg2i=xmlXPathIsInf(arg2->floatval);
- if (inf && strict) {
- if ((arg1i == -1 && arg2i != -1) ||
- (arg2i == 1 && arg1i != 1)) {
- ret = 1;
- } else if (arg1i == 0 && arg2i == 0) {
- ret = (arg1->floatval < arg2->floatval);
- } else {
- ret = 0;
- }
- }
- else if (inf && !strict) {
- if (arg1i == -1 || arg2i == 1) {
- ret = 1;
- } else if (arg1i == 0 && arg2i == 0) {
- ret = (arg1->floatval <= arg2->floatval);
- } else {
- ret = 0;
- }
- }
- else if (!inf && strict) {
- if ((arg1i == 1 && arg2i != 1) ||
- (arg2i == -1 && arg1i != -1)) {
- ret = 1;
- } else if (arg1i == 0 && arg2i == 0) {
- ret = (arg1->floatval > arg2->floatval);
- } else {
- ret = 0;
- }
- }
- else if (!inf && !strict) {
- if (arg1i == 1 || arg2i == -1) {
- ret = 1;
- } else if (arg1i == 0 && arg2i == 0) {
- ret = (arg1->floatval >= arg2->floatval);
- } else {
- ret = 0;
- }
- }
- }
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- return(ret);
- }
- /**
- * xmlXPathValueFlipSign:
- * @ctxt: the XPath Parser context
- *
- * Implement the unary - operation on an XPath object
- * The numeric operators convert their operands to numbers as if
- * by calling the number function.
- */
- void
- xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return;
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- ctxt->value->floatval = -ctxt->value->floatval;
- }
- /**
- * xmlXPathAddValues:
- * @ctxt: the XPath Parser context
- *
- * Implement the add operation on XPath objects:
- * The numeric operators convert their operands to numbers as if
- * by calling the number function.
- */
- void
- xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr arg;
- double val;
- arg = valuePop(ctxt);
- if (arg == NULL)
- XP_ERROR(XPATH_INVALID_OPERAND);
- val = xmlXPathCastToNumber(arg);
- xmlXPathReleaseObject(ctxt->context, arg);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- ctxt->value->floatval += val;
- }
- /**
- * xmlXPathSubValues:
- * @ctxt: the XPath Parser context
- *
- * Implement the subtraction operation on XPath objects:
- * The numeric operators convert their operands to numbers as if
- * by calling the number function.
- */
- void
- xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr arg;
- double val;
- arg = valuePop(ctxt);
- if (arg == NULL)
- XP_ERROR(XPATH_INVALID_OPERAND);
- val = xmlXPathCastToNumber(arg);
- xmlXPathReleaseObject(ctxt->context, arg);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- ctxt->value->floatval -= val;
- }
- /**
- * xmlXPathMultValues:
- * @ctxt: the XPath Parser context
- *
- * Implement the multiply operation on XPath objects:
- * The numeric operators convert their operands to numbers as if
- * by calling the number function.
- */
- void
- xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr arg;
- double val;
- arg = valuePop(ctxt);
- if (arg == NULL)
- XP_ERROR(XPATH_INVALID_OPERAND);
- val = xmlXPathCastToNumber(arg);
- xmlXPathReleaseObject(ctxt->context, arg);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- ctxt->value->floatval *= val;
- }
- /**
- * xmlXPathDivValues:
- * @ctxt: the XPath Parser context
- *
- * Implement the div operation on XPath objects @arg1 / @arg2:
- * The numeric operators convert their operands to numbers as if
- * by calling the number function.
- */
- ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
- void
- xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr arg;
- double val;
- arg = valuePop(ctxt);
- if (arg == NULL)
- XP_ERROR(XPATH_INVALID_OPERAND);
- val = xmlXPathCastToNumber(arg);
- xmlXPathReleaseObject(ctxt->context, arg);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- ctxt->value->floatval /= val;
- }
- /**
- * xmlXPathModValues:
- * @ctxt: the XPath Parser context
- *
- * Implement the mod operation on XPath objects: @arg1 / @arg2
- * The numeric operators convert their operands to numbers as if
- * by calling the number function.
- */
- void
- xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
- xmlXPathObjectPtr arg;
- double arg1, arg2;
- arg = valuePop(ctxt);
- if (arg == NULL)
- XP_ERROR(XPATH_INVALID_OPERAND);
- arg2 = xmlXPathCastToNumber(arg);
- xmlXPathReleaseObject(ctxt->context, arg);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- arg1 = ctxt->value->floatval;
- if (arg2 == 0)
- ctxt->value->floatval = xmlXPathNAN;
- else {
- ctxt->value->floatval = fmod(arg1, arg2);
- }
- }
- /************************************************************************
- * *
- * The traversal functions *
- * *
- ************************************************************************/
- /*
- * A traversal function enumerates nodes along an axis.
- * Initially it must be called with NULL, and it indicates
- * termination on the axis by returning NULL.
- */
- typedef xmlNodePtr (*xmlXPathTraversalFunction)
- (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
- /*
- * xmlXPathTraversalFunctionExt:
- * A traversal function enumerates nodes along an axis.
- * Initially it must be called with NULL, and it indicates
- * termination on the axis by returning NULL.
- * The context node of the traversal is specified via @contextNode.
- */
- typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
- (xmlNodePtr cur, xmlNodePtr contextNode);
- /*
- * xmlXPathNodeSetMergeFunction:
- * Used for merging node sets in xmlXPathCollectAndTest().
- */
- typedef xmlNodeSetPtr (*xmlXPathNodeSetMergeFunction)
- (xmlNodeSetPtr, xmlNodeSetPtr);
- /**
- * xmlXPathNextSelf:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "self" direction
- * The self axis contains just the context node itself
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL)
- return(ctxt->context->node);
- return(NULL);
- }
- /**
- * xmlXPathNextChild:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "child" direction
- * The child axis contains the children of the context node in document order.
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL) {
- if (ctxt->context->node == NULL) return(NULL);
- switch (ctxt->context->node->type) {
- case XML_ELEMENT_NODE:
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_ENTITY_REF_NODE:
- case XML_ENTITY_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- case XML_NOTATION_NODE:
- case XML_DTD_NODE:
- return(ctxt->context->node->children);
- case XML_DOCUMENT_NODE:
- case XML_DOCUMENT_TYPE_NODE:
- case XML_DOCUMENT_FRAG_NODE:
- case XML_HTML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- return(((xmlDocPtr) ctxt->context->node)->children);
- case XML_ELEMENT_DECL:
- case XML_ATTRIBUTE_DECL:
- case XML_ENTITY_DECL:
- case XML_ATTRIBUTE_NODE:
- case XML_NAMESPACE_DECL:
- case XML_XINCLUDE_START:
- case XML_XINCLUDE_END:
- return(NULL);
- }
- return(NULL);
- }
- if ((cur->type == XML_DOCUMENT_NODE) ||
- (cur->type == XML_HTML_DOCUMENT_NODE))
- return(NULL);
- return(cur->next);
- }
- /**
- * xmlXPathNextChildElement:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "child" direction and nodes of type element.
- * The child axis contains the children of the context node in document order.
- *
- * Returns the next element following that axis
- */
- static xmlNodePtr
- xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL) {
- cur = ctxt->context->node;
- if (cur == NULL) return(NULL);
- /*
- * Get the first element child.
- */
- switch (cur->type) {
- case XML_ELEMENT_NODE:
- case XML_DOCUMENT_FRAG_NODE:
- case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */
- case XML_ENTITY_NODE:
- cur = cur->children;
- if (cur != NULL) {
- if (cur->type == XML_ELEMENT_NODE)
- return(cur);
- do {
- cur = cur->next;
- } while ((cur != NULL) &&
- (cur->type != XML_ELEMENT_NODE));
- return(cur);
- }
- return(NULL);
- case XML_DOCUMENT_NODE:
- case XML_HTML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- return(xmlDocGetRootElement((xmlDocPtr) cur));
- default:
- return(NULL);
- }
- return(NULL);
- }
- /*
- * Get the next sibling element node.
- */
- switch (cur->type) {
- case XML_ELEMENT_NODE:
- case XML_TEXT_NODE:
- case XML_ENTITY_REF_NODE:
- case XML_ENTITY_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- case XML_XINCLUDE_END:
- break;
- /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */
- default:
- return(NULL);
- }
- if (cur->next != NULL) {
- if (cur->next->type == XML_ELEMENT_NODE)
- return(cur->next);
- cur = cur->next;
- do {
- cur = cur->next;
- } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE));
- return(cur);
- }
- return(NULL);
- }
- #if 0
- /**
- * xmlXPathNextDescendantOrSelfElemParent:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "descendant-or-self" axis.
- * Additionally it returns only nodes which can be parents of
- * element nodes.
- *
- *
- * Returns the next element following that axis
- */
- static xmlNodePtr
- xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
- xmlNodePtr contextNode)
- {
- if (cur == NULL) {
- if (contextNode == NULL)
- return(NULL);
- switch (contextNode->type) {
- case XML_ELEMENT_NODE:
- case XML_XINCLUDE_START:
- case XML_DOCUMENT_FRAG_NODE:
- case XML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- case XML_HTML_DOCUMENT_NODE:
- return(contextNode);
- default:
- return(NULL);
- }
- return(NULL);
- } else {
- xmlNodePtr start = cur;
- while (cur != NULL) {
- switch (cur->type) {
- case XML_ELEMENT_NODE:
- /* TODO: OK to have XInclude here? */
- case XML_XINCLUDE_START:
- case XML_DOCUMENT_FRAG_NODE:
- if (cur != start)
- return(cur);
- if (cur->children != NULL) {
- cur = cur->children;
- continue;
- }
- break;
- /* Not sure if we need those here. */
- case XML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- case XML_HTML_DOCUMENT_NODE:
- if (cur != start)
- return(cur);
- return(xmlDocGetRootElement((xmlDocPtr) cur));
- default:
- break;
- }
- next_sibling:
- if ((cur == NULL) || (cur == contextNode))
- return(NULL);
- if (cur->next != NULL) {
- cur = cur->next;
- } else {
- cur = cur->parent;
- goto next_sibling;
- }
- }
- }
- return(NULL);
- }
- #endif
- /**
- * xmlXPathNextDescendant:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "descendant" direction
- * the descendant axis contains the descendants of the context node in document
- * order; a descendant is a child or a child of a child and so on.
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL) {
- if (ctxt->context->node == NULL)
- return(NULL);
- if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
- (ctxt->context->node->type == XML_NAMESPACE_DECL))
- return(NULL);
- if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
- return(ctxt->context->doc->children);
- return(ctxt->context->node->children);
- }
- if (cur->type == XML_NAMESPACE_DECL)
- return(NULL);
- if (cur->children != NULL) {
- /*
- * Do not descend on entities declarations
- */
- if (cur->children->type != XML_ENTITY_DECL) {
- cur = cur->children;
- /*
- * Skip DTDs
- */
- if (cur->type != XML_DTD_NODE)
- return(cur);
- }
- }
- if (cur == ctxt->context->node) return(NULL);
- while (cur->next != NULL) {
- cur = cur->next;
- if ((cur->type != XML_ENTITY_DECL) &&
- (cur->type != XML_DTD_NODE))
- return(cur);
- }
- do {
- cur = cur->parent;
- if (cur == NULL) break;
- if (cur == ctxt->context->node) return(NULL);
- if (cur->next != NULL) {
- cur = cur->next;
- return(cur);
- }
- } while (cur != NULL);
- return(cur);
- }
- /**
- * xmlXPathNextDescendantOrSelf:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "descendant-or-self" direction
- * the descendant-or-self axis contains the context node and the descendants
- * of the context node in document order; thus the context node is the first
- * node on the axis, and the first child of the context node is the second node
- * on the axis
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL)
- return(ctxt->context->node);
- if (ctxt->context->node == NULL)
- return(NULL);
- if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
- (ctxt->context->node->type == XML_NAMESPACE_DECL))
- return(NULL);
- return(xmlXPathNextDescendant(ctxt, cur));
- }
- /**
- * xmlXPathNextParent:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "parent" direction
- * The parent axis contains the parent of the context node, if there is one.
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- /*
- * the parent of an attribute or namespace node is the element
- * to which the attribute or namespace node is attached
- * Namespace handling !!!
- */
- if (cur == NULL) {
- if (ctxt->context->node == NULL) return(NULL);
- switch (ctxt->context->node->type) {
- case XML_ELEMENT_NODE:
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_ENTITY_REF_NODE:
- case XML_ENTITY_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- case XML_NOTATION_NODE:
- case XML_DTD_NODE:
- case XML_ELEMENT_DECL:
- case XML_ATTRIBUTE_DECL:
- case XML_XINCLUDE_START:
- case XML_XINCLUDE_END:
- case XML_ENTITY_DECL:
- if (ctxt->context->node->parent == NULL)
- return((xmlNodePtr) ctxt->context->doc);
- if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
- ((ctxt->context->node->parent->name[0] == ' ') ||
- (xmlStrEqual(ctxt->context->node->parent->name,
- BAD_CAST "fake node libxslt"))))
- return(NULL);
- return(ctxt->context->node->parent);
- case XML_ATTRIBUTE_NODE: {
- xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
- return(att->parent);
- }
- case XML_DOCUMENT_NODE:
- case XML_DOCUMENT_TYPE_NODE:
- case XML_DOCUMENT_FRAG_NODE:
- case XML_HTML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- return(NULL);
- case XML_NAMESPACE_DECL: {
- xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
- if ((ns->next != NULL) &&
- (ns->next->type != XML_NAMESPACE_DECL))
- return((xmlNodePtr) ns->next);
- return(NULL);
- }
- }
- }
- return(NULL);
- }
- /**
- * xmlXPathNextAncestor:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "ancestor" direction
- * the ancestor axis contains the ancestors of the context node; the ancestors
- * of the context node consist of the parent of context node and the parent's
- * parent and so on; the nodes are ordered in reverse document order; thus the
- * parent is the first node on the axis, and the parent's parent is the second
- * node on the axis
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- /*
- * the parent of an attribute or namespace node is the element
- * to which the attribute or namespace node is attached
- * !!!!!!!!!!!!!
- */
- if (cur == NULL) {
- if (ctxt->context->node == NULL) return(NULL);
- switch (ctxt->context->node->type) {
- case XML_ELEMENT_NODE:
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_ENTITY_REF_NODE:
- case XML_ENTITY_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- case XML_DTD_NODE:
- case XML_ELEMENT_DECL:
- case XML_ATTRIBUTE_DECL:
- case XML_ENTITY_DECL:
- case XML_NOTATION_NODE:
- case XML_XINCLUDE_START:
- case XML_XINCLUDE_END:
- if (ctxt->context->node->parent == NULL)
- return((xmlNodePtr) ctxt->context->doc);
- if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
- ((ctxt->context->node->parent->name[0] == ' ') ||
- (xmlStrEqual(ctxt->context->node->parent->name,
- BAD_CAST "fake node libxslt"))))
- return(NULL);
- return(ctxt->context->node->parent);
- case XML_ATTRIBUTE_NODE: {
- xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
- return(tmp->parent);
- }
- case XML_DOCUMENT_NODE:
- case XML_DOCUMENT_TYPE_NODE:
- case XML_DOCUMENT_FRAG_NODE:
- case XML_HTML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- return(NULL);
- case XML_NAMESPACE_DECL: {
- xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
- if ((ns->next != NULL) &&
- (ns->next->type != XML_NAMESPACE_DECL))
- return((xmlNodePtr) ns->next);
- /* Bad, how did that namespace end up here ? */
- return(NULL);
- }
- }
- return(NULL);
- }
- if (cur == ctxt->context->doc->children)
- return((xmlNodePtr) ctxt->context->doc);
- if (cur == (xmlNodePtr) ctxt->context->doc)
- return(NULL);
- switch (cur->type) {
- case XML_ELEMENT_NODE:
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_ENTITY_REF_NODE:
- case XML_ENTITY_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- case XML_NOTATION_NODE:
- case XML_DTD_NODE:
- case XML_ELEMENT_DECL:
- case XML_ATTRIBUTE_DECL:
- case XML_ENTITY_DECL:
- case XML_XINCLUDE_START:
- case XML_XINCLUDE_END:
- if (cur->parent == NULL)
- return(NULL);
- if ((cur->parent->type == XML_ELEMENT_NODE) &&
- ((cur->parent->name[0] == ' ') ||
- (xmlStrEqual(cur->parent->name,
- BAD_CAST "fake node libxslt"))))
- return(NULL);
- return(cur->parent);
- case XML_ATTRIBUTE_NODE: {
- xmlAttrPtr att = (xmlAttrPtr) cur;
- return(att->parent);
- }
- case XML_NAMESPACE_DECL: {
- xmlNsPtr ns = (xmlNsPtr) cur;
- if ((ns->next != NULL) &&
- (ns->next->type != XML_NAMESPACE_DECL))
- return((xmlNodePtr) ns->next);
- /* Bad, how did that namespace end up here ? */
- return(NULL);
- }
- case XML_DOCUMENT_NODE:
- case XML_DOCUMENT_TYPE_NODE:
- case XML_DOCUMENT_FRAG_NODE:
- case XML_HTML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- return(NULL);
- }
- return(NULL);
- }
- /**
- * xmlXPathNextAncestorOrSelf:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "ancestor-or-self" direction
- * he ancestor-or-self axis contains the context node and ancestors of
- * the context node in reverse document order; thus the context node is
- * the first node on the axis, and the context node's parent the second;
- * parent here is defined the same as with the parent axis.
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL)
- return(ctxt->context->node);
- return(xmlXPathNextAncestor(ctxt, cur));
- }
- /**
- * xmlXPathNextFollowingSibling:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "following-sibling" direction
- * The following-sibling axis contains the following siblings of the context
- * node in document order.
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
- (ctxt->context->node->type == XML_NAMESPACE_DECL))
- return(NULL);
- if (cur == (xmlNodePtr) ctxt->context->doc)
- return(NULL);
- if (cur == NULL)
- return(ctxt->context->node->next);
- return(cur->next);
- }
- /**
- * xmlXPathNextPrecedingSibling:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "preceding-sibling" direction
- * The preceding-sibling axis contains the preceding siblings of the context
- * node in reverse document order; the first preceding sibling is first on the
- * axis; the sibling preceding that node is the second on the axis and so on.
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
- (ctxt->context->node->type == XML_NAMESPACE_DECL))
- return(NULL);
- if (cur == (xmlNodePtr) ctxt->context->doc)
- return(NULL);
- if (cur == NULL)
- return(ctxt->context->node->prev);
- if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
- cur = cur->prev;
- if (cur == NULL)
- return(ctxt->context->node->prev);
- }
- return(cur->prev);
- }
- /**
- * xmlXPathNextFollowing:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "following" direction
- * The following axis contains all nodes in the same document as the context
- * node that are after the context node in document order, excluding any
- * descendants and excluding attribute nodes and namespace nodes; the nodes
- * are ordered in document order
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if ((cur != NULL) && (cur->type != XML_ATTRIBUTE_NODE) &&
- (cur->type != XML_NAMESPACE_DECL) && (cur->children != NULL))
- return(cur->children);
- if (cur == NULL) {
- cur = ctxt->context->node;
- if (cur->type == XML_ATTRIBUTE_NODE) {
- cur = cur->parent;
- } else if (cur->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) cur;
- if ((ns->next == NULL) ||
- (ns->next->type == XML_NAMESPACE_DECL))
- return (NULL);
- cur = (xmlNodePtr) ns->next;
- }
- }
- if (cur == NULL) return(NULL) ; /* ERROR */
- if (cur->next != NULL) return(cur->next) ;
- do {
- cur = cur->parent;
- if (cur == NULL) break;
- if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
- if (cur->next != NULL) return(cur->next);
- } while (cur != NULL);
- return(cur);
- }
- /*
- * xmlXPathIsAncestor:
- * @ancestor: the ancestor node
- * @node: the current node
- *
- * Check that @ancestor is a @node's ancestor
- *
- * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
- */
- static int
- xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
- if ((ancestor == NULL) || (node == NULL)) return(0);
- if (node->type == XML_NAMESPACE_DECL)
- return(0);
- if (ancestor->type == XML_NAMESPACE_DECL)
- return(0);
- /* nodes need to be in the same document */
- if (ancestor->doc != node->doc) return(0);
- /* avoid searching if ancestor or node is the root node */
- if (ancestor == (xmlNodePtr) node->doc) return(1);
- if (node == (xmlNodePtr) ancestor->doc) return(0);
- while (node->parent != NULL) {
- if (node->parent == ancestor)
- return(1);
- node = node->parent;
- }
- return(0);
- }
- /**
- * xmlXPathNextPreceding:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "preceding" direction
- * the preceding axis contains all nodes in the same document as the context
- * node that are before the context node in document order, excluding any
- * ancestors and excluding attribute nodes and namespace nodes; the nodes are
- * ordered in reverse document order
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
- {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL) {
- cur = ctxt->context->node;
- if (cur->type == XML_ATTRIBUTE_NODE) {
- cur = cur->parent;
- } else if (cur->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) cur;
- if ((ns->next == NULL) ||
- (ns->next->type == XML_NAMESPACE_DECL))
- return (NULL);
- cur = (xmlNodePtr) ns->next;
- }
- }
- if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
- return (NULL);
- if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
- cur = cur->prev;
- do {
- if (cur->prev != NULL) {
- for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
- return (cur);
- }
- cur = cur->parent;
- if (cur == NULL)
- return (NULL);
- if (cur == ctxt->context->doc->children)
- return (NULL);
- } while (xmlXPathIsAncestor(cur, ctxt->context->node));
- return (cur);
- }
- /**
- * xmlXPathNextPrecedingInternal:
- * @ctxt: the XPath Parser context
- * @cur: the current node in the traversal
- *
- * Traversal function for the "preceding" direction
- * the preceding axis contains all nodes in the same document as the context
- * node that are before the context node in document order, excluding any
- * ancestors and excluding attribute nodes and namespace nodes; the nodes are
- * ordered in reverse document order
- * This is a faster implementation but internal only since it requires a
- * state kept in the parser context: ctxt->ancestor.
- *
- * Returns the next element following that axis
- */
- static xmlNodePtr
- xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
- xmlNodePtr cur)
- {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (cur == NULL) {
- cur = ctxt->context->node;
- if (cur == NULL)
- return (NULL);
- if (cur->type == XML_ATTRIBUTE_NODE) {
- cur = cur->parent;
- } else if (cur->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) cur;
- if ((ns->next == NULL) ||
- (ns->next->type == XML_NAMESPACE_DECL))
- return (NULL);
- cur = (xmlNodePtr) ns->next;
- }
- ctxt->ancestor = cur->parent;
- }
- if (cur->type == XML_NAMESPACE_DECL)
- return(NULL);
- if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
- cur = cur->prev;
- while (cur->prev == NULL) {
- cur = cur->parent;
- if (cur == NULL)
- return (NULL);
- if (cur == ctxt->context->doc->children)
- return (NULL);
- if (cur != ctxt->ancestor)
- return (cur);
- ctxt->ancestor = cur->parent;
- }
- cur = cur->prev;
- while (cur->last != NULL)
- cur = cur->last;
- return (cur);
- }
- /**
- * xmlXPathNextNamespace:
- * @ctxt: the XPath Parser context
- * @cur: the current attribute in the traversal
- *
- * Traversal function for the "namespace" direction
- * the namespace axis contains the namespace nodes of the context node;
- * the order of nodes on this axis is implementation-defined; the axis will
- * be empty unless the context node is an element
- *
- * We keep the XML namespace node at the end of the list.
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
- if (cur == NULL) {
- if (ctxt->context->tmpNsList != NULL)
- xmlFree(ctxt->context->tmpNsList);
- ctxt->context->tmpNsList =
- xmlGetNsList(ctxt->context->doc, ctxt->context->node);
- ctxt->context->tmpNsNr = 0;
- if (ctxt->context->tmpNsList != NULL) {
- while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
- ctxt->context->tmpNsNr++;
- }
- }
- return((xmlNodePtr) xmlXPathXMLNamespace);
- }
- if (ctxt->context->tmpNsNr > 0) {
- return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
- } else {
- if (ctxt->context->tmpNsList != NULL)
- xmlFree(ctxt->context->tmpNsList);
- ctxt->context->tmpNsList = NULL;
- return(NULL);
- }
- }
- /**
- * xmlXPathNextAttribute:
- * @ctxt: the XPath Parser context
- * @cur: the current attribute in the traversal
- *
- * Traversal function for the "attribute" direction
- * TODO: support DTD inherited default attributes
- *
- * Returns the next element following that axis
- */
- xmlNodePtr
- xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
- if (ctxt->context->node == NULL)
- return(NULL);
- if (ctxt->context->node->type != XML_ELEMENT_NODE)
- return(NULL);
- if (cur == NULL) {
- if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
- return(NULL);
- return((xmlNodePtr)ctxt->context->node->properties);
- }
- return((xmlNodePtr)cur->next);
- }
- /************************************************************************
- * *
- * NodeTest Functions *
- * *
- ************************************************************************/
- #define IS_FUNCTION 200
- /************************************************************************
- * *
- * Implicit tree core function library *
- * *
- ************************************************************************/
- /**
- * xmlXPathRoot:
- * @ctxt: the XPath Parser context
- *
- * Initialize the context to the root of the document
- */
- void
- xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
- if ((ctxt == NULL) || (ctxt->context == NULL))
- return;
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- (xmlNodePtr) ctxt->context->doc));
- }
- /************************************************************************
- * *
- * The explicit core function library *
- *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
- * *
- ************************************************************************/
- /**
- * xmlXPathLastFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the last() XPath function
- * number last()
- * The last function returns the number of nodes in the context node list.
- */
- void
- xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(0);
- if (ctxt->context->contextSize >= 0) {
- valuePush(ctxt,
- xmlXPathCacheNewFloat(ctxt->context,
- (double) ctxt->context->contextSize));
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext,
- "last() : %d\n", ctxt->context->contextSize);
- #endif
- } else {
- XP_ERROR(XPATH_INVALID_CTXT_SIZE);
- }
- }
- /**
- * xmlXPathPositionFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the position() XPath function
- * number position()
- * The position function returns the position of the context node in the
- * context node list. The first position is 1, and so the last position
- * will be equal to last().
- */
- void
- xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(0);
- if (ctxt->context->proximityPosition >= 0) {
- valuePush(ctxt,
- xmlXPathCacheNewFloat(ctxt->context,
- (double) ctxt->context->proximityPosition));
- #ifdef DEBUG_EXPR
- xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
- ctxt->context->proximityPosition);
- #endif
- } else {
- XP_ERROR(XPATH_INVALID_CTXT_POSITION);
- }
- }
- /**
- * xmlXPathCountFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the count() XPath function
- * number count(node-set)
- */
- void
- xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- CHECK_ARITY(1);
- if ((ctxt->value == NULL) ||
- ((ctxt->value->type != XPATH_NODESET) &&
- (ctxt->value->type != XPATH_XSLT_TREE)))
- XP_ERROR(XPATH_INVALID_TYPE);
- cur = valuePop(ctxt);
- if ((cur == NULL) || (cur->nodesetval == NULL))
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
- else
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
- (double) cur->nodesetval->nodeNr));
- xmlXPathReleaseObject(ctxt->context, cur);
- }
- /**
- * xmlXPathGetElementsByIds:
- * @doc: the document
- * @ids: a whitespace separated list of IDs
- *
- * Selects elements by their unique ID.
- *
- * Returns a node-set of selected elements.
- */
- static xmlNodeSetPtr
- xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
- xmlNodeSetPtr ret;
- const xmlChar *cur = ids;
- xmlChar *ID;
- xmlAttrPtr attr;
- xmlNodePtr elem = NULL;
- if (ids == NULL) return(NULL);
- ret = xmlXPathNodeSetCreate(NULL);
- if (ret == NULL)
- return(ret);
- while (IS_BLANK_CH(*cur)) cur++;
- while (*cur != 0) {
- while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
- cur++;
- ID = xmlStrndup(ids, cur - ids);
- if (ID != NULL) {
- /*
- * We used to check the fact that the value passed
- * was an NCName, but this generated much troubles for
- * me and Aleksey Sanin, people blatantly violated that
- * constraint, like Visa3D spec.
- * if (xmlValidateNCName(ID, 1) == 0)
- */
- attr = xmlGetID(doc, ID);
- if (attr != NULL) {
- if (attr->type == XML_ATTRIBUTE_NODE)
- elem = attr->parent;
- else if (attr->type == XML_ELEMENT_NODE)
- elem = (xmlNodePtr) attr;
- else
- elem = NULL;
- /* TODO: Check memory error. */
- if (elem != NULL)
- xmlXPathNodeSetAdd(ret, elem);
- }
- xmlFree(ID);
- }
- while (IS_BLANK_CH(*cur)) cur++;
- ids = cur;
- }
- return(ret);
- }
- /**
- * xmlXPathIdFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the id() XPath function
- * node-set id(object)
- * The id function selects elements by their unique ID
- * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
- * then the result is the union of the result of applying id to the
- * string value of each of the nodes in the argument node-set. When the
- * argument to id is of any other type, the argument is converted to a
- * string as if by a call to the string function; the string is split
- * into a whitespace-separated list of tokens (whitespace is any sequence
- * of characters matching the production S); the result is a node-set
- * containing the elements in the same document as the context node that
- * have a unique ID equal to any of the tokens in the list.
- */
- void
- xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlChar *tokens;
- xmlNodeSetPtr ret;
- xmlXPathObjectPtr obj;
- CHECK_ARITY(1);
- obj = valuePop(ctxt);
- if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
- if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
- xmlNodeSetPtr ns;
- int i;
- /* TODO: Check memory error. */
- ret = xmlXPathNodeSetCreate(NULL);
- if (obj->nodesetval != NULL) {
- for (i = 0; i < obj->nodesetval->nodeNr; i++) {
- tokens =
- xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
- ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
- /* TODO: Check memory error. */
- ret = xmlXPathNodeSetMerge(ret, ns);
- xmlXPathFreeNodeSet(ns);
- if (tokens != NULL)
- xmlFree(tokens);
- }
- }
- xmlXPathReleaseObject(ctxt->context, obj);
- valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
- return;
- }
- obj = xmlXPathCacheConvertString(ctxt->context, obj);
- if (obj == NULL) return;
- ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
- valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
- xmlXPathReleaseObject(ctxt->context, obj);
- return;
- }
- /**
- * xmlXPathLocalNameFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the local-name() XPath function
- * string local-name(node-set?)
- * The local-name function returns a string containing the local part
- * of the name of the node in the argument node-set that is first in
- * document order. If the node-set is empty or the first node has no
- * name, an empty string is returned. If the argument is omitted it
- * defaults to the context node.
- */
- void
- xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- if (ctxt == NULL) return;
- if (nargs == 0) {
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node));
- nargs = 1;
- }
- CHECK_ARITY(1);
- if ((ctxt->value == NULL) ||
- ((ctxt->value->type != XPATH_NODESET) &&
- (ctxt->value->type != XPATH_XSLT_TREE)))
- XP_ERROR(XPATH_INVALID_TYPE);
- cur = valuePop(ctxt);
- if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- } else {
- int i = 0; /* Should be first in document order !!!!! */
- switch (cur->nodesetval->nodeTab[i]->type) {
- case XML_ELEMENT_NODE:
- case XML_ATTRIBUTE_NODE:
- case XML_PI_NODE:
- if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- else
- valuePush(ctxt,
- xmlXPathCacheNewString(ctxt->context,
- cur->nodesetval->nodeTab[i]->name));
- break;
- case XML_NAMESPACE_DECL:
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
- ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
- break;
- default:
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- }
- }
- xmlXPathReleaseObject(ctxt->context, cur);
- }
- /**
- * xmlXPathNamespaceURIFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the namespace-uri() XPath function
- * string namespace-uri(node-set?)
- * The namespace-uri function returns a string containing the
- * namespace URI of the expanded name of the node in the argument
- * node-set that is first in document order. If the node-set is empty,
- * the first node has no name, or the expanded name has no namespace
- * URI, an empty string is returned. If the argument is omitted it
- * defaults to the context node.
- */
- void
- xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- if (ctxt == NULL) return;
- if (nargs == 0) {
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node));
- nargs = 1;
- }
- CHECK_ARITY(1);
- if ((ctxt->value == NULL) ||
- ((ctxt->value->type != XPATH_NODESET) &&
- (ctxt->value->type != XPATH_XSLT_TREE)))
- XP_ERROR(XPATH_INVALID_TYPE);
- cur = valuePop(ctxt);
- if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- } else {
- int i = 0; /* Should be first in document order !!!!! */
- switch (cur->nodesetval->nodeTab[i]->type) {
- case XML_ELEMENT_NODE:
- case XML_ATTRIBUTE_NODE:
- if (cur->nodesetval->nodeTab[i]->ns == NULL)
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- else
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
- cur->nodesetval->nodeTab[i]->ns->href));
- break;
- default:
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- }
- }
- xmlXPathReleaseObject(ctxt->context, cur);
- }
- /**
- * xmlXPathNameFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the name() XPath function
- * string name(node-set?)
- * The name function returns a string containing a QName representing
- * the name of the node in the argument node-set that is first in document
- * order. The QName must represent the name with respect to the namespace
- * declarations in effect on the node whose name is being represented.
- * Typically, this will be the form in which the name occurred in the XML
- * source. This need not be the case if there are namespace declarations
- * in effect on the node that associate multiple prefixes with the same
- * namespace. However, an implementation may include information about
- * the original prefix in its representation of nodes; in this case, an
- * implementation can ensure that the returned string is always the same
- * as the QName used in the XML source. If the argument it omitted it
- * defaults to the context node.
- * Libxml keep the original prefix so the "real qualified name" used is
- * returned.
- */
- static void
- xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
- {
- xmlXPathObjectPtr cur;
- if (nargs == 0) {
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node));
- nargs = 1;
- }
- CHECK_ARITY(1);
- if ((ctxt->value == NULL) ||
- ((ctxt->value->type != XPATH_NODESET) &&
- (ctxt->value->type != XPATH_XSLT_TREE)))
- XP_ERROR(XPATH_INVALID_TYPE);
- cur = valuePop(ctxt);
- if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- } else {
- int i = 0; /* Should be first in document order !!!!! */
- switch (cur->nodesetval->nodeTab[i]->type) {
- case XML_ELEMENT_NODE:
- case XML_ATTRIBUTE_NODE:
- if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
- valuePush(ctxt,
- xmlXPathCacheNewCString(ctxt->context, ""));
- else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
- (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
- valuePush(ctxt,
- xmlXPathCacheNewString(ctxt->context,
- cur->nodesetval->nodeTab[i]->name));
- } else {
- xmlChar *fullname;
- fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
- cur->nodesetval->nodeTab[i]->ns->prefix,
- NULL, 0);
- if (fullname == cur->nodesetval->nodeTab[i]->name)
- fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
- if (fullname == NULL) {
- XP_ERROR(XPATH_MEMORY_ERROR);
- }
- valuePush(ctxt, xmlXPathCacheWrapString(
- ctxt->context, fullname));
- }
- break;
- default:
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- cur->nodesetval->nodeTab[i]));
- xmlXPathLocalNameFunction(ctxt, 1);
- }
- }
- xmlXPathReleaseObject(ctxt->context, cur);
- }
- /**
- * xmlXPathStringFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the string() XPath function
- * string string(object?)
- * The string function converts an object to a string as follows:
- * - A node-set is converted to a string by returning the value of
- * the node in the node-set that is first in document order.
- * If the node-set is empty, an empty string is returned.
- * - A number is converted to a string as follows
- * + NaN is converted to the string NaN
- * + positive zero is converted to the string 0
- * + negative zero is converted to the string 0
- * + positive infinity is converted to the string Infinity
- * + negative infinity is converted to the string -Infinity
- * + if the number is an integer, the number is represented in
- * decimal form as a Number with no decimal point and no leading
- * zeros, preceded by a minus sign (-) if the number is negative
- * + otherwise, the number is represented in decimal form as a
- * Number including a decimal point with at least one digit
- * before the decimal point and at least one digit after the
- * decimal point, preceded by a minus sign (-) if the number
- * is negative; there must be no leading zeros before the decimal
- * point apart possibly from the one required digit immediately
- * before the decimal point; beyond the one required digit
- * after the decimal point there must be as many, but only as
- * many, more digits as are needed to uniquely distinguish the
- * number from all other IEEE 754 numeric values.
- * - The boolean false value is converted to the string false.
- * The boolean true value is converted to the string true.
- *
- * If the argument is omitted, it defaults to a node-set with the
- * context node as its only member.
- */
- void
- xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- if (ctxt == NULL) return;
- if (nargs == 0) {
- valuePush(ctxt,
- xmlXPathCacheWrapString(ctxt->context,
- xmlXPathCastNodeToString(ctxt->context->node)));
- return;
- }
- CHECK_ARITY(1);
- cur = valuePop(ctxt);
- if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
- valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur));
- }
- /**
- * xmlXPathStringLengthFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the string-length() XPath function
- * number string-length(string?)
- * The string-length returns the number of characters in the string
- * (see [3.6 Strings]). If the argument is omitted, it defaults to
- * the context node converted to a string, in other words the value
- * of the context node.
- */
- void
- xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- if (nargs == 0) {
- if ((ctxt == NULL) || (ctxt->context == NULL))
- return;
- if (ctxt->context->node == NULL) {
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0));
- } else {
- xmlChar *content;
- content = xmlXPathCastNodeToString(ctxt->context->node);
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
- xmlUTF8Strlen(content)));
- xmlFree(content);
- }
- return;
- }
- CHECK_ARITY(1);
- CAST_TO_STRING;
- CHECK_TYPE(XPATH_STRING);
- cur = valuePop(ctxt);
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
- xmlUTF8Strlen(cur->stringval)));
- xmlXPathReleaseObject(ctxt->context, cur);
- }
- /**
- * xmlXPathConcatFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the concat() XPath function
- * string concat(string, string, string*)
- * The concat function returns the concatenation of its arguments.
- */
- void
- xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur, newobj;
- xmlChar *tmp;
- if (ctxt == NULL) return;
- if (nargs < 2) {
- CHECK_ARITY(2);
- }
- CAST_TO_STRING;
- cur = valuePop(ctxt);
- if ((cur == NULL) || (cur->type != XPATH_STRING)) {
- xmlXPathReleaseObject(ctxt->context, cur);
- return;
- }
- nargs--;
- while (nargs > 0) {
- CAST_TO_STRING;
- newobj = valuePop(ctxt);
- if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
- xmlXPathReleaseObject(ctxt->context, newobj);
- xmlXPathReleaseObject(ctxt->context, cur);
- XP_ERROR(XPATH_INVALID_TYPE);
- }
- tmp = xmlStrcat(newobj->stringval, cur->stringval);
- newobj->stringval = cur->stringval;
- cur->stringval = tmp;
- xmlXPathReleaseObject(ctxt->context, newobj);
- nargs--;
- }
- valuePush(ctxt, cur);
- }
- /**
- * xmlXPathContainsFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the contains() XPath function
- * boolean contains(string, string)
- * The contains function returns true if the first argument string
- * contains the second argument string, and otherwise returns false.
- */
- void
- xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr hay, needle;
- CHECK_ARITY(2);
- CAST_TO_STRING;
- CHECK_TYPE(XPATH_STRING);
- needle = valuePop(ctxt);
- CAST_TO_STRING;
- hay = valuePop(ctxt);
- if ((hay == NULL) || (hay->type != XPATH_STRING)) {
- xmlXPathReleaseObject(ctxt->context, hay);
- xmlXPathReleaseObject(ctxt->context, needle);
- XP_ERROR(XPATH_INVALID_TYPE);
- }
- if (xmlStrstr(hay->stringval, needle->stringval))
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
- else
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
- xmlXPathReleaseObject(ctxt->context, hay);
- xmlXPathReleaseObject(ctxt->context, needle);
- }
- /**
- * xmlXPathStartsWithFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the starts-with() XPath function
- * boolean starts-with(string, string)
- * The starts-with function returns true if the first argument string
- * starts with the second argument string, and otherwise returns false.
- */
- void
- xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr hay, needle;
- int n;
- CHECK_ARITY(2);
- CAST_TO_STRING;
- CHECK_TYPE(XPATH_STRING);
- needle = valuePop(ctxt);
- CAST_TO_STRING;
- hay = valuePop(ctxt);
- if ((hay == NULL) || (hay->type != XPATH_STRING)) {
- xmlXPathReleaseObject(ctxt->context, hay);
- xmlXPathReleaseObject(ctxt->context, needle);
- XP_ERROR(XPATH_INVALID_TYPE);
- }
- n = xmlStrlen(needle->stringval);
- if (xmlStrncmp(hay->stringval, needle->stringval, n))
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
- else
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
- xmlXPathReleaseObject(ctxt->context, hay);
- xmlXPathReleaseObject(ctxt->context, needle);
- }
- /**
- * xmlXPathSubstringFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the substring() XPath function
- * string substring(string, number, number?)
- * The substring function returns the substring of the first argument
- * starting at the position specified in the second argument with
- * length specified in the third argument. For example,
- * substring("12345",2,3) returns "234". If the third argument is not
- * specified, it returns the substring starting at the position specified
- * in the second argument and continuing to the end of the string. For
- * example, substring("12345",2) returns "2345". More precisely, each
- * character in the string (see [3.6 Strings]) is considered to have a
- * numeric position: the position of the first character is 1, the position
- * of the second character is 2 and so on. The returned substring contains
- * those characters for which the position of the character is greater than
- * or equal to the second argument and, if the third argument is specified,
- * less than the sum of the second and third arguments; the comparisons
- * and addition used for the above follow the standard IEEE 754 rules. Thus:
- * - substring("12345", 1.5, 2.6) returns "234"
- * - substring("12345", 0, 3) returns "12"
- * - substring("12345", 0 div 0, 3) returns ""
- * - substring("12345", 1, 0 div 0) returns ""
- * - substring("12345", -42, 1 div 0) returns "12345"
- * - substring("12345", -1 div 0, 1 div 0) returns ""
- */
- void
- xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr str, start, len;
- double le=0, in;
- int i = 1, j = INT_MAX;
- if (nargs < 2) {
- CHECK_ARITY(2);
- }
- if (nargs > 3) {
- CHECK_ARITY(3);
- }
- /*
- * take care of possible last (position) argument
- */
- if (nargs == 3) {
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- len = valuePop(ctxt);
- le = len->floatval;
- xmlXPathReleaseObject(ctxt->context, len);
- }
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- start = valuePop(ctxt);
- in = start->floatval;
- xmlXPathReleaseObject(ctxt->context, start);
- CAST_TO_STRING;
- CHECK_TYPE(XPATH_STRING);
- str = valuePop(ctxt);
- if (!(in < INT_MAX)) { /* Logical NOT to handle NaNs */
- i = INT_MAX;
- } else if (in >= 1.0) {
- i = (int)in;
- if (in - floor(in) >= 0.5)
- i += 1;
- }
- if (nargs == 3) {
- double rin, rle, end;
- rin = floor(in);
- if (in - rin >= 0.5)
- rin += 1.0;
- rle = floor(le);
- if (le - rle >= 0.5)
- rle += 1.0;
- end = rin + rle;
- if (!(end >= 1.0)) { /* Logical NOT to handle NaNs */
- j = 1;
- } else if (end < INT_MAX) {
- j = (int)end;
- }
- }
- if (i < j) {
- xmlChar *ret = xmlUTF8Strsub(str->stringval, i - 1, j - i);
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret));
- xmlFree(ret);
- } else {
- valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
- }
- xmlXPathReleaseObject(ctxt->context, str);
- }
- /**
- * xmlXPathSubstringBeforeFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the substring-before() XPath function
- * string substring-before(string, string)
- * The substring-before function returns the substring of the first
- * argument string that precedes the first occurrence of the second
- * argument string in the first argument string, or the empty string
- * if the first argument string does not contain the second argument
- * string. For example, substring-before("1999/04/01","/") returns 1999.
- */
- void
- xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr str;
- xmlXPathObjectPtr find;
- xmlBufPtr target;
- const xmlChar *point;
- int offset;
- CHECK_ARITY(2);
- CAST_TO_STRING;
- find = valuePop(ctxt);
- CAST_TO_STRING;
- str = valuePop(ctxt);
- target = xmlBufCreate();
- if (target) {
- point = xmlStrstr(str->stringval, find->stringval);
- if (point) {
- offset = (int)(point - str->stringval);
- xmlBufAdd(target, str->stringval, offset);
- }
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
- xmlBufContent(target)));
- xmlBufFree(target);
- }
- xmlXPathReleaseObject(ctxt->context, str);
- xmlXPathReleaseObject(ctxt->context, find);
- }
- /**
- * xmlXPathSubstringAfterFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the substring-after() XPath function
- * string substring-after(string, string)
- * The substring-after function returns the substring of the first
- * argument string that follows the first occurrence of the second
- * argument string in the first argument string, or the empty stringi
- * if the first argument string does not contain the second argument
- * string. For example, substring-after("1999/04/01","/") returns 04/01,
- * and substring-after("1999/04/01","19") returns 99/04/01.
- */
- void
- xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr str;
- xmlXPathObjectPtr find;
- xmlBufPtr target;
- const xmlChar *point;
- int offset;
- CHECK_ARITY(2);
- CAST_TO_STRING;
- find = valuePop(ctxt);
- CAST_TO_STRING;
- str = valuePop(ctxt);
- target = xmlBufCreate();
- if (target) {
- point = xmlStrstr(str->stringval, find->stringval);
- if (point) {
- offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
- xmlBufAdd(target, &str->stringval[offset],
- xmlStrlen(str->stringval) - offset);
- }
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
- xmlBufContent(target)));
- xmlBufFree(target);
- }
- xmlXPathReleaseObject(ctxt->context, str);
- xmlXPathReleaseObject(ctxt->context, find);
- }
- /**
- * xmlXPathNormalizeFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the normalize-space() XPath function
- * string normalize-space(string?)
- * The normalize-space function returns the argument string with white
- * space normalized by stripping leading and trailing whitespace
- * and replacing sequences of whitespace characters by a single
- * space. Whitespace characters are the same allowed by the S production
- * in XML. If the argument is omitted, it defaults to the context
- * node converted to a string, in other words the value of the context node.
- */
- void
- xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr obj = NULL;
- xmlChar *source = NULL;
- xmlBufPtr target;
- xmlChar blank;
- if (ctxt == NULL) return;
- if (nargs == 0) {
- /* Use current context node */
- valuePush(ctxt,
- xmlXPathCacheWrapString(ctxt->context,
- xmlXPathCastNodeToString(ctxt->context->node)));
- nargs = 1;
- }
- CHECK_ARITY(1);
- CAST_TO_STRING;
- CHECK_TYPE(XPATH_STRING);
- obj = valuePop(ctxt);
- source = obj->stringval;
- target = xmlBufCreate();
- if (target && source) {
- /* Skip leading whitespaces */
- while (IS_BLANK_CH(*source))
- source++;
- /* Collapse intermediate whitespaces, and skip trailing whitespaces */
- blank = 0;
- while (*source) {
- if (IS_BLANK_CH(*source)) {
- blank = 0x20;
- } else {
- if (blank) {
- xmlBufAdd(target, &blank, 1);
- blank = 0;
- }
- xmlBufAdd(target, source, 1);
- }
- source++;
- }
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
- xmlBufContent(target)));
- xmlBufFree(target);
- }
- xmlXPathReleaseObject(ctxt->context, obj);
- }
- /**
- * xmlXPathTranslateFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the translate() XPath function
- * string translate(string, string, string)
- * The translate function returns the first argument string with
- * occurrences of characters in the second argument string replaced
- * by the character at the corresponding position in the third argument
- * string. For example, translate("bar","abc","ABC") returns the string
- * BAr. If there is a character in the second argument string with no
- * character at a corresponding position in the third argument string
- * (because the second argument string is longer than the third argument
- * string), then occurrences of that character in the first argument
- * string are removed. For example, translate("--aaa--","abc-","ABC")
- * returns "AAA". If a character occurs more than once in second
- * argument string, then the first occurrence determines the replacement
- * character. If the third argument string is longer than the second
- * argument string, then excess characters are ignored.
- */
- void
- xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr str;
- xmlXPathObjectPtr from;
- xmlXPathObjectPtr to;
- xmlBufPtr target;
- int offset, max;
- xmlChar ch;
- const xmlChar *point;
- xmlChar *cptr;
- CHECK_ARITY(3);
- CAST_TO_STRING;
- to = valuePop(ctxt);
- CAST_TO_STRING;
- from = valuePop(ctxt);
- CAST_TO_STRING;
- str = valuePop(ctxt);
- target = xmlBufCreate();
- if (target) {
- max = xmlUTF8Strlen(to->stringval);
- for (cptr = str->stringval; (ch=*cptr); ) {
- offset = xmlUTF8Strloc(from->stringval, cptr);
- if (offset >= 0) {
- if (offset < max) {
- point = xmlUTF8Strpos(to->stringval, offset);
- if (point)
- xmlBufAdd(target, point, xmlUTF8Strsize(point, 1));
- }
- } else
- xmlBufAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
- /* Step to next character in input */
- cptr++;
- if ( ch & 0x80 ) {
- /* if not simple ascii, verify proper format */
- if ( (ch & 0xc0) != 0xc0 ) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathTranslateFunction: Invalid UTF8 string\n");
- /* not asserting an XPath error is probably better */
- break;
- }
- /* then skip over remaining bytes for this char */
- while ( (ch <<= 1) & 0x80 )
- if ( (*cptr++ & 0xc0) != 0x80 ) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathTranslateFunction: Invalid UTF8 string\n");
- /* not asserting an XPath error is probably better */
- break;
- }
- if (ch & 0x80) /* must have had error encountered */
- break;
- }
- }
- }
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
- xmlBufContent(target)));
- xmlBufFree(target);
- xmlXPathReleaseObject(ctxt->context, str);
- xmlXPathReleaseObject(ctxt->context, from);
- xmlXPathReleaseObject(ctxt->context, to);
- }
- /**
- * xmlXPathBooleanFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the boolean() XPath function
- * boolean boolean(object)
- * The boolean function converts its argument to a boolean as follows:
- * - a number is true if and only if it is neither positive or
- * negative zero nor NaN
- * - a node-set is true if and only if it is non-empty
- * - a string is true if and only if its length is non-zero
- */
- void
- xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- CHECK_ARITY(1);
- cur = valuePop(ctxt);
- if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
- cur = xmlXPathCacheConvertBoolean(ctxt->context, cur);
- valuePush(ctxt, cur);
- }
- /**
- * xmlXPathNotFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the not() XPath function
- * boolean not(boolean)
- * The not function returns true if its argument is false,
- * and false otherwise.
- */
- void
- xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(1);
- CAST_TO_BOOLEAN;
- CHECK_TYPE(XPATH_BOOLEAN);
- ctxt->value->boolval = ! ctxt->value->boolval;
- }
- /**
- * xmlXPathTrueFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the true() XPath function
- * boolean true()
- */
- void
- xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(0);
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
- }
- /**
- * xmlXPathFalseFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the false() XPath function
- * boolean false()
- */
- void
- xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(0);
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
- }
- /**
- * xmlXPathLangFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the lang() XPath function
- * boolean lang(string)
- * The lang function returns true or false depending on whether the
- * language of the context node as specified by xml:lang attributes
- * is the same as or is a sublanguage of the language specified by
- * the argument string. The language of the context node is determined
- * by the value of the xml:lang attribute on the context node, or, if
- * the context node has no xml:lang attribute, by the value of the
- * xml:lang attribute on the nearest ancestor of the context node that
- * has an xml:lang attribute. If there is no such attribute, then lang
- * returns false. If there is such an attribute, then lang returns
- * true if the attribute value is equal to the argument ignoring case,
- * or if there is some suffix starting with - such that the attribute
- * value is equal to the argument ignoring that suffix of the attribute
- * value and ignoring case.
- */
- void
- xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr val = NULL;
- const xmlChar *theLang = NULL;
- const xmlChar *lang;
- int ret = 0;
- int i;
- CHECK_ARITY(1);
- CAST_TO_STRING;
- CHECK_TYPE(XPATH_STRING);
- val = valuePop(ctxt);
- lang = val->stringval;
- theLang = xmlNodeGetLang(ctxt->context->node);
- if ((theLang != NULL) && (lang != NULL)) {
- for (i = 0;lang[i] != 0;i++)
- if (toupper(lang[i]) != toupper(theLang[i]))
- goto not_equal;
- if ((theLang[i] == 0) || (theLang[i] == '-'))
- ret = 1;
- }
- not_equal:
- if (theLang != NULL)
- xmlFree((void *)theLang);
- xmlXPathReleaseObject(ctxt->context, val);
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
- }
- /**
- * xmlXPathNumberFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the number() XPath function
- * number number(object?)
- */
- void
- xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- double res;
- if (ctxt == NULL) return;
- if (nargs == 0) {
- if (ctxt->context->node == NULL) {
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0));
- } else {
- xmlChar* content = xmlNodeGetContent(ctxt->context->node);
- res = xmlXPathStringEvalNumber(content);
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
- xmlFree(content);
- }
- return;
- }
- CHECK_ARITY(1);
- cur = valuePop(ctxt);
- valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur));
- }
- /**
- * xmlXPathSumFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the sum() XPath function
- * number sum(node-set)
- * The sum function returns the sum of the values of the nodes in
- * the argument node-set.
- */
- void
- xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr cur;
- int i;
- double res = 0.0;
- CHECK_ARITY(1);
- if ((ctxt->value == NULL) ||
- ((ctxt->value->type != XPATH_NODESET) &&
- (ctxt->value->type != XPATH_XSLT_TREE)))
- XP_ERROR(XPATH_INVALID_TYPE);
- cur = valuePop(ctxt);
- if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
- for (i = 0; i < cur->nodesetval->nodeNr; i++) {
- res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
- }
- }
- valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
- xmlXPathReleaseObject(ctxt->context, cur);
- }
- /**
- * xmlXPathFloorFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the floor() XPath function
- * number floor(number)
- * The floor function returns the largest (closest to positive infinity)
- * number that is not greater than the argument and that is an integer.
- */
- void
- xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(1);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- ctxt->value->floatval = floor(ctxt->value->floatval);
- }
- /**
- * xmlXPathCeilingFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the ceiling() XPath function
- * number ceiling(number)
- * The ceiling function returns the smallest (closest to negative infinity)
- * number that is not less than the argument and that is an integer.
- */
- void
- xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(1);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- #ifdef _AIX
- /* Work around buggy ceil() function on AIX */
- ctxt->value->floatval = copysign(ceil(ctxt->value->floatval), ctxt->value->floatval);
- #else
- ctxt->value->floatval = ceil(ctxt->value->floatval);
- #endif
- }
- /**
- * xmlXPathRoundFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the round() XPath function
- * number round(number)
- * The round function returns the number that is closest to the
- * argument and that is an integer. If there are two such numbers,
- * then the one that is closest to positive infinity is returned.
- */
- void
- xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- double f;
- CHECK_ARITY(1);
- CAST_TO_NUMBER;
- CHECK_TYPE(XPATH_NUMBER);
- f = ctxt->value->floatval;
- if ((f >= -0.5) && (f < 0.5)) {
- /* Handles negative zero. */
- ctxt->value->floatval *= 0.0;
- }
- else {
- double rounded = floor(f);
- if (f - rounded >= 0.5)
- rounded += 1.0;
- ctxt->value->floatval = rounded;
- }
- }
- /************************************************************************
- * *
- * The Parser *
- * *
- ************************************************************************/
- /*
- * a few forward declarations since we use a recursive call based
- * implementation.
- */
- static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
- static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
- static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
- static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
- static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
- int qualified);
- /**
- * xmlXPathCurrentChar:
- * @ctxt: the XPath parser context
- * @cur: pointer to the beginning of the char
- * @len: pointer to the length of the char read
- *
- * The current char value, if using UTF-8 this may actually span multiple
- * bytes in the input buffer.
- *
- * Returns the current char value and its length
- */
- static int
- xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
- unsigned char c;
- unsigned int val;
- const xmlChar *cur;
- if (ctxt == NULL)
- return(0);
- cur = ctxt->cur;
- /*
- * We are supposed to handle UTF8, check it's valid
- * From rfc2044: encoding of the Unicode values on UTF-8:
- *
- * UCS-4 range (hex.) UTF-8 octet sequence (binary)
- * 0000 0000-0000 007F 0xxxxxxx
- * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
- * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
- *
- * Check for the 0x110000 limit too
- */
- c = *cur;
- if (c & 0x80) {
- if ((cur[1] & 0xc0) != 0x80)
- goto encoding_error;
- if ((c & 0xe0) == 0xe0) {
- if ((cur[2] & 0xc0) != 0x80)
- goto encoding_error;
- if ((c & 0xf0) == 0xf0) {
- if (((c & 0xf8) != 0xf0) ||
- ((cur[3] & 0xc0) != 0x80))
- goto encoding_error;
- /* 4-byte code */
- *len = 4;
- val = (cur[0] & 0x7) << 18;
- val |= (cur[1] & 0x3f) << 12;
- val |= (cur[2] & 0x3f) << 6;
- val |= cur[3] & 0x3f;
- } else {
- /* 3-byte code */
- *len = 3;
- val = (cur[0] & 0xf) << 12;
- val |= (cur[1] & 0x3f) << 6;
- val |= cur[2] & 0x3f;
- }
- } else {
- /* 2-byte code */
- *len = 2;
- val = (cur[0] & 0x1f) << 6;
- val |= cur[1] & 0x3f;
- }
- if (!IS_CHAR(val)) {
- XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
- }
- return(val);
- } else {
- /* 1-byte code */
- *len = 1;
- return((int) *cur);
- }
- encoding_error:
- /*
- * If we detect an UTF8 error that probably means that the
- * input encoding didn't get properly advertised in the
- * declaration header. Report the error and switch the encoding
- * to ISO-Latin-1 (if you don't like this policy, just declare the
- * encoding !)
- */
- *len = 0;
- XP_ERROR0(XPATH_ENCODING_ERROR);
- }
- /**
- * xmlXPathParseNCName:
- * @ctxt: the XPath Parser context
- *
- * parse an XML namespace non qualified name.
- *
- * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
- *
- * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
- * CombiningChar | Extender
- *
- * Returns the namespace name or NULL
- */
- xmlChar *
- xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
- const xmlChar *in;
- xmlChar *ret;
- int count = 0;
- if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
- /*
- * Accelerator for simple ASCII names
- */
- in = ctxt->cur;
- if (((*in >= 0x61) && (*in <= 0x7A)) ||
- ((*in >= 0x41) && (*in <= 0x5A)) ||
- (*in == '_')) {
- in++;
- while (((*in >= 0x61) && (*in <= 0x7A)) ||
- ((*in >= 0x41) && (*in <= 0x5A)) ||
- ((*in >= 0x30) && (*in <= 0x39)) ||
- (*in == '_') || (*in == '.') ||
- (*in == '-'))
- in++;
- if ((*in == ' ') || (*in == '>') || (*in == '/') ||
- (*in == '[') || (*in == ']') || (*in == ':') ||
- (*in == '@') || (*in == '*')) {
- count = in - ctxt->cur;
- if (count == 0)
- return(NULL);
- ret = xmlStrndup(ctxt->cur, count);
- ctxt->cur = in;
- return(ret);
- }
- }
- return(xmlXPathParseNameComplex(ctxt, 0));
- }
- /**
- * xmlXPathParseQName:
- * @ctxt: the XPath Parser context
- * @prefix: a xmlChar **
- *
- * parse an XML qualified name
- *
- * [NS 5] QName ::= (Prefix ':')? LocalPart
- *
- * [NS 6] Prefix ::= NCName
- *
- * [NS 7] LocalPart ::= NCName
- *
- * Returns the function returns the local part, and prefix is updated
- * to get the Prefix if any.
- */
- static xmlChar *
- xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
- xmlChar *ret = NULL;
- *prefix = NULL;
- ret = xmlXPathParseNCName(ctxt);
- if (ret && CUR == ':') {
- *prefix = ret;
- NEXT;
- ret = xmlXPathParseNCName(ctxt);
- }
- return(ret);
- }
- /**
- * xmlXPathParseName:
- * @ctxt: the XPath Parser context
- *
- * parse an XML name
- *
- * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
- * CombiningChar | Extender
- *
- * [5] Name ::= (Letter | '_' | ':') (NameChar)*
- *
- * Returns the namespace name or NULL
- */
- xmlChar *
- xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
- const xmlChar *in;
- xmlChar *ret;
- size_t count = 0;
- if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
- /*
- * Accelerator for simple ASCII names
- */
- in = ctxt->cur;
- if (((*in >= 0x61) && (*in <= 0x7A)) ||
- ((*in >= 0x41) && (*in <= 0x5A)) ||
- (*in == '_') || (*in == ':')) {
- in++;
- while (((*in >= 0x61) && (*in <= 0x7A)) ||
- ((*in >= 0x41) && (*in <= 0x5A)) ||
- ((*in >= 0x30) && (*in <= 0x39)) ||
- (*in == '_') || (*in == '-') ||
- (*in == ':') || (*in == '.'))
- in++;
- if ((*in > 0) && (*in < 0x80)) {
- count = in - ctxt->cur;
- if (count > XML_MAX_NAME_LENGTH) {
- ctxt->cur = in;
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- ret = xmlStrndup(ctxt->cur, count);
- ctxt->cur = in;
- return(ret);
- }
- }
- return(xmlXPathParseNameComplex(ctxt, 1));
- }
- static xmlChar *
- xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
- xmlChar buf[XML_MAX_NAMELEN + 5];
- int len = 0, l;
- int c;
- /*
- * Handler for more complex cases
- */
- c = CUR_CHAR(l);
- if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
- (c == '[') || (c == ']') || (c == '@') || /* accelerators */
- (c == '*') || /* accelerators */
- (!IS_LETTER(c) && (c != '_') &&
- ((!qualified) || (c != ':')))) {
- return(NULL);
- }
- while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
- ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
- (c == '.') || (c == '-') ||
- (c == '_') || ((qualified) && (c == ':')) ||
- (IS_COMBINING(c)) ||
- (IS_EXTENDER(c)))) {
- COPY_BUF(l,buf,len,c);
- NEXTL(l);
- c = CUR_CHAR(l);
- if (len >= XML_MAX_NAMELEN) {
- /*
- * Okay someone managed to make a huge name, so he's ready to pay
- * for the processing speed.
- */
- xmlChar *buffer;
- int max = len * 2;
- if (len > XML_MAX_NAME_LENGTH) {
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- buffer = (xmlChar *) xmlMallocAtomic(max * sizeof(xmlChar));
- if (buffer == NULL) {
- XP_ERRORNULL(XPATH_MEMORY_ERROR);
- }
- memcpy(buffer, buf, len);
- while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
- (c == '.') || (c == '-') ||
- (c == '_') || ((qualified) && (c == ':')) ||
- (IS_COMBINING(c)) ||
- (IS_EXTENDER(c))) {
- if (len + 10 > max) {
- xmlChar *tmp;
- if (max > XML_MAX_NAME_LENGTH) {
- xmlFree(buffer);
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- max *= 2;
- tmp = (xmlChar *) xmlRealloc(buffer,
- max * sizeof(xmlChar));
- if (tmp == NULL) {
- xmlFree(buffer);
- XP_ERRORNULL(XPATH_MEMORY_ERROR);
- }
- buffer = tmp;
- }
- COPY_BUF(l,buffer,len,c);
- NEXTL(l);
- c = CUR_CHAR(l);
- }
- buffer[len] = 0;
- return(buffer);
- }
- }
- if (len == 0)
- return(NULL);
- return(xmlStrndup(buf, len));
- }
- #define MAX_FRAC 20
- /**
- * xmlXPathStringEvalNumber:
- * @str: A string to scan
- *
- * [30a] Float ::= Number ('e' Digits?)?
- *
- * [30] Number ::= Digits ('.' Digits?)?
- * | '.' Digits
- * [31] Digits ::= [0-9]+
- *
- * Compile a Number in the string
- * In complement of the Number expression, this function also handles
- * negative values : '-' Number.
- *
- * Returns the double value.
- */
- double
- xmlXPathStringEvalNumber(const xmlChar *str) {
- const xmlChar *cur = str;
- double ret;
- int ok = 0;
- int isneg = 0;
- int exponent = 0;
- int is_exponent_negative = 0;
- #ifdef __GNUC__
- unsigned long tmp = 0;
- double temp;
- #endif
- if (cur == NULL) return(0);
- while (IS_BLANK_CH(*cur)) cur++;
- if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
- return(xmlXPathNAN);
- }
- if (*cur == '-') {
- isneg = 1;
- cur++;
- }
- #ifdef __GNUC__
- /*
- * tmp/temp is a workaround against a gcc compiler bug
- * http://veillard.com/gcc.bug
- */
- ret = 0;
- while ((*cur >= '0') && (*cur <= '9')) {
- ret = ret * 10;
- tmp = (*cur - '0');
- ok = 1;
- cur++;
- temp = (double) tmp;
- ret = ret + temp;
- }
- #else
- ret = 0;
- while ((*cur >= '0') && (*cur <= '9')) {
- ret = ret * 10 + (*cur - '0');
- ok = 1;
- cur++;
- }
- #endif
- if (*cur == '.') {
- int v, frac = 0, max;
- double fraction = 0;
- cur++;
- if (((*cur < '0') || (*cur > '9')) && (!ok)) {
- return(xmlXPathNAN);
- }
- while (*cur == '0') {
- frac = frac + 1;
- cur++;
- }
- max = frac + MAX_FRAC;
- while (((*cur >= '0') && (*cur <= '9')) && (frac < max)) {
- v = (*cur - '0');
- fraction = fraction * 10 + v;
- frac = frac + 1;
- cur++;
- }
- fraction /= pow(10.0, frac);
- ret = ret + fraction;
- while ((*cur >= '0') && (*cur <= '9'))
- cur++;
- }
- if ((*cur == 'e') || (*cur == 'E')) {
- cur++;
- if (*cur == '-') {
- is_exponent_negative = 1;
- cur++;
- } else if (*cur == '+') {
- cur++;
- }
- while ((*cur >= '0') && (*cur <= '9')) {
- if (exponent < 1000000)
- exponent = exponent * 10 + (*cur - '0');
- cur++;
- }
- }
- while (IS_BLANK_CH(*cur)) cur++;
- if (*cur != 0) return(xmlXPathNAN);
- if (isneg) ret = -ret;
- if (is_exponent_negative) exponent = -exponent;
- ret *= pow(10.0, (double)exponent);
- return(ret);
- }
- /**
- * xmlXPathCompNumber:
- * @ctxt: the XPath Parser context
- *
- * [30] Number ::= Digits ('.' Digits?)?
- * | '.' Digits
- * [31] Digits ::= [0-9]+
- *
- * Compile a Number, then push it on the stack
- *
- */
- static void
- xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
- {
- double ret = 0.0;
- int ok = 0;
- int exponent = 0;
- int is_exponent_negative = 0;
- xmlXPathObjectPtr num;
- #ifdef __GNUC__
- unsigned long tmp = 0;
- double temp;
- #endif
- CHECK_ERROR;
- if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
- XP_ERROR(XPATH_NUMBER_ERROR);
- }
- #ifdef __GNUC__
- /*
- * tmp/temp is a workaround against a gcc compiler bug
- * http://veillard.com/gcc.bug
- */
- ret = 0;
- while ((CUR >= '0') && (CUR <= '9')) {
- ret = ret * 10;
- tmp = (CUR - '0');
- ok = 1;
- NEXT;
- temp = (double) tmp;
- ret = ret + temp;
- }
- #else
- ret = 0;
- while ((CUR >= '0') && (CUR <= '9')) {
- ret = ret * 10 + (CUR - '0');
- ok = 1;
- NEXT;
- }
- #endif
- if (CUR == '.') {
- int v, frac = 0, max;
- double fraction = 0;
- NEXT;
- if (((CUR < '0') || (CUR > '9')) && (!ok)) {
- XP_ERROR(XPATH_NUMBER_ERROR);
- }
- while (CUR == '0') {
- frac = frac + 1;
- NEXT;
- }
- max = frac + MAX_FRAC;
- while ((CUR >= '0') && (CUR <= '9') && (frac < max)) {
- v = (CUR - '0');
- fraction = fraction * 10 + v;
- frac = frac + 1;
- NEXT;
- }
- fraction /= pow(10.0, frac);
- ret = ret + fraction;
- while ((CUR >= '0') && (CUR <= '9'))
- NEXT;
- }
- if ((CUR == 'e') || (CUR == 'E')) {
- NEXT;
- if (CUR == '-') {
- is_exponent_negative = 1;
- NEXT;
- } else if (CUR == '+') {
- NEXT;
- }
- while ((CUR >= '0') && (CUR <= '9')) {
- if (exponent < 1000000)
- exponent = exponent * 10 + (CUR - '0');
- NEXT;
- }
- if (is_exponent_negative)
- exponent = -exponent;
- ret *= pow(10.0, (double) exponent);
- }
- num = xmlXPathCacheNewFloat(ctxt->context, ret);
- if (num == NULL) {
- ctxt->error = XPATH_MEMORY_ERROR;
- } else if (PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0, num,
- NULL) == -1) {
- xmlXPathReleaseObject(ctxt->context, num);
- }
- }
- /**
- * xmlXPathParseLiteral:
- * @ctxt: the XPath Parser context
- *
- * Parse a Literal
- *
- * [29] Literal ::= '"' [^"]* '"'
- * | "'" [^']* "'"
- *
- * Returns the value found or NULL in case of error
- */
- static xmlChar *
- xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
- const xmlChar *q;
- xmlChar *ret = NULL;
- if (CUR == '"') {
- NEXT;
- q = CUR_PTR;
- while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
- NEXT;
- if (!IS_CHAR_CH(CUR)) {
- XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
- } else {
- ret = xmlStrndup(q, CUR_PTR - q);
- NEXT;
- }
- } else if (CUR == '\'') {
- NEXT;
- q = CUR_PTR;
- while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
- NEXT;
- if (!IS_CHAR_CH(CUR)) {
- XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
- } else {
- ret = xmlStrndup(q, CUR_PTR - q);
- NEXT;
- }
- } else {
- XP_ERRORNULL(XPATH_START_LITERAL_ERROR);
- }
- return(ret);
- }
- /**
- * xmlXPathCompLiteral:
- * @ctxt: the XPath Parser context
- *
- * Parse a Literal and push it on the stack.
- *
- * [29] Literal ::= '"' [^"]* '"'
- * | "'" [^']* "'"
- *
- * TODO: xmlXPathCompLiteral memory allocation could be improved.
- */
- static void
- xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
- const xmlChar *q;
- xmlChar *ret = NULL;
- xmlXPathObjectPtr lit;
- if (CUR == '"') {
- NEXT;
- q = CUR_PTR;
- while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
- NEXT;
- if (!IS_CHAR_CH(CUR)) {
- XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
- } else {
- ret = xmlStrndup(q, CUR_PTR - q);
- NEXT;
- }
- } else if (CUR == '\'') {
- NEXT;
- q = CUR_PTR;
- while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
- NEXT;
- if (!IS_CHAR_CH(CUR)) {
- XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
- } else {
- ret = xmlStrndup(q, CUR_PTR - q);
- NEXT;
- }
- } else {
- XP_ERROR(XPATH_START_LITERAL_ERROR);
- }
- if (ret == NULL) return;
- lit = xmlXPathCacheNewString(ctxt->context, ret);
- if (lit == NULL) {
- ctxt->error = XPATH_MEMORY_ERROR;
- } else if (PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0, lit,
- NULL) == -1) {
- xmlXPathReleaseObject(ctxt->context, lit);
- }
- xmlFree(ret);
- }
- /**
- * xmlXPathCompVariableReference:
- * @ctxt: the XPath Parser context
- *
- * Parse a VariableReference, evaluate it and push it on the stack.
- *
- * The variable bindings consist of a mapping from variable names
- * to variable values. The value of a variable is an object, which can be
- * of any of the types that are possible for the value of an expression,
- * and may also be of additional types not specified here.
- *
- * Early evaluation is possible since:
- * The variable bindings [...] used to evaluate a subexpression are
- * always the same as those used to evaluate the containing expression.
- *
- * [36] VariableReference ::= '$' QName
- */
- static void
- xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
- xmlChar *name;
- xmlChar *prefix;
- SKIP_BLANKS;
- if (CUR != '$') {
- XP_ERROR(XPATH_VARIABLE_REF_ERROR);
- }
- NEXT;
- name = xmlXPathParseQName(ctxt, &prefix);
- if (name == NULL) {
- xmlFree(prefix);
- XP_ERROR(XPATH_VARIABLE_REF_ERROR);
- }
- ctxt->comp->last = -1;
- if (PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0, name, prefix) == -1) {
- xmlFree(prefix);
- xmlFree(name);
- }
- SKIP_BLANKS;
- if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) {
- XP_ERROR(XPATH_FORBID_VARIABLE_ERROR);
- }
- }
- /**
- * xmlXPathIsNodeType:
- * @name: a name string
- *
- * Is the name given a NodeType one.
- *
- * [38] NodeType ::= 'comment'
- * | 'text'
- * | 'processing-instruction'
- * | 'node'
- *
- * Returns 1 if true 0 otherwise
- */
- int
- xmlXPathIsNodeType(const xmlChar *name) {
- if (name == NULL)
- return(0);
- if (xmlStrEqual(name, BAD_CAST "node"))
- return(1);
- if (xmlStrEqual(name, BAD_CAST "text"))
- return(1);
- if (xmlStrEqual(name, BAD_CAST "comment"))
- return(1);
- if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
- return(1);
- return(0);
- }
- /**
- * xmlXPathCompFunctionCall:
- * @ctxt: the XPath Parser context
- *
- * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
- * [17] Argument ::= Expr
- *
- * Compile a function call, the evaluation of all arguments are
- * pushed on the stack
- */
- static void
- xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
- xmlChar *name;
- xmlChar *prefix;
- int nbargs = 0;
- int sort = 1;
- name = xmlXPathParseQName(ctxt, &prefix);
- if (name == NULL) {
- xmlFree(prefix);
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- SKIP_BLANKS;
- #ifdef DEBUG_EXPR
- if (prefix == NULL)
- xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
- name);
- else
- xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
- prefix, name);
- #endif
- if (CUR != '(') {
- xmlFree(name);
- xmlFree(prefix);
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- NEXT;
- SKIP_BLANKS;
- /*
- * Optimization for count(): we don't need the node-set to be sorted.
- */
- if ((prefix == NULL) && (name[0] == 'c') &&
- xmlStrEqual(name, BAD_CAST "count"))
- {
- sort = 0;
- }
- ctxt->comp->last = -1;
- if (CUR != ')') {
- while (CUR != 0) {
- int op1 = ctxt->comp->last;
- ctxt->comp->last = -1;
- xmlXPathCompileExpr(ctxt, sort);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlFree(name);
- xmlFree(prefix);
- return;
- }
- PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
- nbargs++;
- if (CUR == ')') break;
- if (CUR != ',') {
- xmlFree(name);
- xmlFree(prefix);
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- NEXT;
- SKIP_BLANKS;
- }
- }
- if (PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, name, prefix) == -1) {
- xmlFree(prefix);
- xmlFree(name);
- }
- NEXT;
- SKIP_BLANKS;
- }
- /**
- * xmlXPathCompPrimaryExpr:
- * @ctxt: the XPath Parser context
- *
- * [15] PrimaryExpr ::= VariableReference
- * | '(' Expr ')'
- * | Literal
- * | Number
- * | FunctionCall
- *
- * Compile a primary expression.
- */
- static void
- xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
- SKIP_BLANKS;
- if (CUR == '$') xmlXPathCompVariableReference(ctxt);
- else if (CUR == '(') {
- NEXT;
- SKIP_BLANKS;
- xmlXPathCompileExpr(ctxt, 1);
- CHECK_ERROR;
- if (CUR != ')') {
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- NEXT;
- SKIP_BLANKS;
- } else if (IS_ASCII_DIGIT(CUR) || (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
- xmlXPathCompNumber(ctxt);
- } else if ((CUR == '\'') || (CUR == '"')) {
- xmlXPathCompLiteral(ctxt);
- } else {
- xmlXPathCompFunctionCall(ctxt);
- }
- SKIP_BLANKS;
- }
- /**
- * xmlXPathCompFilterExpr:
- * @ctxt: the XPath Parser context
- *
- * [20] FilterExpr ::= PrimaryExpr
- * | FilterExpr Predicate
- *
- * Compile a filter expression.
- * Square brackets are used to filter expressions in the same way that
- * they are used in location paths. It is an error if the expression to
- * be filtered does not evaluate to a node-set. The context node list
- * used for evaluating the expression in square brackets is the node-set
- * to be filtered listed in document order.
- */
- static void
- xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompPrimaryExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while (CUR == '[') {
- xmlXPathCompPredicate(ctxt, 1);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathScanName:
- * @ctxt: the XPath Parser context
- *
- * Trickery: parse an XML name but without consuming the input flow
- * Needed to avoid insanity in the parser state.
- *
- * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
- * CombiningChar | Extender
- *
- * [5] Name ::= (Letter | '_' | ':') (NameChar)*
- *
- * [6] Names ::= Name (S Name)*
- *
- * Returns the Name parsed or NULL
- */
- static xmlChar *
- xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
- int len = 0, l;
- int c;
- const xmlChar *cur;
- xmlChar *ret;
- cur = ctxt->cur;
- c = CUR_CHAR(l);
- if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
- (!IS_LETTER(c) && (c != '_') &&
- (c != ':'))) {
- return(NULL);
- }
- while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
- ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
- (c == '.') || (c == '-') ||
- (c == '_') || (c == ':') ||
- (IS_COMBINING(c)) ||
- (IS_EXTENDER(c)))) {
- len += l;
- NEXTL(l);
- c = CUR_CHAR(l);
- }
- ret = xmlStrndup(cur, ctxt->cur - cur);
- ctxt->cur = cur;
- return(ret);
- }
- /**
- * xmlXPathCompPathExpr:
- * @ctxt: the XPath Parser context
- *
- * [19] PathExpr ::= LocationPath
- * | FilterExpr
- * | FilterExpr '/' RelativeLocationPath
- * | FilterExpr '//' RelativeLocationPath
- *
- * Compile a path expression.
- * The / operator and // operators combine an arbitrary expression
- * and a relative location path. It is an error if the expression
- * does not evaluate to a node-set.
- * The / operator does composition in the same way as when / is
- * used in a location path. As in location paths, // is short for
- * /descendant-or-self::node()/.
- */
- static void
- xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
- int lc = 1; /* Should we branch to LocationPath ? */
- xmlChar *name = NULL; /* we may have to preparse a name to find out */
- SKIP_BLANKS;
- if ((CUR == '$') || (CUR == '(') ||
- (IS_ASCII_DIGIT(CUR)) ||
- (CUR == '\'') || (CUR == '"') ||
- (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
- lc = 0;
- } else if (CUR == '*') {
- /* relative or absolute location path */
- lc = 1;
- } else if (CUR == '/') {
- /* relative or absolute location path */
- lc = 1;
- } else if (CUR == '@') {
- /* relative abbreviated attribute location path */
- lc = 1;
- } else if (CUR == '.') {
- /* relative abbreviated attribute location path */
- lc = 1;
- } else {
- /*
- * Problem is finding if we have a name here whether it's:
- * - a nodetype
- * - a function call in which case it's followed by '('
- * - an axis in which case it's followed by ':'
- * - a element name
- * We do an a priori analysis here rather than having to
- * maintain parsed token content through the recursive function
- * calls. This looks uglier but makes the code easier to
- * read/write/debug.
- */
- SKIP_BLANKS;
- name = xmlXPathScanName(ctxt);
- if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "PathExpr: Axis\n");
- #endif
- lc = 1;
- xmlFree(name);
- } else if (name != NULL) {
- int len =xmlStrlen(name);
- while (NXT(len) != 0) {
- if (NXT(len) == '/') {
- /* element name */
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "PathExpr: AbbrRelLocation\n");
- #endif
- lc = 1;
- break;
- } else if (IS_BLANK_CH(NXT(len))) {
- /* ignore blanks */
- ;
- } else if (NXT(len) == ':') {
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "PathExpr: AbbrRelLocation\n");
- #endif
- lc = 1;
- break;
- } else if ((NXT(len) == '(')) {
- /* Node Type or Function */
- if (xmlXPathIsNodeType(name)) {
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "PathExpr: Type search\n");
- #endif
- lc = 1;
- #ifdef LIBXML_XPTR_ENABLED
- } else if (ctxt->xptr &&
- xmlStrEqual(name, BAD_CAST "range-to")) {
- lc = 1;
- #endif
- } else {
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "PathExpr: function call\n");
- #endif
- lc = 0;
- }
- break;
- } else if ((NXT(len) == '[')) {
- /* element name */
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "PathExpr: AbbrRelLocation\n");
- #endif
- lc = 1;
- break;
- } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
- (NXT(len) == '=')) {
- lc = 1;
- break;
- } else {
- lc = 1;
- break;
- }
- len++;
- }
- if (NXT(len) == 0) {
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "PathExpr: AbbrRelLocation\n");
- #endif
- /* element name */
- lc = 1;
- }
- xmlFree(name);
- } else {
- /* make sure all cases are covered explicitly */
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- }
- if (lc) {
- if (CUR == '/') {
- PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
- } else {
- PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
- }
- xmlXPathCompLocationPath(ctxt);
- } else {
- xmlXPathCompFilterExpr(ctxt);
- CHECK_ERROR;
- if ((CUR == '/') && (NXT(1) == '/')) {
- SKIP(2);
- SKIP_BLANKS;
- PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
- NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
- xmlXPathCompRelativeLocationPath(ctxt);
- } else if (CUR == '/') {
- xmlXPathCompRelativeLocationPath(ctxt);
- }
- }
- SKIP_BLANKS;
- }
- /**
- * xmlXPathCompUnionExpr:
- * @ctxt: the XPath Parser context
- *
- * [18] UnionExpr ::= PathExpr
- * | UnionExpr '|' PathExpr
- *
- * Compile an union expression.
- */
- static void
- xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompPathExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while (CUR == '|') {
- int op1 = ctxt->comp->last;
- PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
- NEXT;
- SKIP_BLANKS;
- xmlXPathCompPathExpr(ctxt);
- PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompUnaryExpr:
- * @ctxt: the XPath Parser context
- *
- * [27] UnaryExpr ::= UnionExpr
- * | '-' UnaryExpr
- *
- * Compile an unary expression.
- */
- static void
- xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
- int minus = 0;
- int found = 0;
- SKIP_BLANKS;
- while (CUR == '-') {
- minus = 1 - minus;
- found = 1;
- NEXT;
- SKIP_BLANKS;
- }
- xmlXPathCompUnionExpr(ctxt);
- CHECK_ERROR;
- if (found) {
- if (minus)
- PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
- else
- PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
- }
- }
- /**
- * xmlXPathCompMultiplicativeExpr:
- * @ctxt: the XPath Parser context
- *
- * [26] MultiplicativeExpr ::= UnaryExpr
- * | MultiplicativeExpr MultiplyOperator UnaryExpr
- * | MultiplicativeExpr 'div' UnaryExpr
- * | MultiplicativeExpr 'mod' UnaryExpr
- * [34] MultiplyOperator ::= '*'
- *
- * Compile an Additive expression.
- */
- static void
- xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompUnaryExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '*') ||
- ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
- ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
- int op = -1;
- int op1 = ctxt->comp->last;
- if (CUR == '*') {
- op = 0;
- NEXT;
- } else if (CUR == 'd') {
- op = 1;
- SKIP(3);
- } else if (CUR == 'm') {
- op = 2;
- SKIP(3);
- }
- SKIP_BLANKS;
- xmlXPathCompUnaryExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompAdditiveExpr:
- * @ctxt: the XPath Parser context
- *
- * [25] AdditiveExpr ::= MultiplicativeExpr
- * | AdditiveExpr '+' MultiplicativeExpr
- * | AdditiveExpr '-' MultiplicativeExpr
- *
- * Compile an Additive expression.
- */
- static void
- xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompMultiplicativeExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '+') || (CUR == '-')) {
- int plus;
- int op1 = ctxt->comp->last;
- if (CUR == '+') plus = 1;
- else plus = 0;
- NEXT;
- SKIP_BLANKS;
- xmlXPathCompMultiplicativeExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompRelationalExpr:
- * @ctxt: the XPath Parser context
- *
- * [24] RelationalExpr ::= AdditiveExpr
- * | RelationalExpr '<' AdditiveExpr
- * | RelationalExpr '>' AdditiveExpr
- * | RelationalExpr '<=' AdditiveExpr
- * | RelationalExpr '>=' AdditiveExpr
- *
- * A <= B > C is allowed ? Answer from James, yes with
- * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
- * which is basically what got implemented.
- *
- * Compile a Relational expression, then push the result
- * on the stack
- */
- static void
- xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompAdditiveExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '<') || (CUR == '>')) {
- int inf, strict;
- int op1 = ctxt->comp->last;
- if (CUR == '<') inf = 1;
- else inf = 0;
- if (NXT(1) == '=') strict = 0;
- else strict = 1;
- NEXT;
- if (!strict) NEXT;
- SKIP_BLANKS;
- xmlXPathCompAdditiveExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompEqualityExpr:
- * @ctxt: the XPath Parser context
- *
- * [23] EqualityExpr ::= RelationalExpr
- * | EqualityExpr '=' RelationalExpr
- * | EqualityExpr '!=' RelationalExpr
- *
- * A != B != C is allowed ? Answer from James, yes with
- * (RelationalExpr = RelationalExpr) = RelationalExpr
- * (RelationalExpr != RelationalExpr) != RelationalExpr
- * which is basically what got implemented.
- *
- * Compile an Equality expression.
- *
- */
- static void
- xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompRelationalExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
- int eq;
- int op1 = ctxt->comp->last;
- if (CUR == '=') eq = 1;
- else eq = 0;
- NEXT;
- if (!eq) NEXT;
- SKIP_BLANKS;
- xmlXPathCompRelationalExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompAndExpr:
- * @ctxt: the XPath Parser context
- *
- * [22] AndExpr ::= EqualityExpr
- * | AndExpr 'and' EqualityExpr
- *
- * Compile an AND expression.
- *
- */
- static void
- xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompEqualityExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
- int op1 = ctxt->comp->last;
- SKIP(3);
- SKIP_BLANKS;
- xmlXPathCompEqualityExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompileExpr:
- * @ctxt: the XPath Parser context
- *
- * [14] Expr ::= OrExpr
- * [21] OrExpr ::= AndExpr
- * | OrExpr 'or' AndExpr
- *
- * Parse and compile an expression
- */
- static void
- xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
- xmlXPathContextPtr xpctxt = ctxt->context;
- if (xpctxt != NULL) {
- if (xpctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
- XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
- /*
- * Parsing a single '(' pushes about 10 functions on the call stack
- * before recursing!
- */
- xpctxt->depth += 10;
- }
- xmlXPathCompAndExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == 'o') && (NXT(1) == 'r')) {
- int op1 = ctxt->comp->last;
- SKIP(2);
- SKIP_BLANKS;
- xmlXPathCompAndExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
- SKIP_BLANKS;
- }
- if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
- /* more ops could be optimized too */
- /*
- * This is the main place to eliminate sorting for
- * operations which don't require a sorted node-set.
- * E.g. count().
- */
- PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
- }
- if (xpctxt != NULL)
- xpctxt->depth -= 10;
- }
- /**
- * xmlXPathCompPredicate:
- * @ctxt: the XPath Parser context
- * @filter: act as a filter
- *
- * [8] Predicate ::= '[' PredicateExpr ']'
- * [9] PredicateExpr ::= Expr
- *
- * Compile a predicate expression
- */
- static void
- xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
- int op1 = ctxt->comp->last;
- SKIP_BLANKS;
- if (CUR != '[') {
- XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
- }
- NEXT;
- SKIP_BLANKS;
- ctxt->comp->last = -1;
- /*
- * This call to xmlXPathCompileExpr() will deactivate sorting
- * of the predicate result.
- * TODO: Sorting is still activated for filters, since I'm not
- * sure if needed. Normally sorting should not be needed, since
- * a filter can only diminish the number of items in a sequence,
- * but won't change its order; so if the initial sequence is sorted,
- * subsequent sorting is not needed.
- */
- if (! filter)
- xmlXPathCompileExpr(ctxt, 0);
- else
- xmlXPathCompileExpr(ctxt, 1);
- CHECK_ERROR;
- if (CUR != ']') {
- XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
- }
- if (filter)
- PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
- else
- PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
- NEXT;
- SKIP_BLANKS;
- }
- /**
- * xmlXPathCompNodeTest:
- * @ctxt: the XPath Parser context
- * @test: pointer to a xmlXPathTestVal
- * @type: pointer to a xmlXPathTypeVal
- * @prefix: placeholder for a possible name prefix
- *
- * [7] NodeTest ::= NameTest
- * | NodeType '(' ')'
- * | 'processing-instruction' '(' Literal ')'
- *
- * [37] NameTest ::= '*'
- * | NCName ':' '*'
- * | QName
- * [38] NodeType ::= 'comment'
- * | 'text'
- * | 'processing-instruction'
- * | 'node'
- *
- * Returns the name found and updates @test, @type and @prefix appropriately
- */
- static xmlChar *
- xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
- xmlXPathTypeVal *type, xmlChar **prefix,
- xmlChar *name) {
- int blanks;
- if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
- STRANGE;
- return(NULL);
- }
- *type = (xmlXPathTypeVal) 0;
- *test = (xmlXPathTestVal) 0;
- *prefix = NULL;
- SKIP_BLANKS;
- if ((name == NULL) && (CUR == '*')) {
- /*
- * All elements
- */
- NEXT;
- *test = NODE_TEST_ALL;
- return(NULL);
- }
- if (name == NULL)
- name = xmlXPathParseNCName(ctxt);
- if (name == NULL) {
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- blanks = IS_BLANK_CH(CUR);
- SKIP_BLANKS;
- if (CUR == '(') {
- NEXT;
- /*
- * NodeType or PI search
- */
- if (xmlStrEqual(name, BAD_CAST "comment"))
- *type = NODE_TYPE_COMMENT;
- else if (xmlStrEqual(name, BAD_CAST "node"))
- *type = NODE_TYPE_NODE;
- else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
- *type = NODE_TYPE_PI;
- else if (xmlStrEqual(name, BAD_CAST "text"))
- *type = NODE_TYPE_TEXT;
- else {
- if (name != NULL)
- xmlFree(name);
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- *test = NODE_TEST_TYPE;
- SKIP_BLANKS;
- if (*type == NODE_TYPE_PI) {
- /*
- * Specific case: search a PI by name.
- */
- if (name != NULL)
- xmlFree(name);
- name = NULL;
- if (CUR != ')') {
- name = xmlXPathParseLiteral(ctxt);
- if (name == NULL) {
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- *test = NODE_TEST_PI;
- SKIP_BLANKS;
- }
- }
- if (CUR != ')') {
- if (name != NULL)
- xmlFree(name);
- XP_ERRORNULL(XPATH_UNCLOSED_ERROR);
- }
- NEXT;
- return(name);
- }
- *test = NODE_TEST_NAME;
- if ((!blanks) && (CUR == ':')) {
- NEXT;
- /*
- * Since currently the parser context don't have a
- * namespace list associated:
- * The namespace name for this prefix can be computed
- * only at evaluation time. The compilation is done
- * outside of any context.
- */
- #if 0
- *prefix = xmlXPathNsLookup(ctxt->context, name);
- if (name != NULL)
- xmlFree(name);
- if (*prefix == NULL) {
- XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
- }
- #else
- *prefix = name;
- #endif
- if (CUR == '*') {
- /*
- * All elements
- */
- NEXT;
- *test = NODE_TEST_ALL;
- return(NULL);
- }
- name = xmlXPathParseNCName(ctxt);
- if (name == NULL) {
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- }
- return(name);
- }
- /**
- * xmlXPathIsAxisName:
- * @name: a preparsed name token
- *
- * [6] AxisName ::= 'ancestor'
- * | 'ancestor-or-self'
- * | 'attribute'
- * | 'child'
- * | 'descendant'
- * | 'descendant-or-self'
- * | 'following'
- * | 'following-sibling'
- * | 'namespace'
- * | 'parent'
- * | 'preceding'
- * | 'preceding-sibling'
- * | 'self'
- *
- * Returns the axis or 0
- */
- static xmlXPathAxisVal
- xmlXPathIsAxisName(const xmlChar *name) {
- xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
- switch (name[0]) {
- case 'a':
- if (xmlStrEqual(name, BAD_CAST "ancestor"))
- ret = AXIS_ANCESTOR;
- if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
- ret = AXIS_ANCESTOR_OR_SELF;
- if (xmlStrEqual(name, BAD_CAST "attribute"))
- ret = AXIS_ATTRIBUTE;
- break;
- case 'c':
- if (xmlStrEqual(name, BAD_CAST "child"))
- ret = AXIS_CHILD;
- break;
- case 'd':
- if (xmlStrEqual(name, BAD_CAST "descendant"))
- ret = AXIS_DESCENDANT;
- if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
- ret = AXIS_DESCENDANT_OR_SELF;
- break;
- case 'f':
- if (xmlStrEqual(name, BAD_CAST "following"))
- ret = AXIS_FOLLOWING;
- if (xmlStrEqual(name, BAD_CAST "following-sibling"))
- ret = AXIS_FOLLOWING_SIBLING;
- break;
- case 'n':
- if (xmlStrEqual(name, BAD_CAST "namespace"))
- ret = AXIS_NAMESPACE;
- break;
- case 'p':
- if (xmlStrEqual(name, BAD_CAST "parent"))
- ret = AXIS_PARENT;
- if (xmlStrEqual(name, BAD_CAST "preceding"))
- ret = AXIS_PRECEDING;
- if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
- ret = AXIS_PRECEDING_SIBLING;
- break;
- case 's':
- if (xmlStrEqual(name, BAD_CAST "self"))
- ret = AXIS_SELF;
- break;
- }
- return(ret);
- }
- /**
- * xmlXPathCompStep:
- * @ctxt: the XPath Parser context
- *
- * [4] Step ::= AxisSpecifier NodeTest Predicate*
- * | AbbreviatedStep
- *
- * [12] AbbreviatedStep ::= '.' | '..'
- *
- * [5] AxisSpecifier ::= AxisName '::'
- * | AbbreviatedAxisSpecifier
- *
- * [13] AbbreviatedAxisSpecifier ::= '@'?
- *
- * Modified for XPtr range support as:
- *
- * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
- * | AbbreviatedStep
- * | 'range-to' '(' Expr ')' Predicate*
- *
- * Compile one step in a Location Path
- * A location step of . is short for self::node(). This is
- * particularly useful in conjunction with //. For example, the
- * location path .//para is short for
- * self::node()/descendant-or-self::node()/child::para
- * and so will select all para descendant elements of the context
- * node.
- * Similarly, a location step of .. is short for parent::node().
- * For example, ../title is short for parent::node()/child::title
- * and so will select the title children of the parent of the context
- * node.
- */
- static void
- xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
- #ifdef LIBXML_XPTR_ENABLED
- int rangeto = 0;
- int op2 = -1;
- #endif
- SKIP_BLANKS;
- if ((CUR == '.') && (NXT(1) == '.')) {
- SKIP(2);
- SKIP_BLANKS;
- PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
- NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
- } else if (CUR == '.') {
- NEXT;
- SKIP_BLANKS;
- } else {
- xmlChar *name = NULL;
- xmlChar *prefix = NULL;
- xmlXPathTestVal test = (xmlXPathTestVal) 0;
- xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
- xmlXPathTypeVal type = (xmlXPathTypeVal) 0;
- int op1;
- /*
- * The modification needed for XPointer change to the production
- */
- #ifdef LIBXML_XPTR_ENABLED
- if (ctxt->xptr) {
- name = xmlXPathParseNCName(ctxt);
- if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
- op2 = ctxt->comp->last;
- xmlFree(name);
- SKIP_BLANKS;
- if (CUR != '(') {
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- NEXT;
- SKIP_BLANKS;
- xmlXPathCompileExpr(ctxt, 1);
- /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
- CHECK_ERROR;
- SKIP_BLANKS;
- if (CUR != ')') {
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- NEXT;
- rangeto = 1;
- goto eval_predicates;
- }
- }
- #endif
- if (CUR == '*') {
- axis = AXIS_CHILD;
- } else {
- if (name == NULL)
- name = xmlXPathParseNCName(ctxt);
- if (name != NULL) {
- axis = xmlXPathIsAxisName(name);
- if (axis != 0) {
- SKIP_BLANKS;
- if ((CUR == ':') && (NXT(1) == ':')) {
- SKIP(2);
- xmlFree(name);
- name = NULL;
- } else {
- /* an element name can conflict with an axis one :-\ */
- axis = AXIS_CHILD;
- }
- } else {
- axis = AXIS_CHILD;
- }
- } else if (CUR == '@') {
- NEXT;
- axis = AXIS_ATTRIBUTE;
- } else {
- axis = AXIS_CHILD;
- }
- }
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlFree(name);
- return;
- }
- name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
- if (test == 0)
- return;
- if ((prefix != NULL) && (ctxt->context != NULL) &&
- (ctxt->context->flags & XML_XPATH_CHECKNS)) {
- if (xmlXPathNsLookup(ctxt->context, prefix) == NULL) {
- xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR);
- }
- }
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "Basis : computing new set\n");
- #endif
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext, "Basis : ");
- if (ctxt->value == NULL)
- xmlGenericError(xmlGenericErrorContext, "no value\n");
- else if (ctxt->value->nodesetval == NULL)
- xmlGenericError(xmlGenericErrorContext, "Empty\n");
- else
- xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
- #endif
- #ifdef LIBXML_XPTR_ENABLED
- eval_predicates:
- #endif
- op1 = ctxt->comp->last;
- ctxt->comp->last = -1;
- SKIP_BLANKS;
- while (CUR == '[') {
- xmlXPathCompPredicate(ctxt, 0);
- }
- #ifdef LIBXML_XPTR_ENABLED
- if (rangeto) {
- PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
- } else
- #endif
- if (PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
- test, type, (void *)prefix, (void *)name) == -1) {
- xmlFree(prefix);
- xmlFree(name);
- }
- }
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext, "Step : ");
- if (ctxt->value == NULL)
- xmlGenericError(xmlGenericErrorContext, "no value\n");
- else if (ctxt->value->nodesetval == NULL)
- xmlGenericError(xmlGenericErrorContext, "Empty\n");
- else
- xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
- ctxt->value->nodesetval);
- #endif
- }
- /**
- * xmlXPathCompRelativeLocationPath:
- * @ctxt: the XPath Parser context
- *
- * [3] RelativeLocationPath ::= Step
- * | RelativeLocationPath '/' Step
- * | AbbreviatedRelativeLocationPath
- * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
- *
- * Compile a relative location path.
- */
- static void
- xmlXPathCompRelativeLocationPath
- (xmlXPathParserContextPtr ctxt) {
- SKIP_BLANKS;
- if ((CUR == '/') && (NXT(1) == '/')) {
- SKIP(2);
- SKIP_BLANKS;
- PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
- NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
- } else if (CUR == '/') {
- NEXT;
- SKIP_BLANKS;
- }
- xmlXPathCompStep(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while (CUR == '/') {
- if ((CUR == '/') && (NXT(1) == '/')) {
- SKIP(2);
- SKIP_BLANKS;
- PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
- NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
- xmlXPathCompStep(ctxt);
- } else if (CUR == '/') {
- NEXT;
- SKIP_BLANKS;
- xmlXPathCompStep(ctxt);
- }
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompLocationPath:
- * @ctxt: the XPath Parser context
- *
- * [1] LocationPath ::= RelativeLocationPath
- * | AbsoluteLocationPath
- * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
- * | AbbreviatedAbsoluteLocationPath
- * [10] AbbreviatedAbsoluteLocationPath ::=
- * '//' RelativeLocationPath
- *
- * Compile a location path
- *
- * // is short for /descendant-or-self::node()/. For example,
- * //para is short for /descendant-or-self::node()/child::para and
- * so will select any para element in the document (even a para element
- * that is a document element will be selected by //para since the
- * document element node is a child of the root node); div//para is
- * short for div/descendant-or-self::node()/child::para and so will
- * select all para descendants of div children.
- */
- static void
- xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
- SKIP_BLANKS;
- if (CUR != '/') {
- xmlXPathCompRelativeLocationPath(ctxt);
- } else {
- while (CUR == '/') {
- if ((CUR == '/') && (NXT(1) == '/')) {
- SKIP(2);
- SKIP_BLANKS;
- PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
- NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
- xmlXPathCompRelativeLocationPath(ctxt);
- } else if (CUR == '/') {
- NEXT;
- SKIP_BLANKS;
- if ((CUR != 0 ) &&
- ((IS_ASCII_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
- (CUR == '@') || (CUR == '*')))
- xmlXPathCompRelativeLocationPath(ctxt);
- }
- CHECK_ERROR;
- }
- }
- }
- /************************************************************************
- * *
- * XPath precompiled expression evaluation *
- * *
- ************************************************************************/
- static int
- xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
- #ifdef DEBUG_STEP
- static void
- xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
- int nbNodes)
- {
- xmlGenericError(xmlGenericErrorContext, "new step : ");
- switch (op->value) {
- case AXIS_ANCESTOR:
- xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
- break;
- case AXIS_ANCESTOR_OR_SELF:
- xmlGenericError(xmlGenericErrorContext,
- "axis 'ancestors-or-self' ");
- break;
- case AXIS_ATTRIBUTE:
- xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
- break;
- case AXIS_CHILD:
- xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
- break;
- case AXIS_DESCENDANT:
- xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
- break;
- case AXIS_DESCENDANT_OR_SELF:
- xmlGenericError(xmlGenericErrorContext,
- "axis 'descendant-or-self' ");
- break;
- case AXIS_FOLLOWING:
- xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
- break;
- case AXIS_FOLLOWING_SIBLING:
- xmlGenericError(xmlGenericErrorContext,
- "axis 'following-siblings' ");
- break;
- case AXIS_NAMESPACE:
- xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
- break;
- case AXIS_PARENT:
- xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
- break;
- case AXIS_PRECEDING:
- xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
- break;
- case AXIS_PRECEDING_SIBLING:
- xmlGenericError(xmlGenericErrorContext,
- "axis 'preceding-sibling' ");
- break;
- case AXIS_SELF:
- xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
- break;
- }
- xmlGenericError(xmlGenericErrorContext,
- " context contains %d nodes\n", nbNodes);
- switch (op->value2) {
- case NODE_TEST_NONE:
- xmlGenericError(xmlGenericErrorContext,
- " searching for none !!!\n");
- break;
- case NODE_TEST_TYPE:
- xmlGenericError(xmlGenericErrorContext,
- " searching for type %d\n", op->value3);
- break;
- case NODE_TEST_PI:
- xmlGenericError(xmlGenericErrorContext,
- " searching for PI !!!\n");
- break;
- case NODE_TEST_ALL:
- xmlGenericError(xmlGenericErrorContext,
- " searching for *\n");
- break;
- case NODE_TEST_NS:
- xmlGenericError(xmlGenericErrorContext,
- " searching for namespace %s\n",
- op->value5);
- break;
- case NODE_TEST_NAME:
- xmlGenericError(xmlGenericErrorContext,
- " searching for name %s\n", op->value5);
- if (op->value4)
- xmlGenericError(xmlGenericErrorContext,
- " with namespace %s\n", op->value4);
- break;
- }
- xmlGenericError(xmlGenericErrorContext, "Testing : ");
- }
- #endif /* DEBUG_STEP */
- /**
- * xmlXPathNodeSetFilter:
- * @ctxt: the XPath Parser context
- * @set: the node set to filter
- * @filterOpIndex: the index of the predicate/filter op
- * @minPos: minimum position in the filtered set (1-based)
- * @maxPos: maximum position in the filtered set (1-based)
- * @hasNsNodes: true if the node set may contain namespace nodes
- *
- * Filter a node set, keeping only nodes for which the predicate expression
- * matches. Afterwards, keep only nodes between minPos and maxPos in the
- * filtered result.
- */
- static void
- xmlXPathNodeSetFilter(xmlXPathParserContextPtr ctxt,
- xmlNodeSetPtr set,
- int filterOpIndex,
- int minPos, int maxPos,
- int hasNsNodes)
- {
- xmlXPathContextPtr xpctxt;
- xmlNodePtr oldnode;
- xmlDocPtr olddoc;
- xmlXPathStepOpPtr filterOp;
- int oldcs, oldpp;
- int i, j, pos;
- if ((set == NULL) || (set->nodeNr == 0))
- return;
- /*
- * Check if the node set contains a sufficient number of nodes for
- * the requested range.
- */
- if (set->nodeNr < minPos) {
- xmlXPathNodeSetClear(set, hasNsNodes);
- return;
- }
- xpctxt = ctxt->context;
- oldnode = xpctxt->node;
- olddoc = xpctxt->doc;
- oldcs = xpctxt->contextSize;
- oldpp = xpctxt->proximityPosition;
- filterOp = &ctxt->comp->steps[filterOpIndex];
- xpctxt->contextSize = set->nodeNr;
- for (i = 0, j = 0, pos = 1; i < set->nodeNr; i++) {
- xmlNodePtr node = set->nodeTab[i];
- int res;
- xpctxt->node = node;
- xpctxt->proximityPosition = i + 1;
- /*
- * Also set the xpath document in case things like
- * key() are evaluated in the predicate.
- *
- * TODO: Get real doc for namespace nodes.
- */
- if ((node->type != XML_NAMESPACE_DECL) &&
- (node->doc != NULL))
- xpctxt->doc = node->doc;
- res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
- if (ctxt->error != XPATH_EXPRESSION_OK)
- break;
- if (res < 0) {
- /* Shouldn't happen */
- xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
- break;
- }
- if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
- if (i != j) {
- set->nodeTab[j] = node;
- set->nodeTab[i] = NULL;
- }
- j += 1;
- } else {
- /* Remove the entry from the initial node set. */
- set->nodeTab[i] = NULL;
- if (node->type == XML_NAMESPACE_DECL)
- xmlXPathNodeSetFreeNs((xmlNsPtr) node);
- }
- if (res != 0) {
- if (pos == maxPos) {
- i += 1;
- break;
- }
- pos += 1;
- }
- }
- /* Free remaining nodes. */
- if (hasNsNodes) {
- for (; i < set->nodeNr; i++) {
- xmlNodePtr node = set->nodeTab[i];
- if ((node != NULL) && (node->type == XML_NAMESPACE_DECL))
- xmlXPathNodeSetFreeNs((xmlNsPtr) node);
- }
- }
- set->nodeNr = j;
- /* If too many elements were removed, shrink table to preserve memory. */
- if ((set->nodeMax > XML_NODESET_DEFAULT) &&
- (set->nodeNr < set->nodeMax / 2)) {
- xmlNodePtr *tmp;
- int nodeMax = set->nodeNr;
- if (nodeMax < XML_NODESET_DEFAULT)
- nodeMax = XML_NODESET_DEFAULT;
- tmp = (xmlNodePtr *) xmlRealloc(set->nodeTab,
- nodeMax * sizeof(xmlNodePtr));
- if (tmp == NULL) {
- xmlXPathPErrMemory(ctxt, "shrinking nodeset\n");
- } else {
- set->nodeTab = tmp;
- set->nodeMax = nodeMax;
- }
- }
- xpctxt->node = oldnode;
- xpctxt->doc = olddoc;
- xpctxt->contextSize = oldcs;
- xpctxt->proximityPosition = oldpp;
- }
- #ifdef LIBXML_XPTR_ENABLED
- /**
- * xmlXPathLocationSetFilter:
- * @ctxt: the XPath Parser context
- * @locset: the location set to filter
- * @filterOpIndex: the index of the predicate/filter op
- * @minPos: minimum position in the filtered set (1-based)
- * @maxPos: maximum position in the filtered set (1-based)
- *
- * Filter a location set, keeping only nodes for which the predicate
- * expression matches. Afterwards, keep only nodes between minPos and maxPos
- * in the filtered result.
- */
- static void
- xmlXPathLocationSetFilter(xmlXPathParserContextPtr ctxt,
- xmlLocationSetPtr locset,
- int filterOpIndex,
- int minPos, int maxPos)
- {
- xmlXPathContextPtr xpctxt;
- xmlNodePtr oldnode;
- xmlDocPtr olddoc;
- xmlXPathStepOpPtr filterOp;
- int oldcs, oldpp;
- int i, j, pos;
- if ((locset == NULL) || (locset->locNr == 0) || (filterOpIndex == -1))
- return;
- xpctxt = ctxt->context;
- oldnode = xpctxt->node;
- olddoc = xpctxt->doc;
- oldcs = xpctxt->contextSize;
- oldpp = xpctxt->proximityPosition;
- filterOp = &ctxt->comp->steps[filterOpIndex];
- xpctxt->contextSize = locset->locNr;
- for (i = 0, j = 0, pos = 1; i < locset->locNr; i++) {
- xmlNodePtr contextNode = locset->locTab[i]->user;
- int res;
- xpctxt->node = contextNode;
- xpctxt->proximityPosition = i + 1;
- /*
- * Also set the xpath document in case things like
- * key() are evaluated in the predicate.
- *
- * TODO: Get real doc for namespace nodes.
- */
- if ((contextNode->type != XML_NAMESPACE_DECL) &&
- (contextNode->doc != NULL))
- xpctxt->doc = contextNode->doc;
- res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
- if (ctxt->error != XPATH_EXPRESSION_OK)
- break;
- if (res < 0) {
- /* Shouldn't happen */
- xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
- break;
- }
- if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
- if (i != j) {
- locset->locTab[j] = locset->locTab[i];
- locset->locTab[i] = NULL;
- }
- j += 1;
- } else {
- /* Remove the entry from the initial location set. */
- xmlXPathFreeObject(locset->locTab[i]);
- locset->locTab[i] = NULL;
- }
- if (res != 0) {
- if (pos == maxPos) {
- i += 1;
- break;
- }
- pos += 1;
- }
- }
- /* Free remaining nodes. */
- for (; i < locset->locNr; i++)
- xmlXPathFreeObject(locset->locTab[i]);
- locset->locNr = j;
- /* If too many elements were removed, shrink table to preserve memory. */
- if ((locset->locMax > XML_NODESET_DEFAULT) &&
- (locset->locNr < locset->locMax / 2)) {
- xmlXPathObjectPtr *tmp;
- int locMax = locset->locNr;
- if (locMax < XML_NODESET_DEFAULT)
- locMax = XML_NODESET_DEFAULT;
- tmp = (xmlXPathObjectPtr *) xmlRealloc(locset->locTab,
- locMax * sizeof(xmlXPathObjectPtr));
- if (tmp == NULL) {
- xmlXPathPErrMemory(ctxt, "shrinking locset\n");
- } else {
- locset->locTab = tmp;
- locset->locMax = locMax;
- }
- }
- xpctxt->node = oldnode;
- xpctxt->doc = olddoc;
- xpctxt->contextSize = oldcs;
- xpctxt->proximityPosition = oldpp;
- }
- #endif /* LIBXML_XPTR_ENABLED */
- /**
- * xmlXPathCompOpEvalPredicate:
- * @ctxt: the XPath Parser context
- * @op: the predicate op
- * @set: the node set to filter
- * @minPos: minimum position in the filtered set (1-based)
- * @maxPos: maximum position in the filtered set (1-based)
- * @hasNsNodes: true if the node set may contain namespace nodes
- *
- * Filter a node set, keeping only nodes for which the sequence of predicate
- * expressions matches. Afterwards, keep only nodes between minPos and maxPos
- * in the filtered result.
- */
- static void
- xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op,
- xmlNodeSetPtr set,
- int minPos, int maxPos,
- int hasNsNodes)
- {
- if (op->ch1 != -1) {
- xmlXPathCompExprPtr comp = ctxt->comp;
- /*
- * Process inner predicates first.
- */
- if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompOpEvalPredicate: Expected a predicate\n");
- XP_ERROR(XPATH_INVALID_OPERAND);
- }
- if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
- XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
- ctxt->context->depth += 1;
- xmlXPathCompOpEvalPredicate(ctxt, &comp->steps[op->ch1], set,
- 1, set->nodeNr, hasNsNodes);
- ctxt->context->depth -= 1;
- CHECK_ERROR;
- }
- if (op->ch2 != -1)
- xmlXPathNodeSetFilter(ctxt, set, op->ch2, minPos, maxPos, hasNsNodes);
- }
- static int
- xmlXPathIsPositionalPredicate(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op,
- int *maxPos)
- {
- xmlXPathStepOpPtr exprOp;
- /*
- * BIG NOTE: This is not intended for XPATH_OP_FILTER yet!
- */
- /*
- * If not -1, then ch1 will point to:
- * 1) For predicates (XPATH_OP_PREDICATE):
- * - an inner predicate operator
- * 2) For filters (XPATH_OP_FILTER):
- * - an inner filter operator OR
- * - an expression selecting the node set.
- * E.g. "key('a', 'b')" or "(//foo | //bar)".
- */
- if ((op->op != XPATH_OP_PREDICATE) && (op->op != XPATH_OP_FILTER))
- return(0);
- if (op->ch2 != -1) {
- exprOp = &ctxt->comp->steps[op->ch2];
- } else
- return(0);
- if ((exprOp != NULL) &&
- (exprOp->op == XPATH_OP_VALUE) &&
- (exprOp->value4 != NULL) &&
- (((xmlXPathObjectPtr) exprOp->value4)->type == XPATH_NUMBER))
- {
- double floatval = ((xmlXPathObjectPtr) exprOp->value4)->floatval;
- /*
- * We have a "[n]" predicate here.
- * TODO: Unfortunately this simplistic test here is not
- * able to detect a position() predicate in compound
- * expressions like "[@attr = 'a" and position() = 1],
- * and even not the usage of position() in
- * "[position() = 1]"; thus - obviously - a position-range,
- * like it "[position() < 5]", is also not detected.
- * Maybe we could rewrite the AST to ease the optimization.
- */
- if ((floatval > INT_MIN) && (floatval < INT_MAX)) {
- *maxPos = (int) floatval;
- if (floatval == (double) *maxPos)
- return(1);
- }
- }
- return(0);
- }
- static int
- xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op,
- xmlNodePtr * first, xmlNodePtr * last,
- int toBool)
- {
- #define XP_TEST_HIT \
- if (hasAxisRange != 0) { \
- if (++pos == maxPos) { \
- if (addNode(seq, cur) < 0) \
- ctxt->error = XPATH_MEMORY_ERROR; \
- goto axis_range_end; } \
- } else { \
- if (addNode(seq, cur) < 0) \
- ctxt->error = XPATH_MEMORY_ERROR; \
- if (breakOnFirstHit) goto first_hit; }
- #define XP_TEST_HIT_NS \
- if (hasAxisRange != 0) { \
- if (++pos == maxPos) { \
- hasNsNodes = 1; \
- if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
- ctxt->error = XPATH_MEMORY_ERROR; \
- goto axis_range_end; } \
- } else { \
- hasNsNodes = 1; \
- if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
- ctxt->error = XPATH_MEMORY_ERROR; \
- if (breakOnFirstHit) goto first_hit; }
- xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
- xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
- xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
- const xmlChar *prefix = op->value4;
- const xmlChar *name = op->value5;
- const xmlChar *URI = NULL;
- #ifdef DEBUG_STEP
- int nbMatches = 0, prevMatches = 0;
- #endif
- int total = 0, hasNsNodes = 0;
- /* The popped object holding the context nodes */
- xmlXPathObjectPtr obj;
- /* The set of context nodes for the node tests */
- xmlNodeSetPtr contextSeq;
- int contextIdx;
- xmlNodePtr contextNode;
- /* The final resulting node set wrt to all context nodes */
- xmlNodeSetPtr outSeq;
- /*
- * The temporary resulting node set wrt 1 context node.
- * Used to feed predicate evaluation.
- */
- xmlNodeSetPtr seq;
- xmlNodePtr cur;
- /* First predicate operator */
- xmlXPathStepOpPtr predOp;
- int maxPos; /* The requested position() (when a "[n]" predicate) */
- int hasPredicateRange, hasAxisRange, pos;
- int breakOnFirstHit;
- xmlXPathTraversalFunction next = NULL;
- int (*addNode) (xmlNodeSetPtr, xmlNodePtr);
- xmlXPathNodeSetMergeFunction mergeAndClear;
- xmlNodePtr oldContextNode;
- xmlXPathContextPtr xpctxt = ctxt->context;
- CHECK_TYPE0(XPATH_NODESET);
- obj = valuePop(ctxt);
- /*
- * Setup namespaces.
- */
- if (prefix != NULL) {
- URI = xmlXPathNsLookup(xpctxt, prefix);
- if (URI == NULL) {
- xmlXPathReleaseObject(xpctxt, obj);
- XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
- }
- }
- /*
- * Setup axis.
- *
- * MAYBE FUTURE TODO: merging optimizations:
- * - If the nodes to be traversed wrt to the initial nodes and
- * the current axis cannot overlap, then we could avoid searching
- * for duplicates during the merge.
- * But the question is how/when to evaluate if they cannot overlap.
- * Example: if we know that for two initial nodes, the one is
- * not in the ancestor-or-self axis of the other, then we could safely
- * avoid a duplicate-aware merge, if the axis to be traversed is e.g.
- * the descendant-or-self axis.
- */
- mergeAndClear = xmlXPathNodeSetMergeAndClear;
- switch (axis) {
- case AXIS_ANCESTOR:
- first = NULL;
- next = xmlXPathNextAncestor;
- break;
- case AXIS_ANCESTOR_OR_SELF:
- first = NULL;
- next = xmlXPathNextAncestorOrSelf;
- break;
- case AXIS_ATTRIBUTE:
- first = NULL;
- last = NULL;
- next = xmlXPathNextAttribute;
- mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
- break;
- case AXIS_CHILD:
- last = NULL;
- if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) &&
- (type == NODE_TYPE_NODE))
- {
- /*
- * Optimization if an element node type is 'element'.
- */
- next = xmlXPathNextChildElement;
- } else
- next = xmlXPathNextChild;
- mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
- break;
- case AXIS_DESCENDANT:
- last = NULL;
- next = xmlXPathNextDescendant;
- break;
- case AXIS_DESCENDANT_OR_SELF:
- last = NULL;
- next = xmlXPathNextDescendantOrSelf;
- break;
- case AXIS_FOLLOWING:
- last = NULL;
- next = xmlXPathNextFollowing;
- break;
- case AXIS_FOLLOWING_SIBLING:
- last = NULL;
- next = xmlXPathNextFollowingSibling;
- break;
- case AXIS_NAMESPACE:
- first = NULL;
- last = NULL;
- next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
- mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
- break;
- case AXIS_PARENT:
- first = NULL;
- next = xmlXPathNextParent;
- break;
- case AXIS_PRECEDING:
- first = NULL;
- next = xmlXPathNextPrecedingInternal;
- break;
- case AXIS_PRECEDING_SIBLING:
- first = NULL;
- next = xmlXPathNextPrecedingSibling;
- break;
- case AXIS_SELF:
- first = NULL;
- last = NULL;
- next = xmlXPathNextSelf;
- mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
- break;
- }
- #ifdef DEBUG_STEP
- xmlXPathDebugDumpStepAxis(op,
- (obj->nodesetval != NULL) ? obj->nodesetval->nodeNr : 0);
- #endif
- if (next == NULL) {
- xmlXPathReleaseObject(xpctxt, obj);
- return(0);
- }
- contextSeq = obj->nodesetval;
- if ((contextSeq == NULL) || (contextSeq->nodeNr <= 0)) {
- xmlXPathReleaseObject(xpctxt, obj);
- valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
- return(0);
- }
- /*
- * Predicate optimization ---------------------------------------------
- * If this step has a last predicate, which contains a position(),
- * then we'll optimize (although not exactly "position()", but only
- * the short-hand form, i.e., "[n]".
- *
- * Example - expression "/foo[parent::bar][1]":
- *
- * COLLECT 'child' 'name' 'node' foo -- op (we are here)
- * ROOT -- op->ch1
- * PREDICATE -- op->ch2 (predOp)
- * PREDICATE -- predOp->ch1 = [parent::bar]
- * SORT
- * COLLECT 'parent' 'name' 'node' bar
- * NODE
- * ELEM Object is a number : 1 -- predOp->ch2 = [1]
- *
- */
- maxPos = 0;
- predOp = NULL;
- hasPredicateRange = 0;
- hasAxisRange = 0;
- if (op->ch2 != -1) {
- /*
- * There's at least one predicate. 16 == XPATH_OP_PREDICATE
- */
- predOp = &ctxt->comp->steps[op->ch2];
- if (xmlXPathIsPositionalPredicate(ctxt, predOp, &maxPos)) {
- if (predOp->ch1 != -1) {
- /*
- * Use the next inner predicate operator.
- */
- predOp = &ctxt->comp->steps[predOp->ch1];
- hasPredicateRange = 1;
- } else {
- /*
- * There's no other predicate than the [n] predicate.
- */
- predOp = NULL;
- hasAxisRange = 1;
- }
- }
- }
- breakOnFirstHit = ((toBool) && (predOp == NULL)) ? 1 : 0;
- /*
- * Axis traversal -----------------------------------------------------
- */
- /*
- * 2.3 Node Tests
- * - For the attribute axis, the principal node type is attribute.
- * - For the namespace axis, the principal node type is namespace.
- * - For other axes, the principal node type is element.
- *
- * A node test * is true for any node of the
- * principal node type. For example, child::* will
- * select all element children of the context node
- */
- oldContextNode = xpctxt->node;
- addNode = xmlXPathNodeSetAddUnique;
- outSeq = NULL;
- seq = NULL;
- contextNode = NULL;
- contextIdx = 0;
- while (((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) &&
- (ctxt->error == XPATH_EXPRESSION_OK)) {
- xpctxt->node = contextSeq->nodeTab[contextIdx++];
- if (seq == NULL) {
- seq = xmlXPathNodeSetCreate(NULL);
- if (seq == NULL) {
- /* TODO: Propagate memory error. */
- total = 0;
- goto error;
- }
- }
- /*
- * Traverse the axis and test the nodes.
- */
- pos = 0;
- cur = NULL;
- hasNsNodes = 0;
- do {
- if (OP_LIMIT_EXCEEDED(ctxt, 1))
- goto error;
- cur = next(ctxt, cur);
- if (cur == NULL)
- break;
- /*
- * QUESTION TODO: What does the "first" and "last" stuff do?
- */
- if ((first != NULL) && (*first != NULL)) {
- if (*first == cur)
- break;
- if (((total % 256) == 0) &&
- #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
- (xmlXPathCmpNodesExt(*first, cur) >= 0))
- #else
- (xmlXPathCmpNodes(*first, cur) >= 0))
- #endif
- {
- break;
- }
- }
- if ((last != NULL) && (*last != NULL)) {
- if (*last == cur)
- break;
- if (((total % 256) == 0) &&
- #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
- (xmlXPathCmpNodesExt(cur, *last) >= 0))
- #else
- (xmlXPathCmpNodes(cur, *last) >= 0))
- #endif
- {
- break;
- }
- }
- total++;
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
- #endif
- switch (test) {
- case NODE_TEST_NONE:
- total = 0;
- STRANGE
- goto error;
- case NODE_TEST_TYPE:
- if (type == NODE_TYPE_NODE) {
- switch (cur->type) {
- case XML_DOCUMENT_NODE:
- case XML_HTML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- case XML_ELEMENT_NODE:
- case XML_ATTRIBUTE_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_TEXT_NODE:
- XP_TEST_HIT
- break;
- case XML_NAMESPACE_DECL: {
- if (axis == AXIS_NAMESPACE) {
- XP_TEST_HIT_NS
- } else {
- hasNsNodes = 1;
- XP_TEST_HIT
- }
- break;
- }
- default:
- break;
- }
- } else if (cur->type == (xmlElementType) type) {
- if (cur->type == XML_NAMESPACE_DECL)
- XP_TEST_HIT_NS
- else
- XP_TEST_HIT
- } else if ((type == NODE_TYPE_TEXT) &&
- (cur->type == XML_CDATA_SECTION_NODE))
- {
- XP_TEST_HIT
- }
- break;
- case NODE_TEST_PI:
- if ((cur->type == XML_PI_NODE) &&
- ((name == NULL) || xmlStrEqual(name, cur->name)))
- {
- XP_TEST_HIT
- }
- break;
- case NODE_TEST_ALL:
- if (axis == AXIS_ATTRIBUTE) {
- if (cur->type == XML_ATTRIBUTE_NODE)
- {
- if (prefix == NULL)
- {
- XP_TEST_HIT
- } else if ((cur->ns != NULL) &&
- (xmlStrEqual(URI, cur->ns->href)))
- {
- XP_TEST_HIT
- }
- }
- } else if (axis == AXIS_NAMESPACE) {
- if (cur->type == XML_NAMESPACE_DECL)
- {
- XP_TEST_HIT_NS
- }
- } else {
- if (cur->type == XML_ELEMENT_NODE) {
- if (prefix == NULL)
- {
- XP_TEST_HIT
- } else if ((cur->ns != NULL) &&
- (xmlStrEqual(URI, cur->ns->href)))
- {
- XP_TEST_HIT
- }
- }
- }
- break;
- case NODE_TEST_NS:{
- TODO;
- break;
- }
- case NODE_TEST_NAME:
- if (axis == AXIS_ATTRIBUTE) {
- if (cur->type != XML_ATTRIBUTE_NODE)
- break;
- } else if (axis == AXIS_NAMESPACE) {
- if (cur->type != XML_NAMESPACE_DECL)
- break;
- } else {
- if (cur->type != XML_ELEMENT_NODE)
- break;
- }
- switch (cur->type) {
- case XML_ELEMENT_NODE:
- if (xmlStrEqual(name, cur->name)) {
- if (prefix == NULL) {
- if (cur->ns == NULL)
- {
- XP_TEST_HIT
- }
- } else {
- if ((cur->ns != NULL) &&
- (xmlStrEqual(URI, cur->ns->href)))
- {
- XP_TEST_HIT
- }
- }
- }
- break;
- case XML_ATTRIBUTE_NODE:{
- xmlAttrPtr attr = (xmlAttrPtr) cur;
- if (xmlStrEqual(name, attr->name)) {
- if (prefix == NULL) {
- if ((attr->ns == NULL) ||
- (attr->ns->prefix == NULL))
- {
- XP_TEST_HIT
- }
- } else {
- if ((attr->ns != NULL) &&
- (xmlStrEqual(URI,
- attr->ns->href)))
- {
- XP_TEST_HIT
- }
- }
- }
- break;
- }
- case XML_NAMESPACE_DECL:
- if (cur->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) cur;
- if ((ns->prefix != NULL) && (name != NULL)
- && (xmlStrEqual(ns->prefix, name)))
- {
- XP_TEST_HIT_NS
- }
- }
- break;
- default:
- break;
- }
- break;
- } /* switch(test) */
- } while ((cur != NULL) && (ctxt->error == XPATH_EXPRESSION_OK));
- goto apply_predicates;
- axis_range_end: /* ----------------------------------------------------- */
- /*
- * We have a "/foo[n]", and position() = n was reached.
- * Note that we can have as well "/foo/::parent::foo[1]", so
- * a duplicate-aware merge is still needed.
- * Merge with the result.
- */
- if (outSeq == NULL) {
- outSeq = seq;
- seq = NULL;
- } else
- /* TODO: Check memory error. */
- outSeq = mergeAndClear(outSeq, seq);
- /*
- * Break if only a true/false result was requested.
- */
- if (toBool)
- break;
- continue;
- first_hit: /* ---------------------------------------------------------- */
- /*
- * Break if only a true/false result was requested and
- * no predicates existed and a node test succeeded.
- */
- if (outSeq == NULL) {
- outSeq = seq;
- seq = NULL;
- } else
- /* TODO: Check memory error. */
- outSeq = mergeAndClear(outSeq, seq);
- break;
- #ifdef DEBUG_STEP
- if (seq != NULL)
- nbMatches += seq->nodeNr;
- #endif
- apply_predicates: /* --------------------------------------------------- */
- if (ctxt->error != XPATH_EXPRESSION_OK)
- goto error;
- /*
- * Apply predicates.
- */
- if ((predOp != NULL) && (seq->nodeNr > 0)) {
- /*
- * E.g. when we have a "/foo[some expression][n]".
- */
- /*
- * QUESTION TODO: The old predicate evaluation took into
- * account location-sets.
- * (E.g. ctxt->value->type == XPATH_LOCATIONSET)
- * Do we expect such a set here?
- * All what I learned now from the evaluation semantics
- * does not indicate that a location-set will be processed
- * here, so this looks OK.
- */
- /*
- * Iterate over all predicates, starting with the outermost
- * predicate.
- * TODO: Problem: we cannot execute the inner predicates first
- * since we cannot go back *up* the operator tree!
- * Options we have:
- * 1) Use of recursive functions (like is it currently done
- * via xmlXPathCompOpEval())
- * 2) Add a predicate evaluation information stack to the
- * context struct
- * 3) Change the way the operators are linked; we need a
- * "parent" field on xmlXPathStepOp
- *
- * For the moment, I'll try to solve this with a recursive
- * function: xmlXPathCompOpEvalPredicate().
- */
- if (hasPredicateRange != 0)
- xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, maxPos, maxPos,
- hasNsNodes);
- else
- xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, 1, seq->nodeNr,
- hasNsNodes);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- total = 0;
- goto error;
- }
- }
- if (seq->nodeNr > 0) {
- /*
- * Add to result set.
- */
- if (outSeq == NULL) {
- outSeq = seq;
- seq = NULL;
- } else {
- /* TODO: Check memory error. */
- outSeq = mergeAndClear(outSeq, seq);
- }
- if (toBool)
- break;
- }
- }
- error:
- if ((obj->boolval) && (obj->user != NULL)) {
- /*
- * QUESTION TODO: What does this do and why?
- * TODO: Do we have to do this also for the "error"
- * cleanup further down?
- */
- ctxt->value->boolval = 1;
- ctxt->value->user = obj->user;
- obj->user = NULL;
- obj->boolval = 0;
- }
- xmlXPathReleaseObject(xpctxt, obj);
- /*
- * Ensure we return at least an empty set.
- */
- if (outSeq == NULL) {
- if ((seq != NULL) && (seq->nodeNr == 0))
- outSeq = seq;
- else
- /* TODO: Check memory error. */
- outSeq = xmlXPathNodeSetCreate(NULL);
- }
- if ((seq != NULL) && (seq != outSeq)) {
- xmlXPathFreeNodeSet(seq);
- }
- /*
- * Hand over the result. Better to push the set also in
- * case of errors.
- */
- valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, outSeq));
- /*
- * Reset the context node.
- */
- xpctxt->node = oldContextNode;
- /*
- * When traversing the namespace axis in "toBool" mode, it's
- * possible that tmpNsList wasn't freed.
- */
- if (xpctxt->tmpNsList != NULL) {
- xmlFree(xpctxt->tmpNsList);
- xpctxt->tmpNsList = NULL;
- }
- #ifdef DEBUG_STEP
- xmlGenericError(xmlGenericErrorContext,
- "\nExamined %d nodes, found %d nodes at that step\n",
- total, nbMatches);
- #endif
- return(total);
- }
- static int
- xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op, xmlNodePtr * first);
- /**
- * xmlXPathCompOpEvalFirst:
- * @ctxt: the XPath parser context with the compiled expression
- * @op: an XPath compiled operation
- * @first: the first elem found so far
- *
- * Evaluate the Precompiled XPath operation searching only the first
- * element in document order
- *
- * Returns the number of examined objects.
- */
- static int
- xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op, xmlNodePtr * first)
- {
- int total = 0, cur;
- xmlXPathCompExprPtr comp;
- xmlXPathObjectPtr arg1, arg2;
- CHECK_ERROR0;
- if (OP_LIMIT_EXCEEDED(ctxt, 1))
- return(0);
- if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
- XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
- ctxt->context->depth += 1;
- comp = ctxt->comp;
- switch (op->op) {
- case XPATH_OP_END:
- break;
- case XPATH_OP_UNION:
- total =
- xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
- first);
- CHECK_ERROR0;
- if ((ctxt->value != NULL)
- && (ctxt->value->type == XPATH_NODESET)
- && (ctxt->value->nodesetval != NULL)
- && (ctxt->value->nodesetval->nodeNr >= 1)) {
- /*
- * limit tree traversing to first node in the result
- */
- /*
- * OPTIMIZE TODO: This implicitly sorts
- * the result, even if not needed. E.g. if the argument
- * of the count() function, no sorting is needed.
- * OPTIMIZE TODO: How do we know if the node-list wasn't
- * already sorted?
- */
- if (ctxt->value->nodesetval->nodeNr > 1)
- xmlXPathNodeSetSort(ctxt->value->nodesetval);
- *first = ctxt->value->nodesetval->nodeTab[0];
- }
- cur =
- xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
- first);
- CHECK_ERROR0;
- arg2 = valuePop(ctxt);
- arg1 = valuePop(ctxt);
- if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
- (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- XP_ERROR0(XPATH_INVALID_TYPE);
- }
- if ((ctxt->context->opLimit != 0) &&
- (((arg1->nodesetval != NULL) &&
- (xmlXPathCheckOpLimit(ctxt,
- arg1->nodesetval->nodeNr) < 0)) ||
- ((arg2->nodesetval != NULL) &&
- (xmlXPathCheckOpLimit(ctxt,
- arg2->nodesetval->nodeNr) < 0)))) {
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- break;
- }
- /* TODO: Check memory error. */
- arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
- arg2->nodesetval);
- valuePush(ctxt, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- /* optimizer */
- if (total > cur)
- xmlXPathCompSwap(op);
- total += cur;
- break;
- case XPATH_OP_ROOT:
- xmlXPathRoot(ctxt);
- break;
- case XPATH_OP_NODE:
- if (op->ch1 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- if (op->ch2 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node));
- break;
- case XPATH_OP_COLLECT:{
- if (op->ch1 == -1)
- break;
- total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL, 0);
- break;
- }
- case XPATH_OP_VALUE:
- valuePush(ctxt,
- xmlXPathCacheObjectCopy(ctxt->context,
- (xmlXPathObjectPtr) op->value4));
- break;
- case XPATH_OP_SORT:
- if (op->ch1 != -1)
- total +=
- xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
- first);
- CHECK_ERROR0;
- if ((ctxt->value != NULL)
- && (ctxt->value->type == XPATH_NODESET)
- && (ctxt->value->nodesetval != NULL)
- && (ctxt->value->nodesetval->nodeNr > 1))
- xmlXPathNodeSetSort(ctxt->value->nodesetval);
- break;
- #ifdef XP_OPTIMIZED_FILTER_FIRST
- case XPATH_OP_FILTER:
- total += xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
- break;
- #endif
- default:
- total += xmlXPathCompOpEval(ctxt, op);
- break;
- }
- ctxt->context->depth -= 1;
- return(total);
- }
- /**
- * xmlXPathCompOpEvalLast:
- * @ctxt: the XPath parser context with the compiled expression
- * @op: an XPath compiled operation
- * @last: the last elem found so far
- *
- * Evaluate the Precompiled XPath operation searching only the last
- * element in document order
- *
- * Returns the number of nodes traversed
- */
- static int
- xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
- xmlNodePtr * last)
- {
- int total = 0, cur;
- xmlXPathCompExprPtr comp;
- xmlXPathObjectPtr arg1, arg2;
- CHECK_ERROR0;
- if (OP_LIMIT_EXCEEDED(ctxt, 1))
- return(0);
- if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
- XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
- ctxt->context->depth += 1;
- comp = ctxt->comp;
- switch (op->op) {
- case XPATH_OP_END:
- break;
- case XPATH_OP_UNION:
- total =
- xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
- CHECK_ERROR0;
- if ((ctxt->value != NULL)
- && (ctxt->value->type == XPATH_NODESET)
- && (ctxt->value->nodesetval != NULL)
- && (ctxt->value->nodesetval->nodeNr >= 1)) {
- /*
- * limit tree traversing to first node in the result
- */
- if (ctxt->value->nodesetval->nodeNr > 1)
- xmlXPathNodeSetSort(ctxt->value->nodesetval);
- *last =
- ctxt->value->nodesetval->nodeTab[ctxt->value->
- nodesetval->nodeNr -
- 1];
- }
- cur =
- xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
- CHECK_ERROR0;
- if ((ctxt->value != NULL)
- && (ctxt->value->type == XPATH_NODESET)
- && (ctxt->value->nodesetval != NULL)
- && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */
- }
- arg2 = valuePop(ctxt);
- arg1 = valuePop(ctxt);
- if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
- (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- XP_ERROR0(XPATH_INVALID_TYPE);
- }
- if ((ctxt->context->opLimit != 0) &&
- (((arg1->nodesetval != NULL) &&
- (xmlXPathCheckOpLimit(ctxt,
- arg1->nodesetval->nodeNr) < 0)) ||
- ((arg2->nodesetval != NULL) &&
- (xmlXPathCheckOpLimit(ctxt,
- arg2->nodesetval->nodeNr) < 0)))) {
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- break;
- }
- /* TODO: Check memory error. */
- arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
- arg2->nodesetval);
- valuePush(ctxt, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- /* optimizer */
- if (total > cur)
- xmlXPathCompSwap(op);
- total += cur;
- break;
- case XPATH_OP_ROOT:
- xmlXPathRoot(ctxt);
- break;
- case XPATH_OP_NODE:
- if (op->ch1 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- if (op->ch2 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node));
- break;
- case XPATH_OP_COLLECT:{
- if (op->ch1 == -1)
- break;
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last, 0);
- break;
- }
- case XPATH_OP_VALUE:
- valuePush(ctxt,
- xmlXPathCacheObjectCopy(ctxt->context,
- (xmlXPathObjectPtr) op->value4));
- break;
- case XPATH_OP_SORT:
- if (op->ch1 != -1)
- total +=
- xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
- last);
- CHECK_ERROR0;
- if ((ctxt->value != NULL)
- && (ctxt->value->type == XPATH_NODESET)
- && (ctxt->value->nodesetval != NULL)
- && (ctxt->value->nodesetval->nodeNr > 1))
- xmlXPathNodeSetSort(ctxt->value->nodesetval);
- break;
- default:
- total += xmlXPathCompOpEval(ctxt, op);
- break;
- }
- ctxt->context->depth -= 1;
- return (total);
- }
- #ifdef XP_OPTIMIZED_FILTER_FIRST
- static int
- xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op, xmlNodePtr * first)
- {
- int total = 0;
- xmlXPathCompExprPtr comp;
- xmlNodeSetPtr set;
- CHECK_ERROR0;
- comp = ctxt->comp;
- /*
- * Optimization for ()[last()] selection i.e. the last elem
- */
- if ((op->ch1 != -1) && (op->ch2 != -1) &&
- (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
- (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
- int f = comp->steps[op->ch2].ch1;
- if ((f != -1) &&
- (comp->steps[f].op == XPATH_OP_FUNCTION) &&
- (comp->steps[f].value5 == NULL) &&
- (comp->steps[f].value == 0) &&
- (comp->steps[f].value4 != NULL) &&
- (xmlStrEqual
- (comp->steps[f].value4, BAD_CAST "last"))) {
- xmlNodePtr last = NULL;
- total +=
- xmlXPathCompOpEvalLast(ctxt,
- &comp->steps[op->ch1],
- &last);
- CHECK_ERROR0;
- /*
- * The nodeset should be in document order,
- * Keep only the last value
- */
- if ((ctxt->value != NULL) &&
- (ctxt->value->type == XPATH_NODESET) &&
- (ctxt->value->nodesetval != NULL) &&
- (ctxt->value->nodesetval->nodeTab != NULL) &&
- (ctxt->value->nodesetval->nodeNr > 1)) {
- xmlXPathNodeSetKeepLast(ctxt->value->nodesetval);
- *first = *(ctxt->value->nodesetval->nodeTab);
- }
- return (total);
- }
- }
- if (op->ch1 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- if (op->ch2 == -1)
- return (total);
- if (ctxt->value == NULL)
- return (total);
- #ifdef LIBXML_XPTR_ENABLED
- /*
- * Hum are we filtering the result of an XPointer expression
- */
- if (ctxt->value->type == XPATH_LOCATIONSET) {
- xmlLocationSetPtr locset = ctxt->value->user;
- if (locset != NULL) {
- xmlXPathLocationSetFilter(ctxt, locset, op->ch2, 1, 1);
- if (locset->locNr > 0)
- *first = (xmlNodePtr) locset->locTab[0]->user;
- }
- return (total);
- }
- #endif /* LIBXML_XPTR_ENABLED */
- CHECK_TYPE0(XPATH_NODESET);
- set = ctxt->value->nodesetval;
- if (set != NULL) {
- xmlXPathNodeSetFilter(ctxt, set, op->ch2, 1, 1, 1);
- if (set->nodeNr > 0)
- *first = set->nodeTab[0];
- }
- return (total);
- }
- #endif /* XP_OPTIMIZED_FILTER_FIRST */
- /**
- * xmlXPathCompOpEval:
- * @ctxt: the XPath parser context with the compiled expression
- * @op: an XPath compiled operation
- *
- * Evaluate the Precompiled XPath operation
- * Returns the number of nodes traversed
- */
- static int
- xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
- {
- int total = 0;
- int equal, ret;
- xmlXPathCompExprPtr comp;
- xmlXPathObjectPtr arg1, arg2;
- CHECK_ERROR0;
- if (OP_LIMIT_EXCEEDED(ctxt, 1))
- return(0);
- if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
- XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
- ctxt->context->depth += 1;
- comp = ctxt->comp;
- switch (op->op) {
- case XPATH_OP_END:
- break;
- case XPATH_OP_AND:
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- xmlXPathBooleanFunction(ctxt, 1);
- if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
- break;
- arg2 = valuePop(ctxt);
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- if (ctxt->error) {
- xmlXPathFreeObject(arg2);
- break;
- }
- xmlXPathBooleanFunction(ctxt, 1);
- if (ctxt->value != NULL)
- ctxt->value->boolval &= arg2->boolval;
- xmlXPathReleaseObject(ctxt->context, arg2);
- break;
- case XPATH_OP_OR:
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- xmlXPathBooleanFunction(ctxt, 1);
- if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
- break;
- arg2 = valuePop(ctxt);
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- if (ctxt->error) {
- xmlXPathFreeObject(arg2);
- break;
- }
- xmlXPathBooleanFunction(ctxt, 1);
- if (ctxt->value != NULL)
- ctxt->value->boolval |= arg2->boolval;
- xmlXPathReleaseObject(ctxt->context, arg2);
- break;
- case XPATH_OP_EQUAL:
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- if (op->value)
- equal = xmlXPathEqualValues(ctxt);
- else
- equal = xmlXPathNotEqualValues(ctxt);
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
- break;
- case XPATH_OP_CMP:
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
- valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
- break;
- case XPATH_OP_PLUS:
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- if (op->ch2 != -1) {
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- }
- CHECK_ERROR0;
- if (op->value == 0)
- xmlXPathSubValues(ctxt);
- else if (op->value == 1)
- xmlXPathAddValues(ctxt);
- else if (op->value == 2)
- xmlXPathValueFlipSign(ctxt);
- else if (op->value == 3) {
- CAST_TO_NUMBER;
- CHECK_TYPE0(XPATH_NUMBER);
- }
- break;
- case XPATH_OP_MULT:
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- if (op->value == 0)
- xmlXPathMultValues(ctxt);
- else if (op->value == 1)
- xmlXPathDivValues(ctxt);
- else if (op->value == 2)
- xmlXPathModValues(ctxt);
- break;
- case XPATH_OP_UNION:
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- arg2 = valuePop(ctxt);
- arg1 = valuePop(ctxt);
- if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
- (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- XP_ERROR0(XPATH_INVALID_TYPE);
- }
- if ((ctxt->context->opLimit != 0) &&
- (((arg1->nodesetval != NULL) &&
- (xmlXPathCheckOpLimit(ctxt,
- arg1->nodesetval->nodeNr) < 0)) ||
- ((arg2->nodesetval != NULL) &&
- (xmlXPathCheckOpLimit(ctxt,
- arg2->nodesetval->nodeNr) < 0)))) {
- xmlXPathReleaseObject(ctxt->context, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- break;
- }
- if ((arg1->nodesetval == NULL) ||
- ((arg2->nodesetval != NULL) &&
- (arg2->nodesetval->nodeNr != 0)))
- {
- /* TODO: Check memory error. */
- arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
- arg2->nodesetval);
- }
- valuePush(ctxt, arg1);
- xmlXPathReleaseObject(ctxt->context, arg2);
- break;
- case XPATH_OP_ROOT:
- xmlXPathRoot(ctxt);
- break;
- case XPATH_OP_NODE:
- if (op->ch1 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- if (op->ch2 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node));
- break;
- case XPATH_OP_COLLECT:{
- if (op->ch1 == -1)
- break;
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
- break;
- }
- case XPATH_OP_VALUE:
- valuePush(ctxt,
- xmlXPathCacheObjectCopy(ctxt->context,
- (xmlXPathObjectPtr) op->value4));
- break;
- case XPATH_OP_VARIABLE:{
- xmlXPathObjectPtr val;
- if (op->ch1 != -1)
- total +=
- xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- if (op->value5 == NULL) {
- val = xmlXPathVariableLookup(ctxt->context, op->value4);
- if (val == NULL)
- XP_ERROR0(XPATH_UNDEF_VARIABLE_ERROR);
- valuePush(ctxt, val);
- } else {
- const xmlChar *URI;
- URI = xmlXPathNsLookup(ctxt->context, op->value5);
- if (URI == NULL) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
- (char *) op->value4, (char *)op->value5);
- ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
- break;
- }
- val = xmlXPathVariableLookupNS(ctxt->context,
- op->value4, URI);
- if (val == NULL)
- XP_ERROR0(XPATH_UNDEF_VARIABLE_ERROR);
- valuePush(ctxt, val);
- }
- break;
- }
- case XPATH_OP_FUNCTION:{
- xmlXPathFunction func;
- const xmlChar *oldFunc, *oldFuncURI;
- int i;
- int frame;
- frame = xmlXPathSetFrame(ctxt);
- if (op->ch1 != -1) {
- total +=
- xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPathPopFrame(ctxt, frame);
- break;
- }
- }
- if (ctxt->valueNr < ctxt->valueFrame + op->value) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompOpEval: parameter error\n");
- ctxt->error = XPATH_INVALID_OPERAND;
- xmlXPathPopFrame(ctxt, frame);
- break;
- }
- for (i = 0; i < op->value; i++) {
- if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompOpEval: parameter error\n");
- ctxt->error = XPATH_INVALID_OPERAND;
- xmlXPathPopFrame(ctxt, frame);
- break;
- }
- }
- if (op->cache != NULL)
- func = op->cache;
- else {
- const xmlChar *URI = NULL;
- if (op->value5 == NULL)
- func =
- xmlXPathFunctionLookup(ctxt->context,
- op->value4);
- else {
- URI = xmlXPathNsLookup(ctxt->context, op->value5);
- if (URI == NULL) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n",
- (char *)op->value4, (char *)op->value5);
- xmlXPathPopFrame(ctxt, frame);
- ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
- break;
- }
- func = xmlXPathFunctionLookupNS(ctxt->context,
- op->value4, URI);
- }
- if (func == NULL) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompOpEval: function %s not found\n",
- (char *)op->value4);
- XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
- }
- op->cache = func;
- op->cacheURI = (void *) URI;
- }
- oldFunc = ctxt->context->function;
- oldFuncURI = ctxt->context->functionURI;
- ctxt->context->function = op->value4;
- ctxt->context->functionURI = op->cacheURI;
- func(ctxt, op->value);
- ctxt->context->function = oldFunc;
- ctxt->context->functionURI = oldFuncURI;
- if ((ctxt->error == XPATH_EXPRESSION_OK) &&
- (ctxt->valueNr != ctxt->valueFrame + 1))
- XP_ERROR0(XPATH_STACK_ERROR);
- xmlXPathPopFrame(ctxt, frame);
- break;
- }
- case XPATH_OP_ARG:
- if (op->ch1 != -1) {
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- }
- if (op->ch2 != -1) {
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- CHECK_ERROR0;
- }
- break;
- case XPATH_OP_PREDICATE:
- case XPATH_OP_FILTER:{
- xmlNodeSetPtr set;
- /*
- * Optimization for ()[1] selection i.e. the first elem
- */
- if ((op->ch1 != -1) && (op->ch2 != -1) &&
- #ifdef XP_OPTIMIZED_FILTER_FIRST
- /*
- * FILTER TODO: Can we assume that the inner processing
- * will result in an ordered list if we have an
- * XPATH_OP_FILTER?
- * What about an additional field or flag on
- * xmlXPathObject like @sorted ? This way we wouldn't need
- * to assume anything, so it would be more robust and
- * easier to optimize.
- */
- ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
- (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
- #else
- (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
- #endif
- (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
- xmlXPathObjectPtr val;
- val = comp->steps[op->ch2].value4;
- if ((val != NULL) && (val->type == XPATH_NUMBER) &&
- (val->floatval == 1.0)) {
- xmlNodePtr first = NULL;
- total +=
- xmlXPathCompOpEvalFirst(ctxt,
- &comp->steps[op->ch1],
- &first);
- CHECK_ERROR0;
- /*
- * The nodeset should be in document order,
- * Keep only the first value
- */
- if ((ctxt->value != NULL) &&
- (ctxt->value->type == XPATH_NODESET) &&
- (ctxt->value->nodesetval != NULL) &&
- (ctxt->value->nodesetval->nodeNr > 1))
- xmlXPathNodeSetClearFromPos(ctxt->value->nodesetval,
- 1, 1);
- break;
- }
- }
- /*
- * Optimization for ()[last()] selection i.e. the last elem
- */
- if ((op->ch1 != -1) && (op->ch2 != -1) &&
- (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
- (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
- int f = comp->steps[op->ch2].ch1;
- if ((f != -1) &&
- (comp->steps[f].op == XPATH_OP_FUNCTION) &&
- (comp->steps[f].value5 == NULL) &&
- (comp->steps[f].value == 0) &&
- (comp->steps[f].value4 != NULL) &&
- (xmlStrEqual
- (comp->steps[f].value4, BAD_CAST "last"))) {
- xmlNodePtr last = NULL;
- total +=
- xmlXPathCompOpEvalLast(ctxt,
- &comp->steps[op->ch1],
- &last);
- CHECK_ERROR0;
- /*
- * The nodeset should be in document order,
- * Keep only the last value
- */
- if ((ctxt->value != NULL) &&
- (ctxt->value->type == XPATH_NODESET) &&
- (ctxt->value->nodesetval != NULL) &&
- (ctxt->value->nodesetval->nodeTab != NULL) &&
- (ctxt->value->nodesetval->nodeNr > 1))
- xmlXPathNodeSetKeepLast(ctxt->value->nodesetval);
- break;
- }
- }
- /*
- * Process inner predicates first.
- * Example "index[parent::book][1]":
- * ...
- * PREDICATE <-- we are here "[1]"
- * PREDICATE <-- process "[parent::book]" first
- * SORT
- * COLLECT 'parent' 'name' 'node' book
- * NODE
- * ELEM Object is a number : 1
- */
- if (op->ch1 != -1)
- total +=
- xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- if (op->ch2 == -1)
- break;
- if (ctxt->value == NULL)
- break;
- #ifdef LIBXML_XPTR_ENABLED
- /*
- * Hum are we filtering the result of an XPointer expression
- */
- if (ctxt->value->type == XPATH_LOCATIONSET) {
- xmlLocationSetPtr locset = ctxt->value->user;
- xmlXPathLocationSetFilter(ctxt, locset, op->ch2,
- 1, locset->locNr);
- break;
- }
- #endif /* LIBXML_XPTR_ENABLED */
- CHECK_TYPE0(XPATH_NODESET);
- set = ctxt->value->nodesetval;
- if (set != NULL)
- xmlXPathNodeSetFilter(ctxt, set, op->ch2,
- 1, set->nodeNr, 1);
- break;
- }
- case XPATH_OP_SORT:
- if (op->ch1 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- if ((ctxt->value != NULL) &&
- (ctxt->value->type == XPATH_NODESET) &&
- (ctxt->value->nodesetval != NULL) &&
- (ctxt->value->nodesetval->nodeNr > 1))
- {
- xmlXPathNodeSetSort(ctxt->value->nodesetval);
- }
- break;
- #ifdef LIBXML_XPTR_ENABLED
- case XPATH_OP_RANGETO:{
- xmlXPathObjectPtr range;
- xmlXPathObjectPtr res, obj;
- xmlXPathObjectPtr tmp;
- xmlLocationSetPtr newlocset = NULL;
- xmlLocationSetPtr oldlocset;
- xmlNodeSetPtr oldset;
- xmlNodePtr oldnode = ctxt->context->node;
- int oldcs = ctxt->context->contextSize;
- int oldpp = ctxt->context->proximityPosition;
- int i, j;
- if (op->ch1 != -1) {
- total +=
- xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- CHECK_ERROR0;
- }
- if (ctxt->value == NULL) {
- XP_ERROR0(XPATH_INVALID_OPERAND);
- }
- if (op->ch2 == -1)
- break;
- if (ctxt->value->type == XPATH_LOCATIONSET) {
- /*
- * Extract the old locset, and then evaluate the result of the
- * expression for all the element in the locset. use it to grow
- * up a new locset.
- */
- CHECK_TYPE0(XPATH_LOCATIONSET);
- if ((ctxt->value->user == NULL) ||
- (((xmlLocationSetPtr) ctxt->value->user)->locNr == 0))
- break;
- obj = valuePop(ctxt);
- oldlocset = obj->user;
- newlocset = xmlXPtrLocationSetCreate(NULL);
- for (i = 0; i < oldlocset->locNr; i++) {
- /*
- * Run the evaluation with a node list made of a
- * single item in the nodelocset.
- */
- ctxt->context->node = oldlocset->locTab[i]->user;
- ctxt->context->contextSize = oldlocset->locNr;
- ctxt->context->proximityPosition = i + 1;
- tmp = xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node);
- valuePush(ctxt, tmp);
- if (op->ch2 != -1)
- total +=
- xmlXPathCompOpEval(ctxt,
- &comp->steps[op->ch2]);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPtrFreeLocationSet(newlocset);
- goto rangeto_error;
- }
- res = valuePop(ctxt);
- if (res->type == XPATH_LOCATIONSET) {
- xmlLocationSetPtr rloc =
- (xmlLocationSetPtr)res->user;
- for (j=0; j<rloc->locNr; j++) {
- range = xmlXPtrNewRange(
- oldlocset->locTab[i]->user,
- oldlocset->locTab[i]->index,
- rloc->locTab[j]->user2,
- rloc->locTab[j]->index2);
- if (range != NULL) {
- xmlXPtrLocationSetAdd(newlocset, range);
- }
- }
- } else {
- range = xmlXPtrNewRangeNodeObject(
- (xmlNodePtr)oldlocset->locTab[i]->user, res);
- if (range != NULL) {
- xmlXPtrLocationSetAdd(newlocset,range);
- }
- }
- /*
- * Cleanup
- */
- if (res != NULL) {
- xmlXPathReleaseObject(ctxt->context, res);
- }
- if (ctxt->value == tmp) {
- res = valuePop(ctxt);
- xmlXPathReleaseObject(ctxt->context, res);
- }
- }
- } else { /* Not a location set */
- CHECK_TYPE0(XPATH_NODESET);
- obj = valuePop(ctxt);
- oldset = obj->nodesetval;
- newlocset = xmlXPtrLocationSetCreate(NULL);
- if (oldset != NULL) {
- for (i = 0; i < oldset->nodeNr; i++) {
- /*
- * Run the evaluation with a node list made of a single item
- * in the nodeset.
- */
- ctxt->context->node = oldset->nodeTab[i];
- /*
- * OPTIMIZE TODO: Avoid recreation for every iteration.
- */
- tmp = xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node);
- valuePush(ctxt, tmp);
- if (op->ch2 != -1)
- total +=
- xmlXPathCompOpEval(ctxt,
- &comp->steps[op->ch2]);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPtrFreeLocationSet(newlocset);
- goto rangeto_error;
- }
- res = valuePop(ctxt);
- range =
- xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
- res);
- if (range != NULL) {
- xmlXPtrLocationSetAdd(newlocset, range);
- }
- /*
- * Cleanup
- */
- if (res != NULL) {
- xmlXPathReleaseObject(ctxt->context, res);
- }
- if (ctxt->value == tmp) {
- res = valuePop(ctxt);
- xmlXPathReleaseObject(ctxt->context, res);
- }
- }
- }
- }
- /*
- * The result is used as the new evaluation set.
- */
- valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
- rangeto_error:
- xmlXPathReleaseObject(ctxt->context, obj);
- ctxt->context->node = oldnode;
- ctxt->context->contextSize = oldcs;
- ctxt->context->proximityPosition = oldpp;
- break;
- }
- #endif /* LIBXML_XPTR_ENABLED */
- default:
- xmlGenericError(xmlGenericErrorContext,
- "XPath: unknown precompiled operation %d\n", op->op);
- ctxt->error = XPATH_INVALID_OPERAND;
- break;
- }
- ctxt->context->depth -= 1;
- return (total);
- }
- /**
- * xmlXPathCompOpEvalToBoolean:
- * @ctxt: the XPath parser context
- *
- * Evaluates if the expression evaluates to true.
- *
- * Returns 1 if true, 0 if false and -1 on API or internal errors.
- */
- static int
- xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op,
- int isPredicate)
- {
- xmlXPathObjectPtr resObj = NULL;
- start:
- if (OP_LIMIT_EXCEEDED(ctxt, 1))
- return(0);
- /* comp = ctxt->comp; */
- switch (op->op) {
- case XPATH_OP_END:
- return (0);
- case XPATH_OP_VALUE:
- resObj = (xmlXPathObjectPtr) op->value4;
- if (isPredicate)
- return(xmlXPathEvaluatePredicateResult(ctxt, resObj));
- return(xmlXPathCastToBoolean(resObj));
- case XPATH_OP_SORT:
- /*
- * We don't need sorting for boolean results. Skip this one.
- */
- if (op->ch1 != -1) {
- op = &ctxt->comp->steps[op->ch1];
- goto start;
- }
- return(0);
- case XPATH_OP_COLLECT:
- if (op->ch1 == -1)
- return(0);
- xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch1]);
- if (ctxt->error != XPATH_EXPRESSION_OK)
- return(-1);
- xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 1);
- if (ctxt->error != XPATH_EXPRESSION_OK)
- return(-1);
- resObj = valuePop(ctxt);
- if (resObj == NULL)
- return(-1);
- break;
- default:
- /*
- * Fallback to call xmlXPathCompOpEval().
- */
- xmlXPathCompOpEval(ctxt, op);
- if (ctxt->error != XPATH_EXPRESSION_OK)
- return(-1);
- resObj = valuePop(ctxt);
- if (resObj == NULL)
- return(-1);
- break;
- }
- if (resObj) {
- int res;
- if (resObj->type == XPATH_BOOLEAN) {
- res = resObj->boolval;
- } else if (isPredicate) {
- /*
- * For predicates a result of type "number" is handled
- * differently:
- * SPEC XPath 1.0:
- * "If the result is a number, the result will be converted
- * to true if the number is equal to the context position
- * and will be converted to false otherwise;"
- */
- res = xmlXPathEvaluatePredicateResult(ctxt, resObj);
- } else {
- res = xmlXPathCastToBoolean(resObj);
- }
- xmlXPathReleaseObject(ctxt->context, resObj);
- return(res);
- }
- return(0);
- }
- #ifdef XPATH_STREAMING
- /**
- * xmlXPathRunStreamEval:
- * @ctxt: the XPath parser context with the compiled expression
- *
- * Evaluate the Precompiled Streamable XPath expression in the given context.
- */
- static int
- xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
- xmlXPathObjectPtr *resultSeq, int toBool)
- {
- int max_depth, min_depth;
- int from_root;
- int ret, depth;
- int eval_all_nodes;
- xmlNodePtr cur = NULL, limit = NULL;
- xmlStreamCtxtPtr patstream = NULL;
- int nb_nodes = 0;
- if ((ctxt == NULL) || (comp == NULL))
- return(-1);
- max_depth = xmlPatternMaxDepth(comp);
- if (max_depth == -1)
- return(-1);
- if (max_depth == -2)
- max_depth = 10000;
- min_depth = xmlPatternMinDepth(comp);
- if (min_depth == -1)
- return(-1);
- from_root = xmlPatternFromRoot(comp);
- if (from_root < 0)
- return(-1);
- #if 0
- printf("stream eval: depth %d from root %d\n", max_depth, from_root);
- #endif
- if (! toBool) {
- if (resultSeq == NULL)
- return(-1);
- *resultSeq = xmlXPathCacheNewNodeSet(ctxt, NULL);
- if (*resultSeq == NULL)
- return(-1);
- }
- /*
- * handle the special cases of "/" amd "." being matched
- */
- if (min_depth == 0) {
- if (from_root) {
- /* Select "/" */
- if (toBool)
- return(1);
- /* TODO: Check memory error. */
- xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval,
- (xmlNodePtr) ctxt->doc);
- } else {
- /* Select "self::node()" */
- if (toBool)
- return(1);
- /* TODO: Check memory error. */
- xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, ctxt->node);
- }
- }
- if (max_depth == 0) {
- return(0);
- }
- if (from_root) {
- cur = (xmlNodePtr)ctxt->doc;
- } else if (ctxt->node != NULL) {
- switch (ctxt->node->type) {
- case XML_ELEMENT_NODE:
- case XML_DOCUMENT_NODE:
- case XML_DOCUMENT_FRAG_NODE:
- case XML_HTML_DOCUMENT_NODE:
- #ifdef LIBXML_DOCB_ENABLED
- case XML_DOCB_DOCUMENT_NODE:
- #endif
- cur = ctxt->node;
- break;
- case XML_ATTRIBUTE_NODE:
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_ENTITY_REF_NODE:
- case XML_ENTITY_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- case XML_NOTATION_NODE:
- case XML_DTD_NODE:
- case XML_DOCUMENT_TYPE_NODE:
- case XML_ELEMENT_DECL:
- case XML_ATTRIBUTE_DECL:
- case XML_ENTITY_DECL:
- case XML_NAMESPACE_DECL:
- case XML_XINCLUDE_START:
- case XML_XINCLUDE_END:
- break;
- }
- limit = cur;
- }
- if (cur == NULL) {
- return(0);
- }
- patstream = xmlPatternGetStreamCtxt(comp);
- if (patstream == NULL) {
- /*
- * QUESTION TODO: Is this an error?
- */
- return(0);
- }
- eval_all_nodes = xmlStreamWantsAnyNode(patstream);
- if (from_root) {
- ret = xmlStreamPush(patstream, NULL, NULL);
- if (ret < 0) {
- } else if (ret == 1) {
- if (toBool)
- goto return_1;
- /* TODO: Check memory error. */
- xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
- }
- }
- depth = 0;
- goto scan_children;
- next_node:
- do {
- if (ctxt->opLimit != 0) {
- if (ctxt->opCount >= ctxt->opLimit) {
- xmlGenericError(xmlGenericErrorContext,
- "XPath operation limit exceeded\n");
- xmlFreeStreamCtxt(patstream);
- return(-1);
- }
- ctxt->opCount++;
- }
- nb_nodes++;
- switch (cur->type) {
- case XML_ELEMENT_NODE:
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_COMMENT_NODE:
- case XML_PI_NODE:
- if (cur->type == XML_ELEMENT_NODE) {
- ret = xmlStreamPush(patstream, cur->name,
- (cur->ns ? cur->ns->href : NULL));
- } else if (eval_all_nodes)
- ret = xmlStreamPushNode(patstream, NULL, NULL, cur->type);
- else
- break;
- if (ret < 0) {
- /* NOP. */
- } else if (ret == 1) {
- if (toBool)
- goto return_1;
- if (xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur)
- < 0) {
- ctxt->lastError.domain = XML_FROM_XPATH;
- ctxt->lastError.code = XML_ERR_NO_MEMORY;
- }
- }
- if ((cur->children == NULL) || (depth >= max_depth)) {
- ret = xmlStreamPop(patstream);
- while (cur->next != NULL) {
- cur = cur->next;
- if ((cur->type != XML_ENTITY_DECL) &&
- (cur->type != XML_DTD_NODE))
- goto next_node;
- }
- }
- default:
- break;
- }
- scan_children:
- if (cur->type == XML_NAMESPACE_DECL) break;
- if ((cur->children != NULL) && (depth < max_depth)) {
- /*
- * Do not descend on entities declarations
- */
- if (cur->children->type != XML_ENTITY_DECL) {
- cur = cur->children;
- depth++;
- /*
- * Skip DTDs
- */
- if (cur->type != XML_DTD_NODE)
- continue;
- }
- }
- if (cur == limit)
- break;
- while (cur->next != NULL) {
- cur = cur->next;
- if ((cur->type != XML_ENTITY_DECL) &&
- (cur->type != XML_DTD_NODE))
- goto next_node;
- }
- do {
- cur = cur->parent;
- depth--;
- if ((cur == NULL) || (cur == limit) ||
- (cur->type == XML_DOCUMENT_NODE))
- goto done;
- if (cur->type == XML_ELEMENT_NODE) {
- ret = xmlStreamPop(patstream);
- } else if ((eval_all_nodes) &&
- ((cur->type == XML_TEXT_NODE) ||
- (cur->type == XML_CDATA_SECTION_NODE) ||
- (cur->type == XML_COMMENT_NODE) ||
- (cur->type == XML_PI_NODE)))
- {
- ret = xmlStreamPop(patstream);
- }
- if (cur->next != NULL) {
- cur = cur->next;
- break;
- }
- } while (cur != NULL);
- } while ((cur != NULL) && (depth >= 0));
- done:
- #if 0
- printf("stream eval: checked %d nodes selected %d\n",
- nb_nodes, retObj->nodesetval->nodeNr);
- #endif
- if (patstream)
- xmlFreeStreamCtxt(patstream);
- return(0);
- return_1:
- if (patstream)
- xmlFreeStreamCtxt(patstream);
- return(1);
- }
- #endif /* XPATH_STREAMING */
- /**
- * xmlXPathRunEval:
- * @ctxt: the XPath parser context with the compiled expression
- * @toBool: evaluate to a boolean result
- *
- * Evaluate the Precompiled XPath expression in the given context.
- */
- static int
- xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
- {
- xmlXPathCompExprPtr comp;
- if ((ctxt == NULL) || (ctxt->comp == NULL))
- return(-1);
- ctxt->context->depth = 0;
- if (ctxt->valueTab == NULL) {
- /* Allocate the value stack */
- ctxt->valueTab = (xmlXPathObjectPtr *)
- xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
- if (ctxt->valueTab == NULL) {
- xmlXPathPErrMemory(ctxt, "creating evaluation context\n");
- xmlFree(ctxt);
- }
- ctxt->valueNr = 0;
- ctxt->valueMax = 10;
- ctxt->value = NULL;
- ctxt->valueFrame = 0;
- }
- #ifdef XPATH_STREAMING
- if (ctxt->comp->stream) {
- int res;
- if (toBool) {
- /*
- * Evaluation to boolean result.
- */
- res = xmlXPathRunStreamEval(ctxt->context,
- ctxt->comp->stream, NULL, 1);
- if (res != -1)
- return(res);
- } else {
- xmlXPathObjectPtr resObj = NULL;
- /*
- * Evaluation to a sequence.
- */
- res = xmlXPathRunStreamEval(ctxt->context,
- ctxt->comp->stream, &resObj, 0);
- if ((res != -1) && (resObj != NULL)) {
- valuePush(ctxt, resObj);
- return(0);
- }
- if (resObj != NULL)
- xmlXPathReleaseObject(ctxt->context, resObj);
- }
- /*
- * QUESTION TODO: This falls back to normal XPath evaluation
- * if res == -1. Is this intended?
- */
- }
- #endif
- comp = ctxt->comp;
- if (comp->last < 0) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathRunEval: last is less than zero\n");
- return(-1);
- }
- if (toBool)
- return(xmlXPathCompOpEvalToBoolean(ctxt,
- &comp->steps[comp->last], 0));
- else
- xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
- return(0);
- }
- /************************************************************************
- * *
- * Public interfaces *
- * *
- ************************************************************************/
- /**
- * xmlXPathEvalPredicate:
- * @ctxt: the XPath context
- * @res: the Predicate Expression evaluation result
- *
- * Evaluate a predicate result for the current node.
- * A PredicateExpr is evaluated by evaluating the Expr and converting
- * the result to a boolean. If the result is a number, the result will
- * be converted to true if the number is equal to the position of the
- * context node in the context node list (as returned by the position
- * function) and will be converted to false otherwise; if the result
- * is not a number, then the result will be converted as if by a call
- * to the boolean function.
- *
- * Returns 1 if predicate is true, 0 otherwise
- */
- int
- xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
- if ((ctxt == NULL) || (res == NULL)) return(0);
- switch (res->type) {
- case XPATH_BOOLEAN:
- return(res->boolval);
- case XPATH_NUMBER:
- return(res->floatval == ctxt->proximityPosition);
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- if (res->nodesetval == NULL)
- return(0);
- return(res->nodesetval->nodeNr != 0);
- case XPATH_STRING:
- return((res->stringval != NULL) &&
- (xmlStrlen(res->stringval) != 0));
- default:
- STRANGE
- }
- return(0);
- }
- /**
- * xmlXPathEvaluatePredicateResult:
- * @ctxt: the XPath Parser context
- * @res: the Predicate Expression evaluation result
- *
- * Evaluate a predicate result for the current node.
- * A PredicateExpr is evaluated by evaluating the Expr and converting
- * the result to a boolean. If the result is a number, the result will
- * be converted to true if the number is equal to the position of the
- * context node in the context node list (as returned by the position
- * function) and will be converted to false otherwise; if the result
- * is not a number, then the result will be converted as if by a call
- * to the boolean function.
- *
- * Returns 1 if predicate is true, 0 otherwise
- */
- int
- xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
- xmlXPathObjectPtr res) {
- if ((ctxt == NULL) || (res == NULL)) return(0);
- switch (res->type) {
- case XPATH_BOOLEAN:
- return(res->boolval);
- case XPATH_NUMBER:
- #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
- return((res->floatval == ctxt->context->proximityPosition) &&
- (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
- #else
- return(res->floatval == ctxt->context->proximityPosition);
- #endif
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- if (res->nodesetval == NULL)
- return(0);
- return(res->nodesetval->nodeNr != 0);
- case XPATH_STRING:
- return((res->stringval != NULL) && (res->stringval[0] != 0));
- #ifdef LIBXML_XPTR_ENABLED
- case XPATH_LOCATIONSET:{
- xmlLocationSetPtr ptr = res->user;
- if (ptr == NULL)
- return(0);
- return (ptr->locNr != 0);
- }
- #endif
- default:
- STRANGE
- }
- return(0);
- }
- #ifdef XPATH_STREAMING
- /**
- * xmlXPathTryStreamCompile:
- * @ctxt: an XPath context
- * @str: the XPath expression
- *
- * Try to compile the XPath expression as a streamable subset.
- *
- * Returns the compiled expression or NULL if failed to compile.
- */
- static xmlXPathCompExprPtr
- xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
- /*
- * Optimization: use streaming patterns when the XPath expression can
- * be compiled to a stream lookup
- */
- xmlPatternPtr stream;
- xmlXPathCompExprPtr comp;
- xmlDictPtr dict = NULL;
- const xmlChar **namespaces = NULL;
- xmlNsPtr ns;
- int i, j;
- if ((!xmlStrchr(str, '[')) && (!xmlStrchr(str, '(')) &&
- (!xmlStrchr(str, '@'))) {
- const xmlChar *tmp;
- /*
- * We don't try to handle expressions using the verbose axis
- * specifiers ("::"), just the simplified form at this point.
- * Additionally, if there is no list of namespaces available and
- * there's a ":" in the expression, indicating a prefixed QName,
- * then we won't try to compile either. xmlPatterncompile() needs
- * to have a list of namespaces at compilation time in order to
- * compile prefixed name tests.
- */
- tmp = xmlStrchr(str, ':');
- if ((tmp != NULL) &&
- ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':')))
- return(NULL);
- if (ctxt != NULL) {
- dict = ctxt->dict;
- if (ctxt->nsNr > 0) {
- namespaces = xmlMalloc(2 * (ctxt->nsNr + 1) * sizeof(xmlChar*));
- if (namespaces == NULL) {
- xmlXPathErrMemory(ctxt, "allocating namespaces array\n");
- return(NULL);
- }
- for (i = 0, j = 0; (j < ctxt->nsNr); j++) {
- ns = ctxt->namespaces[j];
- namespaces[i++] = ns->href;
- namespaces[i++] = ns->prefix;
- }
- namespaces[i++] = NULL;
- namespaces[i] = NULL;
- }
- }
- stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH, namespaces);
- if (namespaces != NULL) {
- xmlFree((xmlChar **)namespaces);
- }
- if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) {
- comp = xmlXPathNewCompExpr();
- if (comp == NULL) {
- xmlXPathErrMemory(ctxt, "allocating streamable expression\n");
- return(NULL);
- }
- comp->stream = stream;
- comp->dict = dict;
- if (comp->dict)
- xmlDictReference(comp->dict);
- return(comp);
- }
- xmlFreePattern(stream);
- }
- return(NULL);
- }
- #endif /* XPATH_STREAMING */
- static void
- xmlXPathOptimizeExpression(xmlXPathParserContextPtr pctxt,
- xmlXPathStepOpPtr op)
- {
- xmlXPathCompExprPtr comp = pctxt->comp;
- xmlXPathContextPtr ctxt;
- /*
- * Try to rewrite "descendant-or-self::node()/foo" to an optimized
- * internal representation.
- */
- if ((op->op == XPATH_OP_COLLECT /* 11 */) &&
- (op->ch1 != -1) &&
- (op->ch2 == -1 /* no predicate */))
- {
- xmlXPathStepOpPtr prevop = &comp->steps[op->ch1];
- if ((prevop->op == XPATH_OP_COLLECT /* 11 */) &&
- ((xmlXPathAxisVal) prevop->value ==
- AXIS_DESCENDANT_OR_SELF) &&
- (prevop->ch2 == -1) &&
- ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) &&
- ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE))
- {
- /*
- * This is a "descendant-or-self::node()" without predicates.
- * Try to eliminate it.
- */
- switch ((xmlXPathAxisVal) op->value) {
- case AXIS_CHILD:
- case AXIS_DESCENDANT:
- /*
- * Convert "descendant-or-self::node()/child::" or
- * "descendant-or-self::node()/descendant::" to
- * "descendant::"
- */
- op->ch1 = prevop->ch1;
- op->value = AXIS_DESCENDANT;
- break;
- case AXIS_SELF:
- case AXIS_DESCENDANT_OR_SELF:
- /*
- * Convert "descendant-or-self::node()/self::" or
- * "descendant-or-self::node()/descendant-or-self::" to
- * to "descendant-or-self::"
- */
- op->ch1 = prevop->ch1;
- op->value = AXIS_DESCENDANT_OR_SELF;
- break;
- default:
- break;
- }
- }
- }
- /* OP_VALUE has invalid ch1. */
- if (op->op == XPATH_OP_VALUE)
- return;
- /* Recurse */
- ctxt = pctxt->context;
- if (ctxt != NULL) {
- if (ctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
- return;
- ctxt->depth += 1;
- }
- if (op->ch1 != -1)
- xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch1]);
- if (op->ch2 != -1)
- xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch2]);
- if (ctxt != NULL)
- ctxt->depth -= 1;
- }
- /**
- * xmlXPathCtxtCompile:
- * @ctxt: an XPath context
- * @str: the XPath expression
- *
- * Compile an XPath expression
- *
- * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
- * the caller has to free the object.
- */
- xmlXPathCompExprPtr
- xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
- xmlXPathParserContextPtr pctxt;
- xmlXPathCompExprPtr comp;
- #ifdef XPATH_STREAMING
- comp = xmlXPathTryStreamCompile(ctxt, str);
- if (comp != NULL)
- return(comp);
- #endif
- xmlInitParser();
- pctxt = xmlXPathNewParserContext(str, ctxt);
- if (pctxt == NULL)
- return NULL;
- if (ctxt != NULL)
- ctxt->depth = 0;
- xmlXPathCompileExpr(pctxt, 1);
- if( pctxt->error != XPATH_EXPRESSION_OK )
- {
- xmlXPathFreeParserContext(pctxt);
- return(NULL);
- }
- if (*pctxt->cur != 0) {
- /*
- * aleksey: in some cases this line prints *second* error message
- * (see bug #78858) and probably this should be fixed.
- * However, we are not sure that all error messages are printed
- * out in other places. It's not critical so we leave it as-is for now
- */
- xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
- comp = NULL;
- } else {
- comp = pctxt->comp;
- if ((comp->nbStep > 1) && (comp->last >= 0)) {
- if (ctxt != NULL)
- ctxt->depth = 0;
- xmlXPathOptimizeExpression(pctxt, &comp->steps[comp->last]);
- }
- pctxt->comp = NULL;
- }
- xmlXPathFreeParserContext(pctxt);
- if (comp != NULL) {
- comp->expr = xmlStrdup(str);
- #ifdef DEBUG_EVAL_COUNTS
- comp->string = xmlStrdup(str);
- comp->nb = 0;
- #endif
- }
- return(comp);
- }
- /**
- * xmlXPathCompile:
- * @str: the XPath expression
- *
- * Compile an XPath expression
- *
- * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
- * the caller has to free the object.
- */
- xmlXPathCompExprPtr
- xmlXPathCompile(const xmlChar *str) {
- return(xmlXPathCtxtCompile(NULL, str));
- }
- /**
- * xmlXPathCompiledEvalInternal:
- * @comp: the compiled XPath expression
- * @ctxt: the XPath context
- * @resObj: the resulting XPath object or NULL
- * @toBool: 1 if only a boolean result is requested
- *
- * Evaluate the Precompiled XPath expression in the given context.
- * The caller has to free @resObj.
- *
- * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
- * the caller has to free the object.
- */
- static int
- xmlXPathCompiledEvalInternal(xmlXPathCompExprPtr comp,
- xmlXPathContextPtr ctxt,
- xmlXPathObjectPtr *resObjPtr,
- int toBool)
- {
- xmlXPathParserContextPtr pctxt;
- xmlXPathObjectPtr resObj;
- #ifndef LIBXML_THREAD_ENABLED
- static int reentance = 0;
- #endif
- int res;
- CHECK_CTXT_NEG(ctxt)
- if (comp == NULL)
- return(-1);
- xmlInitParser();
- #ifndef LIBXML_THREAD_ENABLED
- reentance++;
- if (reentance > 1)
- xmlXPathDisableOptimizer = 1;
- #endif
- #ifdef DEBUG_EVAL_COUNTS
- comp->nb++;
- if ((comp->string != NULL) && (comp->nb > 100)) {
- fprintf(stderr, "100 x %s\n", comp->string);
- comp->nb = 0;
- }
- #endif
- pctxt = xmlXPathCompParserContext(comp, ctxt);
- res = xmlXPathRunEval(pctxt, toBool);
- if (pctxt->error != XPATH_EXPRESSION_OK) {
- resObj = NULL;
- } else {
- resObj = valuePop(pctxt);
- if (resObj == NULL) {
- if (!toBool)
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompiledEval: No result on the stack.\n");
- } else if (pctxt->valueNr > 0) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompiledEval: %d object(s) left on the stack.\n",
- pctxt->valueNr);
- }
- }
- if (resObjPtr)
- *resObjPtr = resObj;
- else
- xmlXPathReleaseObject(ctxt, resObj);
- pctxt->comp = NULL;
- xmlXPathFreeParserContext(pctxt);
- #ifndef LIBXML_THREAD_ENABLED
- reentance--;
- #endif
- return(res);
- }
- /**
- * xmlXPathCompiledEval:
- * @comp: the compiled XPath expression
- * @ctx: the XPath context
- *
- * Evaluate the Precompiled XPath expression in the given context.
- *
- * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
- * the caller has to free the object.
- */
- xmlXPathObjectPtr
- xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx)
- {
- xmlXPathObjectPtr res = NULL;
- xmlXPathCompiledEvalInternal(comp, ctx, &res, 0);
- return(res);
- }
- /**
- * xmlXPathCompiledEvalToBoolean:
- * @comp: the compiled XPath expression
- * @ctxt: the XPath context
- *
- * Applies the XPath boolean() function on the result of the given
- * compiled expression.
- *
- * Returns 1 if the expression evaluated to true, 0 if to false and
- * -1 in API and internal errors.
- */
- int
- xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp,
- xmlXPathContextPtr ctxt)
- {
- return(xmlXPathCompiledEvalInternal(comp, ctxt, NULL, 1));
- }
- /**
- * xmlXPathEvalExpr:
- * @ctxt: the XPath Parser context
- *
- * Parse and evaluate an XPath expression in the given context,
- * then push the result on the context stack
- */
- void
- xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
- #ifdef XPATH_STREAMING
- xmlXPathCompExprPtr comp;
- #endif
- if (ctxt == NULL) return;
- #ifdef XPATH_STREAMING
- comp = xmlXPathTryStreamCompile(ctxt->context, ctxt->base);
- if (comp != NULL) {
- if (ctxt->comp != NULL)
- xmlXPathFreeCompExpr(ctxt->comp);
- ctxt->comp = comp;
- } else
- #endif
- {
- if (ctxt->context != NULL)
- ctxt->context->depth = 0;
- xmlXPathCompileExpr(ctxt, 1);
- CHECK_ERROR;
- /* Check for trailing characters. */
- if (*ctxt->cur != 0)
- XP_ERROR(XPATH_EXPR_ERROR);
- if ((ctxt->comp->nbStep > 1) && (ctxt->comp->last >= 0)) {
- if (ctxt->context != NULL)
- ctxt->context->depth = 0;
- xmlXPathOptimizeExpression(ctxt,
- &ctxt->comp->steps[ctxt->comp->last]);
- }
- }
- xmlXPathRunEval(ctxt, 0);
- }
- /**
- * xmlXPathEval:
- * @str: the XPath expression
- * @ctx: the XPath context
- *
- * Evaluate the XPath Location Path in the given context.
- *
- * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
- * the caller has to free the object.
- */
- xmlXPathObjectPtr
- xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
- xmlXPathParserContextPtr ctxt;
- xmlXPathObjectPtr res;
- CHECK_CTXT(ctx)
- xmlInitParser();
- ctxt = xmlXPathNewParserContext(str, ctx);
- if (ctxt == NULL)
- return NULL;
- xmlXPathEvalExpr(ctxt);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- res = NULL;
- } else {
- res = valuePop(ctxt);
- if (res == NULL) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompiledEval: No result on the stack.\n");
- } else if (ctxt->valueNr > 0) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompiledEval: %d object(s) left on the stack.\n",
- ctxt->valueNr);
- }
- }
- xmlXPathFreeParserContext(ctxt);
- return(res);
- }
- /**
- * xmlXPathSetContextNode:
- * @node: the node to to use as the context node
- * @ctx: the XPath context
- *
- * Sets 'node' as the context node. The node must be in the same
- * document as that associated with the context.
- *
- * Returns -1 in case of error or 0 if successful
- */
- int
- xmlXPathSetContextNode(xmlNodePtr node, xmlXPathContextPtr ctx) {
- if ((node == NULL) || (ctx == NULL))
- return(-1);
- if (node->doc == ctx->doc) {
- ctx->node = node;
- return(0);
- }
- return(-1);
- }
- /**
- * xmlXPathNodeEval:
- * @node: the node to to use as the context node
- * @str: the XPath expression
- * @ctx: the XPath context
- *
- * Evaluate the XPath Location Path in the given context. The node 'node'
- * is set as the context node. The context node is not restored.
- *
- * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
- * the caller has to free the object.
- */
- xmlXPathObjectPtr
- xmlXPathNodeEval(xmlNodePtr node, const xmlChar *str, xmlXPathContextPtr ctx) {
- if (str == NULL)
- return(NULL);
- if (xmlXPathSetContextNode(node, ctx) < 0)
- return(NULL);
- return(xmlXPathEval(str, ctx));
- }
- /**
- * xmlXPathEvalExpression:
- * @str: the XPath expression
- * @ctxt: the XPath context
- *
- * Alias for xmlXPathEval().
- *
- * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
- * the caller has to free the object.
- */
- xmlXPathObjectPtr
- xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
- return(xmlXPathEval(str, ctxt));
- }
- /************************************************************************
- * *
- * Extra functions not pertaining to the XPath spec *
- * *
- ************************************************************************/
- /**
- * xmlXPathEscapeUriFunction:
- * @ctxt: the XPath Parser context
- * @nargs: the number of arguments
- *
- * Implement the escape-uri() XPath function
- * string escape-uri(string $str, bool $escape-reserved)
- *
- * This function applies the URI escaping rules defined in section 2 of [RFC
- * 2396] to the string supplied as $uri-part, which typically represents all
- * or part of a URI. The effect of the function is to replace any special
- * character in the string by an escape sequence of the form %xx%yy...,
- * where xxyy... is the hexadecimal representation of the octets used to
- * represent the character in UTF-8.
- *
- * The set of characters that are escaped depends on the setting of the
- * boolean argument $escape-reserved.
- *
- * If $escape-reserved is true, all characters are escaped other than lower
- * case letters a-z, upper case letters A-Z, digits 0-9, and the characters
- * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
- * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
- * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
- * A-F).
- *
- * If $escape-reserved is false, the behavior differs in that characters
- * referred to in [RFC 2396] as reserved characters are not escaped. These
- * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
- *
- * [RFC 2396] does not define whether escaped URIs should use lower case or
- * upper case for hexadecimal digits. To ensure that escaped URIs can be
- * compared using string comparison functions, this function must always use
- * the upper-case letters A-F.
- *
- * Generally, $escape-reserved should be set to true when escaping a string
- * that is to form a single part of a URI, and to false when escaping an
- * entire URI or URI reference.
- *
- * In the case of non-ascii characters, the string is encoded according to
- * utf-8 and then converted according to RFC 2396.
- *
- * Examples
- * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
- * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
- * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
- * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
- *
- */
- static void
- xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- xmlXPathObjectPtr str;
- int escape_reserved;
- xmlBufPtr target;
- xmlChar *cptr;
- xmlChar escape[4];
- CHECK_ARITY(2);
- escape_reserved = xmlXPathPopBoolean(ctxt);
- CAST_TO_STRING;
- str = valuePop(ctxt);
- target = xmlBufCreate();
- escape[0] = '%';
- escape[3] = 0;
- if (target) {
- for (cptr = str->stringval; *cptr; cptr++) {
- if ((*cptr >= 'A' && *cptr <= 'Z') ||
- (*cptr >= 'a' && *cptr <= 'z') ||
- (*cptr >= '0' && *cptr <= '9') ||
- *cptr == '-' || *cptr == '_' || *cptr == '.' ||
- *cptr == '!' || *cptr == '~' || *cptr == '*' ||
- *cptr == '\''|| *cptr == '(' || *cptr == ')' ||
- (*cptr == '%' &&
- ((cptr[1] >= 'A' && cptr[1] <= 'F') ||
- (cptr[1] >= 'a' && cptr[1] <= 'f') ||
- (cptr[1] >= '0' && cptr[1] <= '9')) &&
- ((cptr[2] >= 'A' && cptr[2] <= 'F') ||
- (cptr[2] >= 'a' && cptr[2] <= 'f') ||
- (cptr[2] >= '0' && cptr[2] <= '9'))) ||
- (!escape_reserved &&
- (*cptr == ';' || *cptr == '/' || *cptr == '?' ||
- *cptr == ':' || *cptr == '@' || *cptr == '&' ||
- *cptr == '=' || *cptr == '+' || *cptr == '$' ||
- *cptr == ','))) {
- xmlBufAdd(target, cptr, 1);
- } else {
- if ((*cptr >> 4) < 10)
- escape[1] = '0' + (*cptr >> 4);
- else
- escape[1] = 'A' - 10 + (*cptr >> 4);
- if ((*cptr & 0xF) < 10)
- escape[2] = '0' + (*cptr & 0xF);
- else
- escape[2] = 'A' - 10 + (*cptr & 0xF);
- xmlBufAdd(target, &escape[0], 3);
- }
- }
- }
- valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
- xmlBufContent(target)));
- xmlBufFree(target);
- xmlXPathReleaseObject(ctxt->context, str);
- }
- /**
- * xmlXPathRegisterAllFunctions:
- * @ctxt: the XPath context
- *
- * Registers all default XPath functions in this context
- */
- void
- xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
- {
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
- xmlXPathBooleanFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
- xmlXPathCeilingFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
- xmlXPathCountFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
- xmlXPathConcatFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
- xmlXPathContainsFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
- xmlXPathIdFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
- xmlXPathFalseFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
- xmlXPathFloorFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
- xmlXPathLastFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
- xmlXPathLangFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
- xmlXPathLocalNameFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
- xmlXPathNotFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
- xmlXPathNameFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
- xmlXPathNamespaceURIFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
- xmlXPathNormalizeFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
- xmlXPathNumberFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
- xmlXPathPositionFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
- xmlXPathRoundFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
- xmlXPathStringFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
- xmlXPathStringLengthFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
- xmlXPathStartsWithFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
- xmlXPathSubstringFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
- xmlXPathSubstringBeforeFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
- xmlXPathSubstringAfterFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
- xmlXPathSumFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
- xmlXPathTrueFunction);
- xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
- xmlXPathTranslateFunction);
- xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
- (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions",
- xmlXPathEscapeUriFunction);
- }
- #endif /* LIBXML_XPATH_ENABLED */
- #define bottom_xpath
- #include "elfgcchack.h"
|