12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079 |
- # Parser.pm: parse texinfo code into a tree.
- #
- # Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 3 of the License,
- # or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- #
- # Original author: Patrice Dumas <pertusus@free.fr>
- # Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init.
- # The organization of the file is the following:
- # module definitions.
- # default parser state. With explanation of the internal structures.
- # initializations, determination of command types.
- # user visible subroutines.
- # internal subroutines, doing the parsing.
- package Texinfo::Parser;
- # We need the unicode stuff.
- use 5.006;
- use strict;
- # debug
- use Carp qw(cluck);
- use Data::Dumper;
- # to detect if an encoding may be used to open the files
- use Encode;
- # for fileparse
- use File::Basename;
- #use POSIX qw(setlocale LC_ALL LC_CTYPE LC_MESSAGES);
- # commands definitions
- use Texinfo::Common;
- # Error reporting and counting, translation of strings.
- use Texinfo::Report;
- # encoding_alias
- use Texinfo::Encoding;
- # to expand file names in @include and similar @-commands
- use Texinfo::Convert::Text;
- # to normalize node name, anchor, float arg, listoffloats and first *ref argument.
- use Texinfo::Convert::NodeNameNormalization;
- # in error messages, and for macro body expansion
- use Texinfo::Convert::Texinfo;
- require Exporter;
- use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
- @ISA = qw(Exporter Texinfo::Report);
- # Items to export into callers namespace by default. Note: do not export
- # names by default without a very good reason. Use EXPORT_OK instead.
- # Do not simply export all your public functions/methods/constants.
- # This allows declaration use Texinfo::Parser ':all';
- # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
- # will save memory.
- %EXPORT_TAGS = ( 'all' => [ qw(
- parser
- parse_texi_text
- parse_texi_line
- parse_texi_file
- indices_information
- floats_information
- internal_references_information
- labels_information
- global_commands_information
- global_informations
- ) ] );
- @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
- @EXPORT = qw(
- );
- $VERSION = '5.1.90';
- sub N__($)
- {
- return $_[0];
- }
- #sub __($$)
- #{
- # my $self = shift;
- # return &{$self->{'gettext'}}(@_);
- #}
- # Customization variables obeyed by the Parser, and the default values.
- our %default_customization_values = (
- 'TEST' => 0,
- 'DEBUG' => 0, # if >= 10, tree is printed in texi2any.pl after parsing.
- # If >= 100 tree is printed every line.
- 'SHOW_MENU' => 1, # if false no menu error related.
- 'INLINE_INSERTCOPYING' => 0,
- 'IGNORE_BEFORE_SETFILENAME' => 1,
- 'MACRO_BODY_IGNORES_LEADING_SPACE' => 0,
- 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME' => 1,
- 'INPUT_PERL_ENCODING' => undef, # input perl encoding name, set from
- # @documentencoding in the default case
- 'INPUT_ENCODING_NAME' => undef, # encoding name normalized as preferred
- # IANA, set from @documentencoding in the default
- # case
- 'CPP_LINE_DIRECTIVES' => 1, # handle cpp like synchronization lines
- 'MAX_MACRO_CALL_NESTING' => 100000, # max number of nested macro calls
- 'GLOBAL_COMMANDS' => [], # list of commands registered
- # This is not used directly, but passed to Convert::Text through
- # Texinfo::Common::_convert_text_options
- 'ENABLE_ENCODING' => 1, # output accented and special characters
- # based on @documentencoding
- # following are used in Texinfo::Structuring
- 'TOP_NODE_UP' => '(dir)', # up node of Top node
- 'SIMPLE_MENU' => 0, # not used in the parser but in structuring
- 'USE_UP_NODE_FOR_ELEMENT_UP' => 0, # Use node up for Up if there is no
- # section up.
- );
- my %parser_default_configuration = (%Texinfo::Common::default_parser_state_configuration,
- %default_customization_values);
- # the other possible keys for the parser state are:
- #
- # expanded_formats_hash each key comes from expanded_formats value is 1
- # index_names a structure holding the link between index
- # names, prefixes, merged indices,
- # initial value is %index_names in Texinfo::Common.
- # context_stack stack of the contexts, more recent on top.
- # 'line' is added when on a line or
- # block @-command line,
- # 'def' is added instead if on a definition line.
- # 'preformatted' is added in block commands
- # where there is no paragraphs and spaces are kept
- # (format, example, display...)
- # 'rawpreformatted' is added in raw block commands
- # (html, xml, docbook...)
- # 'menu' is added in menu commands
- # 'math', 'footnote', 'caption', 'shortcaption',
- # 'inlineraw' are also added when in those commands
- # conditionals_stack a stack of conditional commands that are expanded.
- # raw_formats_stack a stack of 1 or 0 for raw formats (@html... or
- # @inlineraw), is 0 if within a raw format that is
- # not expanded.
- # macro_stack stack of macros being expanded (more recent first)
- # definfoenclose an hash, key is the command name, value is an array
- # reference with 2 values, beginning and ending.
- # input a stack, with last at bottom. Holds the opened files
- # or text. Pending macro expansion or text expansion
- # is also in that structure.
- # misc_commands the same than %misc_commands in Texinfo::Common,
- # but with index entry commands dynamically added
- # close_paragraph_commands same than %close_paragraph_commands, but with
- # insertcopying removed if INLINE_INSERTCOPYING
- # close_preformatted_commands same than %close_preformatted_commands, but with
- # insertcopying removed if INLINE_INSERTCOPYING
- # no_paragraph_commands the same than %default_no_paragraph_commands
- # below, with index
- # entry commands dynamically added
- # simple_text_commands the same than %simple_text_commands below, but
- # with index entry commands dynamically added
- # current_node last seen node.
- # current_section last seen section.
- # nodes list of nodes.
- # command_index_prefix associate a command name to an index prefix.
- # prefix_to_index_name associate an index prefix to the index name.
- # floats key is the normalized float type, value is an array
- # reference holding all the floats.
- # internal_references an array holding all the internal references.
- # set points to the value set when initializing, for
- # configuration items that are not to be overriden
- # by @-commands. For example documentlanguage.
- # A line information is an hash reference with the keys:
- # line_nr the line number
- # file_name the file name
- # macro if in a macro expansion, the name of the macro
- #
- # A text fragment information is a 2 element array reference, the first is the
- # text fragment, the second is the line information.
- # The input structure is an array, the first is the most recently included
- # file. The last element may be a file if the parsing is done on a file,
- # with parse_texi_file, or simply pending text, if called as parse_texi_text.
- # each element of the array is a hash reference. The key are:
- # pending an array reference containing pending text fragments, either the
- # text given as parse_texi_text or macro expansion text.
- # name file name
- # line_nr current line number in the file
- # fh filehandle for the file
- # content is not copied but reference is copied when duplicating a parser.
- my %tree_informations;
- foreach my $tree_information ('values', 'macros', 'explained_commands', 'labels') {
- $tree_informations{$tree_information} = 1;
- }
- # The commands in initialization_overrides are not set in the document if
- # set at the parser initialization.
- my %initialization_overrides = (
- 'INPUT_ENCODING_NAME' => 1,
- 'documentlanguage' => 1,
- );
- my %no_brace_commands = %Texinfo::Common::no_brace_commands;
- my %misc_commands = %Texinfo::Common::misc_commands;
- my %brace_commands = %Texinfo::Common::brace_commands;
- my %accent_commands = %Texinfo::Common::accent_commands;
- my %context_brace_commands = %Texinfo::Common::context_brace_commands;
- my %block_commands = %Texinfo::Common::block_commands;
- my %block_item_commands = %Texinfo::Common::block_item_commands;
- my %close_paragraph_commands = %Texinfo::Common::close_paragraph_commands;
- my %def_map = %Texinfo::Common::def_map;
- my %def_commands = %Texinfo::Common::def_commands;
- my %def_aliases = %Texinfo::Common::def_aliases;
- my %menu_commands = %Texinfo::Common::menu_commands;
- my %preformatted_commands = %Texinfo::Common::preformatted_commands;
- my %format_raw_commands = %Texinfo::Common::format_raw_commands;
- my %item_container_commands = %Texinfo::Common::item_container_commands;
- my %item_line_commands = %Texinfo::Common::item_line_commands;
- my %deprecated_commands = %Texinfo::Common::deprecated_commands;
- my %root_commands = %Texinfo::Common::root_commands;
- my %sectioning_commands = %Texinfo::Common::sectioning_commands;
- my %command_index_prefix = %Texinfo::Common::command_index_prefix;
- my %command_structuring_level = %Texinfo::Common::command_structuring_level;
- my %ref_commands = %Texinfo::Common::ref_commands;
- my %region_commands = %Texinfo::Common::region_commands;
- my %code_style_commands = %Texinfo::Common::code_style_commands;
- my %in_heading_commands = %Texinfo::Common::in_heading_commands;
- my %explained_commands = %Texinfo::Common::explained_commands;
- my %inline_format_commands = %Texinfo::Common::inline_format_commands;
- my %inline_commands = %Texinfo::Common::inline_commands;
- my %inline_conditional_commands = %Texinfo::Common::inline_conditional_commands;
- my %all_commands = %Texinfo::Common::all_commands;
- # equivalence between a @set flag and an @@-command
- my %set_flag_command_equivalent = (
- 'txicodequoteundirected' => 'codequoteundirected',
- 'txicodequotebacktick' => 'codequotebacktick',
- # 'txideftypefnnl' => 'deftypefnnewline',
- );
- # keep line information for those commands.
- my %keep_line_nr_brace_commands = %context_brace_commands;
- foreach my $keep_line_nr_brace_command ('titlefont', 'anchor') {
- $keep_line_nr_brace_commands{$keep_line_nr_brace_command} = 1;
- }
- foreach my $brace_command (keys (%brace_commands)) {
- $keep_line_nr_brace_commands{$brace_command} = 1
- if ($brace_commands{$brace_command} > 1);
- }
- my %type_with_paragraph;
- foreach my $type ('before_item', 'text_root', 'document_root',
- 'brace_command_context') {
- $type_with_paragraph{$type} = 1;
- }
- my %command_ignore_space_after;
- foreach my $command ('anchor', 'hyphenation', 'caption', 'shortcaption') {
- $command_ignore_space_after{$command} = 1;
- }
- my %global_multiple_commands;
- foreach my $global_multiple_command (
- 'author', 'footnote', 'hyphenation', 'insertcopying', 'printindex',
- 'subtitle','titlefont', 'listoffloats', 'detailmenu', 'part',
- keys(%Texinfo::Common::document_settable_at_commands), ) {
- $global_multiple_commands{$global_multiple_command} = 1;
- }
- my %global_unique_commands;
- foreach my $global_unique_command (
- 'copying', 'settitle',
- 'shorttitlepage', 'title', 'titlepage', 'top',
- keys(%Texinfo::Common::document_settable_unique_at_commands), ) {
- $global_unique_commands{$global_unique_command} = 1;
- }
- my %index_names = %Texinfo::Common::index_names;
- # index names that cannot be set by the user.
- my %forbidden_index_name = ();
- foreach my $name(keys(%index_names)) {
- foreach my $prefix (@{$index_names{$name}->{'prefix'}}) {
- $forbidden_index_name{$prefix} = 1;
- }
- }
- foreach my $other_forbidden_index_name ('info','ps','pdf','htm',
- 'html', 'log','aux','dvi','texi','txi','texinfo','tex','bib') {
- $forbidden_index_name{$other_forbidden_index_name} = 1;
- }
- # @-commands that do not start a paragraph
- my %default_no_paragraph_commands;
- # @-commands that should be at a line beginning
- my %begin_line_commands;
- foreach my $command ('node', 'end') {
- $begin_line_commands{$command} = $command;
- }
- foreach my $no_paragraph_command ('titlefont', 'caption', 'shortcaption',
- 'image', '*', 'hyphenation', 'anchor', 'errormsg') {
- $default_no_paragraph_commands{$no_paragraph_command} = 1;
- }
- foreach my $no_paragraph_command (keys(%misc_commands)) {
- $default_no_paragraph_commands{$no_paragraph_command} = 1;
- $begin_line_commands{$no_paragraph_command} = 1;
- }
- # verbatiminclude is not said to begin at the beginning of the line
- # in the manual
- foreach my $misc_not_begin_line ('comment', 'c', 'sp', 'refill',
- 'noindent', 'indent', 'columnfractions',
- 'tab', 'item', 'headitem', 'verbatiminclude',
- 'set', 'clear',
- 'vskip', keys(%in_heading_commands)) {
- delete $begin_line_commands{$misc_not_begin_line};
- }
- my %block_arg_commands;
- foreach my $block_command (keys(%block_commands)) {
- $begin_line_commands{$block_command} = 1;
- $default_no_paragraph_commands{$block_command} = 1;
- $block_arg_commands{$block_command} = 1
- if ($block_commands{$block_command} ne 'raw');
- # and ! $format_raw_commands{$block_command});
- }
- my %close_preformatted_commands = %close_paragraph_commands;
- foreach my $no_close_preformatted('sp') {
- delete $close_preformatted_commands{$no_close_preformatted};
- }
- # FIXME to close preformated or not to close?
- #foreach my $format_raw_command(keys(%format_raw_commands)) {
- # $close_preformatted_commands{$format_raw_command} = 1;
- #}
- # commands that may appear in accents
- my %in_accent_commands = %accent_commands;
- foreach my $brace_command(keys(%brace_commands)) {
- $in_accent_commands{$brace_command} = 1 if (!$brace_commands{$brace_command});
- }
- foreach my $no_brace_command (keys(%no_brace_commands)) {
- $in_accent_commands{$no_brace_command} = 1;
- }
- $in_accent_commands{'c'} = 1;
- $in_accent_commands{'comment'} = 1;
- # commands that may appear in texts arguments
- my %in_full_text_commands;
- foreach my $command (keys(%brace_commands), keys(%no_brace_commands)) {
- $in_full_text_commands{$command} = 1;
- }
- foreach my $misc_command_in_full_text('c', 'comment', 'refill', 'noindent',
- 'indent', 'columnfractions', 'set', 'clear', 'end') {
- $in_full_text_commands{$misc_command_in_full_text} = 1;
- }
- foreach my $out_format (keys(%format_raw_commands)) {
- $in_full_text_commands{$out_format} = 1;
- }
- delete $in_full_text_commands{'caption'};
- delete $in_full_text_commands{'shortcaption'};
- foreach my $block_command (keys(%block_commands)) {
- $in_full_text_commands{$block_command} = 1
- if ($block_commands{$block_command} eq 'conditional');
- }
- # commands that may happen on lines where everything is
- # permitted
- my %in_full_line_commands = %in_full_text_commands;
- foreach my $not_in_full_line_commands('noindent', 'indent') {
- delete $in_full_line_commands{$not_in_full_line_commands};
- }
- # commands that may happen on sectioning commands
- my %in_full_line_commands_no_refs = %in_full_line_commands;
- foreach my $not_in_full_line_commands_no_refs ('titlefont',
- 'anchor', 'footnote', 'verb') {
- delete $in_full_line_commands_no_refs{$not_in_full_line_commands_no_refs};
- }
- # commands that may happen in simple text arguments
- my %in_simple_text_commands = %in_full_line_commands_no_refs;
- foreach my $not_in_simple_text_command('xref', 'ref', 'pxref', 'inforef') {
- delete $in_simple_text_commands{$not_in_simple_text_command};
- }
- # commands that only accept simple text as argument in any context.
- my %simple_text_commands;
- foreach my $misc_command(keys(%misc_commands)) {
- if ($misc_commands{$misc_command} =~ /^\d+$/
- or ($misc_commands{$misc_command} eq 'line'
- and !($sectioning_commands{$misc_command}
- or $def_commands{$misc_command}))
- or $misc_commands{$misc_command} eq 'text') {
- $simple_text_commands{$misc_command} = 1;
- }
- }
- my %full_line_commands_no_refs = (%sectioning_commands,
- %def_commands);
- delete $simple_text_commands{'center'};
- delete $simple_text_commands{'exdent'};
- foreach my $command ('titlefont', 'anchor', 'xref','ref','pxref',
- 'inforef', 'shortcaption', 'math', 'indicateurl',
- 'email', 'uref', 'url', 'image', 'abbr', 'acronym',
- 'dmn', 'ctrl', 'errormsg') {
- $simple_text_commands{$command} = 1;
- }
- # commands that accept full text, but no block or top-level commands
- my %full_text_commands;
- foreach my $brace_command (keys (%brace_commands)) {
- if ($brace_commands{$brace_command} == 1
- and !$simple_text_commands{$brace_command}
- and !$context_brace_commands{$brace_command}
- and !$accent_commands{$brace_command}) {
- $full_text_commands{$brace_command} = 1;
- }
- }
- # commands that accept almost the same than in full text, except
- # what do not make sense on a line.
- my %full_line_commands;
- $full_line_commands{'center'} = 1;
- $full_line_commands{'exdent'} = 1;
- $full_line_commands{'item'} = 1;
- $full_line_commands{'itemx'} = 1;
- # Fill the valid nestings hash. All commands not in that hash
- # are considered to accept anything within. There are additional
- # context tests, to make sure, for instance that we are testing
- # @-commands on the block, misc or node @-command line and not
- # in the content.
- # index entry commands are dynamically set as in_simple_text_commands
- my %default_valid_nestings;
- foreach my $command (keys(%accent_commands)) {
- $default_valid_nestings{$command} = \%in_accent_commands;
- }
- foreach my $command (keys(%full_text_commands)) {
- $default_valid_nestings{$command} = \%in_full_text_commands;
- }
- foreach my $command (keys(%simple_text_commands)) {
- $default_valid_nestings{$command} = \%in_simple_text_commands;
- }
- foreach my $command (keys(%full_line_commands)) {
- $default_valid_nestings{$command} = \%in_full_line_commands;
- }
- foreach my $command (keys(%full_line_commands_no_refs)) {
- $default_valid_nestings{$command} = \%in_full_line_commands_no_refs;
- }
- # Only for block commands with line arguments
- foreach my $command (keys(%block_commands)) {
- if ($block_commands{$command} and $block_commands{$command} ne 'raw'
- and $block_commands{$command} ne 'conditional'
- and !$def_commands{$command}) {
- $default_valid_nestings{$command} = \%in_simple_text_commands;
- }
- }
- my @preformatted_contexts = ('preformatted', 'rawpreformatted');
- my %preformatted_contexts;
- foreach my $preformatted_context (@preformatted_contexts) {
- $preformatted_contexts{$preformatted_context} = 1;
- }
- # contexts on the context_stack stack where empty line don't trigger
- # paragraph
- my %no_paragraph_contexts;
- foreach my $no_paragraph_context ('math', 'menu', @preformatted_contexts,
- 'def', 'inlineraw') {
- $no_paragraph_contexts{$no_paragraph_context} = 1;
- };
- # Format a bug message
- sub _bug_message($$;$$)
- {
- my $self = shift;
- my $message = shift;
- my $line_number = shift;
- my $current = shift;
- my $line_message = '';
- if ($line_number) {
- my $file = $line_number->{'file_name'};
- $line_message
- = "last location: $line_number->{'file_name'}:$line_number->{'line_nr'}";
- if ($line_number->{'macro'} ne '') {
- $line_message .= " (possibly involving $line_number->{'macro'})";
- }
- $line_message .= "\n";
- }
- my $message_context_stack = "context_stack: (@{$self->{'context_stack'}})\n";
- my $current_element_message = '';
- if ($current) {
- $current_element_message = "current: ". _print_current($current);
- }
- warn "You found a bug: $message\n\n".
- "Additional informations:\n".
- $line_message.$message_context_stack.$current_element_message;
-
- }
- # simple deep copy of a structure
- sub _deep_copy($)
- {
- my $struct = shift;
- my $string = Data::Dumper->Dump([$struct], ['struct']);
- eval $string;
- return $struct;
- }
- # return true if effect of global commands should be ignored.
- sub _ignore_global_commands($)
- {
- my $self = shift;
- return !$self->{'expanded_formats_stack'}->[-1];
- }
- # enter all the commands associated with an index name using the prefix
- # list
- sub _register_index_commands($$)
- {
- my $self = shift;
- my $index_name = shift;
- if (!$self->{'index_names'}->{$index_name}->{'prefix'}) {
- $self->{'index_names'}->{$index_name}->{'prefix'} = [$index_name];
- }
- if (!exists($self->{'index_names'}->{$index_name}->{'name'})) {
- $self->{'index_names'}->{$index_name}->{'name'} = $index_name;
- }
- if (!exists($self->{'index_names'}->{$index_name}->{'contained_indices'})) {
- $self->{'index_names'}->{$index_name}->{'contained_indices'}->{$index_name} = 1;
- }
- foreach my $prefix (@{$self->{'index_names'}->{$index_name}->{'prefix'}}) {
- $self->{'misc_commands'}->{$prefix.'index'} = 'line';
- $self->{'no_paragraph_commands'}->{$prefix.'index'} = 1;
- $self->{'valid_nestings'}->{$prefix.'index'} = \%in_simple_text_commands;
- $self->{'command_index_prefix'}->{$prefix.'index'} = $prefix;
- $self->{'prefix_to_index_name'}->{$prefix} = $index_name;
- }
- }
- # initialization entry point. Set up a parser.
- # The last argument, optional, is a hash provided by the user to change
- # the default values for what is present in %parser_default_configuration.
- # The exact arguments of the function depend on how it was called,
- # in a object oriented way or not.
- sub parser(;$$)
- {
- my $class = shift;
- my $conf;
- my $parser = _deep_copy(\%parser_default_configuration);
- # _deep_copy doesn't handle subs
- $parser->{'gettext'} = $parser_default_configuration{'gettext'};
- $parser->{'pgettext'} = $parser_default_configuration{'pgettext'};
- # called not object-oriented
- if (ref($class) eq 'HASH') {
- #print STDERR "Not oo\n"
- $conf = $class;
- bless $parser;
- } elsif (ref($class)) {
- # called on an existing parser, interpreted as a duplication
- my $old_parser = $class;
- $class = ref($class);
- foreach my $key (keys(%parser_default_configuration)) {
- if ($tree_informations{$key}) {
- if (defined($old_parser->{$key})) {
- foreach my $info_key (keys(%{$old_parser->{$key}})) {
- $parser->{$key}->{$info_key}
- = $old_parser->{$key}->{$info_key};
- }
- }
- } else {
- $parser->{$key} = _deep_copy($old_parser->{$key});
- }
- }
- #$parser = _deep_copy($old_parser);
- $parser->{'gettext'} = $old_parser->{'gettext'};
- $parser->{'pgettext'} = $old_parser->{'pgettext'};
- bless $parser, $class;
- $conf = shift;
- } elsif (defined($class)) {
- bless $parser, $class;
- $conf = shift;
- } else {
- bless $parser;
- $conf = shift;
- }
- if (defined($conf)) {
- foreach my $key (keys(%$conf)) {
- if (exists($parser_default_configuration{$key})) {
- if (ref($conf->{$key}) ne 'CODE' and $key ne 'values') {
- $parser->{$key} = _deep_copy($conf->{$key});
- } else {
- $parser->{$key} = $conf->{$key};
- }
- if ($initialization_overrides{$key}) {
- $parser->{'set'}->{$key} = $parser->{$key};
- }
- } else {
- warn "$key not a possible customization in Texinfo::Parser::parser\n";
- }
- }
- }
- #foreach my $value (keys %{$parser->{'values'}}) {
- # print STDERR " -> $value $parser->{'values'}->{$value}\n";
- #}
- # Now initialize command hash that are dynamically modified, notably
- # those for index commands, and lists, based on defaults and user provided.
- $parser->{'misc_commands'} = _deep_copy (\%misc_commands);
- $parser->{'valid_nestings'} = _deep_copy (\%default_valid_nestings);
- $parser->{'no_paragraph_commands'} = { %default_no_paragraph_commands };
- $parser->{'index_names'} = _deep_copy (\%index_names);
- $parser->{'command_index_prefix'} = {%command_index_prefix};
- $parser->{'close_paragraph_commands'} = {%close_paragraph_commands};
- $parser->{'close_preformatted_commands'} = {%close_preformatted_commands};
- if ($parser->{'INLINE_INSERTCOPYING'}) {
- delete $parser->{'close_paragraph_commands'}->{'insercopying'};
- delete $parser->{'close_preformatted_commands'}->{'insercopying'};
- }
- # a hash is simply concatenated. It should be like %index_names.
- if (ref($parser->{'indices'}) eq 'HASH') {
- %{$parser->{'index_names'}} = (%{$parser->{'index_names'}},
- %{$parser->{'indices'}});
- } else { # an array holds index names defined with @defindex
- foreach my $name (@{$parser->{'indices'}}) {
- $parser->{'index_names'}->{$name} = {'in_code' => 0};
- }
- }
- foreach my $index (keys (%{$parser->{'index_names'}})) {
- $parser->_register_index_commands($index);
- }
- if ($parser->{'merged_indices'}) {
- foreach my $index_from (keys (%{$parser->{'merged_indices'}})) {
- my $index_to = $parser->{'merged_indices'}->{$index_from};
- if (defined($parser->{'index_names'}->{$index_from})
- and defined($parser->{'index_names'}->{$index_to})) {
- $parser->{'index_names'}->{$index_from}->{'merged_in'} = $index_to;
- $parser->{'index_names'}->{$index_to}->{'contained_indices'}->{$index_from} = 1;
- }
- }
- }
- foreach my $explained_command(keys(%explained_commands)) {
- if (!defined($parser->{'explained_commands'}->{$explained_command})) {
- $parser->{'explained_commands'}->{$explained_command} = {};
- }
- }
- $parser->{'context_stack'} = [ $parser->{'context'} ];
- $parser->{'regions_stack'} = [];
- $parser->{'macro_stack'} = [];
- $parser->{'conditionals_stack'} = [];
- $parser->{'expanded_formats_stack'} = [1];
- # turn the array to a hash for speed. Not sure it really matters for such
- # a small array.
- foreach my $expanded_format(@{$parser->{'expanded_formats'}}) {
- $parser->{'expanded_formats_hash'}->{$expanded_format} = 1;
- }
- %{$parser->{'global_commands'}} = %global_multiple_commands;
- foreach my $global_command (@{$parser->{'GLOBAL_COMMANDS'}}) {
- $parser->{'global_commands'}->{$global_command} = 1;
- }
- $parser->Texinfo::Report::new;
- return $parser;
- }
- sub get_conf($$)
- {
- my $self = shift;
- my $var = shift;
- return $self->{$var};
- }
- # split a scalar text in an array lines.
- sub _text_to_lines($)
- {
- my $text = shift;
- die if (!defined($text));
- my $had_final_end_line = chomp($text);
- my $lines = [ map {$_."\n"} split (/\n/, $text, -1) ];
- $lines = [''] if (!@$lines);
- chomp($lines->[-1]) unless ($had_final_end_line);
- return $lines;
- }
- # construct a text fragments array matching a lines array, based on information
- # supplied.
- # If $fixed_line_number is set the line number is not increased, otherwise
- # it is increased, beginning at $first_line.
- sub _complete_line_nr($$;$$$)
- {
- my $lines = shift;
- my $first_line = shift;
- my $file = shift;
- my $macro = shift;
- my $fixed_line_number = shift;
- $macro = '' if (!defined($macro));
- $file = '' if (!defined($file));
- my $new_lines = [];
- if (defined($first_line)) {
- my $line_index = $first_line;
- foreach my $index(0..scalar(@$lines)-1) {
- $line_index = $index+$first_line if (!$fixed_line_number);
- $new_lines->[$index] = [ $lines->[$index],
- { 'line_nr' => $line_index,
- 'file_name' => $file, 'macro' => $macro } ];
- }
- } else {
- foreach my $line (@$lines) {
- push @$new_lines, [ $line ];
- }
- }
- return $new_lines;
- }
- # entry point for text fragments.
- # Used in tests.
- # Note that it has no associated root type a opposed to parse_texi_line
- # and parse_texi_file.
- sub parse_texi_text($$;$$$$)
- {
- my $self = shift;
- my $text = shift;
- my $lines_nr = shift;
- my $file = shift;
- my $macro = shift;
- my $fixed_line_number = shift;
- return undef if (!defined($text));
- my $lines_array = [];
- if (!ref($text)) {
- $text = _text_to_lines($text);
- }
- $lines_nr = [] if (!defined($lines_nr));
- if (!ref($lines_nr)) {
- #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'});
- $lines_array = _complete_line_nr($text, $lines_nr, $file,
- $macro, $fixed_line_number);
- } else {
- while (@$text) {
- my $line_nr = shift @$lines_nr;
- my $line = shift @$text;
- push @$lines_array, [$line, $line_nr];
- }
- }
- $self = parser() if (!defined($self));
- $self->{'input'} = [{'pending' => $lines_array}];
- my $tree = $self->_parse_texi();
- return $tree;
- }
- # Not used for now, as a @contents after the first sectioning command
- # is correct if not using TeX.
- sub _check_contents_location($$)
- {
- my $self = shift;
- my $tree = shift;
- my $commands = $self->global_commands_information();
- return unless ($commands);
- # Find the last sectioning command
- my $index = -1;
- my %ending_root_commands;
- my $found = 0;
- while ($tree->{'contents'}->[$index]) {
- if (defined($tree->{'contents'}->[$index]->{'cmdname'})) {
- $ending_root_commands{$tree->{'contents'}->[$index]} = 1;
- if ($sectioning_commands{$tree->{'contents'}->[$index]->{'cmdname'}}) {
- $found = 1;
- last;
- }
- }
- $index--;
- }
- return if (!$found);
- #print STDERR "ending_root_commands ".join('|',keys(%ending_root_commands))."\n";
- #print STDERR "tree contents: ".join('|', @{$tree->{'contents'}})."\n";
- foreach my $command ('contents', 'shortcontents', 'summarycontents') {
- if ($commands->{$command}) {
- foreach my $current (@{$commands->{$command}}) {
- my $root_command = $self->Texinfo::Common::find_parent_root_command($current);
- #print STDERR "root_command for $current->{'cmdname'}: $root_command\n";
- if (defined($root_command)
- and !$ending_root_commands{$root_command}) {
- $self->line_warn(sprintf($self->__(
- "\@%s should only appear at beginning or end of document"),
- $current->{'cmdname'}), $current->{'line_nr'});
- }
- }
- }
- }
- }
- # parse a texi file
- sub parse_texi_file($$)
- {
- my $self = shift;
- my $file_name = shift;
- my $filehandle = do { local *FH };
- if (! open($filehandle, $file_name)) {
- $self->document_error(sprintf($self->__("could not open %s: %s"),
- $file_name, $!));
- return undef;
- }
- my $line_nr = 0;
- my $line;
- my @first_lines;
- my $pending_first_texi_line;
- # the first line not empty and not with \input is kept in
- # $pending_first_texi_line and put in the pending lines just below
- while ($line = <$filehandle>) {
- $line_nr++;
- if ($line =~ /^ *\\input/ or $line =~ /^\s*$/) {
- $line =~ s/\x{7F}.*\s*//;
- push @first_lines, $line;
- } else {
- $pending_first_texi_line = $line;
- last;
- }
- }
- my $root = { 'contents' => [], 'type' => 'text_root' };
- if (@first_lines) {
- push @{$root->{'contents'}}, { 'type' => 'preamble', 'contents' => [] };
- foreach my $line (@first_lines) {
- push @{$root->{'contents'}->[-1]->{'contents'}},
- { 'text' => $line,
- 'type' => 'preamble_text' };
- }
- }
- my ($directories, $suffix);
- ($file_name, $directories, $suffix) = fileparse($file_name)
- if ($self->{'TEST'});
- $self = parser() if (!defined($self));
- $self->{'input'} = [{
- 'pending' => [[$pending_first_texi_line, {'line_nr' => $line_nr,
- 'macro' => '', 'file_name' => $file_name}]],
- 'name' => $file_name,
- 'line_nr' => $line_nr,
- 'fh' => $filehandle
- }];
- $self->{'info'}->{'input_file_name'} = $file_name;
- my $tree = $self->_parse_texi($root);
- # Find 'text_root', which contains everything before first node/section.
- # if there are elements, 'text_root' is the first content, otherwise it
- # is the root.
- my $text_root;
- if ($tree->{'type'} eq 'text_root') {
- $text_root = $tree;
- } elsif ($tree->{'contents'} and $tree->{'contents'}->[0]
- and $tree->{'contents'}->[0]->{'type'} eq 'text_root') {
- $text_root = $tree->{'contents'}->[0];
- }
- # Put everything before @setfilename in a special type. This allows to
- # ignore everything before @setfilename.
- if ($self->{'IGNORE_BEFORE_SETFILENAME'} and $text_root and
- $self->{'extra'} and $self->{'extra'}->{'setfilename'}
- and $self->{'extra'}->{'setfilename'}->{'parent'} eq $text_root) {
- my $before_setfilename = {'type' => 'preamble_before_setfilename',
- 'parent' => $text_root,
- 'contents' => []};
- while ($text_root->{'contents'}->[0] ne $self->{'extra'}->{'setfilename'}) {
- my $content = shift @{$text_root->{'contents'}};
- $content->{'parent'} = $before_setfilename;
- push @{$before_setfilename->{'contents'}}, $content;
- }
- unshift (@{$text_root->{'contents'}}, $before_setfilename)
- if (@{$before_setfilename->{'contents'}});
- }
- #$self->_check_contents_location($tree);
- return $tree;
- }
- sub parse_texi_line($$;$$$$)
- {
- my $self = shift;
- my $text = shift;
- my $lines_nr = shift;
- my $file = shift;
- my $macro = shift;
- my $fixed_line_number = shift;
- return undef if (!defined($text));
- if (!ref($text)) {
- $text = _text_to_lines($text);
- }
- #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'});
- my $lines_array = _complete_line_nr($text, $lines_nr, $file,
- $macro, $fixed_line_number);
- $self = parser() if (!defined($self));
- $self->{'input'} = [{'pending' => $lines_array}];
- my $tree = $self->_parse_texi({'contents' => [], 'type' => 'root_line'});
- return $tree;
- }
- # return indices informations
- sub indices_information($)
- {
- my $self = shift;
- return ($self->{'index_names'}, $self->{'merged_indices'});
- #return ($self->{'index_names'}, $self->{'merged_indices'}, $self->{'index_entries'});
- }
- sub floats_information($)
- {
- my $self = shift;
- return $self->{'floats'};
- }
- sub internal_references_information($)
- {
- my $self = shift;
- return $self->{'internal_references'};
- }
- sub global_commands_information($)
- {
- my $self = shift;
- return $self->{'extra'};
- }
- # @ dircategory_direntry
- # @ unassociated_menus
- # perl_encoding
- # input_encoding_name
- # input_file_name
- sub global_informations($)
- {
- my $self = shift;
- return $self->{'info'};
- }
- sub labels_information($)
- {
- my $self = shift;
- return $self->{'labels'};
- }
- # Following are the internal subroutines. The most important are
- # _parse_texi: the main parser loop.
- # _end_line: called at an end of line. Handling of @include lines is
- # done here.
- # _next_text: present the next text fragment, from pending text or line,
- # as described above.
- # for debugging
- sub _print_current($)
- {
- my $current = shift;
- return Texinfo::Common::_print_current($current);
- }
- # for debugging
- sub _print_command_args_texi($)
- {
- my $current = shift;
- return '' if (!$current->{'cmdname'});
- my $args = '';
- my $with_brace;
- if ($current->{'args'} and @{$current->{'args'}}) {
- $with_brace
- = ($current->{'args'}->[0]->{'type'} eq 'brace_command_arg'
- or $current->{'args'}->[0]->{'type'} eq 'brace_command_context');
- $args .= '{' if ($with_brace);
- foreach my $arg (@{$current->{'args'}}) {
- $args .= Texinfo::Convert::Texinfo::convert($arg).', ';
- }
- $args =~ s/, $//;
- }
- chomp($args);
- if ($with_brace) {
- $args .= '}';
- }
- return '@'.$current->{'cmdname'} .$args."\n";
- }
- sub _print_current_keys($)
- {
- my $current = shift;
- my $string = _print_current($current);
- foreach my $key (keys (%$current)) {
- $string .= " $key: $current->{$key}\n";
- }
- if ($current->{'extra'}) {
- $string .= " EXTRA\n";
- foreach my $key (keys (%{$current->{'extra'}})) {
- $string .= " $key: $current->{'extra'}->{$key}\n";
- }
- }
- return $string;
- }
- # For debugging
- my @kept_keys = ('contents', 'cmdname', 'type', 'text', 'args');
- my %kept_keys;
- foreach my $key (@kept_keys) {
- $kept_keys{$key} = 1;
- }
- sub _filter_print_keys { [grep {$kept_keys{$_}} ( sort keys %{$_[0]} )] };
- sub _print_tree($)
- {
- my $tree = shift;
- local $Data::Dumper::Sortkeys = \&_filter_print_keys;
- local $Data::Dumper::Purity = 1;
- local $Data::Dumper::Indent = 1;
- return Data::Dumper->Dump([$tree]);
- }
- sub _register_global_command($$$$)
- {
- my $self = shift;
- my $command = shift;
- my $current = shift;
- my $line_nr = shift;
- if ($command eq 'summarycontents' and !$self->{'global_commands'}->{$command}) {
- $command = 'shortcontents';
- }
- if ($self->{'global_commands'}->{$command} and $command ne 'author') {
- push @{$self->{'extra'}->{$command}}, $current
- unless (_ignore_global_commands($self));
- $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'});
- return 1;
- } elsif ($global_unique_commands{$command}) {
- # setfilename ignored in an included file
- $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'});
- if ($command eq 'setfilename'
- and scalar(@{$self->{'input'}}) > 1) {
- } elsif (exists ($self->{'extra'}->{$current->{'cmdname'}})) {
- $self->line_warn(sprintf($self->__('multiple @%s'),
- $current->{'cmdname'}), $line_nr);
- } else {
- $self->{'extra'}->{$current->{'cmdname'}} = $current
- unless (_ignore_global_commands($self));
- }
- return 1;
- }
- return 0;
- }
- # parse a @macro line
- sub _parse_macro_command_line($$$$$;$)
- {
- my $self = shift;
- my $command = shift;
- my $line = shift;
- my $parent = shift;
- my $line_nr = shift;
- my $macro = { 'cmdname' => $command, 'parent' => $parent, 'contents' => [],
- 'extra' => {'arg_line' => $line}, 'line_nr' => $line_nr };
- # REMACRO
- if ($line =~ /^\s+([[:alnum:]][[:alnum:]-]*)\s*(.*)/) {
- my $macro_name = $1;
- my $args_def = $2;
- my @args;
- if ($args_def =~ s/^\s*{\s*(.*?)\s*}\s*//) {
- @args = split(/\s*,\s*/, $1);
- }
-
- # accept an @-command after the arguments in case there is a @c or
- # @comment
- if ($args_def =~ /^\s*[^\@]/) {
- $self->line_error(sprintf($self->__("bad syntax for \@%s argument: %s"),
- $command, $args_def),
- $line_nr);
- $macro->{'extra'}->{'invalid_syntax'} = 1;
- }
- print STDERR "MACRO \@$command $macro_name\n" if ($self->{'DEBUG'});
- $macro->{'args'} = [
- { 'type' => 'macro_name', 'text' => $macro_name,
- 'parent' => $macro } ];
- my $index = 0;
- foreach my $formal_arg (@args) {
- push @{$macro->{'args'}},
- { 'type' => 'macro_arg', 'text' => $formal_arg,
- 'parent' => $macro};
- if ($formal_arg !~ /^[\w\-]+$/) {
- $self->line_error(sprintf($self->__("bad or empty \@%s formal argument: %s"),
- $command, $formal_arg), $line_nr);
- $macro->{'extra'}->{'invalid_syntax'} = 1;
- }
- $macro->{'extra'}->{'args_index'}->{$formal_arg} = $index;
- $index++;
- }
- } elsif ($line !~ /\S/) {
- $self->line_error(sprintf($self->
- __("%c%s requires a name"), ord('@'), $command), $line_nr);
- $macro->{'extra'}->{'invalid_syntax'} = 1;
- } else {
- $self->line_error(sprintf($self->
- __("bad name for \@%s"), $command), $line_nr);
- $macro->{'extra'}->{'invalid_syntax'} = 1;
- }
- return $macro;
- }
- # start a paragraph if in a context where paragraphs are to be started.
- sub _begin_paragraph($$;$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- # !$current->{'type'} is true for @-commands. In fact it is unclear
- # that there may be cases of !$current->{'type'} and !$current->{'cmdname'}
- if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}})
- and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) {
- if (!defined($current->{'contents'})) {
- $self->_bug_message("contents undef", $line_nr, $current);
- die;
- }
- # find whether an @indent precedes the paragraph
- my $indent;
- if (scalar(@{$current->{'contents'}})) {
- my $index = scalar(@{$current->{'contents'}}) -1;
- while ($index >= 0
- and !($current->{'contents'}->[$index]->{'type'}
- and ($current->{'contents'}->[$index]->{'type'} eq 'empty_line'
- or $current->{'contents'}->[$index]->{'type'} eq 'paragraph'))
- and !($current->{'contents'}->[$index]->{'cmdname'}
- and $self->{'close_paragraph_commands'}->{$current->{'contents'}->[$index]->{'cmdname'}})) {
- if ($current->{'contents'}->[$index]->{'cmdname'}
- and ($current->{'contents'}->[$index]->{'cmdname'} eq 'indent'
- or $current->{'contents'}->[$index]->{'cmdname'} eq 'noindent')) {
- $indent = $current->{'contents'}->[$index]->{'cmdname'};
- last;
- }
- $index--;
- }
- }
- push @{$current->{'contents'}},
- { 'type' => 'paragraph', 'parent' => $current, 'contents' => [] };
- $current->{'contents'}->[-1]->{'extra'}->{$indent} = 1 if ($indent);
- $current = $current->{'contents'}->[-1];
- print STDERR "PARAGRAPH\n" if ($self->{'DEBUG'});
- return $current;
- }
- return 0;
- }
- sub _begin_preformatted($)
- {
- my $self = shift;
- my $current = shift;
- if ($preformatted_contexts{$self->{'context_stack'}->[-1]}) {
- push @{$current->{'contents'}},
- { 'type' => $self->{'context_stack'}->[-1],
- 'parent' => $current, 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- print STDERR "PREFORMATTED $self->{'context_stack'}->[-1]\n" if ($self->{'DEBUG'});
- }
- return $current;
- }
- # wrapper around line_warn. Set line_nr to be the line_nr of the command,
- # corresponding to the opening of the command. Call line_warn with
- # sprintf if needed.
- sub _command_warn($$$$;@)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $message = shift;
- if ($current->{'line_nr'}) {
- $line_nr = $current->{'line_nr'};
- }
- if (@_) {
- $self->line_warn(sprintf($message, @_), $line_nr);
- } else {
- $self->line_warn($message, $line_nr);
- }
- }
- sub _command_error($$$$;@)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $message = shift;
- # use the beginning of the @-command for the error message
- # line number if available.
- # FIXME line_nr currently not registered for regular brace commands
- if ($current->{'line_nr'}) {
- $line_nr = $current->{'line_nr'};
- }
- if (@_) {
- $self->line_error(sprintf($message, @_), $line_nr);
- } else {
- $self->line_error($message, $line_nr);
- }
- }
- # currently doesn't do much more than
- # return $_[1]->{'parent'}
- sub _close_brace_command($$$;$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $closed_command = shift;
- my $interrupting_command = shift;
- if ($current->{'cmdname'} ne 'verb' or $current->{'type'} eq '') {
- if (defined($closed_command)) {
- $self->_command_error($current, $line_nr,
- $self->__("\@end %s seen before \@%s closing brace"),
- $closed_command, $current->{'cmdname'});
- } elsif (defined($interrupting_command)) {
- $self->_command_error($current, $line_nr,
- $self->__("\@%s seen before \@%s closing brace"),
- $interrupting_command, $current->{'cmdname'});
-
- } else {
- $self->_command_error($current, $line_nr,
- $self->__("%c%s missing close brace"), ord('@'), $current->{'cmdname'});
- }
- } else {
- $self->_command_error($current, $line_nr,
- $self->__("\@%s missing closing delimiter sequence: %s}"),
- $current->{'cmdname'}, $current->{'type'});
- }
- $current = $current->{'parent'};
- return $current;
- }
- sub _in_code($$)
- {
- my $self = shift;
- my $current = shift;
- while ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
- and exists $brace_commands{$current->{'parent'}->{'cmdname'}}
- and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) {
- return 1 if ($code_style_commands{$current->{'parent'}->{'cmdname'}});
- $current = $current->{'parent'}->{'parent'};
- }
- return 0;
- }
- # close brace commands, that don't set a new context (ie @caption, @footnote)
- sub _close_all_style_commands($$$;$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $closed_command = shift;
- my $interrupting_command = shift;
- while ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
- and exists $brace_commands{$current->{'parent'}->{'cmdname'}}
- and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) {
- $current = _close_brace_command($self, $current->{'parent'}, $line_nr,
- $closed_command, $interrupting_command);
- }
- return $current;
- }
- # close brace commands except for @caption, @footnote then the paragraph
- sub _end_paragraph($$$;$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $closed_command = shift;
- my $interrupting_command = shift;
- $current = _close_all_style_commands($self, $current, $line_nr,
- $closed_command, $interrupting_command);
- if ($current->{'type'} and $current->{'type'} eq 'paragraph') {
- print STDERR "CLOSE PARA\n" if ($self->{'DEBUG'});
- $current = $current->{'parent'};
- }
- return $current;
- }
- # close brace commands except for @caption, @footnote then the preformatted
- sub _end_preformatted($$$;$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $closed_command = shift;
- my $interrupting_command = shift;
- $current = _close_all_style_commands($self, $current, $line_nr,
- $closed_command, $interrupting_command);
- if ($current->{'type'} and $preformatted_contexts{$current->{'type'}}) {
- print STDERR "CLOSE PREFORMATTED $current->{'type'}\n" if ($self->{'DEBUG'});
- # completly remove void preformatted contexts
- if (!@{$current->{'contents'}}) {
- my $removed = pop @{$current->{'parent'}->{'contents'}};
- print STDERR "popping $removed->{'type'}\n" if ($self->{'DEBUG'});
- }
- $current = $current->{'parent'};
- }
- return $current;
- }
- # check that there are no text holding environment (currently
- # checking only paragraphs and preformatted) in contents
- sub _check_no_text($)
- {
- my $current = shift;
- my $after_paragraph = 0;
- foreach my $content (@{$current->{'contents'}}) {
- if ($content->{'type'} and $content->{'type'} eq 'paragraph') {
- $after_paragraph = 1;
- last;
- } elsif ($content->{'type'} and $preformatted_contexts{$content->{'type'}}) {
- foreach my $preformatted_content (@{$content->{'contents'}}) {
- if ((defined($preformatted_content->{'text'})
- and $preformatted_content->{'text'} =~ /\S/)
- or ($preformatted_content->{'cmdname'}
- and ($preformatted_content->{'cmdname'} ne 'c'
- and $preformatted_content->{'cmdname'} ne 'comment')
- and !($preformatted_content->{'type'}
- and $preformatted_content->{'type'} eq 'index_entry_command'))) {
- $after_paragraph = 1;
- last;
- }
- }
- last if ($after_paragraph);
- }
- }
- return $after_paragraph;
- }
- # put everything after the last @item/@itemx in an item_table type container
- # and distinguish table_term and table_entry.
- sub _gather_previous_item($$;$$)
- {
- my $self = shift;
- my $current = shift;
- my $next_command = shift;
- my $line_nr = shift;
- # nothing to do in that case.
- if ($current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'before_item') {
- if ($next_command and $next_command eq 'itemx') {
- $self->line_warn(sprintf($self->__("\@itemx should not begin \@%s"),
- $current->{'cmdname'}), $line_nr);
- }
- return;
- }
- #print STDERR "GATHER "._print_current($current)."\n";
- my $type;
- # if before an itemx, the type is different since there should not be
- # real content, so it may be treated differently
- if ($next_command and $next_command eq 'itemx') {
- $type = 'inter_item';
- } else {
- $type = 'table_item';
- }
- my $table_gathered = {'type' => $type,
- 'contents' => []};
- # remove everything that is not an @item/@items or before_item to
- # put it in the table_item, starting from the end.
- my $contents_count = scalar(@{$current->{'contents'}});
- for (my $i = 0; $i < $contents_count; $i++) {
- #print STDERR "_gather_previous_item $i on $contents_count: "._print_current($current->{'contents'}->[-1])."\n";
- if ($current->{'contents'}->[-1]->{'cmdname'}
- and ($current->{'contents'}->[-1]->{'cmdname'} eq 'item'
- or ($current->{'contents'}->[-1]->{'cmdname'} eq 'itemx'))) {
- last;
- } else {
- my $item_content = pop @{$current->{'contents'}};
- $item_content->{'parent'} = $table_gathered;
- unshift @{$table_gathered->{'contents'}}, $item_content;
- }
- }
- if ($type eq 'table_item') {
- my $table_entry = {'type' => 'table_entry',
- 'parent' => $current,
- 'contents' => []};
- my $table_term = {'type' => 'table_term',
- 'parent' => $table_entry,
- 'contents' => []};
- push @{$table_entry->{'contents'}}, $table_term;
- my $contents_count = scalar(@{$current->{'contents'}});
- for (my $i = 0; $i < $contents_count; $i++) {
- if ($current->{'contents'}->[-1]->{'type'}
- and ($current->{'contents'}->[-1]->{'type'} eq 'before_item'
- or $current->{'contents'}->[-1]->{'type'} eq 'table_entry')) {
- last;
- } else {
- my $item_content = pop @{$current->{'contents'}};
- $item_content->{'parent'} = $table_term;
- unshift @{$table_term->{'contents'}}, $item_content;
- # debug
- if (! (($item_content->{'cmdname'}
- and ($item_content->{'cmdname'} eq 'itemx'
- or $item_content->{'cmdname'} eq 'item'))
- or ($item_content->{'type'}
- and $item_content->{'type'} eq 'inter_item'))) {
- $self->_bug_message("wrong element in table term", $line_nr,
- $item_content);
- }
- }
- }
- push @{$current->{'contents'}}, $table_entry;
- if (scalar(@{$table_gathered->{'contents'}})) {
- push @{$table_entry->{'contents'}}, $table_gathered;
- $table_gathered->{'parent'} = $table_entry;
- }
- } else {
- my $after_paragraph = _check_no_text($table_gathered);
- if ($after_paragraph) {
- $self->line_error($self->__("\@itemx must follow \@item"), $line_nr);
- }
- if (scalar(@{$table_gathered->{'contents'}})) {
- push @{$current->{'contents'}}, $table_gathered;
- $table_gathered->{'parent'} = $current;
- }
- }
- }
- # Starting from the end, gather everything util the def_line to put in
- # a def_item
- sub _gather_def_item($;$)
- {
- my $current = shift;
- my $next_command = shift;
- my $type;
- # means that we are between a @def*x and a @def
- if ($next_command) {
- $type = 'inter_def_item';
- } else {
- $type = 'def_item';
- }
- # This may happen for a construct like
- # @deffnx a b @section
- # but otherwise the end of line will lead to the command closing
- return if (!$current->{'cmdname'} or $current->{'cmdname'} =~ /x$/);
- #print STDERR "_gather_def_item($type) in "._print_current($current)."\n";
- my $def_item = {'type' => $type,
- 'parent' => $current,
- 'contents' => []};
- # remove everything that is not a def_line to put it in the def_item,
- # starting from the end.
- my $contents_count = scalar(@{$current->{'contents'}});
- for (my $i = 0; $i < $contents_count; $i++) {
- #print STDERR "_gather_def_item $type ($i on $contents_count) "._print_current($current->{'contents'}->[-1])."\n";
- if ($current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'def_line') {
- # and !$current->{'contents'}->[-1]->{'extra'}->{'not_after_command'}) {
- last;
- } else {
- my $item_content = pop @{$current->{'contents'}};
- $item_content->{'parent'} = $def_item;
- unshift @{$def_item->{'contents'}}, $item_content;
- }
- }
- if (scalar(@{$def_item->{'contents'}})) {
- push @{$current->{'contents'}}, $def_item;
- }
- }
- # close formats
- sub _close_command_cleanup($$$) {
- my $self = shift;
- my $current = shift;
- return unless ($current->{'cmdname'});
- # remove the dynamic counters in multitable, they are not of use in the final
- # tree. Also determine the multitable_body and multitable_head with
- # @item or @headitem rows.
- if ($current->{'cmdname'} eq 'multitable') {
- my $in_head_or_rows;
- my @contents = @{$current->{'contents'}};
- $current->{'contents'} = [];
- foreach my $row (@contents) {
- if ($row->{'type'} and $row->{'type'} eq 'row') {
- delete $row->{'cells_count'};
- if ($row->{'contents'}->[0]->{'cmdname'} eq 'headitem') {
- if (!$in_head_or_rows) {
- push @{$current->{'contents'}}, {'type' => 'multitable_head',
- 'parent' => $current};
- $in_head_or_rows = 1;
- }
- } elsif ($row->{'contents'}->[0]->{'cmdname'} eq 'item') {
- if (!defined($in_head_or_rows) or $in_head_or_rows) {
- push @{$current->{'contents'}}, {'type' => 'multitable_body',
- 'parent' => $current};
- $in_head_or_rows = 0;
- }
- }
- push @{$current->{'contents'}->[-1]->{'contents'}}, $row;
- $row->{'parent'} = $current->{'contents'}->[-1];
- } else {
- push @{$current->{'contents'}}, $row;
- $in_head_or_rows = undef;
- }
- }
- delete $current->{'rows_count'};
- } elsif ($item_container_commands{$current->{'cmdname'}}) {
- delete $current->{'items_count'};
- }
- # put everything after the last @def*x command in a def_item type container.
- if ($def_commands{$current->{'cmdname'}}) {
- # At this point the end command hasn't been added to the command contents.
- # so checks cannot be done at this point.
- _gather_def_item($current);
- }
- if ($item_line_commands{$current->{'cmdname'}}) {
- # At this point the end command hasn't been added to the command contents.
- # so checks cannot be done at this point.
- if (@{$current->{'contents'}}) {
- $self->_gather_previous_item($current);
- }
- }
-
- # put end out of before_item, and replace it at the end of the parent.
- # remove empty before_item.
- # warn if not empty before_item, but format is empty
- if ($block_item_commands{$current->{'cmdname'}}) {
- if (@{$current->{'contents'}}) {
- my $leading_spaces = 0;
- my $before_item;
- if ($current->{'contents'}->[0]->{'type'}
- and $current->{'contents'}->[0]->{'type'} eq 'empty_line_after_command'
- and $current->{'contents'}->[1]
- and $current->{'contents'}->[1]->{'type'}
- and $current->{'contents'}->[1]->{'type'} eq 'before_item') {
- $leading_spaces = 1;
- $before_item = $current->{'contents'}->[1];
- } elsif ($current->{'contents'}->[0]->{'type'}
- and $current->{'contents'}->[0]->{'type'} eq 'before_item') {
- $before_item = $current->{'contents'}->[0];
- }
- if ($before_item) {
- if ($current->{'extra'}->{'end_command'}
- and @{$before_item->{'contents'}}
- and $before_item->{'contents'}->[-1] eq $current->{'extra'}->{'end_command'}) {
- my $end = pop @{$before_item->{'contents'}};
- $end->{'parent'} = $current;
- push @{$current->{'contents'}}, $end;
- }
- # remove empty before_items
- if (!@{$before_item->{'contents'}}) {
- if ($leading_spaces) {
- my $space = shift @{$current->{'contents'}};
- shift @{$current->{'contents'}};
- unshift @{$current->{'contents'}}, $space;
- } else {
- shift @{$current->{'contents'}};
- }
- } else {
- # warn if not empty before_item, but format is empty
- my $empty_before_item = 1;
- foreach my $before_item_content (@{$before_item->{'contents'}}) {
- if (!$before_item_content->{'cmdname'} or
- ($before_item_content->{'cmdname'} ne 'c'
- and $before_item_content->{'cmdname'} ne 'comment')) {
- $empty_before_item = 0;
- last;
- }
- }
- if (!$empty_before_item) {
- my $empty_format = 1;
- foreach my $format_content (@{$current->{'contents'}}) {
- next if ($format_content eq $before_item);
- if (($format_content->{'cmdname'} and
- ($format_content->{'cmdname'} ne 'c'
- and $format_content->{'cmdname'} ne 'comment'
- and $format_content->{'cmdname'} ne 'end'))
- or ($format_content->{'type'} and
- ($format_content->{'type'} ne 'empty_line_after_command'))) {
- $empty_format = 0;
- last;
- }
- }
- if ($empty_format) {
- $self->line_warn(sprintf($self->__("\@%s has text but no \@item"),
- $current->{'cmdname'}), $current->{'line_nr'});
- }
- }
- }
- }
- }
- }
- }
- # close the current command, with error messages and give the parent.
- # If the last argument is given it is the command being closed if
- # hadn't there be an error, currently only block command, used for a
- # better error message.
- sub _close_current($$$;$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $closed_command = shift;
- my $interrupting_command = shift;
- if ($current->{'cmdname'}) {
- print STDERR "CLOSING(_close_current) \@$current->{'cmdname'}\n" if ($self->{'DEBUG'});
- if (exists($brace_commands{$current->{'cmdname'}})) {
- pop @{$self->{'context_stack'}}
- if (exists $context_brace_commands{$current->{'cmdname'}});
- $current = _close_brace_command($self, $current, $line_nr,
- $closed_command, $interrupting_command);
- } elsif (exists($block_commands{$current->{'cmdname'}})) {
- if (defined($closed_command)) {
- $self->line_error(sprintf($self->__("`\@end' expected `%s', but saw `%s'"),
- $current->{'cmdname'}, $closed_command), $line_nr);
- } elsif ($interrupting_command) {
- $self->line_error(sprintf($self->__("\@%s seen before \@end %s"),
- $interrupting_command, $current->{'cmdname'}),
- $line_nr);
- } else {
- $self->line_error(sprintf($self->__("no matching `%cend %s'"),
- ord('@'), $current->{'cmdname'}), $line_nr);
- if ($block_commands{$current->{'cmdname'}} eq 'conditional') {
- # in this case we are within an ignored conditional
- my $conditional = pop @{$current->{'parent'}->{'contents'}};
- }
- }
- if ($preformatted_commands{$current->{'cmdname'}}
- or $menu_commands{$current->{'cmdname'}}
- or $format_raw_commands{$current->{'cmdname'}}) {
- my $context = pop @{$self->{'context_stack'}};
- pop @{$self->{'expanded_formats_stack'}}
- if ($format_raw_commands{$current->{'cmdname'}});
- }
- pop @{$self->{'regions_stack'}}
- if ($region_commands{$current->{'cmdname'}});
- $current = $current->{'parent'};
- } else {
- # There @item and @tab commands are closed, and also line commands
- # with invalid content
- $current = $current->{'parent'};
- }
- } elsif ($current->{'type'}) {
- print STDERR "CLOSING type $current->{'type'}\n" if ($self->{'DEBUG'});
- if ($current->{'type'} eq 'bracketed') {
- $self->_command_error($current, $line_nr,
- $self->__("misplaced %c"), ord('{'));
- } elsif ($current->{'type'} eq 'menu_comment'
- or $current->{'type'} eq 'menu_entry_description') {
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'preformatted') {
- $self->_bug_message("context $context instead of preformatted",
- $line_nr, $current);
- }
- # close empty menu_comment
- if (!@{$current->{'contents'}}) {
- pop @{$current->{'parent'}->{'contents'}};
- }
- } elsif ($current->{'type'} eq 'misc_line_arg'
- or $current->{'type'} eq 'block_line_arg') {
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'line' and $context ne 'def') {
- $self->_bug_message("context $context instead of line or def",
- $line_nr, $current);
- die;
- }
- }
- $current = $current->{'parent'};
- } else { # Should never go here.
- $current = $current->{'parent'} if ($current->{'parent'});
- $self->_bug_message("No type nor cmdname when closing",
- $line_nr, $current);
- }
- return $current;
- }
- # a closed_command arg means closing until that command is found.
- # no command arg means closing until the root or a root_command
- # is found.
- sub _close_commands($$$;$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $closed_command = shift;
- my $interrupting_command = shift;;
- $current = _end_paragraph($self, $current, $line_nr, $closed_command,
- $interrupting_command);
- $current = _end_preformatted($self, $current, $line_nr, $closed_command,
- $interrupting_command);
- # stop if the command is found
- while (!($closed_command and $current->{'cmdname'}
- and $current->{'cmdname'} eq $closed_command)
- # stop if at the root
- and $current->{'parent'}
- # stop if in a root command
- # or in a context_brace_commands and searching for a specific
- # end block command (with $closed_command set).
- # This second condition means that a footnote is not closed when
- # looking for the end of a block command, but is closed when
- # completly closing the stack.
- and !($current->{'cmdname'}
- and ($root_commands{$current->{'cmdname'}}
- or ($closed_command and $current->{'parent'}->{'cmdname'}
- and $context_brace_commands{$current->{'parent'}->{'cmdname'}})))){
- $self->_close_command_cleanup($current);
- $current = $self->_close_current($current, $line_nr, $closed_command,
- $interrupting_command);
- }
- my $closed_element;
- if ($closed_command and $current->{'cmdname'}
- and $current->{'cmdname'} eq $closed_command) {
- if ($preformatted_commands{$current->{'cmdname'}}) {
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'preformatted') {
- $self->_bug_message("context $context instead of preformatted for $closed_command",
- $line_nr, $current);
- }
- } elsif ($format_raw_commands{$current->{'cmdname'}}) {
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'rawpreformatted') {
- $self->_bug_message("context $context instead of rawpreformatted for $closed_command",
- $line_nr, $current);
- }
- pop @{$self->{'expanded_formats_stack'}};
- } elsif ($menu_commands{$current->{'cmdname'}}) {
- my $context = pop @{$self->{'context_stack'}};
- # may be in menu, but context is preformatted if in a preformatted too.
- if ($context ne 'menu' and $context ne 'preformatted') {
- $self->_bug_message("context $context instead of preformatted or menu for $closed_command",
- $line_nr, $current);
- }
- }
- #print STDERR "close context $context for $current->{'cmdname'}\n"
- # if ($self->{'DEBUG'});
- pop @{$self->{'regions_stack'}}
- if ($region_commands{$current->{'cmdname'}});
- $closed_element = $current;
- #$self->_close_command_cleanup($current);
- $current = $current->{'parent'};
- } elsif ($closed_command) {
- $self->line_error(sprintf($self->__("unmatched `%c%s'"),
- ord('@'), "end $closed_command"), $line_nr);
- }
- return ($closed_element, $current);
- }
- # begin paragraph if needed. If not try to merge with the previous
- # content if it is also some text.
- sub _merge_text($$$)
- {
- my $self = shift;
- my $current = shift;
- my $text = shift;
- my $paragraph;
- my $no_merge_with_following_text = 0;
- if ($text =~ /\S/) {
- my $leading_spaces;
- if ($text =~ /^(\s+)/) {
- $leading_spaces = $1;
- }
- if ($current->{'contents'} and @{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
- or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument'
- or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) {
- $no_merge_with_following_text = 1;
- }
- if (_abort_empty_line($self, $current, $leading_spaces)) {
- $text =~ s/^(\s+)//;
- }
- $paragraph = _begin_paragraph($self, $current);
- $current = $paragraph if ($paragraph);
- }
- if (!defined($current->{'contents'})) {
- $self->_bug_message("No contents in _merge_text",
- undef, $current);
- die;
- }
- if (@{$current->{'contents'}}
- and exists($current->{'contents'}->[-1]->{'text'})
- and $current->{'contents'}->[-1]->{'text'} !~ /\n/
- and !$no_merge_with_following_text) {
- $current->{'contents'}->[-1]->{'text'} .= $text;
- print STDERR "MERGED TEXT: $text|||\n" if ($self->{'DEBUG'});
- } else {
- push @{$current->{'contents'}}, { 'text' => $text, 'parent' => $current };
- print STDERR "NEW TEXT: $text|||\n" if ($self->{'DEBUG'});
- }
- return $current;
- }
- # return the parent if in a item_container command, itemize or enumerate
- sub _item_container_parent($)
- {
- my $current = shift;
- if ((($current->{'cmdname'} and $current->{'cmdname'} eq 'item')
- or ($current->{'type'} and $current->{'type'} eq 'before_item'))
- and ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
- and $item_container_commands{$current->{'parent'}->{'cmdname'}})) {
- return ($current->{'parent'});
- }
- return undef;
- }
- # return the parent if in a item_line command, @*table
- sub _item_line_parent($)
- {
- my $current = shift;
- if ($current->{'cmdname'} and ($current->{'cmdname'} eq 'item'
- or $current->{'cmdname'} eq 'itemx')) {
- $current = $current->{'parent'}->{'parent'};
- } elsif ($current->{'type'} and $current->{'type'} eq 'before_item'
- and $current->{'parent'}) {
- $current = $current->{'parent'};
- }
- return $current if ($current->{'cmdname'}
- and $item_line_commands{$current->{'cmdname'}});
- return undef;
- }
- # return the parent if in a multitable
- sub _item_multitable_parent($)
- {
- my $current = shift;
- if (($current->{'cmdname'} and ($current->{'cmdname'} eq 'headitem'
- or $current->{'cmdname'} eq 'item' or $current->{'cmdname'} eq 'tab'))
- and $current->{'parent'} and $current->{'parent'}->{'parent'}) {
- $current = $current->{'parent'}->{'parent'};
- } elsif ($current->{'type'} and $current->{'type'} eq 'before_item'
- and $current->{'parent'}) {
- $current = $current->{'parent'};
- }
- return $current if ($current->{'cmdname'}
- and $current->{'cmdname'} eq 'multitable');
- return undef;
- }
- # returns next text fragment, be it pending from a macro expansion or
- # text or file
- sub _next_text($$$)
- {
- my $self = shift;
- my $line_nr = shift;
- my $current = shift;
-
- while (@{$self->{'input'}}) {
- my $input = $self->{'input'}->[0];
- if (@{$input->{'pending'}}) {
- my $new_text = shift @{$input->{'pending'}};
- if ($new_text->[1] and $new_text->[1]->{'end_macro'}) {
- delete $new_text->[1]->{'end_macro'};
- my $top_macro = shift @{$self->{'macro_stack'}};
- print STDERR "SHIFT MACRO_STACK(@{$self->{'macro_stack'}}): $top_macro->{'args'}->[0]->{'text'}\n"
- if ($self->{'DEBUG'});
- }
- return ($new_text->[0], $new_text->[1]);
- } elsif ($input->{'fh'}) {
- my $fh = $input->{'fh'};
- my $line = <$fh>;
- while (defined($line)) {
- # add an end of line if there is none at the end of file
- if (eof($fh) and $line !~ /\n/) {
- $line .= "\n";
- }
- $line =~ s/\x{7F}.*\s*//;
- if ($self->{'CPP_LINE_DIRECTIVES'}
- # no cpp directives in ignored/macro/verbatim
- and defined ($current)
- and not
- (($current->{'cmdname'}
- and $block_commands{$current->{'cmdname'}}
- and ($block_commands{$current->{'cmdname'}} eq 'raw'
- or $block_commands{$current->{'cmdname'}} eq 'conditional'))
- or
- ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
- and $current->{'parent'}->{'cmdname'} eq 'verb')
- )
- and $line =~ /^\s*#\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*$/) {
- $input->{'line_nr'} = $2;
- if (defined($5)) {
- $input->{'name'} = $5;
- }
- $line = <$fh>;
- } else {
- $input->{'line_nr'}++;
- return ($line, {'line_nr' => $input->{'line_nr'},
- 'file_name' => $input->{'name'},
- 'macro' => ''});
- }
- }
- }
- my $previous_input = shift(@{$self->{'input'}});
- # Don't close STDIN
- if ($previous_input->{'fh'} and $previous_input->{'name'} ne '-') {
- if (!close($previous_input->{'fh'})) {
- $self->document_warn(sprintf($self->__("error on closing %s: %s"),
- $previous_input->{'name'}, $!));
- }
- }
- }
- return (undef, $line_nr);
- }
- # collect text and line numbers until an end of line is found.
- sub _new_line($$$)
- {
- my $self = shift;
- my $line_nr = shift;
- my $current = shift;
- my $new_line = '';
- while (1) {
- my $new_text;
- ($new_text, $line_nr) = _next_text($self, $line_nr, $current);
- if (!defined($new_text)) {
- $new_line = undef if ($new_line eq '');
- last;
- }
- $new_line .= $new_text;
- my $chomped_text = $new_text;
- last if chomp($chomped_text);
- }
- return ($new_line, $line_nr);
- }
- sub _expand_macro_arguments($$$$)
- {
- my $self = shift;
- my $macro = shift;
- my $line = shift;
- my $line_nr = shift;
- my $braces_level = 1;
- my $arguments = [ '' ];
- my $arg_nr = 0;
- my $args_total = scalar(@{$macro->{'args'}}) -1;
- my $name = $macro->{'args'}->[0]->{'text'};
- my $line_nr_orig = $line_nr;
- while (1) {
- if ($line =~ s/([^\\{},]*)([\\{},])//) {
- my $separator = $2;
- $arguments->[-1] .= $1;
- if ($separator eq '\\') {
- if ($line =~ s/^(.)//) {
- my $protected_char = $1;
- if ($protected_char !~ /[\\{},]/) {
- $arguments->[-1] .= '\\';
- }
- $arguments->[-1] .= $protected_char;
-
- print STDERR "MACRO ARG: $separator: $protected_char\n" if ($self->{'DEBUG'});
- } else {
- $arguments->[-1] .= '\\';
- print STDERR "MACRO ARG: $separator\n" if ($self->{'DEBUG'});
- }
- } elsif ($separator eq ',') {
- if ($braces_level == 1) {
- if (scalar(@$arguments) < $args_total) {
- push @$arguments, '';
- $line =~ s/^[^\S\f]*//;
- print STDERR "MACRO NEW ARG\n" if ($self->{'DEBUG'});
- } else {
- # implicit quoting when there is one argument.
- if ($args_total != 1) {
- $self->line_error(sprintf($self->__(
- "macro `%s' called with too many args"),
- $name), $line_nr);
- }
- $arguments->[-1] .= ',';
- }
- } else {
- $arguments->[-1] .= ',';
- }
- } elsif ($separator eq '}') {
- $braces_level--;
- last if ($braces_level == 0);
- $arguments->[-1] .= $separator;
- } elsif ($separator eq '{') {
- $braces_level++;
- $arguments->[-1] .= $separator;
- }
- } else {
- print STDERR "MACRO ARG end of line\n" if ($self->{'DEBUG'});
- $arguments->[-1] .= $line;
- ($line, $line_nr) = _new_line($self, $line_nr, $macro);
- if (!defined($line)) {
- $self->line_error(sprintf($self->__("\@%s missing close brace"),
- $name), $line_nr_orig);
- return ($arguments, "\n", $line_nr);
- }
- }
- }
- if ($args_total == 0 and $arguments->[0] =~ /[\S\f]/) {
- $self->line_error(sprintf($self->__(
- "macro `%s' declared without argument called with an argument"),
- $name), $line_nr);
- }
- print STDERR "END MACRO ARGS EXPANSION(".scalar(@$arguments)."): ".
- join("|\n", @$arguments) ."|\n" if ($self->{'DEBUG'});
- return ($arguments, $line, $line_nr);
- }
- sub _expand_macro_body($$$$) {
- my $self = shift;
- my $macro = shift;
- my $args = shift;
- my $line_nr = shift;
- my $macrobody = $macro->{'extra'}->{'macrobody'};
- my $args_total = scalar(@{$macro->{'args'}}) -1;
- my $args_index = $macro->{'extra'}->{'args_index'};
- my $i;
- for ($i=0; $i<=$args_total; $i++) {
- $args->[$i] = "" unless (defined($args->[$i]));
- }
- my $result = '';
- while ($macrobody ne '') {
- if ($macrobody =~ s/^([^\\]*)\\//o) {
- $result .= $1;
- if ($macrobody =~ s/^\\//) {
- $result .= '\\';
- } elsif ($macrobody =~ s/^([^\\]*)\\//) {
- my $arg = $1;
- if (defined($args_index->{$arg})) {
- $result .= $args->[$args_index->{$arg}];
- } else {
- $self->line_error(sprintf($self->__(
- "\\ in \@%s expansion followed `%s' instead of parameter name or \\"),
- $macro->{'args'}->[0]->{'text'}, $arg), $line_nr);
- $result .= '\\' . $arg;
- }
- }
- next;
- }
- $result .= $macrobody;
- last;
- }
- return $result;
- }
- # each time a new line appeared, a container is opened to hold the text
- # consisting only of spaces. This container is removed here, typically
- # this is called when non-space happens on a line.
- sub _abort_empty_line($$;$)
- {
- my $self = shift;
- my $current = shift;
- my $additional_text = shift;
- $additional_text = '' if (!defined($additional_text));
- if ($current->{'contents'} and @{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line'
- or $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
- or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument'
- or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) {
- print STDERR "ABORT EMPTY additional text |$additional_text|, current |$current->{'contents'}->[-1]->{'text'}|\n" if ($self->{'DEBUG'});
- $current->{'contents'}->[-1]->{'text'} .= $additional_text;
- # remove empty 'empty*before'.
- if ($current->{'contents'}->[-1]->{'text'} eq '') {
- # as we remove 'empty_spaces_before_argument', 'spaces_before_argument'
- # is removed from 'extra' too.
- if ($current->{'extra'}
- and $current->{'extra'}->{'spaces_before_argument'}
- and $current->{'extra'}->{'spaces_before_argument'}
- eq $current->{'contents'}->[-1]) {
- delete ($current->{'extra'}->{'spaces_before_argument'});
- delete ($current->{'extra'}) if !(keys(%{$current->{'extra'}}));
- }
- pop @{$current->{'contents'}}
- } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line') {
- # exactly the same condition than to begin a paragraph
- if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}})
- and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) {
- $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_before_paragraph';
- } else {
- delete $current->{'contents'}->[-1]->{'type'};
- }
- } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command') {
- $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_after_command';
- }
- return 1;
- }
- return 0;
- }
- # isolate last space in a command to help expansion disregard unuseful spaces.
- sub _isolate_last_space($$;$)
- {
- my $self = shift;
- my $current = shift;
- my $type = shift;
- $type = 'spaces_at_end' if (!defined($type));
- if ($current->{'contents'} and @{$current->{'contents'}}) {
- my $index = -1;
- # we ignore space before a misc command that is last on line.
- # This is primarily to tag spaces before comments, but this will
- # also tag and, in most converter lead to removal of spaces
- # before any misc command, which is not really problematic as
- # in most cases, if it is not a comment, we are in an invalid
- # nesting of misc command on another @-command line.
- $index = -2
- if (scalar(@{$current->{'contents'}}) > 1
- and $current->{'contents'}->[-1]->{'cmdname'}
- and $self->{'misc_commands'}->{$current->{'contents'}->[-1]->{'cmdname'}});
- if (defined($current->{'contents'}->[$index]->{'text'})
- and !$current->{'contents'}->[$index]->{'type'}
- and $current->{'contents'}->[$index]->{'text'} =~ /\s+$/) {
- if ($current->{'contents'}->[$index]->{'text'} !~ /\S/) {
- $current->{'contents'}->[$index]->{'type'} = $type;
- } else {
- $current->{'contents'}->[$index]->{'text'} =~ s/(\s+)$//;
- my $spaces = $1;
- my $new_spaces = { 'text' => $spaces, 'parent' => $current,
- 'type' => $type };
- if ($index == -1) {
- push @{$current->{'contents'}}, $new_spaces;
- } else {
- splice (@{$current->{'contents'}}, $index+1, 0, $new_spaces);
- }
- }
- }
- }
- }
- # used to put a node name in error messages.
- sub _node_extra_to_texi($)
- {
- my $node = shift;
- my $result = '';
- if ($node->{'manual_content'}) {
- $result = '('.Texinfo::Convert::Texinfo::convert({'contents'
- => $node->{'manual_content'}}) .')';
- }
- if ($node->{'node_content'}) {
- $result .= Texinfo::Convert::Texinfo::convert ({'contents'
- => $node->{'node_content'}});
- }
- return $result;
- }
- sub _find_end_brace($$)
- {
- my $text = shift;
- my $braces_count = shift;
- my $before = '';
- while ($braces_count > 0 and length($text)) {
- if ($text =~ s/([^()]*)([()])//) {
- $before .= $1.$2;
- my $brace = $2;
- if ($brace eq '(') {
- $braces_count++;
- } else {
- $braces_count--;
- if ($braces_count == 0) {
- return ($before, $text, 0);
- }
- }
- } else {
- $before .= $text;
- $text = '';
- }
- }
- return ($before, undef, $braces_count);
- }
- # This only counts opening braces, and returns 0 once all the parentheses
- # are closed
- sub _count_opened_tree_braces($$);
- sub _count_opened_tree_braces($$)
- {
- my $current = shift;
- my $braces_count = shift;
- if (defined($current->{'text'})) {
- my ($before, $after);
- ($before, $after, $braces_count) = _find_end_brace($current->{'text'},
- $braces_count);
- }
- if ($current->{'args'}) {
- foreach my $arg (@{$current->{'args'}}) {
- $braces_count = _count_opened_tree_braces($arg, $braces_count);
- return $braces_count if ($braces_count == 0);
- }
- }
- if ($current->{'contents'}) {
- foreach my $content (@{$current->{'contents'}}) {
- $braces_count = _count_opened_tree_braces($content, $braces_count);
- return $braces_count if ($braces_count == 0);
- }
- }
- return $braces_count;
- }
- # retrieve a leading manual name in parentheses, if there is one.
- sub _parse_node_manual($)
- {
- my $node = shift;
- my @contents = @{$node->{'contents'}};
- _trim_spaces_comment_from_content(\@contents);
- my $manual;
- my $result;
- #print STDERR "RRR $contents[0] and $contents[0]->{'text'} \n";
- if ($contents[0] and $contents[0]->{'text'} and $contents[0]->{'text'} =~ /^\(/) {
- my $braces_count = 1;
- if ($contents[0]->{'text'} !~ /^\($/) {
- my $brace = shift @contents;
- my $brace_text = $brace->{'text'};
- $brace_text =~ s/^\(//;
- unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'},
- 'parent' => $brace->{'parent'} } if $brace_text ne '';
- } else {
- shift @contents;
- }
- while(@contents) {
- my $content = shift @contents;
- if (!defined($content->{'text'}) or $content->{'text'} !~ /\)/) {
- push @$manual, $content;
- $braces_count = _count_opened_tree_braces($content, $braces_count);
- # This is an error, braces were closed in a command
- if ($braces_count == 0) {
- last;
- }
- } else {
- my ($before, $after);
- ($before, $after, $braces_count) = _find_end_brace($content->{'text'},
- $braces_count);
- if ($braces_count == 0) {
- $before =~ s/\)$//;
- push @$manual, { 'text' => $before, 'parent' => $content->{'parent'} }
- if ($before ne '');
- $after =~ s/^\s*//;
- unshift @contents, { 'text' => $after, 'parent' => $content->{'parent'} }
- if ($after ne '');
- last;
- } else {
- push @$manual, $content;
- }
- }
- }
- $result->{'manual_content'} = $manual if (defined($manual));
- }
- if (@contents) {
- $result->{'node_content'} = \@contents;
- $result->{'normalized'} =
- Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => \@contents});
- }
- return $result;
- }
- sub _parse_float_type($)
- {
- my $current = shift;
- if (@{$current->{'args'}}) {
- my @type_contents = @{$current->{'args'}->[0]->{'contents'}};
- _trim_spaces_comment_from_content(\@type_contents);
- if (@type_contents) {
- my $normalized
- = Texinfo::Convert::NodeNameNormalization::normalize_node(
- {'contents' => \@type_contents});
- $current->{'extra'}->{'type'}->{'content'} = \@type_contents;
- if ($normalized =~ /[^-]/) {
- $current->{'extra'}->{'type'}->{'normalized'} = $normalized;
- return 1;
- }
- }
- }
- $current->{'extra'}->{'type'}->{'normalized'} = '';
- return 0;
- }
- # used for definition line parsing
- sub _next_bracketed_or_word($$)
- {
- my $self = shift;
- my $contents = shift;
- return undef if (!scalar(@{$contents}));
- my $spaces;
- $spaces = shift @{$contents} if (defined($contents->[0]->{'text'}) and
- $contents->[0]->{'text'} !~ /\S/);
- if (defined($spaces)) {
- $spaces->{'type'} = 'spaces';
- chomp $spaces->{'text'};
- $spaces = undef if ($spaces->{'text'} eq '');
- }
- return ($spaces, undef) if (!scalar(@{$contents}));
- #print STDERR "BEFORE PROCESSING ".Texinfo::Convert::Texinfo::convert({'contents' => $contents});
- if ($contents->[0]->{'type'} and $contents->[0]->{'type'} eq 'bracketed') {
- #print STDERR "Return bracketed\n";
- my $bracketed = shift @{$contents};
- $self->_isolate_last_space($bracketed, 'empty_space_at_end_def_bracketed');
- my $bracketed_def_content = { 'contents' => $bracketed->{'contents'},
- 'parent' => $bracketed->{'parent'},
- 'type' => 'bracketed_def_content', };
- if ($bracketed->{'extra'} and $bracketed->{'extra'}->{'spaces_before_argument'}) {
- $bracketed_def_content->{'extra'}->{'spaces_before_argument'}
- = $bracketed->{'extra'}->{'spaces_before_argument'};
- }
- return ($spaces, $bracketed_def_content);
- } elsif ($contents->[0]->{'cmdname'}) {
- #print STDERR "Return command $contents->[0]->{'cmdname'}\n";
- return ($spaces, shift @{$contents});
- } else {
- #print STDERR "Process $contents->[0]->{'text'}\n";
- $contents->[0]->{'text'} =~ s/^(\s*)//;
- my $space_text = $1;
- $spaces = {'text' => $space_text, 'type' => 'spaces'} if ($space_text);
- $contents->[0]->{'text'} =~ s/^(\S+)//;
- shift @{$contents} if ($contents->[0]->{'text'} eq '');
- return ($spaces, {'text' => $1});
- }
- }
- # definition line parsing
- sub _parse_def($$$)
- {
- my $self = shift;
- my $command = shift;
- my $contents = shift;
-
- my @contents = @$contents;
- shift @contents if ($contents[0] and $contents[0]->{'type'}
- and $contents[0]->{'type'} eq 'empty_spaces_after_command');
- if ($def_aliases{$command}) {
- my $real_command = $def_aliases{$command};
- my $prepended = $def_map{$command}->{$real_command};
- my @prepended_content;
- my $text;
- my $in_bracketed;
- if ($prepended =~ /^\{/) {
- $text = $prepended;
- $text =~ s/\{([^\}]+)\}/$1/;
- $in_bracketed = 1;
- } else {
- $text = $prepended;
- }
- my $tree = $self->gdt($text);
- if ($in_bracketed or @{$tree->{'contents'}} > 1) {
- my $bracketed = { 'type' => 'bracketed' };
- $bracketed->{'contents'} = $tree->{'contents'};
- foreach my $content (@{$tree->{'contents'}}) {
- $content->{'parent'} = $bracketed;
- }
- @prepended_content = ($bracketed);
- } else {
- @prepended_content = (@{$tree->{'contents'}});
- }
- push @prepended_content, { 'text' => ' ' };
- unshift @contents, @prepended_content;
- $command = $def_aliases{$command};
- }
- foreach (my $i = 0; $i < scalar(@contents); $i++) {
- # copy, to avoid changing the original
- $contents[$i] = {'text' => $contents[$i]->{'text'}}
- if (defined($contents[$i]->{'text'}));
- }
- my @result;
- my @args = @{$def_map{$command}};
- my $arg_type;
- # Even when $arg_type is not set, that is for def* that is not documented
- # to take args, everything is as is arg_type was set to arg.
- $arg_type = pop @args if ($args[-1] eq 'arg' or $args[-1] eq 'argtype');
- foreach my $arg (@args) {
- #print STDERR "$command $arg"._print_current($contents[0]);
- #foreach my $content (@contents) {print STDERR " "._print_current($content)};
- #print STDERR " contents ->".Texinfo::Convert::Texinfo::convert ({'contents' => \@contents});
- my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents);
- last if (!defined($next));
- #print STDERR "NEXT[$arg] ".Texinfo::Convert::Texinfo::convert($next)."\n";
- push @result, ['spaces', $spaces] if (defined($spaces));
- push @result, [$arg, $next];
- }
- my @args_results;
- while (@contents) {
- my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents);
- push @args_results, ['spaces', $spaces] if (defined($spaces));
- last if (!defined($next));
- if (defined($next->{'text'})) {
- while (1) {
- if ($next->{'text'} =~ s/^([^\[\](),]+)//) {
- push @args_results, ['arg', {'text' => $1}];
- } elsif ($next->{'text'} =~ s/^([\[\](),])//) {
- push @args_results, ['delimiter',
- {'text' => $1, 'type' => 'delimiter'}];
- } else {
- last;
- }
- }
- } else {
- push @args_results, [ 'arg', $next ];
- }
- }
- if ($arg_type and $arg_type eq 'argtype') {
- my $next_is_type = 1;
- foreach my $arg(@args_results) {
- if ($arg->[0] eq 'spaces') {
- } elsif ($arg->[0] eq 'delimiter') {
- $next_is_type = 1;
- } elsif ($arg->[1]->{'cmdname'} and $arg->[1]->{'cmdname'} ne 'code') {
- $next_is_type = 1;
- } elsif ($next_is_type) {
- $arg->[0] = 'typearg';
- $next_is_type = 0;
- } else {
- $next_is_type = 1;
- }
- }
- }
- return [@result, @args_results];
- }
- # register a label, that is something that may be the target of a reference
- # and must be unique in the document. Corresponds with @node, @anchor and
- # @float second arg.
- sub _register_label($$$$)
- {
- my $self = shift;
- my $current = shift;
- my $label = shift;
- my $line_nr = shift;
- my $normalized = $label->{'normalized'};
- if (_ignore_global_commands($self)) {
- $current->{'extra'}->{'normalized'} = $normalized;
- $current->{'extra'}->{'node_content'} = $label->{'node_content'};
- return 0;
- } elsif ($self->{'labels'}->{$normalized}) {
- $self->line_error(sprintf($self->__("\@%s `%s' previously defined"),
- $current->{'cmdname'},
- Texinfo::Convert::Texinfo::convert({'contents' =>
- $label->{'node_content'}})),
- $line_nr);
- $self->line_error(sprintf($self->__("here is the previous definition as \@%s"),
- $self->{'labels'}->{$normalized}->{'cmdname'}),
- $self->{'labels'}->{$normalized}->{'line_nr'}, 1);
- return 0;
- } else {
- $current->{'extra'}->{'normalized'} = $normalized;
- $current->{'extra'}->{'node_content'} = $label->{'node_content'};
- $self->{'labels'}->{$normalized} = $current;
- return 1;
- }
- }
- sub _non_bracketed_contents($)
- {
- my $current = shift;
- if ($current->{'type'} and $current->{'type'} eq 'bracketed') {
- my $new = {};
- $new->{'contents'} = $current->{'contents'} if ($current->{'parent'});
- $new->{'parent'} = $current->{'parent'} if ($current->{'parent'});
- return $new;
- } else {
- return $current;
- }
- }
- # store an index entry.
- # $current is the command element.
- # $content holds the actual content.
- # for index entries and v|ftable items, it is the index entry content,
- # for def, it is the parsed arguments, based on the definition line
- # arguments.
- sub _enter_index_entry($$$$$$$)
- {
- my $self = shift;
- my $command_container = shift;
- my $command = shift;
- my $current = shift;
- my $content = shift;
- my $content_normalized = shift;
- my $line_nr = shift;
- $content_normalized = $content if (!defined($content_normalized));
- my $prefix = $self->{'command_index_prefix'}->{$command_container};
- my $index_name = $self->{'prefix_to_index_name'}->{$prefix};
- my $index = $self->{'index_names'}->{$index_name};
- my $number;
- unless (_ignore_global_commands($self)) {
- $number = (defined($index->{'index_entries'})
- ? (scalar(@{$index->{'index_entries'}}) + 1)
- : 1);
- }
- my $index_entry = { 'index_name' => $index_name,
- 'index_at_command' => $command,
- 'index_type_command' => $command_container,
- 'index_prefix' => $prefix,
- 'content' => $content,
- 'content_normalized' => $content_normalized,
- 'command' => $current,
- 'number' => $number,
- };
- if (@{$self->{'regions_stack'}}) {
- $index_entry->{'region'} = $self->{'regions_stack'}->[-1];
- } elsif ($self->{'current_node'}) {
- $index_entry->{'node'} = $self->{'current_node'};
- } elsif (!$self->{'current_section'}) {
- $self->line_warn(sprintf($self->__("entry for index `%s' outside of any node"),
- $index_name), $line_nr);
- }
- #print STDERR "INDEX ENTRY \@$command->{'cmdname'} $index_name($number)\n";
- unless (_ignore_global_commands($self)) {
- push @{$index->{'index_entries'}}, $index_entry;
- }
- $current->{'extra'}->{'index_entry'} = $index_entry;
- }
- # This is always called at command closing.
- sub _remove_empty_content_arguments($)
- {
- my $current = shift;
- my $type;
- if ($current->{'extra'}) {
- if ($current->{'extra'}->{'block_command_line_contents'}) {
- $type = 'block_command_line_contents';
- } elsif ($current->{'extra'}->{'brace_command_contents'}) {
- $type = 'brace_command_contents';
- }
- }
- if ($type) {
- while (@{$current->{'extra'}->{$type}}
- and not defined($current->{'extra'}->{$type}->[-1])) {
- pop @{$current->{'extra'}->{$type}};
- }
- delete $current->{'extra'}->{$type} if (!@{$current->{'extra'}->{$type}});
- delete $current->{'extra'} if (!keys(%{$current->{'extra'}}));
- }
- }
- sub _strip_macrobody_leading_space($$)
- {
- my $self = shift;
- my $text = shift;
- if ($self->{'MACRO_BODY_IGNORES_LEADING_SPACE'}) {
- $text =~ s/^\s*//mg;
- }
- return $text;
- }
- # close constructs and do stuff at end of line (or end of the document)
- sub _end_line($$$);
- sub _end_line($$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $current_old = $current;
- my $included_file = 0;
- # a line consisting only of spaces.
- if ($current->{'contents'} and @{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'empty_line') {
- print STDERR "END EMPTY LINE\n" if ($self->{'DEBUG'});
- if ($current->{'type'} and $current->{'type'} eq 'paragraph') {
- my $empty_line = pop @{$current->{'contents'}};
- $current = _end_paragraph($self, $current, $line_nr);
- push @{$current->{'contents'}}, $empty_line;
- $empty_line->{'parent'} = $current;
- } elsif ($current->{'type'}
- and $current->{'type'} eq 'preformatted'
- and $current->{'parent'}->{'type'}
- and $current->{'parent'}->{'type'} eq 'menu_entry_description') {
- my $empty_line = pop @{$current->{'contents'}};
- if ($current->{'type'} eq 'preformatted') {
- my $empty_preformatted = (!@{$current->{'contents'}});
- $current = $current->{'parent'};
- pop @{$current->{'contents'}} if ($empty_preformatted);
- }
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'preformatted') {
- $self->_bug_message("context $context instead of preformatted in empty line after menu_entry_description",
- $line_nr, $current);
- }
-
- # first parent is menu_entry
- $current = $current->{'parent'}->{'parent'};
-
- push @{$current->{'contents'}}, { 'type' => 'menu_comment',
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- push @{$current->{'contents'}}, { 'type' => 'preformatted',
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- push @{$current->{'contents'}}, { 'type' => 'after_description_line',
- 'text' => $empty_line->{'text'},
- 'parent' => $current };
- push @{$self->{'context_stack'}}, 'preformatted';
- print STDERR "MENU: END DESCRIPTION, OPEN COMMENT\n" if ($self->{'DEBUG'});
- } elsif (!$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) {
- $current = _end_paragraph($self, $current, $line_nr);
- }
- # end of a menu line.
- } elsif ($current->{'type'}
- and ($current->{'type'} eq 'menu_entry_name'
- or $current->{'type'} eq 'menu_entry_node')) {
- my $empty_menu_entry_node = 0;
- my $end_comment;
- if ($current->{'type'} eq 'menu_entry_node') {
- if (@{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'cmdname'}
- and ($current->{'contents'}->[-1]->{'cmdname'} eq 'c'
- or $current->{'contents'}->[-1]->{'cmdname'} eq 'comment')) {
- $end_comment = pop @{$current->{'contents'}};
- }
- if (!@{$current->{'contents'}}
- # empty if only the end of line or spaces
- or (@{$current->{'contents'}} == 1
- and defined($current->{'contents'}->[-1]->{'text'})
- and $current->{'contents'}->[-1]->{'text'} !~ /\S/)) {
- $empty_menu_entry_node = 1;
- push @{$current->{'contents'}}, $end_comment if ($end_comment);
- }
- }
- # we abort the menu entry if there is no node name
- if ($empty_menu_entry_node
- or $current->{'type'} eq 'menu_entry_name') {
- my $description_or_menu_comment;
- print STDERR "FINALLY NOT MENU ENTRY\n" if ($self->{'DEBUG'});
- my $menu = $current->{'parent'}->{'parent'};
- my $menu_entry = pop @{$menu->{'contents'}};
- if (@{$menu->{'contents'}} and $menu->{'contents'}->[-1]->{'type'}
- and $menu->{'contents'}->[-1]->{'type'} eq 'menu_entry') {
- my $entry = $menu->{'contents'}->[-1];
- my $description;
- foreach my $entry_element (reverse(@{$entry->{'args'}})) {
- if ($entry_element->{'type'} eq 'menu_entry_description') {
- $description = $entry_element;
- last;
- }
- }
- if ($description) {
- $description_or_menu_comment = $description;
- } else {
- # Normally this cannot happen
- $self->_bug_message("No description in menu_entry",
- $line_nr, $current);
- push @{$entry->{'args'}}, {'type' => 'menu_entry_description',
- 'parent' => $entry,
- 'contents' => [] };
- $description_or_menu_comment = $entry->{'args'}->[-1];
- }
- } elsif (@{$menu->{'contents'}} and $menu->{'contents'}->[-1]->{'type'}
- and $menu->{'contents'}->[-1]->{'type'} eq 'menu_comment') {
- $description_or_menu_comment = $menu->{'contents'}->[-1];
- }
- if ($description_or_menu_comment) {
- $current = $description_or_menu_comment;
- if ($current->{'contents'}->[-1] and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'preformatted') {
- $current = $current->{'contents'}->[-1];
- } else {
- # this should not happen
- $self->_bug_message("description or menu comment not in preformatted",
- $line_nr, $current);
- push @{$current->{'contents'}}, {'type' => 'preformatted',
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- }
- push @{$self->{'context_stack'}}, 'preformatted';
- } else {
- push @{$menu->{'contents'}}, {'type' => 'menu_comment',
- 'parent' => $menu,
- 'contents' => [] };
- $current = $menu->{'contents'}->[-1];
- push @{$current->{'contents'}}, {'type' => 'preformatted',
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- push @{$self->{'context_stack'}}, 'preformatted';
- print STDERR "THEN MENU_COMMENT OPEN\n" if ($self->{'DEBUG'});
- }
- while (@{$menu_entry->{'args'}}) {
- my $arg = shift @{$menu_entry->{'args'}};
- if (defined($arg->{'text'})) {
- $current = _merge_text($self, $current, $arg->{'text'});
- } else {
- while (@{$arg->{'contents'}}) {
- my $content = shift @{$arg->{'contents'}};
- if (defined($content->{'text'})) {
- $current = _merge_text($self, $current,
- $content->{'text'});
- $content = undef;
- } else {
- $content->{'parent'} = $current;
- push @{$current->{'contents'}}, $content;
- }
- }
- }
- $arg = undef;
- }
- # MENU_COMMENT open
- $menu_entry = undef;
- } else {
- print STDERR "MENU ENTRY END LINE\n" if ($self->{'DEBUG'});
- $current = $current->{'parent'};
- $current = _enter_menu_entry_node($self, $current, $line_nr);
- if (defined($end_comment)) {
- $end_comment->{'parent'} = $current;
- push @{$current->{'contents'}}, $end_comment;
- }
- }
- # def line
- } elsif ($current->{'parent'}
- and $current->{'parent'}->{'type'}
- and $current->{'parent'}->{'type'} eq 'def_line') {
- my $def_context = pop @{$self->{'context_stack'}};
- if ($def_context ne 'def') {
- $self->_bug_message("context $def_context instead of def",
- $line_nr, $current);
- die;
- }
- my $def_command = $current->{'parent'}->{'extra'}->{'def_command'};
- my $arguments = $self->_parse_def($def_command,
- $current->{'contents'});
- if (scalar(@$arguments)) {
- $current->{'parent'}->{'extra'}->{'def_args'} = $arguments;
- my $def_parsed_hash;
- foreach my $arg (@$arguments) {
- die if (!defined($arg->[0]));
- last if ($arg->[0] eq 'arg' or $arg->[0] eq 'typearg'
- or $arg->[0] eq 'delimiter');
- next if ($arg->[0] eq 'spaces');
- # change of type is done in _parse_def.
- #if ($arg->[1]->{'type'} and $arg->[1]->{'type'} eq 'bracketed') {
- # $def_parsed_hash->{$arg->[0]} = { 'contents' => $arg->[1]->{'contents'},
- # 'type' => 'bracketed_def_content',
- # 'parent' => $arg->[1]->{'parent'}};
- #} else {
- # $def_parsed_hash->{$arg->[0]} = $arg->[1];
- #}
- $def_parsed_hash->{$arg->[0]} = $arg->[1];
- }
- $current->{'parent'}->{'extra'}->{'def_parsed_hash'} = $def_parsed_hash;
- # do an standard index entry tree
- my $index_entry;
- if (defined($def_parsed_hash->{'name'})) {
- $index_entry = $def_parsed_hash->{'name'}
- # empty bracketed
- unless ($def_parsed_hash->{'name'}->{'type'}
- and $def_parsed_hash->{'name'}->{'type'} eq 'bracketed_def_content'
- and (!$def_parsed_hash->{'name'}->{'contents'}
- or (!scalar(@{$def_parsed_hash->{'name'}->{'contents'}}))
- or (scalar(@{$def_parsed_hash->{'name'}->{'contents'}}) == 1
- and defined($def_parsed_hash->{'name'}->{'contents'}->[0]->{'text'})
- and $def_parsed_hash->{'name'}->{'contents'}->[0]->{'text'} !~ /\S/)));
- }
- if (defined($index_entry)) {
- my $index_contents_normalized;
- if ($def_parsed_hash->{'class'}) {
- if ($command_index_prefix{$def_command} eq 'f') {
- $index_entry = $self->gdt('{name} on {class}',
- {'name' => $def_parsed_hash->{'name'},
- 'class' => $def_parsed_hash->{'class'}});
- $index_contents_normalized
- = [_non_bracketed_contents($def_parsed_hash->{'name'}),
- { 'text' => ' on '},
- _non_bracketed_contents($def_parsed_hash->{'class'})];
- } elsif ($command_index_prefix{$def_command} eq 'v'
- and $def_command ne 'defcv') {
- $index_entry = $self->gdt('{name} of {class}',
- {'name' => $def_parsed_hash->{'name'},
- 'class' => $def_parsed_hash->{'class'}});
- $index_contents_normalized
- = [_non_bracketed_contents($def_parsed_hash->{'name'}),
- { 'text' => ' of '},
- _non_bracketed_contents($def_parsed_hash->{'class'})];
- }
- }
- $index_contents_normalized = [$index_entry]
- if (!defined($index_contents_normalized));
- my $index_contents;
- # 'root_line' is the container returned by gdt.
- if ($index_entry->{'type'} and $index_entry->{'type'} eq 'root_line') {
- $index_contents = $index_entry->{'contents'};
- } else {
- $index_contents = [$index_entry];
- }
- _enter_index_entry($self,
- $current->{'parent'}->{'extra'}->{'def_command'},
- $current->{'parent'}->{'extra'}->{'original_def_cmdname'},
- $current->{'parent'}, $index_contents,
- $index_contents_normalized, $line_nr);
- } else {
- $self->_command_warn($current->{'parent'}, $line_nr,
- $self->__('missing name for @%s'),
- $current->{'parent'}->{'extra'}->{'original_def_cmdname'});
- }
- } else {
- $self->_command_warn($current->{'parent'}, $line_nr,
- $self->__('missing category for @%s'),
- $current->{'parent'}->{'extra'}->{'original_def_cmdname'});
- }
- $current = $current->{'parent'}->{'parent'};
- $current = $self->_begin_preformatted($current);
- # other block command lines
- } elsif ($current->{'type'}
- and $current->{'type'} eq 'block_line_arg') {
- my $empty_text;
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'line') {
- $self->_bug_message("context $context instead of line in block_line_arg",
- $line_nr, $current);
- }
- # @multitable args
- if ($current->{'parent'}->{'cmdname'}
- and $current->{'parent'}->{'cmdname'} eq 'multitable') {
- # parse the prototypes and put them in a special arg
- my @prototype_row;
- # do the same but keeping spaces information
- my @prototype_line;
- foreach my $content (@{$current->{'contents'}}) {
- if ($content->{'type'} and $content->{'type'} eq 'bracketed') {
- push @prototype_row, { 'contents' => $content->{'contents'},
- 'parent' => $content->{'parent'},
- 'type' => 'bracketed_multitable_prototype'};
- push @prototype_line, $content;
- } elsif ($content->{'text'}) {
- if ($content->{'text'} =~ /\S/) {
- foreach my $prototype (split /\s+/, $content->{'text'}) {
- push @prototype_row, { 'text' => $prototype,
- 'type' => 'row_prototype' } unless ($prototype eq '');
- }
- }
- # The regexp breaks between characters, with a non space followed
- # by a space or a space followed by non space. It is like \b, but
- # for \s \S, and not \w \W.
- foreach my $prototype_or_space (split /(?<=\S)(?=\s)|(?=\S)(?<=\s)/,
- $content->{'text'}) {
- if ($prototype_or_space =~ /\S/) {
- push @prototype_line, {'text' => $prototype_or_space,
- 'type' => 'row_prototype' };
- } elsif ($prototype_or_space =~ /\s/) {
- push @prototype_line, {'text' => $prototype_or_space,
- 'type' => 'prototype_space' };
- }
- }
- } else {
- # FIXME could this happen? Should be a debug message?
- if (!$content->{'cmdname'}) {
- $self->_command_warn($current, $line_nr,
- $self->__("unexpected argument on \@%s line: %s"),
- $current->{'cmdname'},
- Texinfo::Convert::Texinfo::convert({ $content->{'contents'} }));
- } elsif ($content->{'cmdname'} eq 'c'
- or $content->{'cmdname'} eq 'comment') {
- } else {
- push @prototype_row, $content;
- push @prototype_line, $content;
- }
- }
- }
-
- my $multitable = $current->{'parent'};
- $multitable->{'extra'}->{'max_columns'} = scalar(@prototype_row);
- if (!scalar(@prototype_row)) {
- $self->_command_warn($multitable, $line_nr,
- $self->__("empty multitable"));
- }
- $multitable->{'extra'}->{'prototypes'} = \@prototype_row;
- $multitable->{'extra'}->{'prototypes_line'} = \@prototype_line;
- } else {
- $self->_isolate_last_space($current, 'space_at_end_block_command');
- $self->_register_command_arg($current, 'block_command_line_contents');
- }
- # @float args
- if ($current->{'parent'}->{'cmdname'}
- and $current->{'parent'}->{'cmdname'} eq 'float') {
- my $float = $current->{'parent'};
- $float->{'line_nr'} = $line_nr;
- my $type = '';
- if (@{$float->{'args'}}) {
- if ($float->{'args'}->[1]) {
- my $float_label = _parse_node_manual($float->{'args'}->[1]);
- _check_internal_node($self, $float_label, $line_nr);
- if (defined($float_label) and $float_label->{'node_content'}
- and $float_label->{'normalized'} =~ /[^-]/) {
- _register_label($self, $float, $float_label, $line_nr);
- }
- }
- _parse_float_type($float);
- $type = $float->{'extra'}->{'type'}->{'normalized'};
- }
- push @{$self->{'floats'}->{$type}}, $float
- unless (_ignore_global_commands($self));
- $float->{'float_section'} = $self->{'current_section'}
- if (defined($self->{'current_section'}));
- }
- $current = $current->{'parent'};
- delete $current->{'remaining_args'};
- # don't consider empty argument of block @-commands as argument,
- # reparent them as contents
- if ($current->{'args'}->[0]->{'contents'}->[0]
- and $current->{'args'}->[0]->{'contents'}->[0]->{'type'}
- and $current->{'args'}->[0]->{'contents'}->[0]->{'type'} eq 'empty_line_after_command')
- {
- my $empty_text = $current->{'args'}->[0]->{'contents'}->[0];
- $empty_text->{'parent'} = $current;
- unshift @{$current->{'contents'}}, $empty_text;
- delete $current->{'args'};
- }
- # Additionally, remove empty arguments as far as possible
- _remove_empty_content_arguments($current);
- if ($current->{'cmdname'}
- and $block_item_commands{$current->{'cmdname'}}) {
- if ($current->{'cmdname'} eq 'enumerate') {
- my $spec = 1;
- if ($current->{'extra'}->{'block_command_line_contents'}
- and defined($current->{'extra'}->{'block_command_line_contents'}->[0])) {
- if (scalar(@{$current->{'extra'}->{'block_command_line_contents'}->[0]}) > 1) {
- $self->_command_error($current, $line_nr,
- $self->__("superfluous argument to \@%s"),
- $current->{'cmdname'});
- }
- my $arg = $current->{'extra'}->{'block_command_line_contents'}->[0]->[0];
- if (!defined($arg->{'text'}) or $arg->{'text'} !~ /^(([[:digit:]]+)|([[:alpha:]]+))$/) {
- $self->_command_error($current, $line_nr,
- $self->__("bad argument to \@%s"),
- $current->{'cmdname'});
- } else {
- $spec = $arg->{'text'};
- }
- }
- $current->{'extra'}->{'enumerate_specification'} = $spec;
- } elsif ($item_line_commands{$current->{'cmdname'}}) {
- if (!$current->{'extra'}
- or !$current->{'extra'}->{'command_as_argument'}) {
- $self->_command_error($current, $line_nr,
- $self->__("%s requires an argument: the formatter for %citem"),
- $current->{'cmdname'}, ord('@'));
- } elsif (!$brace_commands{$current->{'extra'}->{'command_as_argument'}->{'cmdname'}}
- and !$self->{'definfoenclose'}->{$current->{'extra'}->{'command_as_argument'}->{'cmdname'}}) {
- $self->_command_error($current, $line_nr,
- $self->__("command \@%s not accepting argument in brace should not be on \@%s line"),
- $current->{'extra'}->{'command_as_argument'}->{'cmdname'},
- $current->{'cmdname'});
- delete $current->{'extra'}->{'command_as_argument'};
- }
- }
- # This code checks that the command_as_argument of the @itemize
- # is alone on the line, otherwise it is not a command_as_argument.
- if ($current->{'extra'}
- and $current->{'extra'}->{'command_as_argument'}
- and $current->{'cmdname'} eq 'itemize') {
- my @args = @{$current->{'args'}->[0]->{'contents'}};
- while (@args) {
- my $arg = shift @args;
- last if ($arg eq $current->{'extra'}->{'command_as_argument'});
- }
- while (@args) {
- my $arg = shift @args;
- if (!(($arg->{'cmdname'}
- and ($arg->{'cmdname'} eq 'c'
- or $arg->{'cmdname'} eq 'comment'))
- or (defined($arg->{'text'}) and $arg->{'text'} !~ /\S/))) {
- #print STDERR " -> stop at "._print_current($arg)."\n";
- delete $current->{'extra'}->{'command_as_argument'}->{'type'};
- delete $current->{'extra'}->{'command_as_argument'};
- last;
- }
- }
- }
- if ($current->{'extra'}
- and $current->{'extra'}->{'command_as_argument'}
- and $accent_commands{$current->{'extra'}->{'command_as_argument'}->{'cmdname'}}
- and ($current->{'cmdname'} eq 'itemize'
- or $item_line_commands{$current->{'cmdname'}})) {
- $self->_command_warn($current, $line_nr,
- $self->__("accent command `\@%s' not allowed as \@%s argument"),
- $current->{'extra'}->{'command_as_argument'}->{'cmdname'},
- $current->{'cmdname'});
- delete $current->{'extra'}->{'command_as_argument'};
- delete $current->{'extra'}->{'block_command_line_contents'};
- }
- if (!$current->{'extra'}->{'block_command_line_contents'}
- and $current->{'cmdname'} eq 'itemize') {
- $current->{'extra'}->{'block_command_line_contents'} = [
- [ { 'cmdname' => 'bullet',
- 'type' => 'command_as_argument',
- 'parent' => $current }
- ]
- ];
- $current->{'extra'}->{'command_as_argument'} =
- $current->{'extra'}->{'block_command_line_contents'}->[0]->[0];
- } elsif ($item_line_commands{$current->{'cmdname'}} and
- ! $current->{'extra'}->{'command_as_argument'}) {
- $current->{'extra'}->{'block_command_line_contents'} = [
- [ { 'cmdname' => 'asis',
- 'type' => 'command_as_argument',
- 'parent' => $current }
- ]
- ];
- $current->{'extra'}->{'command_as_argument'} =
- $current->{'extra'}->{'block_command_line_contents'}->[0]->[0];
- }
- push @{$current->{'contents'}}, { 'type' => 'before_item',
- 'contents' => [], 'parent', $current };
- $current = $current->{'contents'}->[-1];
- }
- if ($current->{'cmdname'} and $menu_commands{$current->{'cmdname'}}) {
- push @{$current->{'contents'}}, {'type' => 'menu_comment',
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- print STDERR "MENU_COMMENT OPEN\n" if ($self->{'DEBUG'});
- push @{$self->{'context_stack'}}, 'preformatted';
- }
- $current = $self->_begin_preformatted($current);
- # if we are after a @end verbatim, we must restart a preformatted if needed,
- # since there is no @end command explicitly associated to raw commands
- # it won't be done elsewhere.
- } elsif ($current->{'contents'}
- and $current->{'contents'}->[-1]
- and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
- and $current->{'contents'}->[-1]->{'extra'}
- and $current->{'contents'}->[-1]->{'extra'}->{'command'}
- and $current->{'contents'}->[-1]->{'extra'}->{'command'}->{'cmdname'} eq 'verbatim') {
- $current = $self->_begin_preformatted($current);
- # misc command line arguments
- # Never go here if skipline/noarg/...
- } elsif ($current->{'type'}
- and $current->{'type'} eq 'misc_line_arg') {
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'line') {
- $self->_bug_message("context $context instead of line in misc_line_arg",
- $line_nr, $current);
- }
- $self->_isolate_last_space($current);
- # first parent is the @command, second is the parent
- $current = $current->{'parent'};
- my $misc_cmd = $current;
- my $command = $current->{'cmdname'};
- my $end_command;
- print STDERR "MISC END \@$command\n" if ($self->{'DEBUG'});
- if ($self->{'misc_commands'}->{$command} =~ /^\d$/) {
- my $args = _parse_line_command_args($self, $current, $line_nr);
- $current->{'extra'}->{'misc_args'} = $args if (defined($args));
- } elsif ($self->{'misc_commands'}->{$command} eq 'text') {
- my $text = Texinfo::Convert::Text::convert($current->{'args'}->[0],
- {'code' => 1,
- Texinfo::Common::_convert_text_options($self)});
- if ($text eq '') {
- $self->_command_warn($current, $line_nr,
- $self->__("\@%s missing argument"), $command);
- $current->{'extra'}->{'missing_argument'} = 1;
- } else {
- $current->{'extra'}->{'text_arg'} = $text;
- if ($command eq 'end') {
- # REMACRO
- my $line = $text;
- if ($line =~ s/^([[:alnum:]][[:alnum:]-]+)//) {
- $end_command = $1;
-
- if (!exists $block_commands{$end_command}) {
- $self->_command_warn($current, $line_nr,
- $self->__("unknown \@end %s"), $end_command);
- $end_command = undef;
- } else {
- print STDERR "END BLOCK $end_command\n" if ($self->{'DEBUG'});
- if ($block_commands{$end_command} eq 'conditional') {
- if (@{$self->{'conditionals_stack'}}
- and $self->{'conditionals_stack'}->[-1] eq $end_command) {
- pop @{$self->{'conditionals_stack'}};
- } else {
- $self->_command_error($current, $line_nr,
- $self->__("unmatched `%c%s'"), ord('@'), 'end');
- $end_command = undef;
- }
- }
- $current->{'extra'}->{'command_argument'} = $end_command
- if (defined($end_command));
- }
- if ($line =~ /\S/ and defined($end_command)) {
- my $texi_line
- = Texinfo::Convert::Texinfo::convert($current->{'args'}->[0]);
- $texi_line =~ s/^\s*([[:alnum:]][[:alnum:]-]+)//;
- $self->_command_error($current, $line_nr,
- $self->__("superfluous argument to \@%s %s: %s"),
- $command, $end_command, $texi_line);
- }
- } else {
- $self->_command_error($current, $line_nr,
- $self->__("bad argument to \@%s: %s"),
- $command, $line);
- }
- } elsif ($command eq 'include') {
- my $file = Texinfo::Common::locate_include_file($self, $text) ;
- if (defined($file)) {
- my $filehandle = do { local *FH };
- if (open ($filehandle, $file)) {
- $included_file = 1;
- binmode($filehandle, ":encoding($self->{'INPUT_PERL_ENCODING'})")
- if (defined($self->{'INPUT_PERL_ENCODING'}));
- print STDERR "Included $file($filehandle)\n" if ($self->{'DEBUG'});
- unshift @{$self->{'input'}}, {
- 'name' => $file,
- 'line_nr' => 0,
- 'pending' => [],
- 'fh' => $filehandle };
- } else {
- $self->_command_error($current, $line_nr,
- $self->__("\@%s: could not open %s: %s"),
- $command, $text, $!);
- }
- } else {
- $self->_command_error($current, $line_nr,
- $self->__("\@%s: could not find %s"),
- $command, $text);
- }
- } elsif ($command eq 'documentencoding') {
- my ($texinfo_encoding, $perl_encoding, $input_encoding)
- = Texinfo::Encoding::encoding_alias($text);
- $self->_command_warn($current, $line_nr,
- $self->__("encoding `%s' is not a canonical texinfo encoding"),
- $text)
- if (!$texinfo_encoding or $texinfo_encoding ne lc($text));
- if (! _ignore_global_commands($self)) {
- if ($input_encoding) {
- $current->{'extra'}->{'input_encoding_name'} = $input_encoding;
- }
- if (!$perl_encoding) {
- $self->_command_warn($current, $line_nr,
- $self->__("unrecognized encoding name `%s'"), $text);
- } else {
- $current->{'extra'}->{'input_perl_encoding'} = $perl_encoding;
- if ($input_encoding) {
- if (!$self->{'set'}->{'INPUT_ENCODING_NAME'}) {
- $self->{'INPUT_ENCODING_NAME'} = $input_encoding;
- $self->{'info'}->{'input_encoding_name'} = $input_encoding;
- }
- }
- if (!$self->{'set'}->{'INPUT_PERL_ENCODING'}) {
- $self->{'INPUT_PERL_ENCODING'} = $perl_encoding;
- $self->{'info'}->{'input_perl_encoding'} = $perl_encoding;
- foreach my $input (@{$self->{'input'}}) {
- binmode($input->{'fh'}, ":encoding($perl_encoding)") if ($input->{'fh'});
- }
- }
- }
- }
- } elsif ($command eq 'documentlanguage') {
- my @messages = Texinfo::Common::warn_unknown_language($text,
- $self->{'gettext'});
- foreach my $message(@messages) {
- $self->_command_warn($current, $line_nr, $message);
- }
- if (!$self->{'set'}->{'documentlanguage'}
- and !_ignore_global_commands($self)) {
- $self->{'documentlanguage'} = $text;
- }
- }
- }
- } elsif ($command eq 'node') {
- foreach my $arg (@{$current->{'args'}}) {
- my $node = _parse_node_manual($arg);
- push @{$current->{'extra'}->{'nodes_manuals'}}, $node;
- }
- if (_check_node_label($self, $current->{'extra'}->{'nodes_manuals'}->[0],
- $command, $line_nr)) {
- if (_register_label($self, $current,
- $current->{'extra'}->{'nodes_manuals'}->[0], $line_nr)) {
- $self->{'current_node'} = $current;
- push @{$self->{'nodes'}}, $current;
- }
- }
- } elsif ($command eq 'listoffloats') {
- # Empty listoffloats is allowed
- _parse_float_type($current);
- #if (!_parse_float_type($current)) {
- # $self->line_error (sprintf($self->__("\@%s missing argument"),
- # $command), $line_nr);
- #}
- # handle all the other 'line' commands. Here just check that they
- # have an argument and prepare contents without spaces.
- } else {
- my @contents = @{$current->{'args'}->[0]->{'contents'}};
- _trim_spaces_comment_from_content(\@contents);
- # empty @top is allowed
- if (!scalar(@contents) and $command ne 'top') {
- $self->_command_warn($current, $line_nr,
- $self->__("\@%s missing argument"), $command);
- $current->{'extra'}->{'missing_argument'} = 1;
- } else {
- $current->{'extra'}->{'misc_content'} = \@contents;
- if (($command eq 'item' or $command eq 'itemx')
- and $self->{'command_index_prefix'}->{$current->{'parent'}->{'cmdname'}}) {
- _enter_index_entry($self, $current->{'parent'}->{'cmdname'},
- $command, $current,
- $current->{'extra'}->{'misc_content'},
- undef, $line_nr);
- } elsif ($self->{'command_index_prefix'}->{$current->{'cmdname'}}) {
- _enter_index_entry($self, $current->{'cmdname'},
- $current->{'cmdname'}, $current,
- $current->{'extra'}->{'misc_content'},
- undef, $line_nr);
- $current->{'type'} = 'index_entry_command';
- }
- }
- if (defined($command_structuring_level{$command})) {
- $current->{'level'} = $command_structuring_level{$command};
- }
- }
- $current = $current->{'parent'};
- if ($end_command) {
- print STDERR "END COMMAND $end_command\n" if ($self->{'DEBUG'});
- my $end = pop @{$current->{'contents'}};
- if ($block_commands{$end_command} ne 'conditional') {
- my $closed_command;
- ($closed_command, $current)
- = _close_commands($self, $current, $line_nr, $end_command);
- my $inline_copying;
- if ($closed_command) {
- $misc_cmd->{'extra'}->{'command'} = $closed_command;
- $closed_command->{'extra'}->{'end_command'} = $misc_cmd;
- $self->_close_command_cleanup($closed_command);
- $end->{'parent'} = $closed_command;
- # register @insertcopying as a macro if INLINE_INSERTCOPYING is set.
- if ($end_command eq 'copying' and $self->{'INLINE_INSERTCOPYING'}) {
- # remove the end of line following @copying.
- my @contents = @{$closed_command->{'contents'}};
- shift @contents if ($contents[0] and $contents[0]->{'type'}
- and ($contents[0]->{'type'} eq 'empty_line_after_command'
- or $contents[0]->{'type'} eq 'empty_spaces_after_command'));
- # the macrobody is the @copying content converted to Texinfo.
- my $body = Texinfo::Convert::Texinfo::convert(
- {'contents' => \@contents});
-
- #chomp ($body);
- $self->{'macros'}->{'insertcopying'} = {
- 'args' => [{'text' => 'insertcopying', 'type' => 'macro_name'}],
- 'cmdname' => 'macro',
- 'extra' => {'macrobody' =>
- $self->_strip_macrobody_leading_space($body)}
- };
- $inline_copying = 1;
- print STDERR "INLINE_INSERTCOPYING as macro\n" if ($self->{'DEBUG'});
- }
- push @{$closed_command->{'contents'}}, $end;
- # closing a menu command, but still in a menu. Open a menu_comment
- if ($menu_commands{$closed_command->{'cmdname'}}
- and $self->{'context_stack'}->[-1] eq 'menu') {
- print STDERR "CLOSE MENU but still in menu context\n"
- if ($self->{'DEBUG'});
- push @{$current->{'contents'}}, {'type' => 'menu_comment',
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- push @{$self->{'context_stack'}}, 'preformatted';
- }
- } else {
- #print STDERR "LLLLLLLLLLLL Cannot be here...\n";
- }
- $current = $self->_begin_preformatted($current)
- if ($close_preformatted_commands{$end_command});
- }
- } else {
- $current = $self->_begin_preformatted($current)
- if ($close_preformatted_commands{$command});
- }
- # if a file was included, remove completly the include file command.
- # Also ignore @setfilename in included file, as said in the manual.
- if ($included_file or ($command eq 'setfilename'
- and scalar(@{$self->{'input'}}) > 1)) {
- # TODO keep the information with sourcemark
- pop @{$current->{'contents'}};
- } elsif ($command eq 'setfilename'
- and ($self->{'current_node'} or $self->{'current_section'})) {
- $self->_command_warn($misc_cmd, $line_nr,
- $self->__("\@%s after the first element"), $command);
- # columnfractions
- } elsif ($command eq 'columnfractions') {
- # in a multitable, we are in a block_line_arg
- if (!$current->{'parent'} or !$current->{'parent'}->{'cmdname'}
- or $current->{'parent'}->{'cmdname'} ne 'multitable') {
- $self->_command_error($current, $line_nr,
- $self->__("\@%s only meaningful on a \@multitable line"),
- $command);
- } else {
- # This is the multitable block_line_arg line context
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'line') {
- $self->_bug_message("context $context instead of line for multitable",
- $line_nr, $current);
- }
- $current = $current->{'parent'};
- $current->{'extra'}->{'max_columns'} = 0;
- if (defined($misc_cmd->{'extra'}->{'misc_args'})) {
- $current->{'extra'}->{'max_columns'} =
- scalar(@{$misc_cmd->{'extra'}->{'misc_args'}});
- $current->{'extra'}->{'columnfractions'} = $misc_cmd->{'extra'}->{'misc_args'};
- }
- push @{$current->{'contents'}}, { 'type' => 'before_item',
- 'contents' => [], 'parent', $current };
- $current = $current->{'contents'}->[-1];
- }
- } elsif ($root_commands{$command}) {
- $current = $current->{'contents'}->[-1];
- delete $current->{'remaining_args'};
- $current->{'contents'} = [];
- # we never should be in a raw format bock, so we don't check for
- # _ignore_global_commands($self)
- # associate the section (not part) with the current node.
- if ($command ne 'node' and $command ne 'part') {
- if ($self->{'current_node'}
- and !$self->{'current_node'}->{'extra'}->{'associated_section'}) {
- $self->{'current_node'}->{'extra'}->{'associated_section'} = $current;
- $current->{'extra'}->{'associated_node'} = $self->{'current_node'};
- }
- if ($self->{'current_parts'}) {
- $current->{'extra'}->{'associated_part'} = $self->{'current_parts'}->[-1];
- foreach my $part (@{$self->{'current_parts'}}) {
- $part->{'extra'}->{'part_associated_section'} = $current;
- if ($current->{'cmdname'} eq 'top') {
- $self->line_warn(sprintf($self->__(
- "\@%s should not be associated with \@top"),
- $part->{'cmdname'}), $part->{'line_nr'});
- }
- }
- delete $self->{'current_parts'};
- }
- $self->{'current_section'} = $current;
- } elsif ($command eq 'part') {
- push @{$self->{'current_parts'}}, $current;
- if ($self->{'current_node'}
- and !$self->{'current_node'}->{'extra'}->{'associated_section'}) {
- $self->line_warn (sprintf($self->__(
- "\@node precedes \@%s, but parts may not be associated with nodes"),
- $command), $line_nr);
- }
- }
- }
- # do that last in order to have the line processed if one of the above
- # case is also set.
- } elsif (
- $current->{'contents'}
- and (scalar(@{$current->{'contents'}}) == 1
- and (($current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'))
- or (scalar(@{$current->{'contents'}}) == 2
- and $current->{'contents'}->[-1]->{'cmdname'}
- and ($current->{'contents'}->[-1]->{'cmdname'} eq 'c'
- or $current->{'contents'}->[-1]->{'cmdname'} eq 'comment')
- and $current->{'contents'}->[-2]
- and $current->{'contents'}->[-2]->{'type'}
- and $current->{'contents'}->[-2]->{'type'} eq 'empty_line_after_command'))) {
- # empty line after a @menu or before a preformatted. Reparent to the menu
- # or other format
- if ($current->{'type'}
- and $preformatted_contexts{$current->{'type'}}) {
- my $parent = $current->{'parent'};
- if ($parent->{'type'} and $parent->{'type'} eq 'menu_comment'
- and scalar(@{$parent->{'contents'}}) == 1) {
- $parent = $parent->{'parent'};
- }
- my $to_reparent = pop @{$parent->{'contents'}};
- print STDERR "LINE AFTER COMMAND IN PREFORMATTED ($to_reparent->{'type'})\n" if ($self->{'DEBUG'});
- while (@{$current->{'contents'}}) {
- my $content = shift @{$current->{'contents'}};
- $content->{'parent'} = $parent;
- push @{$parent->{'contents'}}, $content;
- }
- push @{$parent->{'contents'}}, $to_reparent;
- }
- }
- # this happens if there is a nesting of line @-commands on a line.
- # they are reprocessed here.
- if ($self->{'context_stack'}->[-1] eq 'line'
- or $self->{'context_stack'}->[-1] eq 'def') {
- print STDERR "Still opened line command $self->{'context_stack'}->[-1]:"._print_current($current)
- if ($self->{'DEBUG'});
- if ($self->{'context_stack'}->[-1] eq 'def') {
- while ($current->{'parent'} and !($current->{'parent'}->{'type'}
- and $current->{'parent'}->{'type'} eq 'def_line')) {
- $current = $self->_close_current($current, $line_nr);
- }
- } else {
- while ($current->{'parent'} and !($current->{'type'}
- and ($current->{'type'} eq 'block_line_arg'
- or $current->{'type'} eq 'misc_line_arg'))) {
- $current = $self->_close_current($current, $line_nr);
- }
- }
- # check for infinite loop bugs...
- if ($current eq $current_old) {
- my $indent = '- ';
- my $tree_msg = $indent . _print_current($current);
- while ($current->{'parent'}) {
- $indent = '-'.$indent;
- $current = $current->{'parent'};
- $tree_msg .= $indent . _print_current($current);
- }
- $self->_bug_message("Nothing closed while a line context remains\n$tree_msg",
- $line_nr);
- die;
- }
- $current = $self->_end_line($current, $line_nr);
- }
- return $current;
- }
- # $command may be undef if we are after a wrong misc command such as
- # a buggy @tab.
- sub _start_empty_line_after_command($$$) {
- my $line = shift;
- my $current = shift;
- my $command = shift;
- $line =~ s/^([^\S\r\n]*)//;
- push @{$current->{'contents'}}, { 'type' => 'empty_line_after_command',
- 'text' => $1,
- 'parent' => $current,
- };
- if (defined($command)) {
- $current->{'contents'}->[-1]->{'extra'} = {'command' => $command};
- $command->{'extra'}->{'spaces_after_command'} = $current->{'contents'}->[-1];
- }
- return $line;
- }
- sub _check_empty_node($$$$)
- {
- my $self = shift;
- my $parsed_node = shift;
- my $command = shift;
- my $line_nr = shift;
- if (!defined($parsed_node) or !$parsed_node->{'node_content'}) {
- $self->line_error (sprintf($self->__("empty argument in \@%s"),
- $command), $line_nr);
- return 0;
- } elsif ($parsed_node->{'normalized'} !~ /[^-]/) {
- $self->line_error (sprintf($self->__("empty node name after expansion `%s'"),
- Texinfo::Convert::Texinfo::convert({'contents'
- => $parsed_node->{'node_content'}})),
- $line_nr);
- return 0;
- } else {
- return 1;
- }
- }
- sub _check_internal_node($$$)
- {
- my $self = shift;
- my $parsed_node = shift;
- my $line_nr = shift;
- if ($parsed_node and $parsed_node->{'manual_content'}) {
- $self->line_error (sprintf($self->__("syntax for an external node used for `%s'"),
- _node_extra_to_texi($parsed_node)), $line_nr)
- }
- }
- sub _check_node_label($$$$)
- {
- my $self = shift;
- my $parsed_node = shift;
- my $command = shift;
- my $line_nr = shift;
- _check_internal_node($self, $parsed_node, $line_nr);
- return _check_empty_node($self, $parsed_node, $command, $line_nr);
- }
- sub _register_extra_menu_entry_information($$;$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- foreach my $arg (@{$current->{'args'}}) {
- if ($arg->{'type'} eq 'menu_entry_name') {
- $current->{'extra'}->{'menu_entry_name'} = $arg;
- my $normalized_menu_entry_name =
- Texinfo::Convert::NodeNameNormalization::normalize_node($arg);
- if ($normalized_menu_entry_name !~ /[^-]/) {
- $self->line_warn(sprintf($self->__("empty menu entry name in `%s'"),
- Texinfo::Convert::Texinfo::convert($current)), $line_nr);
- }
- } elsif ($arg->{'type'} eq 'menu_entry_node') {
- $self->_isolate_last_space($arg, 'space_at_end_menu_node');
- my $parsed_entry_node = _parse_node_manual($arg);
- if (! defined($parsed_entry_node)) {
- if ($self->{'SHOW_MENU'}) {
- $self->line_error ($self->__("empty node name in menu entry"), $line_nr);
- }
- } else {
- $current->{'extra'}->{'menu_entry_node'} = $parsed_entry_node;
- }
- } elsif ($arg->{'type'} eq 'menu_entry_description') {
- $current->{'extra'}->{'menu_entry_description'} = $arg;
- }
- }
- }
- sub _enter_menu_entry_node($$$)
- {
- my $self = shift;
- my $current = shift;
- my $line_nr = shift;
- my $description = { 'type' => 'menu_entry_description',
- 'contents' => [],
- 'parent' => $current };
- push @{$current->{'args'}}, $description;
- _register_extra_menu_entry_information($self, $current, $line_nr);
- $current->{'line_nr'} = $line_nr;
- $current = $description;
- push @{$current->{'contents'}}, {'type' => 'preformatted',
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- push @{$self->{'context_stack'}}, 'preformatted';
- return $current;
- }
- sub _register_command_arg($$$)
- {
- my $self = shift;
- my $current = shift;
- my $type = shift;
- my @contents = @{$current->{'contents'}};
- _trim_spaces_comment_from_content(\@contents);
- if (scalar(@contents)) {
- push @{$current->{'parent'}->{'extra'}->{$type}}, \@contents;
- } else {
- push @{$current->{'parent'}->{'extra'}->{$type}}, undef;
- }
- }
- sub _command_with_command_as_argument($)
- {
- my $current = shift;
- return ($current and $current->{'type'}
- and $current->{'type'} eq 'block_line_arg'
- and $current->{'parent'}
- and $current->{'parent'}->{'cmdname'} and
- ($current->{'parent'}->{'cmdname'} eq 'itemize'
- or $item_line_commands{$current->{'parent'}->{'cmdname'}})
- and (scalar(@{$current->{'contents'}}) == 1
- or (scalar(@{$current->{'contents'}}) == 2
- and defined($current->{'contents'}->[0]->{'text'})
- and $current->{'contents'}->[0]->{'text'}
- =~ /^[^\S\r\n]*/)))
- }
- # $marked_as_invalid_command may be undef, if there is no
- # tree element because the @-command construct is incorrect, for example
- # wrong @tab.
- sub _mark_and_warn_invalid($$$$$)
- {
- my $self = shift;
- my $command = shift;
- my $invalid_parent = shift;
- my $line_nr = shift;
- my $marked_as_invalid_command = shift;
- if (defined($invalid_parent)) {
- $self->line_warn(sprintf($self->__("\@%s should not appear in \@%s"),
- $command, $invalid_parent), $line_nr);
- $marked_as_invalid_command->{'extra'}->{'invalid_nesting'} = 1
- if (defined($marked_as_invalid_command));
- }
- }
- # the different types
- #c 'menu_entry'
- #c 'menu_entry'
- # t 'menu_entry_leading_text'
- #
- #t 'macro_arg_name'
- #t 'macro_arg_args'
- #
- #t 'raw'
- #
- #t 'misc_arg'
- #c 'misc_line_arg'
- #
- #c 'block_line_arg'
- #
- #c 'brace_command_arg'
- #c 'brace_command_context'
- #
- #c 'before_item' what comes after @*table, @itemize, @enumerate before
- # an @item
- #
- #c 'paragraph'
- #
- #a 'def_line'
- #
- #special for @verb, type is the character
- # the main subroutine
- sub _parse_texi($;$)
- {
- my $self = shift;
- my $root = shift;
- $root = { 'contents' => [], 'type' => 'text_root' } if (!defined($root));
- my $current = $root;
- my $line_nr;
-
- NEXT_LINE:
- while (1) {
- my $line;
- ($line, $line_nr) = _next_text($self, $line_nr, $current);
- last if (!defined($line));
- if ($self->{'DEBUG'}) {
- $current->{'HERE !!!!'} = 1; # marks where we are in the tree
- if ($self->{'DEBUG'} >= 100) {
- local $Data::Dumper::Indent = 1;
- local $Data::Dumper::Purity = 1;
- print STDERR "".Data::Dumper->Dump([$root], ['$root']);
- }
- my $line_text = '';
- $line_text = "$line_nr->{'line_nr'}.$line_nr->{'macro'}" if ($line_nr);
- print STDERR "NEW LINE(".join('|', @{$self->{'context_stack'}}).":@{$self->{'conditionals_stack'}}:$line_text): $line";
- #print STDERR "CONTEXT_STACK ".join('|',@{$self->{'context_stack'}})."\n";
- delete $current->{'HERE !!!!'};
- }
- if (not
- # raw format or verb
- (($current->{'cmdname'}
- and $block_commands{$current->{'cmdname'}}
- and ($block_commands{$current->{'cmdname'}} eq 'raw'
- or $block_commands{$current->{'cmdname'}} eq 'conditional'))
- or
- ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
- and $current->{'parent'}->{'cmdname'} eq 'verb')
- )
- # not def line
- and $self->{'context_stack'}->[-1] ne 'def') {
- print STDERR "BEGIN LINE\n" if ($self->{'DEBUG'});
- $line =~ s/^([^\S\r\n]*)//;
- push @{$current->{'contents'}}, { 'type' => 'empty_line',
- 'text' => $1,
- 'parent' => $current };
- }
- while (1) {
- # in a raw or ignored conditional block command
- if ($current->{'cmdname'} and
- $block_commands{$current->{'cmdname'}} and
- ($block_commands{$current->{'cmdname'}} eq 'raw'
- or $block_commands{$current->{'cmdname'}} eq 'conditional')) {
- # r?macro may be nested
- if (($current->{'cmdname'} eq 'macro'
- or $current->{'cmdname'} eq 'rmacro')
- and $line =~ /^\s*\@r?macro\s+/) {
- $line =~ s/\s*\@(r?macro)//;
- push @{$current->{'contents'}}, { 'cmdname' => $1,
- 'parent' => $current,
- 'contents' => [],
- 'extra' => {'arg_line' => $line }};
- $current = $current->{'contents'}->[-1];
- last;
- # ifclear/ifset may be nested
- } elsif (($current->{'cmdname'} eq 'ifclear'
- or $current->{'cmdname'} eq 'ifset'
- or $current->{'cmdname'} eq 'ifcommanddefined'
- or $current->{'cmdname'} eq 'ifcommandnotdefined')
- and $line =~ /^\s*\@$current->{'cmdname'}/) {
- $line =~ s/\s*\@($current->{'cmdname'})//;
- push @{$current->{'contents'}}, { 'cmdname' => $1,
- 'parent' => $current,
- 'contents' => [],
- 'extra' => {'line' => $line }};
- $current = $current->{'contents'}->[-1];
- last;
- } elsif ($line =~ /^(\s*?)\@end\s+([a-zA-Z][\w-]*)/
- and ($2 eq $current->{'cmdname'})) {
- my $end_command = $2;
- my $raw_command = $current;
- $line =~ s/^(\s*?)(\@end\s+$current->{'cmdname'})//;
- if ($1 eq '') {
- # FIXME exclude other formats, like @macro, @ifset, @ignore?
- if ($current->{'cmdname'} ne 'verbatim'
- and @{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'raw') {
- if ($current->{'contents'}->[-1]->{'text'} =~ s/(\n)//) {
- push @{$current->{'contents'}}, {'type' => 'last_raw_newline',
- 'text' => $1, 'parent' => $current};
- }
- }
- } else {
- push @{$current->{'contents'}},
- { 'text' => $1, 'type' => 'raw', 'parent' => $current };
- $self->line_warn(sprintf($self->__("\@end %s should only appear at a line beginning"),
- $end_command), $line_nr);
- }
- # if there is a user defined macro that expandes to spaces, there
- # will be a spurious warning.
- $self->line_warn(sprintf($self->
- __("superfluous argument to \@%s %s: %s"), 'end', $end_command,
- $line), $line_nr)
- if ($line =~ /\S/ and $line !~ /^\s*\@c(omment)?\b/);
- # store toplevel macro specification
- if (($end_command eq 'macro' or $end_command eq 'rmacro')
- and (! $current->{'parent'}
- or !$current->{'parent'}->{'cmdname'}
- or ($current->{'parent'}->{'cmdname'} ne 'macro'
- and $current->{'parent'}->{'cmdname'} ne 'rmacro'))) {
- $current->{'extra'}->{'macrobody'} =
- $self->_strip_macrobody_leading_space(
- Texinfo::Convert::Texinfo::convert({ 'contents'
- => $current->{'contents'} }));
- if ($current->{'args'} and $current->{'args'}->[0]
- and !_ignore_global_commands($self)) {
- my $name = $current->{'args'}->[0]->{'text'};
- if (exists($self->{'macros'}->{$name})) {
- $self->line_warn(sprintf($self->__("macro `%s' previously defined"),
- $name), $current->{'line_nr'});
- $self->line_warn(sprintf($self->__(
- "here is the previous definition of `%s'"),
- $name), $self->{'macros'}->{$name}->{'line_nr'});
- }
- if ($all_commands{$name}) {
- $self->line_warn(sprintf($self->__(
- "redefining Texinfo language command: \@%s"),
- $name), $current->{'line_nr'});
- }
- $self->{'macros'}->{$name} = $current
- unless ($current->{'extra'}->{'invalid_syntax'});
- }
- }
- $current = $current->{'parent'};
- if ($block_commands{$end_command} eq 'conditional') {
- # don't store ignored @if*
- my $conditional = pop @{$current->{'contents'}};
- if (!defined($conditional->{'cmdname'}
- or $conditional->{'cmdname'} ne $end_command)) {
- $self->_bug_message("Ignored command is not the conditional $end_command",
- $line_nr, $conditional);
- die;
- }
- # Ignore until end of line
- if ($line !~ /\n/) {
- ($line, $line_nr) = _new_line($self, $line_nr, $conditional);
- print STDERR "IGNORE CLOSE line: $line" if ($self->{'DEBUG'});
- }
- print STDERR "CLOSED conditional $end_command\n" if ($self->{'DEBUG'});
- last;
- } else {
- print STDERR "CLOSED raw $end_command\n" if ($self->{'DEBUG'});
- $line = _start_empty_line_after_command($line, $current, $raw_command);
- }
- } else {
- if (@{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
- and $current->{'contents'}->[-1]->{'text'} !~ /\n/
- and $line !~ /\S/) {
- $current->{'contents'}->[-1]->{'text'} .= $line;
- } else {
- push @{$current->{'contents'}},
- { 'text' => $line, 'type' => 'raw', 'parent' => $current };
- }
- last;
- }
- # in @verb. type should be 'brace_command_arg'
- } elsif ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
- and $current->{'parent'}->{'cmdname'} eq 'verb') {
- # collect the first character if not already done
- if (!defined($current->{'parent'}->{'type'})) {
- if ($line =~ /^$/) {
- $current->{'parent'}->{'type'} = '';
- $self->line_error(sprintf($self->
- __("\@%s without associated character"), 'verb'), $line_nr);
- } else {
- $line =~ s/^(.)//;
- $current->{'parent'}->{'type'} = $1;
- }
- }
- my $char = quotemeta($current->{'parent'}->{'type'});
- if ($line =~ s/^(.*?)$char\}/\}/) {
- push @{$current->{'contents'}},
- { 'text' => $1, 'type' => 'raw', 'parent' => $current }
- if ($1 ne '');
- print STDERR "END VERB\n" if ($self->{'DEBUG'});
- } else {
- push @{$current->{'contents'}},
- { 'text' => $line, 'type' => 'raw', 'parent' => $current };
- print STDERR "LINE VERB: $line" if ($self->{'DEBUG'});
- last;
- }
- }
- # this mostly happens in the following cases:
- # after expansion of user defined macro that doesn't end with EOL
- # after a protection of @\n in @def* line
- # at the end of an expanded Texinfo fragment
- while ($line eq '') {
- print STDERR "EMPTY TEXT\n"
- if ($self->{'DEBUG'});
- ($line, $line_nr) = _next_text($self, $line_nr, $current);
- if (!defined($line)) {
- # end of the file or of a text fragment.
- $current = _end_line ($self, $current, $line_nr);
- # It may happen that there is an @include file on the line, it
- # will be picked up at NEXT_LINE, beginning a new line
- next NEXT_LINE;
- }
- }
- # handle user defined macros before anything else since
- # their expansion may lead to changes in the line
- # REMACRO
- if ($line =~ /^\@([[:alnum:]][[:alnum:]-]*)/
- and ($self->{'macros'}->{$1}
- or (exists $self->{'aliases'}->{$1} and
- $self->{'macros'}->{$self->{'aliases'}->{$1}}))) {
- $line =~ s/^\@([[:alnum:]][[:alnum:]-]*)//o;
- my $command = $1;
- my $alias_command;
- if (exists($self->{'aliases'}->{$command})) {
- $alias_command = $command;
- $command = $self->{'aliases'}->{$command};
- }
- my $expanded_macro = $self->{'macros'}->{$command};
- my $args_number = scalar(@{$expanded_macro->{'args'}}) -1;
- my $arguments = [];
- if ($line =~ s/^\s*{[^\S\f]*//) { # macro with args
- ($arguments, $line, $line_nr) =
- _expand_macro_arguments($self, $expanded_macro, $line, $line_nr);
- } elsif (($args_number >= 2) or ($args_number <1)) {
- # as agreed on the bug-texinfo mailing list, no warn when zero
- # arg and not called with {}.
- $self->line_warn(sprintf($self->__(
- "\@%s defined with zero or more than one argument should be invoked with {}"),
- $command), $line_nr)
- if ($args_number >= 2);
- } else {
- if ($line !~ /\n/) {
- ($line, $line_nr) = _new_line($self, $line_nr, $expanded_macro);
- $line = '' if (!defined($line));
- }
- $line =~ s/^[^\S\f]*// if ($line =~ /[\S\f]/);
- my $has_end_of_line = chomp $line;
- $arguments = [$line];
- $line = "\n" if ($has_end_of_line);
- }
- my $expanded = _expand_macro_body ($self, $expanded_macro,
- $arguments, $line_nr);
- print STDERR "MACROBODY: $expanded".'||||||'."\n"
- if ($self->{'DEBUG'});
- # empty result. It is ignored here.
- if ($expanded eq '') {
- next;
- }
- if ($self->{'MAX_MACRO_CALL_NESTING'}
- and scalar(@{$self->{'macro_stack'}}) > $self->{'MAX_MACRO_CALL_NESTING'}) {
- $self->line_warn(sprintf($self->__(
- "macro call nested too deeply (set MAX_NESTED_MACROS to override; current value %d)"),
- $self->{'MAX_MACRO_CALL_NESTING'}), $line_nr);
- next;
- }
- if ($expanded_macro->{'cmdname'} eq 'macro') {
- my $found = 0;
- foreach my $macro (@{$self->{'macro_stack'}}) {
- if ($macro->{'args'}->[0]->{'text'} eq $command) {
- $self->line_error(sprintf($self->__(
- "recursive call of macro %s is not allowed; use \@rmacro if needed"),
- $command), $line_nr);
- $found = 1;
- last;
- }
- }
- next if ($found);
- }
- unshift @{$self->{'macro_stack'}}, $expanded_macro;
- print STDERR "UNSHIFT MACRO_STACK: $expanded_macro->{'args'}->[0]->{'text'}\n"
- if ($self->{'DEBUG'});
- my $expanded_lines = _text_to_lines($expanded);
- chomp ($expanded_lines->[-1]);
- pop @$expanded_lines if ($expanded_lines->[-1] eq '');
- print STDERR "MACRO EXPANSION LINES: ".join('|', @$expanded_lines)
- ."|\nEND LINES MACRO EXPANSION\n" if ($self->{'DEBUG'});
- next if (!@$expanded_lines);
- my $new_lines = _complete_line_nr($expanded_lines,
- $line_nr->{'line_nr'}, $line_nr->{'file_name'},
- $expanded_macro->{'args'}->[0]->{'text'}, 1);
- $line_nr->{'end_macro'} = 1;
- unshift @{$self->{'input'}->[0]->{'pending'}}, [$line, $line_nr];
- my $new_text = shift @$new_lines;
- ($line, $line_nr) = ($new_text->[0], $new_text->[1]);
- unshift @{$self->{'input'}->[0]->{'pending'}}, @$new_lines;
- # Now handle all the cases that may lead to command closing
- # or following character association with an @-command, especially
- # accent command, that is handle @-command with braces that don't
- # always need a brace.
- # The condition below is only caught right after command opening,
- # otherwise we are in the 'args' and not right in the command container.
- } elsif ($current->{'cmdname'} and
- (defined($brace_commands{$current->{'cmdname'}}) or
- $self->{'definfoenclose'}->{$current->{'cmdname'}})
- and $line !~ /^{/) {
- # special case for @-command as argument of @itemize or @*table.
- if (_command_with_command_as_argument($current->{'parent'})) {
- delete $current->{'contents'};
- print STDERR "FOR PARENT \@$current->{'parent'}->{'parent'}->{'cmdname'} command_as_argument $current->{'cmdname'}\n" if ($self->{'DEBUG'});
- $current->{'type'} = 'command_as_argument' if (!$current->{'type'});
- $current->{'parent'}->{'parent'}->{'extra'}->{'command_as_argument'}
- = $current;
- $current = $current->{'parent'};
- # now accent commands
- } elsif ($accent_commands{$current->{'cmdname'}}) {
- if ($line =~ /^[^\S\r\n]/) {
- if ($current->{'cmdname'} =~ /^[a-zA-Z]/) {
- $line =~ s/^([^\S\r\n]+)//;
- $current->{'extra'}->{'spaces'} = ''
- if (!defined($current->{'extra'}->{'spaces'}));
- $current->{'extra'}->{'spaces'} .= $1;
- } else {
- $self->line_warn(sprintf($self->
- __("accent command `\@%s' must not be followed by whitespace"),
- $current->{'cmdname'}), $line_nr);
- $current = $current->{'parent'};
- }
- } elsif ($line =~ /^\@/) {
- $self->line_error(sprintf($self->
- __("use braces to give a command as an argument to \@%s"),
- $current->{'cmdname'}), $line_nr);
- $current = $current->{'parent'};
- } elsif ($line =~ s/^(.)//o) {
- print STDERR "ACCENT \@$current->{'cmdname'}\n"
- if ($self->{'DEBUG'});
- my $following_arg = {'type' => 'following_arg',
- 'parent' => $current};
- $following_arg->{'contents'} = [{ 'text' => $1,
- 'parent' => $following_arg } ];
- $current->{'args'} = [ $following_arg ];
- if ($current->{'cmdname'} eq 'dotless' and $1 ne 'i' and $1 ne 'j') {
- $self->line_error(sprintf($self->
- __("%c%s expects `i' or `j' as argument, not `%s'"),
- ord('@'), $current->{'cmdname'}, $1), $line_nr);
- }
- if ($current->{'cmdname'} =~ /^[a-zA-Z]/) {
- $current->{'args'}->[-1]->{'type'} = 'space_command_arg';
- }
- delete $current->{'contents'};
- $current = $current->{'parent'};
- } else { # The accent is at end of line
- # whitespace for commands with letter.
- print STDERR "STRANGE ACC \@$current->{'cmdname'}\n" if ($self->{'DEBUG'});
- $self->line_warn(sprintf($self->
- __("accent command `\@%s' must not be followed by new line"),
- $current->{'cmdname'}), $line_nr);
- $current = $current->{'parent'};
- }
- next;
- } else {
- # ignore space after a braced @-command like TeX does
- if ($self->{'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME'}
- and $line =~ s/^\s+//) {
- next;
- }
- $self->line_error(sprintf($self->__("\@%s expected braces"),
- $current->{'cmdname'}), $line_nr);
- $current = $current->{'parent'};
- }
- # maybe a menu entry beginning: a * at the beginning of a menu line
- } elsif ($line =~ /^\*/ and $current->{'type'}
- and $current->{'type'} eq 'preformatted'
- and $current->{'parent'}->{'type'}
- and ($current->{'parent'}->{'type'} eq 'menu_comment'
- or $current->{'parent'}->{'type'} eq 'menu_entry_description')
- and @{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'empty_line'
- and $current->{'contents'}->[-1]->{'text'} eq '') {
- print STDERR "MENU STAR\n" if ($self->{'DEBUG'});
- _abort_empty_line($self, $current);
- $line =~ s/^\*//;
- push @{$current->{'contents'}}, { 'type' => 'menu_star',
- 'text' => '*' };
- # a space after a * at the beginning of a menu line
- } elsif ($line =~ /^\s+/ and $current->{'contents'}
- and @{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'menu_star') {
- print STDERR "MENU ENTRY (certainly)\n" if ($self->{'DEBUG'});
- # this is the menu star collected previously
- pop @{$current->{'contents'}};
- $line =~ s/^(\s+)//;
- my $leading_text = '*' . $1;
- if ($current->{'type'} eq 'preformatted'
- and $current->{'parent'}->{'type'}
- and $current->{'parent'}->{'type'} eq 'menu_comment') {
- my $menu = $current->{'parent'}->{'parent'};
- if (!@{$current->{'contents'}}) {
- pop @{$current->{'parent'}->{'contents'}};
- if (!scalar(@{$current->{'parent'}->{'contents'}})) {
- pop @{$menu->{'contents'}};
- }
- }
- $current = $menu;
- #print STDERR "Close MENU_COMMENT because new menu entry\n";
- } else {
- # first parent preformatted, third is menu_entry
- if ($current->{'type'} ne 'preformatted'
- or $current->{'parent'}->{'type'} ne 'menu_entry_description'
- or $current->{'parent'}->{'parent'}->{'type'} ne 'menu_entry'
- or !$menu_commands{$current->{'parent'}->{'parent'}->{'parent'}->{'cmdname'}}) {
- $self->_bug_message("Not in menu comment nor description",
- $line_nr, $current);
- }
- $current = $current->{'parent'}->{'parent'}->{'parent'};
- }
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'preformatted') {
- $self->_bug_message("context $context instead of preformatted after menu leading star",
- $line_nr, $current);
- }
- push @{$current->{'contents'}}, { 'type' => 'menu_entry',
- 'parent' => $current,
- };
- $current = $current->{'contents'}->[-1];
- $current->{'args'} = [ { 'type' => 'menu_entry_leading_text',
- 'text' => $leading_text,
- 'parent' => $current },
- { 'type' => 'menu_entry_name',
- 'contents' => [],
- 'parent' => $current } ];
- $current = $current->{'args'}->[-1];
- # * followed by something else than a space.
- } elsif ($current->{'contents'} and @{$current->{'contents'}}
- and $current->{'contents'}->[-1]->{'type'}
- and $current->{'contents'}->[-1]->{'type'} eq 'menu_star') {
- print STDERR "ABORT MENU STAR ($line)\n" if ($self->{'DEBUG'});
- delete $current->{'contents'}->[-1]->{'type'};
- # after a separator in menu
- } elsif ($current->{'args'} and @{$current->{'args'}}
- and $current->{'args'}->[-1]->{'type'}
- and $current->{'args'}->[-1]->{'type'} eq 'menu_entry_separator') {
- my $separator = $current->{'args'}->[-1]->{'text'};
- # separator is ::, we concatenate and let the while restart
- # in order to collect spaces below
- if ($separator eq ':' and $line =~ s/^(:)//) {
- $current->{'args'}->[-1]->{'text'} .= $1;
- # a . not followed by a space. Not a separator.
- } elsif ($separator eq '.' and $line =~ /^\S/) {
- pop @{$current->{'args'}};
- $current = $current->{'args'}->[-1];
- $current = _merge_text($self, $current, $separator);
- # here we collect spaces following separators.
- } elsif ($line =~ s/^([^\S\r\n]+)//) {
- # FIXME a trailing end of line could be considered to be part
- # of the separator. Right now it is part of the description,
- # since it is catched (in the next while) as one of the case below
- $current->{'args'}->[-1]->{'text'} .= $1;
- # now handle the menu part that was closed
- } elsif ($separator =~ /^::/) {
- print STDERR "MENU NODE no entry $separator\n" if ($self->{'DEBUG'});
- # it was previously registered as menu_entry_name, it is
- # changed to node
- $current->{'args'}->[-2]->{'type'} = 'menu_entry_node';
- $current = _enter_menu_entry_node($self, $current, $line_nr);
- # end of the menu entry name
- } elsif ($separator =~ /^:/) {
- print STDERR "MENU ENTRY $separator\n" if ($self->{'DEBUG'});
- push @{$current->{'args'}}, { 'type' => 'menu_entry_node',
- 'contents' => [],
- 'parent' => $current };
- $current = $current->{'args'}->[-1];
- # anything else is the end of the menu node following a menu_entry_name
- } else {
- print STDERR "MENU NODE $separator\n" if ($self->{'DEBUG'});
- $current = _enter_menu_entry_node($self, $current, $line_nr);
- }
- # REMACRO
- } elsif ($line =~ s/^\@(["'~\@\}\{,\.!\?\s\*\-\^`=:\|\/\\])//o
- or $line =~ s/^\@([[:alnum:]][[:alnum:]-]*)//o) {
- my $command = $1;
- my $alias_command;
- if (exists($self->{'aliases'}->{$command})) {
- $alias_command = $command;
- $command = $self->{'aliases'}->{$command};
- }
- print STDERR "COMMAND $command\n" if ($self->{'DEBUG'});
- if ($command eq 'value') {
- $line =~ s/^\s*//
- if ($self->{'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME'});
- # REVALUE
- if ($line =~ s/^{([\w\-][^\s{\\}~`\^+"<>|@]*)}//) {
- my $value = $1;
- if (exists($self->{'values'}->{$value})) {
- if (!defined($self->{'values'}->{$value})) {
- print STDERR "BUG? $value exists but not defined\n";
- } elsif (!ref($self->{'values'}->{$value})) {
- $line = $self->{'values'}->{$value} . $line;
- # the push @{$current->{'contents'}}, {}; prevents a trailing
- # text to be merged, to avoid having the value tree modified.
- } elsif (ref($self->{'values'}->{$value}) eq 'ARRAY') {
- # we don't know for sure, but if we don't do it here it
- # won't be done
- _abort_empty_line($self, $current);
- foreach my $content (@{$self->{'values'}->{$value}}) {
- push @{$current->{'contents'}}, $content;
- }
- push @{$current->{'contents'}}, {};
- } elsif (ref($self->{'values'}->{$value}) eq 'HASH') {
- # we don't know for sure, but if we don't do it here it
- # won't be done
- _abort_empty_line($self, $current);
- my $content = $self->{'values'}->{$value};
- push @{$current->{'contents'}}, $content;
- push @{$current->{'contents'}}, {};
- }
- } else {
- # caller should expand something along
- # gdt('@{No value for `{value}\'@}', {'value' => $value}, {'keep_texi'=> 1});
- push @{$current->{'contents'}}, { 'cmdname' => 'value',
- 'type' => $value };
- $self->line_warn(
- sprintf($self->__("undefined flag: %s"), $value), $line_nr);
- }
- } else {
- $self->line_error($self->__("bad syntax for \@value"), $line_nr);
- }
- next;
- }
- if (defined($deprecated_commands{$command})) {
- if ($deprecated_commands{$command} eq '') {
- $self->line_warn(sprintf($self->__("%c%s is obsolete."),
- ord('@'), $command), $line_nr);
- } else {
- $self->line_warn(sprintf($self->__("%c%s is obsolete; %s"),
- ord('@'), $command,
- $self->__($deprecated_commands{$command})), $line_nr);
- }
- }
- if (not _abort_empty_line($self, $current)
- and $begin_line_commands{$command}) {
- $self->line_warn(
- sprintf($self->__("\@%s should only appear at a line beginning"),
- $command), $line_nr);
- }
- my $invalid_parent;
- # error messages for forbidden constructs, like @node in @r,
- # block command on line command, @xref in @anchor or node...
- if ($current->{'parent'}) {
- if ($current->{'parent'}->{'cmdname'}) {
- if (defined($self->{'valid_nestings'}->{$current->{'parent'}->{'cmdname'}})
- and !$self->{'valid_nestings'}->{$current->{'parent'}->{'cmdname'}}->{$command}
- # we make sure that we are on a root @-command line and
- # not in contents
- and (!$root_commands{$current->{'parent'}->{'cmdname'}}
- or ($current->{'type'}
- and $current->{'type'} eq 'misc_line_arg'))
- # we make sure that we are on a block @-command line and
- # not in contents
- and (!($block_commands{$current->{'parent'}->{'cmdname'}})
- or ($current->{'type'}
- and $current->{'type'} eq 'block_line_arg'))
- # we make sure that we are on an @item/@itemx line and
- # not in an @enumerate, @multitable or @itemize @item.
- and (($current->{'parent'}->{'cmdname'} ne 'itemx'
- and $current->{'parent'}->{'cmdname'} ne 'item')
- or ($current->{'type'}
- and $current->{'type'} eq 'misc_line_arg'))) {
- $invalid_parent = $current->{'parent'}->{'cmdname'};
- }
- } elsif ($self->{'context_stack'}->[-1] eq 'def'
- # FIXME instead of hardcoding in_full_line_commands_no_refs
- # it would be better to use the parent command valid_nesting.
- and !$in_full_line_commands_no_refs{$command}) {
- my $def_block = $current;
- while ($def_block->{'parent'} and (!$def_block->{'parent'}->{'type'}
- or $def_block->{'parent'}->{'type'} ne 'def_line')) {
- $def_block = $def_block->{'parent'};
- }
- $invalid_parent = $def_block->{'parent'}->{'parent'}->{'cmdname'};
- }
- }
- # special case with @ followed by a newline protecting end of lines
- # in @def*
- last if ($self->{'context_stack'}->[-1] eq 'def' and $command eq "\n");
- unless ($self->{'no_paragraph_commands'}->{$command}) {
- my $paragraph = _begin_paragraph($self, $current, $line_nr);
- $current = $paragraph if ($paragraph);
- }
- if ($self->{'close_paragraph_commands'}->{$command}) {
- $current = _end_paragraph($self, $current, $line_nr);
- }
- if ($self->{'close_preformatted_commands'}->{$command}) {
- $current = _end_preformatted($self, $current, $line_nr);
- }
- # commands without braces and not block commands, ie no @end
- if (defined($self->{'misc_commands'}->{$command})) {
- if ($root_commands{$command} or $command eq 'bye') {
- $current = _close_commands($self, $current, $line_nr, undef,
- $command);
- # root_level commands leads to setting a new root
- # for the whole document and stuffing the preceding text
- # as the first content, this is done only once.
- if ($current->{'type'} and $current->{'type'} eq 'text_root') {
- if ($command ne 'bye') {
- $root = { 'type' => 'document_root', 'contents' => [$current] };
- $current->{'parent'} = $root;
- $current = $root;
- }
- } else {
- die if (!defined($current->{'parent'}));
- $current = $current->{'parent'};
- }
- }
- # noarg skipline skipspace text line lineraw /^\d$/
- my $arg_spec = $self->{'misc_commands'}->{$command};
- my $misc;
- if ($arg_spec eq 'noarg') {
- my $ignored = 0;
- my $only_in_headings = 0;
- if ($command eq 'insertcopying') {
- my $parent = $current;
- while ($parent) {
- if ($parent->{'cmdname'} and $parent->{'cmdname'} eq 'copying') {
- $self->line_error(
- sprintf($self->__("\@%s not allowed inside `\@%s' block"),
- $command, $parent->{'cmdname'}), $line_nr);
- $ignored = 1;
- last;
- }
- $parent = $parent->{'parent'};
- }
- } elsif ($in_heading_commands{$command}) {
- $self->line_error(
- sprintf($self->__("\@%s should only appear in heading or footing"),
- $command), $line_nr);
- $only_in_headings = 1;
- }
- if (!$ignored) {
- $misc = {'cmdname' => $command,
- 'parent' => $current};
- push @{$current->{'contents'}}, $misc;
- # also sets invalid_nesting in that case
- $misc->{'extra'}->{'invalid_nesting'} = 1 if ($only_in_headings);
- $self->_register_global_command($command, $misc, $line_nr);
- }
- $self->_mark_and_warn_invalid($command, $invalid_parent,
- $line_nr, $misc);
- $current = $self->_begin_preformatted($current)
- if ($close_preformatted_commands{$command});
- # all the cases using the raw line
- } elsif ($arg_spec eq 'skipline' or $arg_spec eq 'lineraw'
- or $arg_spec eq 'special') {
- # complete the line if there was a user macro expansion
- if ($line !~ /\n/) {
- my ($new_line, $new_line_nr) = _new_line($self, $line_nr, undef);
- $line .= $new_line if (defined($new_line));
- }
- $misc = {'cmdname' => $command,
- 'parent' => $current};
- my $args = [];
- my $has_comment;
- if ($arg_spec eq 'lineraw' or $arg_spec eq 'skipline') {
- $args = [ $line ];
- } elsif ($arg_spec eq 'special') {
- ($args, $has_comment)
- = $self->_parse_special_misc_command($line, $command, $line_nr);
- $misc->{'extra'}->{'arg_line'} = $line;
- }
- # if using the @set txi* instead of a proper @-command, replace
- # by the tree obtained with the @-command. Even though
- # _end_line is called below, as $current is not misc_line_arg
- # there should not be anything done in addition than what is
- # done for @clear or @set.
- if (($command eq 'set' or $command eq 'clear')
- and scalar(@$args) >= 1
- and $set_flag_command_equivalent{$args->[0]}) {
- my $arg;
- if ($command eq 'set') {
- $arg = 'on';
- } else {
- $arg = 'off';
- }
- $command = $set_flag_command_equivalent{$args->[0]};
- $misc = {'cmdname' => $command,
- 'parent' => $current,
- 'line_nr' => $line_nr,
- 'extra' => {'misc_args' => [$arg]}};
- my $misc_line_args = {'type' => 'misc_line_arg',
- 'parent' => $misc};
- $misc->{'args'} = [$misc_line_args];
- my $spaces_after_command
- = { 'type' => 'empty_spaces_after_command',
- 'text' => ' ',
- 'parent' => $misc_line_args,
- 'extra' => {'command' => $misc} };
- $misc->{'extra'}->{'spaces_after_command'}
- = $spaces_after_command;
- $misc_line_args->{'contents'} = [ $spaces_after_command,
- { 'text' => $arg,
- 'parent' => $misc_line_args, },
- { 'text' => "\n",
- 'parent' => $misc_line_args,
- 'type' => 'spaces_at_end', } ];
- push @{$current->{'contents'}}, $misc;
- } else {
- push @{$current->{'contents'}}, $misc;
- foreach my $arg (@$args) {
- push @{$misc->{'args'}},
- { 'type' => 'misc_arg', 'text' => $arg,
- 'parent' => $current->{'contents'}->[-1] };
- }
- $misc->{'extra'}->{'misc_args'} = $args
- if (scalar(@$args) and $arg_spec ne 'skipline');
- }
- if (! _ignore_global_commands($self)) {
- if ($command eq 'raisesections') {
- $self->{'sections_level'}++;
- } elsif ($command eq 'lowersections') {
- $self->{'sections_level'}--;
- } elsif ($command eq 'novalidate') {
- $self->{'novalidate'} = 1;
- }
- }
- $self->_mark_and_warn_invalid($command, $invalid_parent,
- $line_nr, $misc);
- $self->_register_global_command($command, $misc, $line_nr);
- # the end of line is ignored for special commands
- if ($arg_spec ne 'special' or !$has_comment) {
- $current = _end_line($self, $current, $line_nr);
- }
- last NEXT_LINE if ($command eq 'bye');
- # Even if _end_line is called, it is not done since there is
- # no misc_line_arg
- $current = $self->_begin_preformatted($current)
- if ($close_preformatted_commands{$command});
- last;
- } else {
- # $arg_spec is text, line, skipspace or a number
- my $line_arg = 0;
- $line_arg = 1 if ($arg_spec ne 'skipspace');
- if ($command eq 'item' or $command eq 'itemx'
- or $command eq 'headitem' or $command eq 'tab') {
- my $parent;
- # itemize or enumerate
- if ($parent = _item_container_parent($current)) {
- if ($command eq 'item') {
- print STDERR "ITEM_CONTAINER\n" if ($self->{'DEBUG'});
- $parent->{'items_count'}++;
- $misc = { 'cmdname' => $command, 'parent' => $parent,
- 'contents' => [],
- 'extra' =>
- {'item_number' => $parent->{'items_count'}} };
- push @{$parent->{'contents'}}, $misc;
- $current = $parent->{'contents'}->[-1];
- } else {
- $self->line_error(sprintf($self->__(
- "\@%s not meaningful inside `\@%s' block"),
- $command, $parent->{'cmdname'}), $line_nr);
- }
- $current = $self->_begin_preformatted($current);
- # *table
- } elsif ($parent = _item_line_parent($current)) {
- if ($command eq 'item' or $command eq 'itemx') {
- print STDERR "ITEM_LINE\n" if ($self->{'DEBUG'});
- $current = $parent;
- $self->_gather_previous_item($current, $command, $line_nr);
- $misc = { 'cmdname' => $command, 'parent' => $current };
- push @{$current->{'contents'}}, $misc;
- # since in the %misc_commands hash the entry for those
- # commands is 'skipspace' we set $line_arg here.
- $line_arg = 1;
- } else {
- $self->line_error(sprintf($self->__(
- "\@%s not meaningful inside `\@%s' block"),
- $command, $parent->{'cmdname'}), $line_nr);
- $current = $self->_begin_preformatted($current);
- }
- # multitable
- } elsif ($parent = _item_multitable_parent($current)) {
- if ($command eq 'item' or $command eq 'headitem'
- or $command eq 'tab') {
- if (!$parent->{'extra'}->{'max_columns'}) {
- $self->line_warn(
- sprintf($self->__("\@%s in empty multitable"),
- $command), $line_nr);
- } elsif ($command eq 'tab') {
- my $row = $parent->{'contents'}->[-1];
- die if (!$row->{'type'});
- if ($row->{'type'} eq 'before_item') {
- $self->line_error($self->__("\@tab before \@item"), $line_nr);
- } elsif ($row->{'cells_count'} >= $parent->{'extra'}->{'max_columns'}) {
- $self->line_error(sprintf($self->__(
- "too many columns in multitable item (max %d)"),
- $parent->{'extra'}->{'max_columns'}), $line_nr);
- } else {
- $row->{'cells_count'}++;
- $misc = { 'cmdname' => $command,
- 'parent' => $row,
- 'contents' => [],
- 'extra' =>
- {'cell_number' => $row->{'cells_count'}} };
- push @{$row->{'contents'}}, $misc;
- $current = $row->{'contents'}->[-1];
- #$current = $self->_begin_preformatted($current);
- print STDERR "TAB\n" if ($self->{'DEBUG'});
- }
- } else {
- print STDERR "ROW\n" if ($self->{'DEBUG'});
- $parent->{'rows_count'}++;
- my $row = { 'type' => 'row', 'contents' => [],
- 'cells_count' => 1,
- 'extra' => {'row_number' => $parent->{'rows_count'} },
- 'parent' => $parent };
- push @{$parent->{'contents'}}, $row;
- $misc = { 'cmdname' => $command,
- 'parent' => $row,
- 'contents' => [],
- 'extra' => {'cell_number' => 1}};
- push @{$row->{'contents'}}, $misc;
- $current = $row->{'contents'}->[-1];
- }
- } else {
- $self->line_error(sprintf($self->__(
- "\@%s not meaningful inside `\@%s' block"),
- $command, $parent->{'cmdname'}), $line_nr);
- }
- $current = $self->_begin_preformatted($current);
- } elsif ($command eq 'tab') {
- $self->line_error($self->__(
- "ignoring \@tab outside of multitable"), $line_nr);
- $current = $self->_begin_preformatted($current);
- } else {
- $self->line_error (sprintf($self->__(
- "\@%s outside of table or list"), $command), $line_nr);
- $current = $self->_begin_preformatted($current);
- }
- $misc->{'line_nr'} = $line_nr if (defined($misc));
- } else {
- $misc = { 'cmdname' => $command, 'parent' => $current,
- 'line_nr' => $line_nr };
- push @{$current->{'contents'}}, $misc;
- if ($self->{'sections_level'} and $root_commands{$command}
- and $command ne 'node' and $command ne 'part') {
- $current->{'contents'}->[-1]->{'extra'}->{'sections_level'}
- = $self->{'sections_level'};
- }
- # def*x
- if ($def_commands{$command}) {
- my $base_command = $command;
- $base_command =~ s/x$//;
- # check that the def*x is first after @def*, no paragraph
- # in-between.
- my $after_paragraph = _check_no_text($current);
- push @{$self->{'context_stack'}}, 'def';
- $current->{'contents'}->[-1]->{'type'} = 'def_line';
- $current->{'contents'}->[-1]->{'extra'} =
- {'def_command' => $base_command,
- 'original_def_cmdname' => $command};
- if ($current->{'cmdname'}
- and $current->{'cmdname'} eq $base_command) {
- pop @{$current->{'contents'}};
- _gather_def_item($current, $command);
- push @{$current->{'contents'}}, $misc;
- }
- if (!$current->{'cmdname'}
- or $current->{'cmdname'} ne $base_command
- or $after_paragraph) {
- $self->line_error(sprintf($self->__(
- "must be after `\@%s' to use `\@%s'"),
- $base_command, $command), $line_nr);
- $current->{'contents'}->[-1]->{'extra'}->{'not_after_command'} = 1;
- }
- }
- }
- # a container for what is on the @-command line, considered to
- # be the @-command argument
- if ($line_arg) {
- $current = $current->{'contents'}->[-1];
- $current->{'args'} = [{ 'type' => 'misc_line_arg',
- 'contents' => [],
- 'parent' => $current }];
- # @node is the only misc command with args separated with comma
- # FIXME a 3 lingering here deep into the code may not
- # be very wise... However having a hash only for one @-command
- # is not very appealing either...
- if ($command eq 'node') {
- $current->{'remaining_args'} = 3;
- } elsif ($command eq 'author') {
- my $parent = $current;
- my $found;
- while ($parent->{'parent'}) {
- $parent = $parent->{'parent'};
- last if ($parent->{'type'}
- and $parent->{'type'} eq 'brace_command_context');
- if ($parent->{'cmdname'}) {
- if ($parent->{'cmdname'} eq 'titlepage') {
- push @{$self->{'extra'}->{'author'}}, $current;
- $current->{'extra'}->{'titlepage'} = $parent;
- $found = 1;
- } elsif ($parent->{'cmdname'} eq 'quotation' or
- $parent->{'cmdname'} eq 'smallquotation') {
- push @{$parent->{'extra'}->{'authors'}}, $current;
- $current->{'extra'}->{'quotation'} = $parent;
- $found = 1;
- }
- last if ($found);
- }
- }
- if (!$found) {
- $self->line_warn(sprintf($self->__(
- "\@%s not meaningful outside `\@titlepage' and `\@quotation' environments"),
- $command), $current->{'line_nr'});
- }
- } elsif ($command eq 'dircategory' and $self->{'current_node'}) {
- $self->line_warn($self->__("\@dircategory after first node"),
- $line_nr);
- }
- $current = $current->{'args'}->[-1];
- push @{$self->{'context_stack'}}, 'line'
- unless ($def_commands{$command});
- }
- $line = _start_empty_line_after_command($line, $current, $misc);
- }
- $self->_mark_and_warn_invalid($command, $invalid_parent,
- $line_nr, $misc);
- $self->_register_global_command($command, $misc, $line_nr);
- if ($command eq 'dircategory'
- and ! _ignore_global_commands($self)) {
- push @{$self->{'info'}->{'dircategory_direntry'}}, $misc;
- }
- # @-command with matching @end opening
- } elsif (exists($block_commands{$command})) {
- if ($command eq 'macro' or $command eq 'rmacro') {
- my $macro = _parse_macro_command_line($self, $command, $line,
- $current, $line_nr);
- push @{$current->{'contents'}}, $macro;
- $self->_mark_and_warn_invalid($command, $invalid_parent,
- $line_nr, $current->{'contents'}->[-1]);
- $current = $current->{'contents'}->[-1];
- last;
- } elsif ($block_commands{$command} eq 'conditional') {
- my $ifvalue_true = 0;
- if ($command eq 'ifclear' or $command eq 'ifset') {
- # REVALUE
- if ($line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) {
- my $name = $1;
- if ((exists($self->{'values'}->{$name}) and $command eq 'ifset')
- or (!exists($self->{'values'}->{$name})
- and $command eq 'ifclear')) {
- $ifvalue_true = 1;
- }
- print STDERR "CONDITIONAL \@$command $name: $ifvalue_true\n" if ($self->{'DEBUG'});
- } elsif ($line !~ /\S/) {
- $self->line_error(sprintf($self->
- __("%c%s requires a name"), ord('@'), $command), $line_nr);
- } else {
- $self->line_error(sprintf($self->
- __("bad name for \@%s"), $command), $line_nr);
- }
- } elsif ($command eq 'ifcommanddefined'
- or $command eq 'ifcommandnotdefined') {
- # REMACRO
- if ($line =~ /^\s+([[:alnum:]][[:alnum:]\-]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) {
- my $name = $1;
- my $command_is_defined = (
- exists($Texinfo::Common::all_commands{$name})
- or $self->{'macros'}->{$name}
- or $self->{'definfoenclose'}->{$name}
- or $self->{'aliases'}->{$name}
- or $self->{'command_index_prefix'}->{$name}
- );
- if (($command_is_defined
- and $command eq 'ifcommanddefined')
- or (! $command_is_defined
- and $command eq 'ifcommandnotdefined')) {
- $ifvalue_true = 1;
- }
- print STDERR "CONDITIONAL \@$command $name: $ifvalue_true\n" if ($self->{'DEBUG'});
- } elsif ($line !~ /\S/) {
- $self->line_error(sprintf($self->
- __("%c%s requires a name"), ord('@'), $command), $line_nr);
- } else {
- $self->line_error(sprintf($self->
- __("bad name for \@%s"), $command), $line_nr);
- }
- } elsif ($command =~ /^ifnot(.*)/) {
- $ifvalue_true = 1 if !($self->{'expanded_formats_hash'}->{$1}
- # exception as explained in the texinfo manual
- or ($1 eq 'info'
- and $self->{'expanded_formats_hash'}->{'plaintext'}));
- print STDERR "CONDITIONAL \@$command format $1: $ifvalue_true\n" if ($self->{'DEBUG'});
- } else {
- die unless ($command =~ /^if(.*)/);
- $ifvalue_true = 1 if ($self->{'expanded_formats_hash'}->{$1}
- or ($1 eq 'info'
- and $self->{'expanded_formats_hash'}->{'plaintext'}));
- print STDERR "CONDITIONAL \@$command format $1: $ifvalue_true\n" if ($self->{'DEBUG'});
- }
- if ($ifvalue_true) {
- push @{$self->{'conditionals_stack'}}, $command;
- } else {
- push @{$current->{'contents'}}, { 'cmdname' => $command,
- 'parent' => $current,
- 'contents' => [] };
- $current = $current->{'contents'}->[-1];
- }
- # FIXME(Karl) ignore what is remaining on the line, to eat
- # the end of line?
- last;
- } else {
- my $block;
- # a menu command closes a menu_comment, but not the other
- # block commands. This won't catch menu commands buried in
- # other formats (that are incorrect anyway).
- if ($menu_commands{$command} and $current->{'type'}
- and ($current->{'type'} eq 'menu_comment'
- or $current->{'type'} eq 'menu_entry_description')) {
- my $menu;
- $menu = $current->{'parent'};
- pop @{$menu->{'contents'}}
- if (!@{$current->{'contents'}});
- my $context = pop @{$self->{'context_stack'}};
- if ($context ne 'preformatted') {
- $self->_bug_message("context $context instead of preformatted in new menu",
- $line_nr, $current);
- }
-
- if ($menu->{'type'} and $menu->{'type'} eq 'menu_entry') {
- $menu = $menu->{'parent'};
- }
- $current = $menu;
- }
- # the def command holds a line_def* which corresponds with the
- # definition line. This allows to have a treatement similar
- # with def*x.
- if ($def_commands{$command}) {
- push @{$self->{'context_stack'}}, 'def';
- $block = { 'parent' => $current,
- 'cmdname' => $command,
- 'contents' => [] };
- push @{$current->{'contents'}}, $block;
- $current = $current->{'contents'}->[-1];
- push @{$current->{'contents'}}, {
- 'type' => 'def_line',
- 'parent' => $current,
- 'line_nr' => $line_nr,
- 'extra' =>
- {'def_command' => $command,
- 'original_def_cmdname' => $command}
- };
- } else {
- $block = { 'cmdname' => $command,
- 'parent' => $current,
- 'contents' => [] };
- push @{$current->{'contents'}}, $block;
- }
- $current = $current->{'contents'}->[-1];
- if ($block_arg_commands{$command}) {
- if ($preformatted_commands{$command}) {
- push @{$self->{'context_stack'}}, 'preformatted';
- } elsif ($format_raw_commands{$command}) {
- push @{$self->{'context_stack'}}, 'rawpreformatted';
- if ($self->{'expanded_formats_hash'}->{$command}
- and $self->{'expanded_formats_stack'}->[-1]) {
- push @{$self->{'expanded_formats_stack'}}, $command;
- } else {
- push @{$self->{'expanded_formats_stack'}}, 0;
- }
- }
- if ($region_commands{$command}) {
- if (@{$self->{'regions_stack'}}) {
- $self->line_error(
- sprintf($self->__("region %s inside region %s is not allowed"),
- $command, $self->{'regions_stack'}->[-1]->{'cmdname'}),
- $line_nr);
- }
- push @{$self->{'regions_stack'}}, $block;
- }
- if ($menu_commands{$command}) {
- if ($self->{'context_stack'}->[-1] eq 'preformatted') {
- push @{$self->{'context_stack'}}, 'preformatted';
- } else {
- push @{$self->{'context_stack'}}, 'menu';
- }
- if (! _ignore_global_commands($self)) {
- push @{$self->{'info'}->{'dircategory_direntry'}}, $block
- if ($command eq 'direntry');
- if ($self->{'current_node'}) {
- if ($command eq 'direntry') {
- if ($self->{'SHOW_MENU'}) {
- $self->line_warn($self->__("\@direntry after first node"),
- $line_nr);
- }
- } elsif ($command eq 'menu') {
- push @{$self->{'current_node'}->{'menus'}}, $current;
- }
- } elsif ($command ne 'direntry') {
- if ($self->{'SHOW_MENU'}) {
- $self->line_error(sprintf($self->__("\@%s seen before first \@node"),
- $command), $line_nr);
- $self->line_error($self->__(
- "perhaps your \@top node should be wrapped in \@ifnottex rather than \@ifinfo?"),
- $line_nr, 1);
- }
- if ($command eq 'menu') {
- push @{$self->{'info'}->{'unassociated_menus'}}, $current;
- }
- }
- }
- }
- $current->{'args'} = [ {
- 'type' => 'block_line_arg',
- 'contents' => [],
- 'parent' => $current } ];
-
- $current->{'remaining_args'} = $block_commands{$command} -1
- if ($block_commands{$command} =~ /^\d+$/
- and $block_commands{$command} -1 > 0);
- $current = $current->{'args'}->[-1];
- push @{$self->{'context_stack'}}, 'line'
- unless ($def_commands{$command});
- }
- $block->{'line_nr'} = $line_nr;
- $self->_mark_and_warn_invalid($command, $invalid_parent,
- $line_nr, $block);
- $self->_register_global_command($command, $block, $line_nr);
- $line = _start_empty_line_after_command($line, $current, $block);
- }
- } elsif (defined($brace_commands{$command})
- or defined($self->{'definfoenclose'}->{$command})) {
-
- push @{$current->{'contents'}}, { 'cmdname' => $command,
- 'parent' => $current,
- 'contents' => [] };
- $current->{'contents'}->[-1]->{'line_nr'} = $line_nr
- if ($keep_line_nr_brace_commands{$command});
- $self->_mark_and_warn_invalid($command, $invalid_parent,
- $line_nr, $current->{'contents'}->[-1]);
- $current = $current->{'contents'}->[-1];
- if ($command eq 'click') {
- $current->{'extra'}->{'clickstyle'} = $self->{'clickstyle'};
- } elsif ($command eq 'kbd') {
- if ($self->{'context_stack'}->[-1] eq 'preformatted'
- and $self->{'kbdinputstyle'} ne 'distinct') {
- $current->{'extra'}->{'code'} = 1;
- } elsif ($self->{'kbdinputstyle'} eq 'code'
- or ($self->{'kbdinputstyle'} eq 'example'
- and $self->_in_code($current->{'parent'}))) {
- $current->{'extra'}->{'code'} = 1;
- }
- }
- if ($self->{'definfoenclose'}->{$command}) {
- $current->{'type'} = 'definfoenclose_command';
- $current->{'extra'} = {
- 'begin' => $self->{'definfoenclose'}->{$command}->[0],
- 'end' => $self->{'definfoenclose'}->{$command}->[1] };
- }
- } elsif (exists ($no_brace_commands{$command})) {
- push @{$current->{'contents'}},
- { 'cmdname' => $command, 'parent' => $current };
- # FIXME generalize?
- if ($command eq '\\' and $self->{'context_stack'}->[-1] ne 'math') {
- $self->line_warn(sprintf($self->__("\@%s should only appear in math context"),
- $command), $line_nr);
- }
- if ($command eq "\n") {
- $current = _end_line($self, $current, $line_nr);
- last;
- }
- } else {
- $self->line_error(sprintf($self->__("unknown command `%s'"),
- $command), $line_nr);
- }
- } elsif ($line =~ s/^([{}@,:\t.\f])//) {
- my $separator = $1;
- print STDERR "SEPARATOR: $separator\n" if ($self->{'DEBUG'});
- if ($separator eq '@') {
- # this may happen with a @ at the very end of a file, therefore
- # not followed by anything.
- $self->line_error($self->__("unexpected \@"), $line_nr);
- } elsif ($separator eq '{') {
- _abort_empty_line($self, $current);
- if ($current->{'cmdname'}
- and (defined($brace_commands{$current->{'cmdname'}})
- or $self->{'definfoenclose'}->{$current->{'cmdname'}})) {
- my $command = $current->{'cmdname'};
- $current->{'args'} = [ { 'parent' => $current,
- 'contents' => [] } ];
- $current->{'remaining_args'} = $brace_commands{$command} -1
- if ($brace_commands{$command} and $brace_commands{$command} -1);
- $current->{'line_nr'} = $line_nr if ($brace_commands{$command});
- if ($self->{'definfoenclose'}->{$command}) {
- $current->{'remaining_args'} = 0;
- }
- $current = $current->{'args'}->[-1];
- if ($context_brace_commands{$command}) {
- if ($command eq 'caption' or $command eq 'shortcaption') {
- my $float;
- if (!$current->{'parent'}->{'parent'}
- or !$current->{'parent'}->{'parent'}->{'cmdname'}
- or $current->{'parent'}->{'parent'}->{'cmdname'} ne 'float') {
- $float = $current->{'parent'};
- while ($float->{'parent'} and !($float->{'cmdname'}
- and $float->{'cmdname'} eq 'float')) {
- $float = $float->{'parent'};
- }
- if (!($float->{'cmdname'} and $float->{'cmdname'} eq 'float')) {
- $self->line_error(sprintf($self->__(
- "\@%s is not meaningful outside `\@float' environment"),
- $command), $line_nr);
- $float = undef;
- } else {
- $self->line_warn(sprintf($self->__(
- "\@%s should be right below `\@float'"),
- $command), $line_nr);
- }
- } else {
- $float = $current->{'parent'}->{'parent'};
- }
- if ($float) {
- if ($float->{'extra'}->{$command}) {
- $self->line_warn(sprintf($self->__("ignoring multiple \@%s"),
- $command), $line_nr);
- } else {
- $current->{'parent'}->{'extra'}->{'float'} = $float;
- $float->{'extra'}->{$command} = $current->{'parent'};
- }
- }
- }
- push @{$self->{'context_stack'}}, $command;
- $line =~ s/([^\S\f\n]*)//;
- $current->{'type'} = 'brace_command_context';
- push @{$current->{'contents'}}, { 'type' => 'empty_spaces_before_argument',
- 'text' => $1,
- 'parent' => $current };
- $current->{'parent'}->{'extra'}->{'spaces_before_argument'}
- = $current->{'contents'}->[-1];
- } else {
- $current->{'type'} = 'brace_command_arg';
- if ($brace_commands{$command}
- and ($brace_commands{$command} > 1
- or $simple_text_commands{$command})) {
- push @{$current->{'contents'}},
- {'type' => 'empty_spaces_before_argument',
- 'text' => '' };
- $current->{'parent'}->{'extra'}->{'spaces_before_argument'}
- = $current->{'contents'}->[-1];
- }
- if ($inline_commands{$command}) {
- # this is changed when the first argument is known.
- push @{$self->{'expanded_formats_stack'}}, 0;
- push @{$self->{'context_stack'}}, $command
- if ($command eq 'inlineraw');
- }
- }
- print STDERR "OPENED \@$current->{'parent'}->{'cmdname'}, remaining: "
- .(defined($current->{'parent'}->{'remaining_args'}) ? "remaining: $current->{'parent'}->{'remaining_args'}, " : '')
- .($current->{'type'} ? "type: $current->{'type'}" : '')."\n"
- if ($self->{'DEBUG'});
- } elsif ($current->{'parent'}
- and (($current->{'parent'}->{'cmdname'}
- and $current->{'parent'}->{'cmdname'} eq 'multitable')
- or ($current->{'parent'}->{'type'}
- and $current->{'parent'}->{'type'} eq 'def_line'))) {
- push @{$current->{'contents'}},
- { 'type' => 'bracketed', 'contents' => [],
- 'parent' => $current };
- $current = $current->{'contents'}->[-1];
- # we need the line number here in case @ protects end of line
- $current->{'line_nr'} = $line_nr
- if ($current->{'parent'}->{'parent'}->{'type'}
- and $current->{'parent'}->{'parent'}->{'type'} eq 'def_line');
- push @{$current->{'contents'}},
- {'type' => 'empty_spaces_before_argument',
- 'text' => '' };
- print STDERR "BRACKETED in def/multitable\n" if ($self->{'DEBUG'});
- $current->{'extra'}->{'spaces_before_argument'}
- = $current->{'contents'}->[-1];
- # lone braces accepted right in a rawpreformatted
- } elsif ($current->{'type'}
- and $current->{'type'} eq 'rawpreformatted') {
- push @{$current->{'contents'}}, {'text' => '{' };
- # matching braces accepted in a rawpreformatted or math or ignored
- # code
- } elsif ($self->{'context_stack'}->[-1] eq 'math'
- or $self->{'context_stack'}->[-1] eq 'rawpreformatted'
- or $self->{'context_stack'}->[-1] eq 'inlineraw'
- or $self->_ignore_global_commands()) {
- push @{$current->{'contents'}},
- { 'type' => 'bracketed', 'contents' => [],
- 'parent' => $current, 'line_nr' => $line_nr };
- $current = $current->{'contents'}->[-1];
- print STDERR "BRACKETED in math\n" if ($self->{'DEBUG'});
- } else {
- $self->line_error(sprintf($self->__("misplaced %c"),
- ord('{')), $line_nr);
- }
- } elsif ($separator eq '}') {
- _abort_empty_line($self, $current);
- #print STDERR "GGGGG". _print_current ($current);
- if ($current->{'type'} and ($current->{'type'} eq 'bracketed')) {
- $current = $current->{'parent'};
- # the following will not happen for footnote if there is
- # a paragraph withing the footnote
- } elsif ($current->{'parent'}
- and $current->{'parent'}->{'cmdname'}
- and (exists $brace_commands{$current->{'parent'}->{'cmdname'}}
- or $self->{'definfoenclose'}->{$current->{'parent'}->{'cmdname'}})) {
- # for math and footnote out of paragraph
- if ($context_brace_commands{$current->{'parent'}->{'cmdname'}}) {
- my $context_command = pop @{$self->{'context_stack'}};
- if ($context_command ne $current->{'parent'}->{'cmdname'}) {
- $self->_bug_message("context $context_command instead of brace command $current->{'parent'}->{'cmdname'}",
- $line_nr, $current);
- die;
- }
- }
- # first is the arg.
-
- if ($brace_commands{$current->{'parent'}->{'cmdname'}}
- and ($brace_commands{$current->{'parent'}->{'cmdname'}} > 1
- or $simple_text_commands{$current->{'parent'}->{'cmdname'}})
- and $current->{'parent'}->{'cmdname'} ne 'math') {
- # @inline* always have end spaces considered as normal text
- $self->_isolate_last_space($current)
- unless ($inline_commands{$current->{'parent'}->{'cmdname'}});
- $self->_register_command_arg($current, 'brace_command_contents');
- # Remove empty arguments, as far as possible
- _remove_empty_content_arguments($current);
- }
- my $closed_command = $current->{'parent'}->{'cmdname'};
- print STDERR "CLOSING(brace) \@$current->{'parent'}->{'cmdname'}\n"
- if ($self->{'DEBUG'});
- delete $current->{'parent'}->{'remaining_args'};
- if (defined($brace_commands{$closed_command})
- and $brace_commands{$closed_command} == 0
- and @{$current->{'contents'}}) {
- $self->line_warn(sprintf($self->__(
- "command \@%s does not accept arguments"),
- $closed_command), $line_nr);
- }
- if ($current->{'parent'}->{'cmdname'} eq 'anchor') {
- $current->{'parent'}->{'line_nr'} = $line_nr;
- my $parsed_anchor = _parse_node_manual($current);
- if (_check_node_label($self, $parsed_anchor,
- $current->{'parent'}->{'cmdname'}, $line_nr)) {
- _register_label($self, $current->{'parent'},
- $parsed_anchor, $line_nr);
- if (@{$self->{'regions_stack'}}) {
- $current->{'extra'}->{'region'} = $self->{'regions_stack'}->[-1];
- }
- }
- } elsif ($ref_commands{$current->{'parent'}->{'cmdname'}}) {
- my $ref = $current->{'parent'};
- if (@{$ref->{'args'}}) {
- my @args = @{$ref->{'extra'}->{'brace_command_contents'}};
- if (($closed_command eq 'inforef'
- and !defined($args[0]) and !defined($args[2]))
- or ($closed_command ne 'inforef'
- and !defined($args[0]) and !defined($args[3])
- and !defined($args[4]))) {
- $self->line_warn(sprintf($self->__(
- "command \@%s missing a node or external manual argument"),
- $closed_command), $line_nr);
- } else {
- my $parsed_ref_node = _parse_node_manual($ref->{'args'}->[0]);
- $ref->{'extra'}->{'node_argument'} = $parsed_ref_node
- if (defined($parsed_ref_node));
- if ($closed_command ne 'inforef'
- and !defined($args[3]) and !defined($args[4])
- and !$parsed_ref_node->{'manual_content'}
- and ! _ignore_global_commands($self)) {
- push @{$self->{'internal_references'}}, $ref;
- }
- }
- if (defined($args[1])) {
- my $normalized_cross_ref_name =
- Texinfo::Convert::NodeNameNormalization::normalize_node(
- {'contents' => $args[1]});
- if ($normalized_cross_ref_name !~ /[^-]/) {
- $self->line_warn(sprintf($self->__(
- "in \@%s empty cross reference name after expansion `%s'"),
- $closed_command,
- Texinfo::Convert::Texinfo::convert({'contents' => $args[1]})),
- $line_nr);
- }
- }
- if ($closed_command ne 'inforef' and defined($args[2])) {
- my $normalized_cross_ref_title =
- Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => $args[2]});
- if ($normalized_cross_ref_title !~ /[^-]/) {
- $self->line_warn(sprintf($self->__(
- "in \@%s empty cross reference title after expansion `%s'"),
- $closed_command,
- Texinfo::Convert::Texinfo::convert({'contents' => $args[2]})),
- $line_nr);
- }
- }
- }
- } elsif ($current->{'parent'}->{'cmdname'} eq 'image') {
- my $image = $current->{'parent'};
- if (!@{$image->{'args'}}
- or !@{$image->{'extra'}->{'brace_command_contents'}}
- or !defined($image->{'extra'}->{'brace_command_contents'}->[0])) {
- $self->line_error(
- $self->__("\@image missing filename argument"), $line_nr);
- }
- } elsif($current->{'parent'}->{'cmdname'} eq 'dotless') {
- my $dotless = $current->{'parent'};
- if (@{$current->{'contents'}}) {
- my $text = $current->{'contents'}->[0]->{'text'};
- if (!defined ($text)
- or ($text ne 'i' and $text ne 'j')) {
- $self->line_error(sprintf($self->
- __("%c%s expects `i' or `j' as argument, not `%s'"),
- ord('@'), $dotless->{'cmdname'},
- Texinfo::Convert::Texinfo::convert($current)), $line_nr);
- }
- }
- } elsif ($explained_commands{$current->{'parent'}->{'cmdname'}}
- or $inline_commands{$current->{'parent'}->{'cmdname'}}) {
- my $current_command = $current->{'parent'};
- if ($inline_commands{$current_command->{'cmdname'}}) {
- if ($current_command->{'cmdname'} eq 'inlineraw') {
- my $context_command = pop @{$self->{'context_stack'}};
- if ($context_command ne $current_command->{'cmdname'}) {
- $self->_bug_message("context $context_command instead of inlineraw $current_command->{'cmdname'}",
- $line_nr, $current);
- die;
- }
- }
- pop @{$self->{'expanded_formats_stack'}};
- }
- if (!@{$current_command->{'args'}}
- or !@{$current_command->{'extra'}->{'brace_command_contents'}}
- or !defined($current_command->{'extra'}->{'brace_command_contents'}->[0])) {
- $self->line_warn(
- sprintf($self->__("\@%s missing first argument"),
- $current_command->{'cmdname'}), $line_nr);
- } else {
- if ($explained_commands{$current_command->{'cmdname'}}) {
- my $normalized_type
- = Texinfo::Convert::NodeNameNormalization::normalize_node(
- {'contents' =>
- $current_command->{'extra'}->{'brace_command_contents'}->[0]});
- $current_command->{'extra'}->{'normalized'} = $normalized_type;
- if (!$current_command->{'extra'}->{'brace_command_contents'}->[1]) {
- if ($self->{'explained_commands'}->{$current_command->{'cmdname'}}->{$normalized_type}) {
- $current_command->{'extra'}->{'explanation_contents'}
- = $self->{'explained_commands'}->{$current_command->{'cmdname'}}->{$normalized_type};
- }
- } elsif (! _ignore_global_commands($self)) {
- $self->{'explained_commands'}->{$current_command->{'cmdname'}}->{$normalized_type}
- = $current_command->{'extra'}->{'brace_command_contents'}->[1];
- }
- }# else {
- # my $argument
- # = Texinfo::Convert::Text::convert({'contents' =>
- # $current_command->{'extra'}->{'brace_command_contents'}->[0]},
- # {Texinfo::Common::_convert_text_options($self)});
- # $current_command->{'extra'}->{'format'} = $argument;
- #}
- }
- } elsif ($current->{'parent'}->{'cmdname'} eq 'errormsg') {
- if (! _ignore_global_commands($self)) {
- my $error_message_text
- = Texinfo::Convert::Text::convert($current,
- {Texinfo::Common::_convert_text_options($self)});
- $self->line_error($error_message_text, $line_nr);
- }
- } elsif (_command_with_command_as_argument($current->{'parent'}->{'parent'})
- and scalar(@{$current->{'contents'}}) == 0) {
- print STDERR "FOR PARENT \@$current->{'parent'}->{'parent'}->{'parent'}->{'cmdname'} command_as_argument braces $current->{'cmdname'}\n" if ($self->{'DEBUG'});
- $current->{'parent'}->{'type'} = 'command_as_argument'
- if (!$current->{'parent'}->{'type'});
- $current->{'parent'}->{'parent'}->{'parent'}->{'extra'}->{'command_as_argument'}
- = $current->{'parent'};
- }
- $self->_register_global_command($current->{'parent'}->{'cmdname'},
- $current->{'parent'}, $line_nr);
- if ($command_ignore_space_after{$current->{'parent'}->{'cmdname'}}) {
- push @{$current->{'parent'}->{'parent'}->{'contents'}},
- {'type' => 'empty_spaces_after_close_brace',
- 'text' => '' };
- }
- $current = $current->{'parent'}->{'parent'};
- $current = $self->_begin_preformatted ($current)
- if ($close_preformatted_commands{$closed_command});
- # lone braces accepted right in a rawpreformatted
- } elsif ($current->{'type'}
- and $current->{'type'} eq 'rawpreformatted') {
- push @{$current->{'contents'}}, {'text' => '}' };
- # footnote caption closing, when there is a paragraph inside.
- } elsif ($context_brace_commands{$self->{'context_stack'}->[-1]}) {
- # closing the context under broader situations
- $current = _end_paragraph($self, $current, $line_nr);
- if ($current->{'parent'}
- and $current->{'parent'}->{'cmdname'}
- and $context_brace_commands{$current->{'parent'}->{'cmdname'}}
- and $current->{'parent'}->{'cmdname'} eq $self->{'context_stack'}->[-1]) {
- my $context_command = pop @{$self->{'context_stack'}};
- if ($context_command ne $current->{'parent'}->{'cmdname'}) {
- $self->_bug_message("context $context_command instead of brace isolated $current->{'parent'}->{'cmdname'}",
- $line_nr, $current);
- die;
- }
- print STDERR "CLOSING(context command) \@$current->{'parent'}->{'cmdname'}\n" if ($self->{'DEBUG'});
- my $closed_command = $current->{'parent'}->{'cmdname'};
- $self->_register_global_command($current->{'parent'}->{'cmdname'},
- $current->{'parent'}, $line_nr);
- $current = $current->{'parent'}->{'parent'};
- $current = $self->_begin_preformatted ($current)
- if ($close_preformatted_commands{$closed_command});
- }
- } else {
- $self->line_error(sprintf($self->__("misplaced %c"),
- ord('}')), $line_nr);
- }
- } elsif ($separator eq ','
- and $current->{'parent'}->{'remaining_args'}) {
- _abort_empty_line ($self, $current);
- if ($brace_commands{$current->{'parent'}->{'cmdname'}}
- and ($brace_commands{$current->{'parent'}->{'cmdname'}} > 1
- or $simple_text_commands{$current->{'parent'}->{'cmdname'}})) {
- $self->_isolate_last_space($current);
- $self->_register_command_arg($current, 'brace_command_contents');
- } else {
- $self->_isolate_last_space($current);
- if (exists $block_commands{$current->{'parent'}->{'cmdname'}}) {
- $self->_register_command_arg($current, 'block_command_line_contents');
- }
- }
- my $type = $current->{'type'};
- $current = $current->{'parent'};
- if ($inline_commands{$current->{'cmdname'}}) {
- if (! $current->{'extra'}->{'format'}) {
- # change the top of the raw_formats_stack now that we know the
- # first arg of the inlineraw
- my $inline_type
- = Texinfo::Convert::Text::convert({'contents' =>
- $current->{'extra'}->{'brace_command_contents'}->[0]},
- {Texinfo::Common::_convert_text_options($self)});
- if ($self->{'expanded_formats_stack'}->[-2]) {
- if ($inline_format_commands{$current->{'cmdname'}}) {
- if ($self->{'expanded_formats_hash'}->{$inline_type}) {
- $self->{'expanded_formats_stack'}->[-1] = $inline_type;
- $current->{'extra'}->{'expand_index'} = 1;
- } else {
- $self->{'expanded_formats_stack'}->[-1] = 0;
- }
- } elsif (($current->{'cmdname'} eq 'inlineifset'
- and exists($self->{'values'}->{$inline_type}))
- or ($current->{'cmdname'} eq 'inlineifclear'
- and ! exists($self->{'values'}->{$inline_type}))) {
- $self->{'expanded_formats_stack'}->[-1]
- = "$current->{'cmdname'} $inline_type";
- $current->{'extra'}->{'expand_index'} = 1;
- } else {
- $self->{'expanded_formats_stack'}->[-1] = 0;
- }
- } else {
- $self->{'expanded_formats_stack'}->[-1] = 0;
- }
- $current->{'extra'}->{'format'} = $inline_type;
- } else {
- # happens for the second arg of inlinefmtifelse
- my $inline_type = $current->{'extra'}->{'format'};
- if ($self->{'expanded_formats_stack'}->[-2]
- and ! ($self->{'expanded_formats_hash'}->{$inline_type})) {
- $self->{'expanded_formats_stack'}->[-1] = $inline_type;
- $current->{'extra'}->{'expand_index'} = 2;
- } else {
- $self->{'expanded_formats_stack'}->[-1] = 0;
- }
- }
- }
- $current->{'remaining_args'}--;
- push @{$current->{'args'}},
- { 'type' => $type, 'parent' => $current, 'contents' => [] };
- #if ($inline_commands{$current->{'cmdname'}}
- # and ! $self->{'expanded_formats_stack'}->[-1]) {
- # $current->{'args'}->[-1]->{'extra'}->{'ignore'} = 1;
- #}
- $current = $current->{'args'}->[-1];
- push @{$current->{'contents'}},
- {'type' => 'empty_spaces_before_argument',
- 'text' => '' };
- } elsif ($separator eq ',' and $current->{'type'}
- and $current->{'type'} eq 'misc_line_arg'
- and $current->{'parent'}->{'cmdname'}
- and $current->{'parent'}->{'cmdname'} eq 'node') {
- $self->line_warn($self->__("superfluous arguments for node"), $line_nr);
- # end of menu node (. must be followed by a space to stop the node).
- } elsif (($separator =~ /[,\t.]/ and $current->{'type'}
- and $current->{'type'} eq 'menu_entry_node')
- or ($separator eq ':' and $current->{'type'}
- and $current->{'type'} eq 'menu_entry_name')) {
- $current = $current->{'parent'};
- push @{$current->{'args'}}, { 'type' => 'menu_entry_separator',
- 'text' => $separator,
- 'parent' => $current };
- } elsif ($separator eq "\f" and $current->{'type'}
- and $current->{'type'} eq 'paragraph') {
- # form feed stops and restart a paragraph.
- $current = $self->_end_paragraph($current);
- push @{$current->{'contents'}}, {'text' => $separator,
- 'type' => 'empty_line',
- 'parent' => $current };
- push @{$current->{'contents'}}, { 'type' => 'empty_line',
- 'text' => '',
- 'parent' => $current };
-
- } else {
- $current = _merge_text($self, $current, $separator);
- }
- # Misc text except end of line
- } elsif ($line =~ s/^([^{}@,:\t.\n\f]+)//) {
- my $new_text = $1;
- $current = _merge_text($self, $current, $new_text);
- # end of line
- } else {
- if ($self->{'DEBUG'}) {
- print STDERR "END LINE: ". _print_current($current)."\n";
- }
- if ($line =~ s/^(\n)//) {
- $current = _merge_text($self, $current, $1);
- } else {
- if (scalar(@{$self->{'input'}})) {
- $self->_bug_message("Text remaining without normal text but `$line'",
- $line_nr, $current);
- die;
- }
- }
- #print STDERR "END LINE AFTER MERGE END OF LINE: ". _print_current($current)."\n";
- $current = _end_line($self, $current, $line_nr);
- last;
- }
- }
- }
- while (@{$self->{'conditionals_stack'}}) {
- my $end_conditional = pop @{$self->{'conditionals_stack'}};
- $self->line_error(sprintf($self->__("expected \@end %s"), $end_conditional),
- $line_nr);
- }
- $current = _close_commands($self, $current, $line_nr);
- if (@{$self->{'context_stack'}} != 1) {
- # This happens in 2 cases in the tests:
- # @verb not closed on misc commands line
- # def line escaped with @ ending the file
- if ($self->{'DEBUG'}) {
- print STDERR "CONTEXT_STACK no empty end _parse_texi: ".join('|', @{$self->{'context_stack'}})."\n";
- }
- @{$self->{'context_stack'}} = ($self->{'context'});
- }
- if (@{$self->{'expanded_formats_stack'}} != 1) {
- if ($self->{'DEBUG'}) {
- print STDERR "EXPANDED_FORMATS_STACK no empty end _parse_texi: ".join('|', @{$self->{'expanded_formats_stack'}})."\n";
- }
- @{$self->{'expanded_formats_stack'}} = ($self->{'expanded_formats_stack'}->[0]);
- }
- return $root;
- }
- # parse special line @-commands, unmacro, set, clear, clickstyle.
- # Also remove spaces or ignore text, as specified in the misc_commands hash.
- sub _parse_special_misc_command($$$$)
- {
- my $self = shift;
- my $line = shift;
- my $command = shift;
- my $line_nr = shift;
- my $args = [];
- my $has_comment = 0;
- my $remaining;
- if ($command eq 'set') {
- # REVALUE
- #if ($line =~ s/^\s+([\w\-]+)(\s+(.*?))\s*$//) {
- if ($line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)(\@(c|comment)((\@|\s+).*)?|[^\S\f]+(.*?))?[^\S\f]*$/) {
- if ($line =~ s/\@(c|comment)((\@|\s+).*)?$//) {
- $has_comment = 1;
- }
- $line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)([^\S\f]+(.*?))?[^\S\f]*$/;
- my $name = $1;
- my $arg = $3;
- $arg = '' if (!defined($arg));
- $args = [$name, $arg];
- $self->{'values'}->{$name} = $arg
- unless(_ignore_global_commands($self));
- } elsif ($line !~ /\S/) {
- $self->line_error(sprintf($self->
- __("%c%s requires a name"), ord('@'), $command), $line_nr);
- } else {
- $self->line_error(sprintf($self->
- __("bad name for \@%s"), $command), $line_nr);
- }
- } elsif ($command eq 'clear') {
- # REVALUE
- if ($line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) {
- $args = [$1];
- delete $self->{'values'}->{$1}
- unless(_ignore_global_commands($self));
- $has_comment = 1 if (defined($3));
- #$remaining = $line;
- #$remaining =~ s/^\s+([\w\-]+)\s*(\@(c|comment)((\@|\s+).*)?)?//;
- } elsif ($line !~ /\S/) {
- $self->line_error(sprintf($self->
- __("%c%s requires a name"), ord('@'), $command), $line_nr);
- } else {
- $self->line_error(sprintf($self->
- __("bad name for \@%s"), $command), $line_nr);
- }
- } elsif ($command eq 'unmacro') {
- # REMACRO
- if ($line =~ /^\s+([[:alnum:]][[:alnum:]\-]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) {
- $args = [$1];
- delete $self->{'macros'}->{$1}
- unless(_ignore_global_commands($self));
- $has_comment = 1 if (defined($3));
- print STDERR "UNMACRO $1\n" if ($self->{'DEBUG'});
- } elsif ($line !~ /\S/) {
- $self->line_error(sprintf($self->
- __("%c%s requires a name"), ord('@'), $command), $line_nr);
- } else {
- $self->line_error(sprintf($self->
- __("bad name for \@%s"), $command), $line_nr);
- }
- } elsif ($command eq 'clickstyle') {
- # REMACRO
- if ($line =~ /^\s+@([[:alnum:]][[:alnum:]\-]*)({})?\s*/) {
- $args = ['@'.$1];
- $self->{'clickstyle'} = $1
- unless(_ignore_global_commands($self));
- $remaining = $line;
- $remaining =~ s/^\s+@([[:alnum:]][[:alnum:]\-]*)({})?\s*(\@(c|comment)((\@|\s+).*)?)?//;
- $has_comment = 1 if (defined($4));
- } else {
- $self->line_error (sprintf($self->__(
- "\@%s should only accept a \@-command as argument, not `%s'"),
- $command, $line), $line_nr);
- }
- } else {
- die $self->_bug_message("Unknown special command $command", $line_nr);
- }
- if (defined($remaining)) {
- chomp($remaining);
- if ($remaining ne '') {
- $self->line_warn(sprintf($self->__(
- "remaining argument on \@%s line: %s"),
- $command, $remaining), $line_nr);
- }
- }
- return ($args, $has_comment);
- }
- sub _trim_spaces_comment_from_content($)
- {
- Texinfo::Common::trim_spaces_comment_from_content($_[0]);
- }
- # at the end of a @-command line with arguments, parse the resulting
- # text, to collect aliases, definfoenclose and collect errors on
- # wrong arguments.
- sub _parse_line_command_args($$$)
- {
- my $self = shift;
- my $line_command = shift;
- my $line_nr = shift;
- my $args;
- my $command = $line_command->{'cmdname'};
- my $arg = $line_command->{'args'}->[0];
- if ($self->{'DEBUG'}) {
- print STDERR "MISC ARGS \@$command\n";
- if (@{$arg->{'contents'}}) {
- my $idx = 0;
- foreach my $content (@{$arg->{'contents'}}) {
- my $name = '';
- $name = '@' . $content->{'cmdname'} if ($content->{'cmdname'});
- my $type = ', t: ';
- $type .= $content->{'type'} if ($content->{'type'});
- my $text = ', ';
- $type .= $content->{'text'} if ($content->{'text'});
- print STDERR " -> $idx $name $type $text\n";
- $idx++;
- }
- }
- }
- my @contents = @{$arg->{'contents'}};
- _trim_spaces_comment_from_content(\@contents);
- if (! @contents) {
- $self->_command_error($line_command, $line_nr,
- $self->__("\@%s missing argument"), $command);
- $line_command->{'extra'}->{'missing_argument'} = 1;
- return undef;
- }
- if (@contents > 1
- or (!defined($contents[0]->{'text'}))) {
- $self->line_error (sprintf($self->__("superfluous argument to \@%s"),
- $command), $line_nr);
- }
- return undef if (!defined($contents[0]->{'text'}));
-
- my $line = $contents[0]->{'text'};
- if ($command eq 'alias') {
- # REMACRO
- if ($line =~ s/^([[:alnum:]][[:alnum:]-]*)(\s*=\s*)([[:alnum:]][[:alnum:]-]*)$//) {
- my $new_command = $1;
- my $existing_command = $3;
- $args = [$1, $3];
- $self->{'aliases'}->{$new_command} = $existing_command
- unless (_ignore_global_commands($self));
- if (exists($block_commands{$existing_command})) {
- $self->line_warn(sprintf($self->
- __("environment command %s as argument to \@%s"),
- $existing_command, $command), $line_nr);
- }
- } else {
- $self->line_error(sprintf($self->
- __("bad argument to \@%s"), $command), $line_nr);
- }
- } elsif ($command eq 'definfoenclose') {
- # REMACRO
- if ($line =~ s/^([[:alnum:]][[:alnum:]\-]*)\s*,\s*([^\s,]*)\s*,\s*([^\s,]*)$//) {
- $args = [$1, $2, $3 ];
- $self->{'definfoenclose'}->{$1} = [ $2, $3 ]
- unless (_ignore_global_commands($self));
- print STDERR "DEFINFOENCLOSE \@$1: $2, $3\n" if ($self->{'DEBUG'});
- } else {
- $self->line_error(sprintf($self->
- __("bad argument to \@%s"), $command), $line_nr);
- }
- } elsif ($command eq 'columnfractions') {
- my @possible_fractions = split (/\s+/, $line);
- if (!@possible_fractions) {
- $self->line_error (sprintf($self->__("empty \@%s"), $command),
- $line_nr);
- } else {
- foreach my $fraction (@possible_fractions) {
- if ($fraction =~ /^(\d*\.\d+)|(\d+)\.?$/) {
- push @$args, $fraction;
- } else {
- $self->line_error (sprintf($self->
- __("column fraction not a number: %s"),
- $fraction), $line_nr);
- }
- }
- }
- } elsif ($command eq 'sp') {
- if ($line =~ /^([0-9]+)$/) {
- $args = [$1];
- } else {
- $self->line_error(sprintf($self->__("\@sp arg must be numeric, not `%s'"),
- $line), $line_nr);
- }
- } elsif ($command eq 'defindex' || $command eq 'defcodeindex') {
- # REMACRO
- if ($line =~ /^([[:alnum:]][[:alnum:]\-]*)$/) {
- my $name = $1;
- if ($forbidden_index_name{$name}) {
- $self->line_error(sprintf($self->
- __("reserved index name %s"),$name), $line_nr);
- } else {
- my $in_code = 0;
- $in_code = 1 if ($command eq 'defcodeindex');
- $args = [$name];
- if (! _ignore_global_commands($self)) {
- $self->{'index_names'}->{$name} = {'in_code' => $in_code};
- $self->_register_index_commands($name);
- }
- }
- } else {
- $self->line_error(sprintf($self->
- __("bad argument to \@%s: %s"), $command, $line), $line_nr);
- }
- } elsif ($command eq 'synindex' || $command eq 'syncodeindex') {
- # REMACRO
- if ($line =~ /^([[:alnum:]][[:alnum:]\-]*)\s+([[:alnum:]][[:alnum:]\-]*)$/) {
- my $index_from = $1;
- my $index_to = $2;
- $self->line_error(sprintf($self->__("unknown source index in \@%s: %s"),
- $command, $index_from), $line_nr)
- unless $self->{'index_names'}->{$index_from};
- $self->line_error(sprintf($self->__("unknown destination index in \@%s: %s"),
- $command, $index_to), $line_nr)
- unless $self->{'index_names'}->{$index_to};
- if ($self->{'index_names'}->{$index_from}
- and $self->{'index_names'}->{$index_to}) {
- my $current_to = $index_to;
- # find the merged indices recursively avoiding loops
- while ($current_to ne $index_from
- and $self->{'merged_indices'}->{$current_to}) {
- $current_to = $self->{'merged_indices'}->{$current_to};
- }
- if ($current_to ne $index_from) {
- my $index_from_info = $self->{'index_names'}->{$index_from};
- my $index_to_info = $self->{'index_names'}->{$current_to};
- my $in_code = 0;
- $in_code = 1 if ($command eq 'syncodeindex');
- if (! _ignore_global_commands($self)) {
- $self->{'merged_indices'}->{$index_from} = $current_to;
- $index_from_info->{'in_code'} = $in_code;
- foreach my $contained_index (keys %{$index_from_info->{'contained_indices'}}) {
- $index_to_info->{'contained_indices'}->{$contained_index} = 1;
- $self->{'index_names'}->{$contained_index}->{'merged_in'} = $current_to;
- }
- $index_from_info->{'merged_in'} = $current_to;
- $index_to_info->{'contained_indices'}->{$index_from} = 1;
- }
- $args = [$index_from, $index_to];
- } else {
- $self->line_warn(sprintf($self->__(
- "\@%s leads to a merging of %s in itself, ignoring"),
- $command, $index_from), $line_nr);
- }
- }
- } else {
- $self->line_error(sprintf($self->__("bad argument to \@%s: %s"),
- $command, $line), $line_nr);
- }
- } elsif ($command eq 'printindex') {
- # REMACRO
- if ($line =~ /^([[:alnum:]][[:alnum:]\-]*)$/) {
- my $name = $1;
- if (!exists($self->{'index_names'}->{$name})) {
- $self->line_error(sprintf($self->__("unknown index `%s' in \@printindex"),
- $name), $line_nr);
-
- } else {
- if ($self->{'merged_indices'}->{$name}) {
- $self->line_warn(sprintf($self->__(
- "printing an index `%s' merged in another one `%s'"),
- $name, $self->{'merged_indices'}->{$name}),
- $line_nr);
- }
- if (!defined($self->{'current_node'})
- and !defined($self->{'current_section'})
- and !scalar(@{$self->{'regions_stack'}})) {
- $self->line_warn(sprintf($self->__(
- "printindex before document beginning: \@printindex %s"),
- $name), $line_nr);
- }
- $args = [$name];
- }
- } else {
- $self->line_error(sprintf($self->
- __("bad argument to \@%s: %s"), $command, $line), $line_nr);
- }
- } elsif (grep {$_ eq $command} ('everyheadingmarks', 'everyfootingmarks',
- 'evenheadingmarks', 'oddheadingmarks',
- 'evenfootingmarks', 'oddfootingmarks')) {
- if ($line eq 'top' or $line eq 'bottom') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__(
- "\@%s arg must be `top' or `bottom', not `%s'"),
- $command, $line), $line_nr);
- }
- } elsif ($command eq 'fonttextsize') {
- if ($line eq '10' or $line eq '11') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__(
- "Only \@%s 10 or 11 is supported, not `%s'"),
- $command, $line), $line_nr);
- }
- } elsif ($command eq 'footnotestyle') {
- if ($line eq 'separate' or $line eq 'end') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__(
- "\@%s arg must be `separate' or `end', not `%s'"),
- $command, $line), $line_nr);
- }
- } elsif ($command eq 'setchapternewpage') {
- if ($line eq 'on' or $line eq 'off' or $line eq 'odd') {
- $args = [$1];
- } else {
- $self->line_error(sprintf($self->__(
- "\@%s arg must be `on', `off' or `odd', not `%s'"),
- $command, $line), $line_nr);
- }
- } elsif ($command eq 'need') { # only a warning
- if (($line =~ /^([0-9]+(\.[0-9]*)?)$/) or
- ($line =~ /^(\.[0-9]+)$/)) {
- $args = [$1];
- } else {
- $self->line_error(sprintf($self->__("bad argument to \@%s: %s"),
- $command, $line), $line_nr);
- }
- } elsif ($command eq 'paragraphindent') {
- if ($line =~ /^([\w\-]+)$/) {
- my $value = $1;
- if ($value =~ /^([0-9]+)$/ or $value eq 'none' or $value eq 'asis') {
- $args = [$1];
- } else {
- $self->line_error(sprintf($self->__(
- "\@paragraphindent arg must be numeric/`none'/`asis', not `%s'"),
- $value), $line_nr);
- }
- } else {
- $self->line_error(sprintf($self->__(
- "\@paragraphindent arg must be numeric/`none'/`asis', not `%s'"),
- $line), $line_nr);
- }
- } elsif ($command eq 'firstparagraphindent') {
- if ($line eq 'none' or $line eq 'insert') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__(
- "\@firstparagraphindent arg must be `none' or `insert', not `%s'"),
- $line), $line_nr);
- }
- } elsif ($command eq 'exampleindent') {
- if ($line =~ /^([0-9]+)/) {
- $args = [$1];
- } elsif ($line =~ /^(asis)$/) {
- $args = [$1];
- } else {
- $self->line_error(sprintf($self->__(
- "\@exampleindent arg must be numeric/`asis', not `%s'"),
- $line), $line_nr);
- }
- } elsif ($command eq 'frenchspacing'
- or $command eq 'xrefautomaticsectiontitle'
- or $command eq 'codequoteundirected'
- or $command eq 'codequotebacktick'
- or $command eq 'deftypefnnewline') {
- if ($line eq 'on' or $line eq 'off') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__("expected \@%s on or off, not `%s'"),
- $command, $line), $line_nr);
- }
- } elsif ($command eq 'kbdinputstyle') {
- if ($line eq 'code' or $line eq 'example' or $line eq 'distinct') {
- $self->{'kbdinputstyle'} = $line
- unless (_ignore_global_commands($self));
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__(
- "\@kbdinputstyle arg must be `code'/`example'/`distinct', not `%s'"),
- $line), $line_nr);
- }
- } elsif ($command eq 'allowcodebreaks') {
- if ($line eq 'true' or $line eq 'false') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__(
- "\@allowcodebreaks arg must be `true' or `false', not `%s'"),
- $line), $line_nr);
- }
- } elsif ($command eq 'urefbreakstyle') {
- if ($line eq 'after' or $line eq 'before' or $line eq 'none') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__(
- "\@urefbreakstyle arg must be `after'/`before'/`none', not `%s'"),
- $line), $line_nr);
- }
- } elsif ($command eq 'headings') {
- if ($line eq 'off' or $line eq 'on' or $line eq 'single'
- or $line eq 'double' or $line eq 'singleafter' or $line eq 'doubleafter') {
- $args = [$line];
- } else {
- $self->line_error(sprintf($self->__("bad argument to \@%s: %s"),
- $command, $line), $line_nr);
- }
- }
- return $args;
- }
- 1;
- __END__
- =head1 NAME
- Texinfo::Parser - Parse Texinfo code in a Perl tree
- =head1 SYNOPSIS
- use Texinfo::Parser;
- my $parser = Texinfo::Parser::parser();
- my $tree = $parser->parse_texi_file("somefile.texi");
- my ($errors, $errors_count) = $parser->errors();
- foreach my $error_message (@$errors) {
- warn $error_message->{'error_line'};
- }
- my ($index_names, $merged_indices_hash)
- = $parser->indices_information();
- my $float_types_arrays = $parser->floats_information();
- my $internal_references_array
- = $parser->internal_references_information();
- # An hash reference on normalized node/float/anchor names
- my $labels_information = $parser->labels_information();
- # A hash reference, keys are @-command names, value is an
- # array reference holding all the corresponding @-commands.
- my $global_commands_information = $parser->global_commands_information();
- # a hash reference on some document informations (encodings,
- # input file name, dircategory and direntry list, for exampel).
- my $global_informations = $parser->global_informations();
- =head1 DESCRIPTION
- Texinfo::Parser will parse Texinfo text into a perl tree. In one pass
- it expands user defined @-commands, conditionals (@ifset, @ifinfo...)
- and @value and constructs the tree. Some extra information is gathered
- while doing the tree, for example the block command associated with @end,
- the number of row in a multitable, the node associated with a section.
- =head1 METHODS
- No method is exported in the default case. The module allows both
- an object oriented syntax, or traditional function, with the parser
- as an opaque data structure given in argument to every function.
- =head2 Initialization
- The following method is used to construct a new C<Texinfo::Parser> object:
- =over
- =item $parser = Texinfo::Parser::parser($options);
- This method creates a new parser. The options may be provided as a hash
- reference. There are two types of options. The first type of options
- change the way the parser behave, they are described right here. The
- other type of options allow to give to the parser some information as if
- it came from texinfo code, for example allow to set aliases (as with
- C<@alias>), values (as with C<@set>), merged indices (as with
- C<@synindex>). These options are described below in L</Texinfo Parser options>.
- =over
- =item expanded_formats
- An array reference of the output formats for which C<@ifI<FORMAT>>
- conditional blocks should be expanded. Default is empty.
- The raw block formats (within C<@html> blocks, for example) are
- always kept.
- =item gettext
- If set, the function reference is used to translate error and warning
- messages. It takes a string as argument and returns a string. The default
- function returns the error message as is.
- =item GLOBAL_COMMANDS
- The associated value is a reference on an array. All the commands in the
- array are collected during parsing. They are afterwards available
- through L<global_informations|/$info = global_informations($parser)>.
- =item include_directories
- An array reference of directories in which C<@include> files should be
- searched for. Default contains the working directory, F<.>.
- =item INLINE_INSERTCOPYING
- If set, C<@insertcopying> is replaced by the C<@copying> content as if
- C<@insertcopying> was a user-defined macro. In the default case, it is
- considered to be a simple @-command and kept as is in the tree.
- =item IGNORE_BEFORE_SETFILENAME
- If set, and C<@setfilename> exists, everything before C<@setfilename>
- is put in a special container type, @C<preamble_before_setfilename>.
- This option is set in the default case.
- =item IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME
- If set, spaces after an @-commande name that take braces are ignored.
- Default on.
- =item MACRO_BODY_IGNORES_LEADING_SPACE
- If set, leading spaces are stripped from user defined macro bodies.
- =item MAX_MACRO_CALL_NESTING
- Maximal number of nested user-defined macro calls. Default is 100000.
- =item SHOW_MENU
- If false, no menu related error are reported. Default is true.
- =begin :comment
- Used by Sectioning only
- =item TOP_NODE_UP
- Text for the up node of the Top node. The default is C<(dir)>. The
- string may contain @-commands.
- =end :comment
- =back
- =back
- =head2 Parsing Texinfo text
- There are three methods that may be called to parse some Texinfo code,
- C<parse_texi_line> for a line, C<parse_texi_text> for a text fragment,
- and C<parse_texi_file> for a file.
- For all those functions, if the I<$parser> argument is undef, a new
- parser object is generated to parse the line, otherwise the parser given
- in argument is used to parse into a tree.
- When C<parse_texi_text> is used, the resulting tree is rooted at
- a C<root_line> type container. Otherwise, the resulting tree should be
- rooted at a C<text_root> type container if it do not contain nodes or
- sections, at a C<document_root> type container otherwise.
- =over
- =item $tree = parse_texi_line($parser, $text, $first_line_number, $file_name, $macro_name, $fixed_line_number)
- This function is used to parse a short fragment of Texinfo code.
- I<$text> may be either an array reference of lines, or a text.
- The other arguments are optional and allow to specify the position
- information of the Texinfo code. I<$first_line_number> is the line number
- of the first text line. I<$file_name> is the name of the file the
- text comes from. I<$macro> is for the user-defined macro name the text
- is expanded from. If I<$fixed_line_number> is set, the line number is
- not increased for the different lines, as if the text was the expansion
- of a macro.
- =item $tree = parse_texi_text ($parser, $text, $line_numbers_specification, $file_name, $macro_name, $fixed_line_number)
- This function is used to parse some Texinfo text.
- I<$text> may be either an array reference of lines, or a text.
- The other arguments are optional and allow to specify the position
- information of the Texinfo code. There are two distinct cases for
- I<$line_numbers_specification>.
- =over
- =item 1.
- If it is an array reference, it is considered to hold objects describing
- the position information of each text lines.
- =item 2.
- If I<$line_numbers_specification> is a scalar, it is the line number of
- the first text line. In that case (like for C<parse_texi_text>),
- I<$file_name> is the name of the file the text comes from.
- I<$macro> is for the user-defined macro name the text
- is expanded from. If I<$fixed_line_number> is set, the line number is
- not increased for the different lines, as if the text was the expansion
- of a macro.
- =back
- =item $tree = parse_texi_file($parser, $file_name)
- The file with name I<$file_name> is considered to be a Texinfo file and
- is parsed into a tree.
- undef is returned if the file couldn't be read.
- =back
- The errors collected during the tree parsing are available through the
- C<errors> method. This method comes from C<Texinfo::Report>, and is
- described in L<errors|Texinfo::Report/($error_warnings_list, $error_count) = errors ($converter)>.
- =head2 Getting informations on the document
- After parsing some informations about the Texinfo code that was processed
- are available from the parser.
- Some global informations is available through C<global_informations>
- =over
- =item $info = global_informations($parser)
- The I<$info> returned is a hash reference. The possible keys are
- =over
- =item input_file_name
- The name of the main Texinfo input file.
- =item input_encoding_name
- =item input_perl_encoding
- C<input_encoding_name> string is the encoding name used for the
- Texinfo code.
- C<input_perl_encoding> string is a corresponding perl encoding name.
- =item dircategory_direntry
- An array of successive C<@dircategory> and C<@direntry> as they appear
- in the document.
- =item unassociated_menus
- An array of menus that are not associated with a node.
- =back
- =back
- Some command lists are available, such that it is possible to go through
- the corresponding tree elements without walking the tree. They are
- available through C<global_commands_information>
- =over
- =item $commands = global_commands_information($parser)
- I<$commands> is an hash reference. The keys are @-command names. The
- associated values are array references containing all the corresponding
- tree elements.
- =back
- All the @-commands that have an associated label, that can be the
- target of cross references, C<@node>, C<@anchor> and C<@float> with
- label have a normalized name associated, constructed as described in the
- B<HTML Xref> node in the Texinfo manual. Those normalized labels and
- the association with @-commands is available through C<labels_information>:
- =over
- =item $labels_information = labels_information($parser)
- I<$labels_information> is a hash reference whose keys are normalized
- labels, and the associated value is the corresponding @-command.
- =back
- Information on C<@float> is also available, grouped by type of
- floats, each type correponding to potential C<@listoffloats>.
- This information is available through the method C<floats_information>.
- =over
- =item $float_types = floats_information($parser)
- I<$float_types> is a hash reference whose keys are normalized float
- types (the first float argument, or the C<@listoffloats> argument).
- The normalization is the same than for node names. The value is the list
- of float tree elements appearing in the texinfo document.
- =back
- Internal references, that is, @-commands that refers to node, anchors
- or floats within the document are also available:
- =over
- =item $internal_references_array = internal_references_information($parser);
- The function returns a list of cross reference commands referring to
- the same document.
- =back
- Information about defined indices, merged indices and index entries is
- also available through the C<indices_information> method.
- =over
- =item indices_information
- ($index_names, $merged_indices_hash)
- = indices_information($parser);
- The index names is a hash reference. The keys are
- =over
- =item in_code
- 1 if the index entries should be formatted as code, 0 in the opposite case.
- =item name
- The index name.
- =item prefix
- An array reference of prefix associated to the index.
- =item merged_in
- In case the index is merged to another index, this key holds the name of
- the index the index is merged into. It takes into account indirectly
- merged indices.
- =item contained_indices
- An hash reference holding names of indices that are merged to the index,
- including itself. It also contains indirectly merged indices. This key
- is present even if the index is itself later merged to another index.
- =item index_entries
- An array reference containing index entry structures for index entries
- associated with the index. The index entry could be associated to
- @-commands like C<@cindex>, or C<@item> in C<@vtable>, or definition
- commands entries like C<@deffn>.
- The keys of the index entry structures are
- =over
- =item index_name
- The index name.
- =item index_prefix
- The associated index prefix.
- =item index_at_command
- The name of the @-command associated with the index entry.
- =item index_type_command
- The @-command associated with the index entry allowing to
- find the index type.
- =item content
- An array reference corresponding to the index entry content.
- =item content_normalized
- An array reference corresponding to the index entry content, independent
- of the current language.
- =item command
- The element in the parsed tree associated with the @-command holding the
- index entry.
- =item node
- The node in the parsed tree containing the index entry.
- =item number
- The number of the index entry.
- =item region
- The region command (C<@copying>, C<@titlepage>) containing the index entry,
- if it is in such an environement.
- =back
- =back
- The following shows the references corresponding with the default indexes
- I<cp> and I<fn>, the I<fn> index having its entries formatted as code and
- the indices corresponding to the following texinfo
- @defindex some
- @defcodeindex code
- $index_names = {'cp' => {'name' => 'cp', 'in_code' => 0,
- 'prefix' => ['c', 'cp']},
- 'fn' => {'name' => 'fn', 'in_code' => 1,
- 'prefix' => ['f', 'fn']},
- 'some' => {'in_code' => 0},
- 'code' => {'in_code' => 1}};
- If C<name> is not set, it is set to the index name. If C<prefix> is
- not set, it is set to an array containing the index name.
- I<$merged_indices_hash> is a hash reference, the key is an index
- name merged in the value.
- =back
- =head2 Texinfo Parser options
- Setting those options is the same as seeing some Texinfo constructs in the
- document.
- =over
- =item aliases
- A hash reference. The key is a command name, the value is the alias, as
- could be set by C<@alias>.
- =item clickstyle
- A string, the command name associated with C<@clickstyle>.
- =item documentlanguage
- A string corresponding to a document language set by C<@documentlanguage>.
- =item explained_commands
- A hash reference of explained commands (currently abbr or acronym).
- The value is also a hash reference. The key of this hash is a normalized
- first argument of these commands, the value is a content array
- corresponding to the explanation associated with this first argument.
- For example giving as an option:
- 'explained_commands'
- => {'acronym' => {'EU' => [{'text' => 'European Union'}]}
- is the same as having the following texinfo code in the document:
- @acronym{EU, European Union}
- =item INPUT_ENCODING_NAME
- =item INPUT_PERL_ENCODING
- C<INPUT_ENCODING_NAME> string is the encoding name as set
- by C<@documentencoding>.
- C<INPUT_PERL_ENCODING> string is a corresponding perl encoding name.
- In general those two strings should be set simultaneously.
- =item indices
- If it is a hash reference, the keys are index names, the values are
- index prefix hash references. The index prefix hash reference values are
- prefix, the value is set if the corresponding index entries should be
- formatted as if in C<@code>. An example is as L</indices_information>.
- If it is an array reference, it is a list of index names, as if they were
- entered as
- @defindex name
- =item kbdinputstyle
- A string, the C<@kbdinputstyle> style.
- =item labels
- A hash reference. Keys are normalized node names as described in the
- B<HTML Xref> node in the Texinfo manual. Instead of a node, it may also
- be a float label or an anchor name. The value is the corresponding
- @-command element in the tree.
- =item macros
- The associated hash reference has as key user-defined macro names. The
- value is the reference on a macro definition element as obtained by
- the Parser when parsing a C<@macro>. For example
- @macro mymacro{arg}
- coucou \arg\ after arg
- @end macro
- Is associated to a macro definition element
- {'cmdname' => 'macro',
- 'args' => [{'text' => 'mymacro', 'type' => 'macro_name'},
- {'text' => 'arg', 'type' => 'macro_arg}],
- 'contents' => [{'text' => "coucou \arg\ after arg\n", 'type' => 'raw'}],
- 'extra' => {'arg_line' => " mymacro{arg}\n",
- 'macrobody' => "coucou \arg\ after arg\n"}}
- = item merged_indices
- The associated hash reference holds merged indices information, each key
- is merged in the value. Same as setting C<@synindex> or C<syncodeindex>.
- =item novalidate
- If set, it is as if C<@novalidate> was set in the document.
- =item sections_level
- Modifier of the sections level. Same as calling C<@lowersections> or
- C<@raisesections>.
- =item values
- A hash reference. Keys are names, values are the corresponding values.
- Same as values set by C<@set>.
- =back
- =head1 TEXINFO TREE
- A Texinfo tree element (called element because node is overloaded in
- the Texinfo world) is an hash reference. There are three main category
- of tree element. Tree elements associated with an @-command have a
- C<cmdname> key holding the @-command name. Tree element corresponding
- to text fragments have a C<text> key holding the corresponding text.
- The last category corresponds to other containers (hereafter called
- containers). In most case these containers have a C<type> key holding
- their name. Text fragments and @-command elements may also have an
- associated type when such information is needed.
- The children of @-command or container elements are in the array
- correponding with the C<args> key or with the C<contents> key. The
- C<args> key is for arguments of @-commands, in braces or on the @-command
- line. C<args> is also used for the elements of a menu entry, as a menu
- entry is well structured with a limited number of arguments.
- The C<contents> key array holds the contents of the texinfo
- code appearing within a block @-command, within a container,
- within a C<@node> or sectioning @-command.
- Another important key for the elements is the C<extra> key which is
- associated to a hash reference and holds all kinds of informations gathered
- during the parsing and that may help with the conversion.
- =head2 Element keys
- =over
- =item cmdname
- The command name of @-command elements.
- =item text
- The text fragment of text elements.
- =item type
- The type of the element. For C<@verb> it is the delimiter. But otherwise
- it is the type of element considered as a container. Frequent types
- encountered are I<paragraph> for a paragraph container,
- I<brace_command_arg> for the container holding the brace @-commands
- contents, I<misc_line_arg> and I<block_line_arg> contain the arguments
- appearing on the line of @-commands. Text fragments may have a type to
- give an information of the kind of text fragment, for example
- C<empty_spaces_before_argument> is associated to spaces after a brace
- opening and before the argument. Many @-commands elements don't have
- a type associated.
- =item args
- Arguments in braces or on @-command line, and the elements of a menu entry.
- =item contents
- The Texinfo appearing in the element. For block commands, other
- containers, C<@node> and sectioning commands.
- =item parent
- The parent element.
- =item line_nr
- An hash reference corresponding to information on the location of the
- element in the Texinfo input manual. It should only be available for
- @-command elements, and only for @-commands that are considered to be
- complex enough that the location in the document is needed, for example
- to prepare an error message.
- The keys of the line number hash references are
- =over
- =item line_nr
- The line number of the @-command.
- =item file_name
- The file name where @-command appeared.
- =item macro
- The user macro name the @-command is expanded from.
- =back
- =item extra
- A hash reference holding any additional information.
- See L</Information available in the extra key>.
- =back
- =head2 The containers and types
- Some types are associated with @-commands. As said above, for C<@verb>
- the type is the delimiter. For a C<@value> command that is not
- expanded because there is no corresponding value set, the type is the
- value argument string.
- The following types also happen for @-commands:
- =over
- =item def_line
- This type may be associated with a definition command with a x form,
- like C<@defunx>, C<@defvrx>. For the form without x, the associated
- I<def_line> is the first C<contents> element. It is described in more
- details below.
- =item command_as_argument
- This is the type of a command given in argument of C<@itemize>,
- C<@table>, C<@vtable> or C<@ftable>. For example in
- @itemize @bullet
- @item item
- @end itemize
- the element corresponding with bullet has the following keys:
- 'cmdname' => 'bullet'
- 'type' => 'command_as_argument'
- The parent @-command has an entry in extra for the I<command_as_argument>
- element:
- 'cmdname' => 'itemize'
- 'extra => {'command_as_argument' => $command_element_as_argument}
- =item index_entry_command
- This is the type of index entry command like C<@cindex>, and, more
- importantly user defined index entry commands. So for example if there
- is
- @defindex foo
- ...
- @fooindex index entry
- the C<@fooindex> @-command element will have the I<index_entry_command>
- type.
- =item following_arg
- This type is set for non alphabetic accent @-commands that don't use brace
- but instead have their argument right after them, as
- @~n
- =item space_command_arg
- This type is set for accent @-commands that don't use brace but instead
- have their argument after some space, as
- @ringaccent A
- =item definfoenclose_command
- This type is set for an @-command that is redefined by C<@definfoenclose>.
- The beginning is in C<< {'extra'}->{'begin'} >> and the end in
- C<< {'extra'}->{'end'} >>.
- =back
- The text elements may be associated to the following types:
- =over
- =item empty_line
- An empty line.
- =item raw
- Text in an environment where it should be kept as is (in C<@verbatim>,
- C<@verb>, C<@html>, C<@macro> body).
- =item last_raw_newline
- The last end of line in a raw block (except for C<@verbatim>).
- =item empty_line_after_command
- =item empty_spaces_after_command
- The text is spaces for I<empty_spaces_after_command>
- or spaces followed by a newline for
- I<empty_line_after_command>, appearing after a @-command that
- takes an argument on the line or a block
- @-commands.
- =item spaces_at_end
- Space at the end of a @-command line, at the end of some @-commands
- with braces or at the end of a bracketed content on a
- C<@multitable> line.
- =item empty_space_at_end_def_bracketed
- Space at the end of a bracketed content on definition line.
- =item space_at_end_block_command
- Space at the end of a block @-command line.
- =item empty_spaces_before_argument
- The text is spaces appearing after an opening brace of after a
- comma separated @-command arguments.
- =item empty_spaces_after_close_brace
- Spaces appearing after a closing brace, for some rare commands for which
- this space should be ignorable (like C<@caption>).
- =item empty_spaces_before_paragraph
- Space appearing before a paragraph beginning.
- =item preamble_text
- Text appearing before real content, including the C<\input texinfo.tex>.
- =item space_at_end_menu_node
- =item after_description_line
- Space after a node in the menu entry, when there is no description,
- and space appearing after the description line.
- =back
- Other special types are described in the following.
- =over
- =item text_root
- =item document_root
- =item root_line
- These types correspond to document roots. C<text_root> is the document
- root when there is no C<@node> or sectioning command. When
- such a command appears, a new root container is used, C<document_root>,
- and C<text_root> becomes the first content of C<document_root>.
- C<root_line> is the type of the root tree when parsing Texinfo line
- fragments using C<parse_texi_line>.
- =item preamble
- This container holds the text appearing before the first content, including
- the C<\input texinfo.tex> line and following blank lines.
- =item preamble_before_setfilename
- This container holds everything that appears before C<@setfilename>
- if I<IGNORE_BEFORE_SETFILENAME> parser option is set.
- =item paragraph
- A paragraph.
- =item preformatted
- Texinfo code within a format that is not filled. Happens within some
- block commands as C<@example>, but also in menu (in menu descriptions,
- menu comments...).
- =item brace_command_arg
- =item brace_command_context
- =item block_line_arg
- =item misc_line_arg
- Those containers are within C<args> of @-commands with braces for
- I<brace_command_arg>, @-commands with braces that start a new context
- (C<@footnote>, C<@caption>, C<@math>) for I<brace_command_context>,
- block command argument on their line for I<block_line_arg> and
- other commands that take texinfo code as argument on their line
- (C<@settitle>, C<@node>, C<@section> and similar) for I<misc_line_arg>.
- They hold the content of the command argument.
- For example
- @code{in code}
- leads to
- {'cmdname' => 'code',
- 'args' => [{'type' => 'brace_command_arg',
- 'contents' => [{'text' => 'in code'}]}]}
- =item misc_arg
- Argument of @-command taking specific textual arguments on the line.
- For example C<@set>, C<@clickstyle>, C<@unmacro>, C<@comment>.
- The argument is associated to the I<text> key.
- =item menu_entry
- =item menu_entry_leading_text
- =item menu_entry_name
- =item menu_entry_separator
- =item menu_entry_node
- =item menu_entry_description
- A I<menu_entry> holds a full menu entry, like
- * node:: description.
- The different elements of the menu entry are directly in the
- I<menu_entry> C<args> array reference.
- I<menu_entry_leading_text> holds the star and following spaces.
- I<menu_entry_name> is the menu entry name (if present), I<menu_entry_node>
- corresponds to the node in the menu entry, I<menu_entry_separator> holds
- the text after the node and before the description, in most case
- C<:: >. Last I<menu_entry_description> is for the description.
- =item menu_comment
- The I<menu_comment> container holds what is between menu entries
- in menus. For example in
- @menu
- Menu title
- * entry::
- Between entries
- * other::
- @end menu
- Both
- Menu title
- and
- Between entries
- will be in I<menu_comment>.
- =item macro_name
- =item macro_arg
- Taken from C<@macro> definition and put in the C<args> key array of
- the macro, I<macro_name> is the type of the text fragment corresponding
- to the macro name, I<macro_arg> is the type of the text fragments
- correponding to macro formal arguments.
- =item before_item
- A container for content before the first C<@item> of block @-commands
- with items (C<@table>, C<@multitable>, C<@enumerate>...).
- =item table_entry
- =item table_term
- =item table_item
- =item inter_item
- Those containers appear in C<@table>, C<@ftable> and C<@vtable>.
- A I<table_entry> container contains C<@item> and C<@itemx> and
- the text following the C<@item> and C<@itemx> entries. A I<table_term>
- container holds all the C<@item> and C<@itemx> of the I<table_entry>.
- The I<table_item> container holds the content following the I<table_term>.
- If there is some content before an C<@itemx> (normally only comments,
- empty lines or maybe index entriees are allowed), it will be in
- a container with type I<inter_item>.
- =item def_line
- =item def_item
- =item inter_def_item
- The I<def_line> type is either associated with a container within a
- definition command, or is the type of a definition command with a x
- form, like C<@deffnx>. It holds the definition line arguments.
- The container with type I<def_item> holds the definition text content.
- Content appearing before a definition command with a x form is in
- an I<inter_def_item> container.
- =item multitable_head
- =item multitable_body
- =item row
- In C<@multitable>, a I<multitable_head> container contains all the row
- with C<@headitem>, while I<multitable_body> contains the rows associated
- with C<@item>. A I<row> container contains the C<@item> and @<tab>
- forming a row.
- =item bracketed
- This a special type containing content in brackets in the context
- where they are valid, in C<@math>.
- =item bracketed_def_content
- Content in brackets on definition command lines.
- =item bracketed_multitable_prototype
- =item row_prototype
- On C<@multitable> line, content in brackets is in
- I<bracketed_multitable_prototype>, text not in brackets
- is in I<row_prototype>.
- =back
- =head2 Information available in the extra key
- Some extra keys are available for more than one @-command:
- =over
- =item block_command_line_contents
- =item brace_command_contents
- An array associated with block @-commands or @-commands with braces
- taking more than one argument or with a simple text content
- (C<@anchor>, C<@titlefont>, C<@dmn>). Each of the element of the
- array is either undef, if there is no argument at that place,
- or an array reference holding the argument contents.
- =item misc_content
- The contents of an @-command taking regular Texinfo code as
- argument, like C<@sttitle> or C<@exdent>.
- =item end_command
- The C<@end> associated to the block @-command.
- =item missing_argument
- Set for some @-commands with line arguments and a missing argument.
- =item invalid_nesting
- Set if the @-command appears in a context it shouldn't appear in,
- like a block @-command on sectioning @-command line.
- =item arg_line
- The string correspond to the line after the @-command
- for @-commands that have special arguments on their line,
- and for C<@macro> line.
- =item text_arg
- The string correspond to the line after the @-command for @-commands
- that have an argument interpreted as simple text, like C<@setfilename>,
- C<@end> or C<@documentencoding>.
- =item index_entry
- The index entry information (described in L</index_entries>
- in details) is associated to @-commands that have an associated
- index entry.
- =item misc_args
- An array holding strings, the arguments of @-commands taking simple
- textual arguments as arguments, like C<@everyheadingmarks>,
- C<@frenchspacing>, C<@alias>, C<@synindex>, C<@columnfractions>.
- Also filled for C<@set>, C<@clickstyle>, C<@unmacro> or C<@comment>
- arguments.
- =item spaces_after_command
- For @-commands followed by spaces, a reference to the corresponding
- text element.
- =item spaces_before_argument
- For @-commands with opening brace followed by spaces held in a
- C<empty_spaces_before_argument> element, a reference to that element.
- =item spaces
- For accent commands consisting in letter only, like C<@ringaccent>
- appearing like
- @ringaccent A
- there is a I<spaces> key which holds the spaces appearing between
- the command and the argument.
- =back
- Then there are extra keys specific of @-commands or containers.
- =over
- =item C<@macro>
- I<invalid_syntax> is set if there was an error on the C<@macro>
- line. The key I<args_index> associates format arguments with
- their index on the @macro line formal arguments definition.
- The I<macrobody> holds the @macro body. I<arg_line> holds the
- line after C<@macro>.
- =item C<@node>
- The arguments are in the I<nodes_manuals> array. Each
- of the entry has a I<node_content> key for
- an array holding the corresponding content, a I<manual_content>
- if there is an associated external manual name, and I<normalized>
- key for the normalized label, built as specified in the Texinfo manual
- in the B<HTML Xref> node.
- An I<associated_section> key holds the tree element of the
- sectioning command that follows the node.
- =item C<@part>
- The next sectioning command is in I<part_associated_section>.
- =item sectioning command
- The node preceding the command is in I<associated_node>.
- The part preceding the command is in I<associated_part>.
- If the level of the document was modified by C<@raisections>
- or C<@lowersections>, the differential level is in I<sections_level>.
- =item C<@float>
- =item C<@listoffloats>
- If float has a second argument, and for C<@listoffloats>
- argument there is a I<type> key which is also a hash reference,
- with two keys. I<content> is an array holding the associated
- contents, I<normalized> holds the normalized float type.
- I<caption> and I<shortcaption> holds the corresponding
- tree elements for float. The C<@caption> or C<@shortcaption>
- have the float tree element stored in I<float>.
- =item C<@float>
- =item C<@anchor>
- @-Commands that are targets for cross references have a I<normalized>
- key for the normalized label, built as specified in the Texinfo manual
- in the B<HTML Xref> node. There is also a I<node_content> key for
- an array holding the corresponding content.
- C<@anchor> also has I<region> set to the special region name if
- in a special region (C<@copying>, C<@titlepage>).
- =item C<@ref>
- =item C<@xref>
- =item C<@pxref>
- =item C<@inforef>
- The I<node_argument> entry holds a parsed node entry, like
- the one appearing in the I<nodes_manuals> array for C<@node>.
- =item C<@abbr>
- =item C<@acronym>
- The first argument normalized is in I<normalized>. If there is no
- second argument, but a second argument appeared previously for the
- same first argument, the second argument content of the previous
- command is stored in I<explanation_contents>.
- =item definition command
- I<def_command> holds the command name, without x if it is
- an x form of a definition command.
- I<original_def_cmdname> is the original def command.
- If it is an x form, it has I<not_after_command> set if not
- appearing after the definition command without x.
- =item def_line
- The I<def_arg> extra key holds an array reference corresponding to
- the parsed definition line argument. Each of the the element of the
- array is a two element array reference. The first element is the type
- which could be I<spaces> for a space, types specific of the
- definition, like I<category>, I<name>, I<class>, I<type>, and, at the
- end, a mix of I<arg>, I<typearg>, I<delimiter> depending on the definition.
- The second element is a hash reference holding the content of the
- type.
- The I<def_parsed_hash> hash reference has as key the type specific
- of the definition, and as value the corresponding content tree.
- =item C<@multitable>
- The key I<max_columns> holds the maximal number of columns. If there
- are prototypes on the line they are in the array associated with
- I<prototypes>. In that case, I<prototypes_line> also holds this
- information, and, in addition, keeps spaces with type C<prototype_space>.
- If there is a C<@columnfractions> as argument, then the
- I<columnfractions> key is associated with the array of columnfractions
- arguments, holding all the column fractions.
- =item C<@enumerate>
- The extra key I<enumerate_specification> contains the enumerate
- argument.
- =item C<@itemize>
- =item C<@table>
- =item C<@vtable>
- =item C<@ftable>
- The I<command_as_argument> extra key points on the @-command on
- as argument on the @-command line.
- =item paragraph
- The I<indent> or I<noindent> key value is set if the corresponding
- @-commands are associated with that paragraph.
- =item C<@item> in C<@enumerate> or C<@itemize>
- The I<item_number> extra key holds the number of this item.
- =item C<@item> and C<@tab> in C<@multitable>
- The I<cell_count> index key holds the index of the column of
- the cell.
- =item row
- The I<row_number> index key holds the index of the row in
- the C<@multitable>.
- =item C<@author>
- If in a C<@titlepage>, the titlepage is in I<titlepage>, if in
- C<@quotation> or C<@smallquotation>, the corresponding tree element
- is in I<quotation>.
- The author tree element is in the I<author> array of the C<@titlepage>
- or the C<@quotation> or C<@smallquotation> it is associated with.
- =item C<@ifclear>
- =item C<@ifset>
- The original line is in I<line>.
- =item C<@end>
- The textual argument is in I<command_argument>.
- The corresponding @-command is in I<command>.
- =item C<@documentencoding>
- The argument, normalized is in I<input_encoding_name> if it is recognized.
- The corresponding perl encoding name is in I<input_perl_encoding>.
- =item C<@click>
- In I<clickstyle> there is the current clickstyle command.
- =item C<@kbd>
- I<code> is set depending on the context and C<@kbdinputstyle>.
- =item definfoenclose defined commands
- I<begin> holds the string beginning the definfoenclose,
- I<end> holds the string ending the definfoenclose.
- =item menu_entry
- The I<menu_entry_description> and I<menu_entry_name> keys
- are associated with the corresponding tree elements. The
- I<menu_entry_node> holds the parsed node entry, like
- the one appearing in the I<nodes_manuals> array for C<@node>.
- =item empty_line_after_command
- The corresponding command is in I<command>.
- =item C<@inlinefmt>
- =item C<@inlineraw>
- =item C<@inlinefmtifelse>
- =item C<@inlineifclear>
- =item C<@inlineifset>
- The first argument is in I<format>. If an argument has been determined
- as being expanded by the Parser, the index of this argument is in
- I<expand_index>. Index numbering begins at 0, but the first argument is
- always the format or flag name, so, if set, it should be 1 or 2 for
- C<@inlinefmtifelse>, and 1 for other commands.
- =back
- =head1 SEE ALSO
- L<Texinfo manual|http://www.gnu.org/s/texinfo/manual/texinfo/>
- =begin :comment
- Mention other useful documentation such as the documentation of
- related modules or operating system documentation (such as man pages
- in UNIX), or any relevant external documentation such as RFCs or
- standards.
- If you have a mailing list set up for your module, mention it here.
- If you have a web site set up for your module, mention it here.
- =end :comment
- =head1 AUTHOR
- Patrice Dumas, E<lt>pertusus@free.frE<gt>
- =head1 COPYRIGHT AND LICENSE
- Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
- This library is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License,
- or (at your option) any later version.
- =cut
|