| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834 |
- /*
- * 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>
- #include <math.h>
- #include <float.h>
- #include <ctype.h>
- #include <libxml/xmlmemory.h>
- #include <libxml/tree.h>
- #include <libxml/xpath.h>
- #include <libxml/xpathInternals.h>
- #include <libxml/parserInternals.h>
- #include <libxml/hash.h>
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- #include <libxml/xpointer.h>
- #endif
- #ifdef LIBXML_DEBUG_ENABLED
- #include <libxml/debugXML.h>
- #endif
- #include <libxml/xmlerror.h>
- #include <libxml/threads.h>
- #ifdef LIBXML_PATTERN_ENABLED
- #include <libxml/pattern.h>
- #endif
- #include "private/buf.h"
- #include "private/error.h"
- #include "private/xpath.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
- /*
- * 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
- #elif defined(_WIN32)
- /* Windows typically limits stack size to 1MB. */
- #define XPATH_MAX_RECURSION_DEPTH 1000
- #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)
- */
- #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
- /************************************************************************
- * *
- * Floating point stuff *
- * *
- ************************************************************************/
- double xmlXPathNAN = 0.0;
- double xmlXPathPINF = 0.0;
- double xmlXPathNINF = 0.0;
- /**
- * xmlXPathInit:
- *
- * DEPRECATED: Alias for xmlInitParser.
- */
- void
- xmlXPathInit(void) {
- xmlInitParser();
- }
- /**
- * xmlInitXPathInternal:
- *
- * Initialize the XPath environment
- */
- ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
- void
- xmlInitXPathInternal(void) {
- #if defined(NAN) && defined(INFINITY)
- xmlXPathNAN = NAN;
- xmlXPathPINF = INFINITY;
- xmlXPathNINF = -INFINITY;
- #else
- /* MSVC doesn't allow division by zero in constant expressions. */
- double zero = 0.0;
- xmlXPathNAN = 0.0 / zero;
- xmlXPathPINF = 1.0 / zero;
- xmlXPathNINF = -xmlXPathPINF;
- #endif
- }
- /**
- * xmlXPathIsNaN:
- * @val: a double value
- *
- * Checks whether a double is a NaN.
- *
- * 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
- *
- * Checks whether a double is an infinity.
- *
- * 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] = ' '
- */
- 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
- static void
- xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes);
- #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 */
- /************************************************************************
- * *
- * 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* const 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;
- }
- /* Only report the first error */
- if (ctxt->error != 0)
- 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->size <= list->number) {
- void **tmp;
- size_t newSize;
- if (list->size == 0) {
- if (initialSize <= 0)
- initialSize = 1;
- newSize = initialSize;
- } else {
- if (list->size > 50000000) {
- xmlXPathErrMemory(NULL,
- "xmlPointerListAddSize: re-allocating item\n");
- return(-1);
- }
- newSize = list->size * 2;
- }
- tmp = (void **) xmlRealloc(list->items, newSize * sizeof(void *));
- if (tmp == NULL) {
- xmlXPathErrMemory(NULL,
- "xmlPointerListAddSize: re-allocating item\n");
- return(-1);
- }
- list->items = tmp;
- list->size = newSize;
- }
- 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_LOCS_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 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;
- 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 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;
- };
- /************************************************************************
- * *
- * 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_LOCS_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_LOCS_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;
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- 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:
- fprintf(output, "Object is a Location Set:\n");
- xmlXPathDebugDumpLocationSet(output,
- (xmlLocationSetPtr) cur->user, depth);
- break;
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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_LOCS_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);
- }
- }
- #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 , 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);
- }
- 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.
- *
- * In case of error the node set is destroyed and NULL is returned.
- */
- 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;
- 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;
- 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;
- 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;
- }
- }
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- xmlNodeSetPtr set;
- /*
- * Fallback to misc-cache.
- */
- set = xmlXPathNodeSetCreate(val);
- if (set == NULL) {
- ctxt->lastError.domain = XML_FROM_XPATH;
- ctxt->lastError.code = XML_ERR_NO_MEMORY;
- return(NULL);
- }
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_NODESET;
- ret->boolval = 0;
- ret->nodesetval = set;
- return(ret);
- }
- }
- return(xmlXPathNewNodeSet(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;
- xmlChar *copy;
- if (val == NULL)
- val = BAD_CAST "";
- copy = xmlStrdup(val);
- if (copy == NULL) {
- xmlXPathErrMemory(ctxt, NULL);
- return(NULL);
- }
- ret = (xmlXPathObjectPtr)
- cache->stringObjs->items[--cache->stringObjs->number];
- ret->type = XPATH_STRING;
- ret->stringval = copy;
- return(ret);
- } else if ((cache->miscObjs != NULL) &&
- (cache->miscObjs->number != 0))
- {
- xmlXPathObjectPtr ret;
- xmlChar *copy;
- if (val == NULL)
- val = BAD_CAST "";
- copy = xmlStrdup(val);
- if (copy == NULL) {
- xmlXPathErrMemory(ctxt, NULL);
- return(NULL);
- }
- ret = (xmlXPathObjectPtr)
- cache->miscObjs->items[--cache->miscObjs->number];
- ret->type = XPATH_STRING;
- ret->stringval = copy;
- return(ret);
- }
- }
- return(xmlXPathNewString(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)
- {
- return xmlXPathCacheNewString(ctxt, BAD_CAST 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);
- 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);
- 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;
- 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;
- 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:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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 *
- * *
- ************************************************************************/
- /**
- * 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);
- 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.
- *
- * The object is destroyed 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");
- xmlXPathFreeObject(value);
- return (-1);
- }
- tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
- 2 * ctxt->valueMax *
- sizeof(ctxt->valueTab[0]));
- if (tmp == NULL) {
- xmlXPathPErrMemory(ctxt, "pushing value\n");
- xmlXPathFreeObject(value);
- 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++] = 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(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 , 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 * sizeof(xmlNodePtr));
- ret->nodeMax = XML_NODESET_DEFAULT;
- if (val->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) val;
- xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- if (nsNode == NULL) {
- xmlXPathFreeNodeSet(ret);
- return(NULL);
- }
- ret->nodeTab[ret->nodeNr++] = nsNode;
- } 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;
- xmlNodePtr nsNode;
- 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 * 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;
- }
- nsNode = xmlXPathNodeSetDupNs(node, ns);
- if(nsNode == NULL)
- return(-1);
- cur->nodeTab[cur->nodeNr++] = nsNode;
- 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 * 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;
- xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- if (nsNode == NULL)
- return(-1);
- cur->nodeTab[cur->nodeNr++] = nsNode;
- } 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 * 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;
- xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- if (nsNode == NULL)
- return(-1);
- cur->nodeTab[cur->nodeNr++] = nsNode;
- } 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.
- *
- * Frees @val1 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);
- }
- /* @@ 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");
- goto error;
- }
- memset(val1->nodeTab, 0 ,
- XML_NODESET_DEFAULT * 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");
- goto error;
- }
- temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
- sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- goto error;
- }
- val1->nodeTab = temp;
- val1->nodeMax *= 2;
- }
- if (n2->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) n2;
- xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
- if (nsNode == NULL)
- goto error;
- val1->nodeTab[val1->nodeNr++] = nsNode;
- } else
- val1->nodeTab[val1->nodeNr++] = n2;
- }
- return(val1);
- error:
- xmlXPathFreeNodeSet(val1);
- return(NULL);
- }
- /**
- * 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.
- *
- * Frees @set1 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.
- */
- 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");
- goto error;
- }
- memset(set1->nodeTab, 0,
- XML_NODESET_DEFAULT * 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");
- goto error;
- }
- temp = (xmlNodePtr *) xmlRealloc(
- set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- goto error;
- }
- set1->nodeTab = temp;
- set1->nodeMax *= 2;
- }
- set1->nodeTab[set1->nodeNr++] = n2;
- skip_node:
- set2->nodeTab[i] = NULL;
- }
- }
- set2->nodeNr = 0;
- return(set1);
- error:
- xmlXPathFreeNodeSet(set1);
- xmlXPathNodeSetClear(set2, 1);
- return(NULL);
- }
- /**
- * 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.
- *
- * Frees @set1 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");
- goto error;
- }
- memset(set1->nodeTab, 0,
- XML_NODESET_DEFAULT * 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");
- goto error;
- }
- temp = (xmlNodePtr *) xmlRealloc(
- set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
- if (temp == NULL) {
- xmlXPathErrMemory(NULL, "merging nodeset\n");
- goto error;
- }
- set1->nodeTab = temp;
- set1->nodeMax *= 2;
- }
- set1->nodeTab[set1->nodeNr++] = n2;
- set2->nodeTab[i] = NULL;
- }
- }
- set2->nodeNr = 0;
- return(set1);
- error:
- xmlXPathFreeNodeSet(set1);
- xmlXPathNodeSetClear(set2, 1);
- return(NULL);
- }
- /**
- * 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 */
- 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);
- }
- /**
- * 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 , 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 @@ */
- 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 , sizeof(xmlXPathObject));
- ret->type = XPATH_XSLT_TREE;
- ret->boolval = 1;
- ret->user = (void *) val;
- ret->nodesetval = xmlXPathNodeSetCreate(val);
- 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.
- *
- * In case of error the node set is destroyed and NULL is returned.
- */
- xmlXPathObjectPtr
- xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating node set object\n");
- xmlXPathFreeNodeSet(val);
- return(NULL);
- }
- memset(ret, 0 , sizeof(xmlXPathObject));
- ret->type = XPATH_NODESET;
- ret->nodesetval = val;
- 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;
- 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) {
- if (xmlHashAddEntry(hash, strval, strval) < 0) {
- xmlFree(strval);
- goto error;
- }
- if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
- goto error;
- } else {
- xmlFree(strval);
- }
- }
- xmlHashFree(hash, xmlHashDefaultDeallocator);
- return(ret);
- error:
- xmlHashFree(hash, xmlHashDefaultDeallocator);
- xmlXPathFreeNodeSet(ret);
- return(NULL);
- }
- /**
- * 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_FPTR_CAST_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_FPTR_CAST_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) {
- xmlChar *copy;
- 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));
- copy = xmlStrdup(ns_uri);
- if (copy == NULL)
- return(-1);
- if (xmlHashUpdateEntry(ctxt->nsHash, prefix, copy,
- xmlHashDefaultDeallocator) < 0) {
- xmlFree(copy);
- return(-1);
- }
- return(0);
- }
- /**
- * 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 , sizeof(xmlXPathObject));
- ret->type = XPATH_NUMBER;
- ret->floatval = val;
- 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 , sizeof(xmlXPathObject));
- ret->type = XPATH_BOOLEAN;
- ret->boolval = (val != 0);
- 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 , sizeof(xmlXPathObject));
- ret->type = XPATH_STRING;
- if (val == NULL)
- val = BAD_CAST "";
- ret->stringval = xmlStrdup(val);
- if (ret->stringval == NULL) {
- xmlFree(ret);
- return(NULL);
- }
- return(ret);
- }
- /**
- * xmlXPathWrapString:
- * @val: the xmlChar * value
- *
- * Wraps the @val string into an XPath object.
- *
- * Returns the newly created object.
- *
- * Frees @val in case of error.
- */
- xmlXPathObjectPtr
- xmlXPathWrapString (xmlChar *val) {
- xmlXPathObjectPtr ret;
- ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating string object\n");
- xmlFree(val);
- return(NULL);
- }
- memset(ret, 0 , sizeof(xmlXPathObject));
- ret->type = XPATH_STRING;
- ret->stringval = val;
- 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) {
- return(xmlXPathNewString(BAD_CAST val));
- }
- /**
- * 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 , sizeof(xmlXPathObject));
- ret->type = XPATH_USERS;
- ret->user = val;
- 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 , sizeof(xmlXPathObject));
- switch (val->type) {
- case XPATH_BOOLEAN:
- case XPATH_NUMBER:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- break;
- case XPATH_STRING:
- ret->stringval = xmlStrdup(val->stringval);
- if (ret->stringval == NULL) {
- xmlFree(ret);
- return(NULL);
- }
- 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;
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_LOCATIONSET:
- {
- 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_LOCS_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);
- }
- 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_LOCS_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:
- 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);
- 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:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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 , 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 , 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 , 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->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(string[0] + (string[1] << 8));
- case XML_NAMESPACE_DECL:
- string = ((xmlNsPtr)node)->href;
- if (string == NULL)
- return(0);
- if (string[0] == 0)
- return(0);
- return(string[0] + (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 + (string[0] << 8));
- }
- if (string[1] == 0) {
- len = 1;
- ret = string[0];
- } else {
- return(string[0] + (string[1] << 8));
- }
- }
- /*
- * Skip to next node
- */
- if ((tmp->children != NULL) &&
- (tmp->type != XML_DTD_NODE) &&
- (tmp->type != XML_ENTITY_REF_NODE) &&
- (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(0);
- if (string[0] == 0)
- return(0);
- return(string[0] + (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);
- CHECK_ERROR0;
- 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:
- break;
- case XPATH_BOOLEAN:
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- break;
- case XPATH_BOOLEAN:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- TODO
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- break;
- }
- break;
- case XPATH_NUMBER:
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- break;
- case XPATH_BOOLEAN:
- ret = (arg2->boolval==
- xmlXPathCastNumberToBoolean(arg1->floatval));
- break;
- case XPATH_STRING:
- valuePush(ctxt, arg2);
- xmlXPathNumberFunction(ctxt, 1);
- arg2 = valuePop(ctxt);
- if (ctxt->error)
- break;
- /* 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- TODO
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- break;
- }
- break;
- case XPATH_STRING:
- switch (arg2->type) {
- case XPATH_UNDEFINED:
- 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);
- if (ctxt->error)
- break;
- /* 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- TODO
- break;
- case XPATH_NODESET:
- case XPATH_XSLT_TREE:
- break;
- }
- break;
- case XPATH_USERS:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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) {
- 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:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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) {
- 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:
- 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:
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- case XPATH_POINT:
- case XPATH_RANGE:
- case XPATH_LOCATIONSET:
- #endif /* LIBXML_XPTR_LOCS_ENABLED */
- 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 (arg2->type != XPATH_NUMBER) {
- valuePush(ctxt, arg2);
- xmlXPathNumberFunction(ctxt, 1);
- arg2 = valuePop(ctxt);
- }
- if (ctxt->error)
- goto error;
- /*
- * 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;
- }
- }
- }
- error:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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));
- } 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));
- } 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)
- xmlXPathPErrMemory(ctxt, NULL);
- 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 = 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 = 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) {
- xmlChar *source, *target;
- int 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);
- source = ctxt->value->stringval;
- if (source == NULL)
- return;
- 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 = 1;
- } else {
- if (blank) {
- *target++ = 0x20;
- blank = 0;
- }
- *target++ = *source;
- }
- source++;
- }
- *target = 0;
- }
- /**
- * 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;
- int 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(*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);
- 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);
- 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 == '-') {
- isneg = 1;
- cur++;
- }
- if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) {
- return(xmlXPathNAN);
- }
- #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) {
- xmlXPathPErrMemory(ctxt, 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;
- 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 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)))) {
- 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)) {
- lc = 1;
- xmlFree(name);
- } else if (name != NULL) {
- int len =xmlStrlen(name);
- while (NXT(len) != 0) {
- if (NXT(len) == '/') {
- /* element name */
- lc = 1;
- break;
- } else if (IS_BLANK_CH(NXT(len))) {
- /* ignore blanks */
- ;
- } else if (NXT(len) == ':') {
- lc = 1;
- break;
- } else if ((NXT(len) == '(')) {
- /* Node Type or Function */
- if (xmlXPathIsNodeType(name)) {
- lc = 1;
- #ifdef LIBXML_XPTR_LOCS_ENABLED
- } else if (ctxt->xptr &&
- xmlStrEqual(name, BAD_CAST "range-to")) {
- lc = 1;
- #endif
- } else {
- lc = 0;
- }
- break;
- } else if ((NXT(len) == '[')) {
- /* element name */
- lc = 1;
- break;
- } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
- (NXT(len) == '=')) {
- lc = 1;
- break;
- } else {
- lc = 1;
- break;
- }
- len++;
- }
- if (NXT(len) == 0) {
- /* element name */
- lc = 1;
- }
- xmlFree(name);
- } else {
- /* make sure all cases are covered explicitly */
- XP_ERROR(XPATH_EXPR_ERROR);
- }
- }
- if (lc) {
- if (CUR == '/') {
- PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
- } else {
- PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
- }
- xmlXPathCompLocationPath(ctxt);
- } else {
- xmlXPathCompFilterExpr(ctxt);
- CHECK_ERROR;
- if ((CUR == '/') && (NXT(1) == '/')) {
- SKIP(2);
- SKIP_BLANKS;
- PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
- NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
- xmlXPathCompRelativeLocationPath(ctxt);
- } else if (CUR == '/') {
- xmlXPathCompRelativeLocationPath(ctxt);
- }
- }
- SKIP_BLANKS;
- }
- /**
- * xmlXPathCompUnionExpr:
- * @ctxt: the XPath Parser context
- *
- * [18] UnionExpr ::= PathExpr
- * | UnionExpr '|' PathExpr
- *
- * Compile an union expression.
- */
- static void
- xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompPathExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while (CUR == '|') {
- int op1 = ctxt->comp->last;
- PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
- NEXT;
- SKIP_BLANKS;
- xmlXPathCompPathExpr(ctxt);
- PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompUnaryExpr:
- * @ctxt: the XPath Parser context
- *
- * [27] UnaryExpr ::= UnionExpr
- * | '-' UnaryExpr
- *
- * Compile an unary expression.
- */
- static void
- xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
- int minus = 0;
- int found = 0;
- SKIP_BLANKS;
- while (CUR == '-') {
- minus = 1 - minus;
- found = 1;
- NEXT;
- SKIP_BLANKS;
- }
- xmlXPathCompUnionExpr(ctxt);
- CHECK_ERROR;
- if (found) {
- if (minus)
- PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
- else
- PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
- }
- }
- /**
- * xmlXPathCompMultiplicativeExpr:
- * @ctxt: the XPath Parser context
- *
- * [26] MultiplicativeExpr ::= UnaryExpr
- * | MultiplicativeExpr MultiplyOperator UnaryExpr
- * | MultiplicativeExpr 'div' UnaryExpr
- * | MultiplicativeExpr 'mod' UnaryExpr
- * [34] MultiplyOperator ::= '*'
- *
- * Compile an Additive expression.
- */
- static void
- xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompUnaryExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '*') ||
- ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
- ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
- int op = -1;
- int op1 = ctxt->comp->last;
- if (CUR == '*') {
- op = 0;
- NEXT;
- } else if (CUR == 'd') {
- op = 1;
- SKIP(3);
- } else if (CUR == 'm') {
- op = 2;
- SKIP(3);
- }
- SKIP_BLANKS;
- xmlXPathCompUnaryExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompAdditiveExpr:
- * @ctxt: the XPath Parser context
- *
- * [25] AdditiveExpr ::= MultiplicativeExpr
- * | AdditiveExpr '+' MultiplicativeExpr
- * | AdditiveExpr '-' MultiplicativeExpr
- *
- * Compile an Additive expression.
- */
- static void
- xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompMultiplicativeExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '+') || (CUR == '-')) {
- int plus;
- int op1 = ctxt->comp->last;
- if (CUR == '+') plus = 1;
- else plus = 0;
- NEXT;
- SKIP_BLANKS;
- xmlXPathCompMultiplicativeExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompRelationalExpr:
- * @ctxt: the XPath Parser context
- *
- * [24] RelationalExpr ::= AdditiveExpr
- * | RelationalExpr '<' AdditiveExpr
- * | RelationalExpr '>' AdditiveExpr
- * | RelationalExpr '<=' AdditiveExpr
- * | RelationalExpr '>=' AdditiveExpr
- *
- * A <= B > C is allowed ? Answer from James, yes with
- * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
- * which is basically what got implemented.
- *
- * Compile a Relational expression, then push the result
- * on the stack
- */
- static void
- xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompAdditiveExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '<') || (CUR == '>')) {
- int inf, strict;
- int op1 = ctxt->comp->last;
- if (CUR == '<') inf = 1;
- else inf = 0;
- if (NXT(1) == '=') strict = 0;
- else strict = 1;
- NEXT;
- if (!strict) NEXT;
- SKIP_BLANKS;
- xmlXPathCompAdditiveExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompEqualityExpr:
- * @ctxt: the XPath Parser context
- *
- * [23] EqualityExpr ::= RelationalExpr
- * | EqualityExpr '=' RelationalExpr
- * | EqualityExpr '!=' RelationalExpr
- *
- * A != B != C is allowed ? Answer from James, yes with
- * (RelationalExpr = RelationalExpr) = RelationalExpr
- * (RelationalExpr != RelationalExpr) != RelationalExpr
- * which is basically what got implemented.
- *
- * Compile an Equality expression.
- *
- */
- static void
- xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompRelationalExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
- int eq;
- int op1 = ctxt->comp->last;
- if (CUR == '=') eq = 1;
- else eq = 0;
- NEXT;
- if (!eq) NEXT;
- SKIP_BLANKS;
- xmlXPathCompRelationalExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompAndExpr:
- * @ctxt: the XPath Parser context
- *
- * [22] AndExpr ::= EqualityExpr
- * | AndExpr 'and' EqualityExpr
- *
- * Compile an AND expression.
- *
- */
- static void
- xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
- xmlXPathCompEqualityExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
- int op1 = ctxt->comp->last;
- SKIP(3);
- SKIP_BLANKS;
- xmlXPathCompEqualityExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
- SKIP_BLANKS;
- }
- }
- /**
- * xmlXPathCompileExpr:
- * @ctxt: the XPath Parser context
- *
- * [14] Expr ::= OrExpr
- * [21] OrExpr ::= AndExpr
- * | OrExpr 'or' AndExpr
- *
- * Parse and compile an expression
- */
- static void
- xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
- xmlXPathContextPtr xpctxt = ctxt->context;
- if (xpctxt != NULL) {
- if (xpctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
- XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
- /*
- * Parsing a single '(' pushes about 10 functions on the call stack
- * before recursing!
- */
- xpctxt->depth += 10;
- }
- xmlXPathCompAndExpr(ctxt);
- CHECK_ERROR;
- SKIP_BLANKS;
- while ((CUR == 'o') && (NXT(1) == 'r')) {
- int op1 = ctxt->comp->last;
- SKIP(2);
- SKIP_BLANKS;
- xmlXPathCompAndExpr(ctxt);
- CHECK_ERROR;
- PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
- SKIP_BLANKS;
- }
- if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
- /* more ops could be optimized too */
- /*
- * This is the main place to eliminate sorting for
- * operations which don't require a sorted node-set.
- * E.g. count().
- */
- PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
- }
- if (xpctxt != NULL)
- xpctxt->depth -= 10;
- }
- /**
- * xmlXPathCompPredicate:
- * @ctxt: the XPath Parser context
- * @filter: act as a filter
- *
- * [8] Predicate ::= '[' PredicateExpr ']'
- * [9] PredicateExpr ::= Expr
- *
- * Compile a predicate expression
- */
- static void
- xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
- int op1 = ctxt->comp->last;
- SKIP_BLANKS;
- if (CUR != '[') {
- XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
- }
- NEXT;
- SKIP_BLANKS;
- ctxt->comp->last = -1;
- /*
- * This call to xmlXPathCompileExpr() will deactivate sorting
- * of the predicate result.
- * TODO: Sorting is still activated for filters, since I'm not
- * sure if needed. Normally sorting should not be needed, since
- * a filter can only diminish the number of items in a sequence,
- * but won't change its order; so if the initial sequence is sorted,
- * subsequent sorting is not needed.
- */
- if (! filter)
- xmlXPathCompileExpr(ctxt, 0);
- else
- xmlXPathCompileExpr(ctxt, 1);
- CHECK_ERROR;
- if (CUR != ']') {
- XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
- }
- if (filter)
- PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
- else
- PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
- NEXT;
- SKIP_BLANKS;
- }
- /**
- * xmlXPathCompNodeTest:
- * @ctxt: the XPath Parser context
- * @test: pointer to a xmlXPathTestVal
- * @type: pointer to a xmlXPathTypeVal
- * @prefix: placeholder for a possible name prefix
- *
- * [7] NodeTest ::= NameTest
- * | NodeType '(' ')'
- * | 'processing-instruction' '(' Literal ')'
- *
- * [37] NameTest ::= '*'
- * | NCName ':' '*'
- * | QName
- * [38] NodeType ::= 'comment'
- * | 'text'
- * | 'processing-instruction'
- * | 'node'
- *
- * Returns the name found and updates @test, @type and @prefix appropriately
- */
- static xmlChar *
- xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
- xmlXPathTypeVal *type, xmlChar **prefix,
- xmlChar *name) {
- int blanks;
- if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
- STRANGE;
- return(NULL);
- }
- *type = (xmlXPathTypeVal) 0;
- *test = (xmlXPathTestVal) 0;
- *prefix = NULL;
- SKIP_BLANKS;
- if ((name == NULL) && (CUR == '*')) {
- /*
- * All elements
- */
- NEXT;
- *test = NODE_TEST_ALL;
- return(NULL);
- }
- if (name == NULL)
- name = xmlXPathParseNCName(ctxt);
- if (name == NULL) {
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- blanks = IS_BLANK_CH(CUR);
- SKIP_BLANKS;
- if (CUR == '(') {
- NEXT;
- /*
- * NodeType or PI search
- */
- if (xmlStrEqual(name, BAD_CAST "comment"))
- *type = NODE_TYPE_COMMENT;
- else if (xmlStrEqual(name, BAD_CAST "node"))
- *type = NODE_TYPE_NODE;
- else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
- *type = NODE_TYPE_PI;
- else if (xmlStrEqual(name, BAD_CAST "text"))
- *type = NODE_TYPE_TEXT;
- else {
- if (name != NULL)
- xmlFree(name);
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- *test = NODE_TEST_TYPE;
- SKIP_BLANKS;
- if (*type == NODE_TYPE_PI) {
- /*
- * Specific case: search a PI by name.
- */
- if (name != NULL)
- xmlFree(name);
- name = NULL;
- if (CUR != ')') {
- name = xmlXPathParseLiteral(ctxt);
- if (name == NULL) {
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- *test = NODE_TEST_PI;
- SKIP_BLANKS;
- }
- }
- if (CUR != ')') {
- if (name != NULL)
- xmlFree(name);
- XP_ERRORNULL(XPATH_UNCLOSED_ERROR);
- }
- NEXT;
- return(name);
- }
- *test = NODE_TEST_NAME;
- if ((!blanks) && (CUR == ':')) {
- NEXT;
- /*
- * Since currently the parser context don't have a
- * namespace list associated:
- * The namespace name for this prefix can be computed
- * only at evaluation time. The compilation is done
- * outside of any context.
- */
- #if 0
- *prefix = xmlXPathNsLookup(ctxt->context, name);
- if (name != NULL)
- xmlFree(name);
- if (*prefix == NULL) {
- XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
- }
- #else
- *prefix = name;
- #endif
- if (CUR == '*') {
- /*
- * All elements
- */
- NEXT;
- *test = NODE_TEST_ALL;
- return(NULL);
- }
- name = xmlXPathParseNCName(ctxt);
- if (name == NULL) {
- XP_ERRORNULL(XPATH_EXPR_ERROR);
- }
- }
- return(name);
- }
- /**
- * xmlXPathIsAxisName:
- * @name: a preparsed name token
- *
- * [6] AxisName ::= 'ancestor'
- * | 'ancestor-or-self'
- * | 'attribute'
- * | 'child'
- * | 'descendant'
- * | 'descendant-or-self'
- * | 'following'
- * | 'following-sibling'
- * | 'namespace'
- * | 'parent'
- * | 'preceding'
- * | 'preceding-sibling'
- * | 'self'
- *
- * Returns the axis or 0
- */
- static xmlXPathAxisVal
- xmlXPathIsAxisName(const xmlChar *name) {
- xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
- switch (name[0]) {
- case 'a':
- if (xmlStrEqual(name, BAD_CAST "ancestor"))
- ret = AXIS_ANCESTOR;
- if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
- ret = AXIS_ANCESTOR_OR_SELF;
- if (xmlStrEqual(name, BAD_CAST "attribute"))
- ret = AXIS_ATTRIBUTE;
- break;
- case 'c':
- if (xmlStrEqual(name, BAD_CAST "child"))
- ret = AXIS_CHILD;
- break;
- case 'd':
- if (xmlStrEqual(name, BAD_CAST "descendant"))
- ret = AXIS_DESCENDANT;
- if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
- ret = AXIS_DESCENDANT_OR_SELF;
- break;
- case 'f':
- if (xmlStrEqual(name, BAD_CAST "following"))
- ret = AXIS_FOLLOWING;
- if (xmlStrEqual(name, BAD_CAST "following-sibling"))
- ret = AXIS_FOLLOWING_SIBLING;
- break;
- case 'n':
- if (xmlStrEqual(name, BAD_CAST "namespace"))
- ret = AXIS_NAMESPACE;
- break;
- case 'p':
- if (xmlStrEqual(name, BAD_CAST "parent"))
- ret = AXIS_PARENT;
- if (xmlStrEqual(name, BAD_CAST "preceding"))
- ret = AXIS_PRECEDING;
- if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
- ret = AXIS_PRECEDING_SIBLING;
- break;
- case 's':
- if (xmlStrEqual(name, BAD_CAST "self"))
- ret = AXIS_SELF;
- break;
- }
- return(ret);
- }
- /**
- * xmlXPathCompStep:
- * @ctxt: the XPath Parser context
- *
- * [4] Step ::= AxisSpecifier NodeTest Predicate*
- * | AbbreviatedStep
- *
- * [12] AbbreviatedStep ::= '.' | '..'
- *
- * [5] AxisSpecifier ::= AxisName '::'
- * | AbbreviatedAxisSpecifier
- *
- * [13] AbbreviatedAxisSpecifier ::= '@'?
- *
- * Modified for XPtr range support as:
- *
- * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
- * | AbbreviatedStep
- * | 'range-to' '(' Expr ')' Predicate*
- *
- * Compile one step in a Location Path
- * A location step of . is short for self::node(). This is
- * particularly useful in conjunction with //. For example, the
- * location path .//para is short for
- * self::node()/descendant-or-self::node()/child::para
- * and so will select all para descendant elements of the context
- * node.
- * Similarly, a location step of .. is short for parent::node().
- * For example, ../title is short for parent::node()/child::title
- * and so will select the title children of the parent of the context
- * node.
- */
- static void
- xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
- #ifdef LIBXML_XPTR_LOCS_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_LOCS_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 LIBXML_XPTR_LOCS_ENABLED
- eval_predicates:
- #endif
- op1 = ctxt->comp->last;
- ctxt->comp->last = -1;
- SKIP_BLANKS;
- while (CUR == '[') {
- xmlXPathCompPredicate(ctxt, 0);
- }
- #ifdef LIBXML_XPTR_LOCS_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);
- }
- }
- }
- /**
- * 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);
- /**
- * 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_LOCS_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_LOCS_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;
- 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;
- }
- 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++;
- 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:
- 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;
- 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;
- }
- 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;
- xmlXPathObjectPtr obj;
- 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_LOCS_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_LOCS_ENABLED */
- /*
- * In case of errors, xmlXPathNodeSetFilter can pop additional nodes from
- * the stack. We have to temporarily remove the nodeset object from the
- * stack to avoid freeing it prematurely.
- */
- CHECK_TYPE0(XPATH_NODESET);
- obj = valuePop(ctxt);
- set = obj->nodesetval;
- if (set != NULL) {
- xmlXPathNodeSetFilter(ctxt, set, op->ch2, 1, 1, 1);
- if (set->nodeNr > 0)
- *first = set->nodeTab[0];
- }
- valuePush(ctxt, obj);
- 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 = ctxt->valueNr;
- if (op->ch1 != -1) {
- total +=
- xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
- if (ctxt->error != XPATH_EXPRESSION_OK)
- break;
- }
- if (ctxt->valueNr < frame + op->value) {
- xmlGenericError(xmlGenericErrorContext,
- "xmlXPathCompOpEval: parameter error\n");
- ctxt->error = XPATH_INVALID_OPERAND;
- 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;
- 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);
- 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 != frame + 1))
- XP_ERROR0(XPATH_STACK_ERROR);
- 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:{
- xmlXPathObjectPtr obj;
- 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_LOCS_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_LOCS_ENABLED */
- /*
- * In case of errors, xmlXPathNodeSetFilter can pop additional
- * nodes from the stack. We have to temporarily remove the
- * nodeset object from the stack to avoid freeing it
- * prematurely.
- */
- CHECK_TYPE0(XPATH_NODESET);
- obj = valuePop(ctxt);
- set = obj->nodesetval;
- if (set != NULL)
- xmlXPathNodeSetFilter(ctxt, set, op->ch2,
- 1, set->nodeNr, 1);
- valuePush(ctxt, obj);
- 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_LOCS_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_LOCS_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;
- 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:
- 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++;
- }
- 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 (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;
- int oldDepth;
- if ((ctxt == NULL) || (ctxt->comp == NULL))
- return(-1);
- 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");
- return(-1);
- }
- ctxt->valueNr = 0;
- ctxt->valueMax = 10;
- ctxt->value = NULL;
- }
- #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);
- }
- oldDepth = ctxt->context->depth;
- if (toBool)
- return(xmlXPathCompOpEvalToBoolean(ctxt,
- &comp->steps[comp->last], 0));
- else
- xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
- ctxt->context->depth = oldDepth;
- 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_LOCS_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");
- xmlFreePattern(stream);
- 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;
- int oldDepth = 0;
- #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)
- oldDepth = ctxt->depth;
- xmlXPathCompileExpr(pctxt, 1);
- if (ctxt != NULL)
- ctxt->depth = oldDepth;
- 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)
- oldDepth = ctxt->depth;
- xmlXPathOptimizeExpression(pctxt, &comp->steps[comp->last]);
- if (ctxt != NULL)
- ctxt->depth = oldDepth;
- }
- pctxt->comp = NULL;
- }
- xmlXPathFreeParserContext(pctxt);
- if (comp != NULL) {
- comp->expr = xmlStrdup(str);
- }
- 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
- pctxt = xmlXPathCompParserContext(comp, ctxt);
- if (pctxt == NULL)
- return(-1);
- 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
- int oldDepth = 0;
- 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)
- oldDepth = ctxt->context->depth;
- xmlXPathCompileExpr(ctxt, 1);
- if (ctxt->context != NULL)
- ctxt->context->depth = oldDepth;
- 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)
- oldDepth = ctxt->context->depth;
- xmlXPathOptimizeExpression(ctxt,
- &ctxt->comp->steps[ctxt->comp->last]);
- if (ctxt->context != NULL)
- ctxt->context->depth = oldDepth;
- }
- }
- 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 */
|