1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732 |
- /*
- * 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
- */
- void
- xmlXPathInit(void) {
- /* Use MSVC definitions */
- xmlXPathNAN = NAN;
- xmlXPathPINF = INFINITY;
- xmlXPathNINF = -INFINITY;
- }
- /**
- * 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 -= 1;
- }
- /**
- * 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);
- CHECK_ERROR NULL;
- *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"
|