ms2html.c 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. enum
  5. {
  6. SSIZE = 10,
  7. Maxnh= 8, /* highest NH level */
  8. HH= 4, /* heading level used for SH and NH */
  9. Maxmstack= 10, /* deepest macro/string nesting */
  10. Narg= 20, /* max args to a macro */
  11. Maxsstack= 5, /* deepest nesting of .so's */
  12. Nline= 1024,
  13. Maxget= 10,
  14. Maxif = 20,
  15. /* list types */
  16. Lordered = 1,
  17. Lunordered,
  18. Ldef,
  19. Lother,
  20. };
  21. int quiet;
  22. float indent; /* from .in */
  23. Biobuf bout;
  24. int isup;
  25. int isdown;
  26. int debug;
  27. int nh[Maxnh];
  28. int ifwastrue[Maxif];
  29. int list, listnum, example;
  30. int hangingau, hangingdt, hanginghead;
  31. int indirective, paragraph, sol, titleseen, ignore_nl, weBref;
  32. typedef struct Goobie Goobie;
  33. typedef struct Goobieif Goobieif;
  34. struct Goobie
  35. {
  36. char *name;
  37. void (*f)(int, char**);
  38. };
  39. typedef void F(int, char**);
  40. typedef void Fif(char*, char*);
  41. struct Goobieif
  42. {
  43. char *name;
  44. Fif *f;
  45. };
  46. /* if, ie */
  47. Fif g_as, g_ds, g_el, g_ie, g_if;
  48. Goobieif gtabif[] = {
  49. { "as", g_as },
  50. { "ds", g_ds },
  51. { "if", g_if },
  52. { "ie", g_ie },
  53. { "el", g_el },
  54. { nil, nil },
  55. };
  56. /* pseudo ops */
  57. F g_notyet, g_ignore, g_hrule, g_startgif;
  58. /* ms macros */
  59. F g_AU, g_B, g_BI, g_CW, g_I, g_IP, g_LP, g_PP, g_SH, g_NH;
  60. F g_P1, g_P2, g_TL, g_R, g_AB, g_AE, g_EQ, g_TS, g_TE, g_FS, g_FE;
  61. F g_PY, g_IH, g_MH, g_HO, g_BX, g_QS, g_QE, g_RS, g_RE;
  62. /* pictures macro */
  63. F g_BP;
  64. /* real troff */
  65. F g_br, g_ft, g_sp, g_de, g_lf, g_so, g_rm, g_in;
  66. F g_nr, g_ig, g_RT, g_BS, g_BE, g_LB, g_ta;
  67. /* macros to include ML in output */
  68. F g__H, g__T;
  69. Goobie gtab[] =
  70. {
  71. { "_T", g__T, },
  72. { "_H", g__H, },
  73. { "1C", g_ignore, },
  74. { "2C", g_ignore, },
  75. { "AB", g_AB, },
  76. { "AE", g_AE, },
  77. { "AI", g_ignore, },
  78. { "AU", g_AU, },
  79. { "B", g_B, },
  80. { "B1", g_hrule, },
  81. { "B2", g_hrule, },
  82. { "BI", g_BI, },
  83. { "BP", g_BP, },
  84. { "BT", g_ignore, },
  85. { "BX", g_BX, },
  86. { "CW", g_CW, },
  87. { "CT", g_ignore, },
  88. { "DA", g_ignore, },
  89. { "DE", g_P2, },
  90. { "DS", g_P1, },
  91. { "EG", g_ignore, },
  92. { "EN", g_ignore, },
  93. { "EQ", g_startgif, },
  94. { "FE", g_FE, },
  95. { "FP", g_ignore, },
  96. { "FS", g_FS, },
  97. { "HO", g_HO, },
  98. { "I", g_I, },
  99. { "IH", g_IH, },
  100. { "IM", g_ignore, },
  101. { "IP", g_IP, },
  102. { "KE", g_ignore, },
  103. { "KF", g_ignore, },
  104. { "KS", g_ignore, },
  105. { "LG", g_ignore, },
  106. { "LP", g_LP, },
  107. { "LT", g_ignore, },
  108. { "MF", g_ignore, },
  109. { "MH", g_MH, },
  110. { "MR", g_ignore, },
  111. { "ND", g_ignore, },
  112. { "NH", g_NH, },
  113. { "NL", g_ignore, },
  114. { "P1", g_P1, },
  115. { "P2", g_P2, },
  116. { "PE", g_ignore, },
  117. { "PF", g_ignore, },
  118. { "PP", g_PP, },
  119. { "PS", g_startgif, },
  120. { "PY", g_PY, },
  121. { "QE", g_QE, },
  122. { "QP", g_QS, },
  123. { "QS", g_QS, },
  124. { "R", g_R, },
  125. { "RE", g_RE, },
  126. { "RP", g_ignore, },
  127. { "RS", g_RS, },
  128. { "SG", g_ignore, },
  129. { "SH", g_SH, },
  130. { "SM", g_ignore, },
  131. { "TA", g_ignore, },
  132. { "TE", g_ignore, },
  133. { "TH", g_TL, },
  134. { "TL", g_TL, },
  135. { "TM", g_ignore, },
  136. { "TR", g_ignore, },
  137. { "TS", g_startgif, },
  138. { "UL", g_I, },
  139. { "UX", g_ignore, },
  140. { "WH", g_ignore, },
  141. { "RT", g_RT, },
  142. { "br", g_br, },
  143. { "ti", g_br, },
  144. { "nf", g_P1, },
  145. { "fi", g_P2, },
  146. { "ft", g_ft, },
  147. { "sp", g_sp, },
  148. { "rm", g_rm, },
  149. { "de", g_de, },
  150. { "am", g_de, },
  151. { "lf", g_lf, },
  152. { "so", g_so, },
  153. { "ps", g_ignore },
  154. { "vs", g_ignore },
  155. { "nr", g_nr },
  156. { "in", g_in },
  157. { "ne", g_ignore },
  158. { "ig", g_ig },
  159. { "BS", g_BS },
  160. { "BE", g_BE },
  161. { "LB", g_LB },
  162. { nil, nil },
  163. };
  164. typedef struct Entity Entity;
  165. struct Entity
  166. {
  167. char *name;
  168. int value;
  169. };
  170. Entity entity[] =
  171. {
  172. { "&#SPACE;", L' ', },
  173. { "&#RS;", L'\n', },
  174. { "&#RE;", L'\r', },
  175. { "&quot;", L'"', },
  176. { "&AElig;", L'Æ', },
  177. { "&Aacute;", L'Á', },
  178. { "&Acirc;", L'Â', },
  179. { "&Agrave;", L'À', },
  180. { "&Aring;", L'Å', },
  181. { "&Atilde;", L'Ã', },
  182. { "&Auml;", L'Ä', },
  183. { "&Ccedil;", L'Ç', },
  184. { "&ETH;", L'Ð', },
  185. { "&Eacute;", L'É', },
  186. { "&Ecirc;", L'Ê', },
  187. { "&Egrave;", L'È', },
  188. { "&Euml;", L'Ë', },
  189. { "&Iacute;", L'Í', },
  190. { "&Icirc;", L'Î', },
  191. { "&Igrave;", L'Ì', },
  192. { "&Iuml;", L'Ï', },
  193. { "&Ntilde;", L'Ñ', },
  194. { "&Oacute;", L'Ó', },
  195. { "&Ocirc;", L'Ô', },
  196. { "&Ograve;", L'Ò', },
  197. { "&Oslash;", L'Ø', },
  198. { "&Otilde;", L'Õ', },
  199. { "&Ouml;", L'Ö', },
  200. { "&THORN;", L'Þ', },
  201. { "&Uacute;", L'Ú', },
  202. { "&Ucirc;", L'Û', },
  203. { "&Ugrave;", L'Ù', },
  204. { "&Uuml;", L'Ü', },
  205. { "&Yacute;", L'Ý', },
  206. { "&aacute;", L'á', },
  207. { "&acirc;", L'â', },
  208. { "&aelig;", L'æ', },
  209. { "&agrave;", L'à', },
  210. { "&amp;", L'&', },
  211. { "&aring;", L'å', },
  212. { "&atilde;", L'ã', },
  213. { "&auml;", L'ä', },
  214. { "&ccedil;", L'ç', },
  215. { "&eacute;", L'é', },
  216. { "&ecirc;", L'ê', },
  217. { "&egrave;", L'è', },
  218. { "&eth;", L'ð', },
  219. { "&euml;", L'ë', },
  220. { "&gt;", L'>', },
  221. { "&iacute;", L'í', },
  222. { "&icirc;", L'î', },
  223. { "&igrave;", L'ì', },
  224. { "&iuml;", L'ï', },
  225. { "&lt;", L'<', },
  226. { "&ntilde;", L'ñ', },
  227. { "&oacute;", L'ó', },
  228. { "&ocirc;", L'ô', },
  229. { "&ograve;", L'ò', },
  230. { "&oslash;", L'ø', },
  231. { "&otilde;", L'õ', },
  232. { "&ouml;", L'ö', },
  233. { "&szlig;", L'ß', },
  234. { "&thorn;", L'þ', },
  235. { "&uacute;", L'ú', },
  236. { "&ucirc;", L'û', },
  237. { "&ugrave;", L'ù', },
  238. { "&uuml;", L'ü', },
  239. { "&yacute;", L'ý', },
  240. { "&yuml;", L'ÿ', },
  241. { "&#161;", L'¡', },
  242. { "&#162;", L'¢', },
  243. { "&#163;", L'£', },
  244. { "&#164;", L'¤', },
  245. { "&#165;", L'¥', },
  246. { "&#166;", L'¦', },
  247. { "&#167;", L'§', },
  248. { "&#168;", L'¨', },
  249. { "&#169;", L'©', },
  250. { "&#170;", L'ª', },
  251. { "&#171;", L'«', },
  252. { "&#172;", L'¬', },
  253. { "&#173;", L'­', },
  254. { "&#174;", L'®', },
  255. { "&#175;", L'¯', },
  256. { "&#176;", L'°', },
  257. { "&#177;", L'±', },
  258. { "&#178;", L'²', },
  259. { "&#179;", L'³', },
  260. { "&#180;", L'´', },
  261. { "&#181;", L'µ', },
  262. { "&#182;", L'¶', },
  263. { "&#183;", L'·', },
  264. { "&#184;", L'¸', },
  265. { "&#185;", L'¹', },
  266. { "&#186;", L'º', },
  267. { "&#187;", L'»', },
  268. { "&#188;", L'¼', },
  269. { "&#189;", L'½', },
  270. { "&#190;", L'¾', },
  271. { "&#191;", L'¿', },
  272. { "*", L'•', },
  273. { "&#164;", L'□', },
  274. { "&#186;", L'◊', },
  275. { "(tm)", L'™', },
  276. { "CAP-DELTA", L'Δ', },
  277. { "ALPHA", L'α', },
  278. { "BETA", L'β', },
  279. { "DELTA", L'δ', },
  280. { "EPSILON", L'ε', },
  281. { "THETA", L'θ', },
  282. { "MU", L'μ', },
  283. { "PI", L'π', },
  284. { "TAU", L'τ', },
  285. { "CHI", L'χ', },
  286. { "CYRILLIC XYZZY", L'й', },
  287. { "CYRILLIC XYZZY", L'ъ', },
  288. { "CYRILLIC Y", L'ь', },
  289. { "CYRILLIC YA", L'я', },
  290. { "CYRILLIC YA", L'ё', },
  291. { "&#191;", L'ℱ', },
  292. { "<-", L'←', },
  293. { "^", L'↑', },
  294. { "->", L'→', },
  295. { "v", L'↓', },
  296. { "!=", L'≠', },
  297. { "<=", L'≤', },
  298. { nil, 0 },
  299. };
  300. typedef struct Troffspec Troffspec;
  301. struct Troffspec
  302. {
  303. char *name;
  304. char *value;
  305. };
  306. Troffspec tspec[] =
  307. {
  308. { "A*", "&Aring;", },
  309. { "o\"", "&ouml;", },
  310. { "ff", "ff", },
  311. { "fi", "fi", },
  312. { "fl", "fl", },
  313. { "Fi", "ffi", },
  314. { "ru", "_", },
  315. { "em", "&#173;", },
  316. { "14", "&#188;", },
  317. { "12", "&#189;", },
  318. { "co", "&#169;", },
  319. { "de", "&#176;", },
  320. { "dg", "&#161;", },
  321. { "fm", "&#180;", },
  322. { "rg", "&#174;", },
  323. { "bu", "*", },
  324. { "sq", "&#164;", },
  325. { "hy", "-", },
  326. { "pl", "+", },
  327. { "mi", "-", },
  328. { "mu", "&#215;", },
  329. { "di", "&#247;", },
  330. { "eq", "=", },
  331. { "==", "==", },
  332. { ">=", ">=", },
  333. { "<=", "<=", },
  334. { "!=", "!=", },
  335. { "+-", "&#177;", },
  336. { "no", "&#172;", },
  337. { "sl", "/", },
  338. { "ap", "&", },
  339. { "~=", "~=", },
  340. { "pt", "oc", },
  341. { "gr", "GRAD", },
  342. { "->", "->", },
  343. { "<-", "<-", },
  344. { "ua", "^", },
  345. { "da", "v", },
  346. { "is", "Integral", },
  347. { "pd", "DIV", },
  348. { "if", "oo", },
  349. { "sr", "-/", },
  350. { "sb", "(~", },
  351. { "sp", "~)", },
  352. { "cu", "U", },
  353. { "ca", "(^)", },
  354. { "ib", "(=", },
  355. { "ip", "=)", },
  356. { "mo", "C", },
  357. { "es", "&Oslash;", },
  358. { "aa", "&#180;", },
  359. { "ga", "`", },
  360. { "ci", "O", },
  361. { "L1", "DEATHSTAR", },
  362. { "sc", "&#167;", },
  363. { "dd", "++", },
  364. { "lh", "<=", },
  365. { "rh", "=>", },
  366. { "lt", "(", },
  367. { "rt", ")", },
  368. { "lc", "|", },
  369. { "rc", "|", },
  370. { "lb", "(", },
  371. { "rb", ")", },
  372. { "lf", "|", },
  373. { "rf", "|", },
  374. { "lk", "|", },
  375. { "rk", "|", },
  376. { "bv", "|", },
  377. { "ts", "s", },
  378. { "br", "|", },
  379. { "or", "|", },
  380. { "ul", "_", },
  381. { "rn", " ", },
  382. { "**", "*", },
  383. { nil, nil, },
  384. };
  385. typedef struct Font Font;
  386. struct Font
  387. {
  388. char *start;
  389. char *end;
  390. };
  391. Font bfont = { "<B>", "</B>" };
  392. Font ifont = { "<I>", "</I>" };
  393. Font bifont = { "<B><I>", "</I></B>" };
  394. Font cwfont = { "<TT>", "</TT>" };
  395. Font *prevfont;
  396. Font *curfont;
  397. typedef struct String String;
  398. struct String
  399. {
  400. String *next;
  401. char *name;
  402. char *val;
  403. };
  404. String *numregs, *strings;
  405. char *strstack[Maxmstack];
  406. char *mustfree[Maxmstack];
  407. int strsp = -1;
  408. int elsetop = -1;
  409. typedef struct Mstack Mstack;
  410. struct Mstack
  411. {
  412. char *ptr;
  413. char *argv[Narg+1];
  414. };
  415. String *macros;
  416. Mstack mstack[Maxmstack];
  417. int msp = -1;
  418. typedef struct Srcstack Srcstack;
  419. struct Srcstack
  420. {
  421. char filename[256];
  422. int fd;
  423. int lno;
  424. int rlno;
  425. Biobuf in;
  426. };
  427. Srcstack sstack[Maxsstack];
  428. Srcstack *ssp = &sstack[-1];
  429. char token[128];
  430. void closel(void);
  431. void closefont(void);
  432. void*
  433. emalloc(uint n)
  434. {
  435. void *p;
  436. p = mallocz(n, 1);
  437. if(p == nil){
  438. fprint(2, "ms2html: malloc failed: %r\n");
  439. exits("malloc");
  440. }
  441. return p;
  442. }
  443. /* define a string variable */
  444. void
  445. dsnr(char *name, char *val, String **l)
  446. {
  447. String *s;
  448. for(s = *l; s != nil; s = *l){
  449. if(strcmp(s->name, name) == 0)
  450. break;
  451. l = &s->next;
  452. }
  453. if(s == nil){
  454. s = emalloc(sizeof(String));
  455. *l = s;
  456. s->name = strdup(name);
  457. } else
  458. free(s->val);
  459. s->val = strdup(val);
  460. }
  461. void
  462. ds(char *name, char *val)
  463. {
  464. dsnr(name, val, &strings);
  465. }
  466. /* look up a defined string */
  467. char*
  468. getds(char *name)
  469. {
  470. String *s;
  471. for(s = strings; s != nil; s = s->next)
  472. if(strcmp(name, s->name) == 0)
  473. break;
  474. if(s != nil)
  475. return s->val;
  476. return "";
  477. }
  478. char *
  479. getnr(char *name)
  480. {
  481. String *s;
  482. for(s = numregs; s != nil; s = s->next)
  483. if(strcmp(name, s->name) == 0)
  484. break;
  485. if(s != nil)
  486. return s->val;
  487. return "0";
  488. }
  489. void
  490. pushstr(char *p)
  491. {
  492. if(p == nil)
  493. return;
  494. if(strsp >= Maxmstack - 1)
  495. return;
  496. strstack[++strsp] = p;
  497. }
  498. /* lookup a defined macro */
  499. char*
  500. getmacro(char *name)
  501. {
  502. String *s;
  503. for(s = macros; s != nil; s = s->next)
  504. if(strcmp(name, s->name) == 0)
  505. return s->val;
  506. return nil;
  507. }
  508. enum
  509. {
  510. Dstring,
  511. Macro,
  512. Input,
  513. };
  514. int lastsrc;
  515. void
  516. pushsrc(char *name)
  517. {
  518. Dir *d;
  519. int fd;
  520. if(ssp == &sstack[Maxsstack-1]){
  521. fprint(2, "ms2html: .so's too deep\n");
  522. return;
  523. }
  524. d = nil;
  525. if(name == nil){
  526. d = dirfstat(0);
  527. if(d == nil){
  528. fprint(2, "ms2html: can't stat %s: %r\n", name);
  529. return;
  530. }
  531. name = d->name;
  532. fd = 0;
  533. } else {
  534. fd = open(name, OREAD);
  535. if(fd < 0){
  536. fprint(2, "ms2html: can't open %s: %r\n", name);
  537. return;
  538. }
  539. }
  540. ssp++;
  541. ssp->fd = fd;
  542. Binit(&ssp->in, fd, OREAD);
  543. snprint(ssp->filename, sizeof(ssp->filename), "%s", name);
  544. ssp->lno = ssp->rlno = 1;
  545. free(d);
  546. }
  547. /* get next logical byte. from stdin or a defined string */
  548. int
  549. getrune(void)
  550. {
  551. int i;
  552. Rune r;
  553. int c;
  554. Mstack *m;
  555. while(strsp >= 0){
  556. i = chartorune(&r, strstack[strsp]);
  557. if(r != 0){
  558. strstack[strsp] += i;
  559. lastsrc = Dstring;
  560. return r;
  561. }
  562. if (mustfree[strsp]) {
  563. free(mustfree[strsp]);
  564. mustfree[strsp] = nil;
  565. }
  566. strsp--;
  567. }
  568. while(msp >= 0){
  569. m = &mstack[msp];
  570. i = chartorune(&r, m->ptr);
  571. if(r != 0){
  572. m->ptr += i;
  573. lastsrc = Macro;
  574. return r;
  575. }
  576. for(i = 0; m->argv[i] != nil; i++)
  577. free(m->argv[i]);
  578. msp--;
  579. }
  580. lastsrc = Input;
  581. do {
  582. if(ssp < sstack)
  583. return -1;
  584. c = Bgetrune(&ssp->in);
  585. if(c >= 0){
  586. r = c;
  587. break;
  588. }
  589. close(ssp->fd);
  590. ssp--;
  591. } while(r < 0);
  592. return r;
  593. }
  594. void
  595. ungetrune(void)
  596. {
  597. switch(lastsrc){
  598. case Dstring:
  599. if(strsp >= 0)
  600. strstack[strsp]--;
  601. break;
  602. case Macro:
  603. if(msp >= 0)
  604. mstack[msp].ptr--;
  605. break;
  606. case Input:
  607. if(ssp >= sstack)
  608. Bungetrune(&ssp->in);
  609. break;
  610. }
  611. }
  612. int vert;
  613. char*
  614. changefont(Font *f)
  615. {
  616. token[0] = 0;
  617. if(curfont != nil)
  618. strcpy(token, curfont->end);
  619. if(f != nil)
  620. strcat(token, f->start);
  621. prevfont = curfont;
  622. curfont = f;
  623. return token;
  624. }
  625. /* get next logical character. expand it with escapes */
  626. char*
  627. getnext(void)
  628. {
  629. int r;
  630. Entity *e;
  631. Troffspec *t;
  632. Rune R;
  633. char str[4];
  634. r = getrune();
  635. if(r < 0)
  636. return nil;
  637. if(r > 128 || r == '<' || r == '>'){
  638. for(e = entity; e->name; e++)
  639. if(e->value == r)
  640. return e->name;
  641. return "&#191;";
  642. }
  643. switch(r){
  644. case '\\':
  645. r = getrune();
  646. if(r < 0)
  647. return nil;
  648. switch(r){
  649. case ' ':
  650. return " ";
  651. /* chars to ignore */
  652. case '&':
  653. case '|':
  654. case '%':
  655. return "";
  656. /* small space in troff, nothing in nroff */
  657. case '^':
  658. return getnext();
  659. /* ignore arg */
  660. case 'k':
  661. getrune();
  662. return getnext();
  663. /* comment */
  664. case '"':
  665. while(getrune() != '\n')
  666. ;
  667. return "\n";
  668. /* ignore line */
  669. case '!':
  670. while(getrune() != '\n')
  671. ;
  672. ungetrune();
  673. return getnext();
  674. /* defined strings */
  675. case '*':
  676. r = getrune();
  677. if(r == '('){
  678. str[0] = getrune();
  679. str[1] = getrune();
  680. str[2] = 0;
  681. } else {
  682. str[0] = r;
  683. str[1] = 0;
  684. }
  685. pushstr(getds(str));
  686. return getnext();
  687. /* macro args */
  688. case '$':
  689. r = getrune();
  690. if(r < '1' || r > '9'){
  691. token[0] = '\\';
  692. token[1] = '$';
  693. token[2] = r;
  694. token[3] = 0;
  695. return token;
  696. }
  697. r -= '0';
  698. if(msp >= 0)
  699. pushstr(mstack[msp].argv[r]);
  700. return getnext();
  701. /* special chars */
  702. case '(':
  703. token[0] = getrune();
  704. token[1] = getrune();
  705. token[2] = 0;
  706. for(t = tspec; t->name; t++)
  707. if(strcmp(token, t->name) == 0)
  708. return t->value;
  709. return "&#191;";
  710. /* ignore immediately following newline */
  711. case 'c':
  712. r = getrune();
  713. if (r == '\n') {
  714. sol = ignore_nl = 1;
  715. if (indirective)
  716. break;
  717. }
  718. else
  719. ungetrune();
  720. return getnext();
  721. /* escape backslash */
  722. case 'e':
  723. return "\\";
  724. break;
  725. /* font change */
  726. case 'f':
  727. r = getrune();
  728. switch(r){
  729. case '(':
  730. str[0] = getrune();
  731. str[1] = getrune();
  732. str[2] = 0;
  733. token[0] = 0;
  734. if(strcmp("BI", str) == 0)
  735. return changefont(&bifont);
  736. else if(strcmp("CW", str) == 0)
  737. return changefont(&cwfont);
  738. else
  739. return changefont(nil);
  740. case '3':
  741. case 'B':
  742. return changefont(&bfont);
  743. case '2':
  744. case 'I':
  745. return changefont(&ifont);
  746. case '4':
  747. return changefont(&bifont);
  748. case '5':
  749. return changefont(&cwfont);
  750. case 'P':
  751. return changefont(prevfont);
  752. case 'R':
  753. default:
  754. return changefont(nil);
  755. }
  756. /* number register */
  757. case 'n':
  758. r = getrune();
  759. if (r == '(') /*)*/ {
  760. r = getrune();
  761. if (r < 0)
  762. return nil;
  763. str[0] = r;
  764. r = getrune();
  765. if (r < 0)
  766. return nil;
  767. str[1] = r;
  768. str[2] = 0;
  769. }
  770. else {
  771. str[0] = r;
  772. str[1] = 0;
  773. }
  774. pushstr(getnr(str));
  775. return getnext();
  776. /* font size */
  777. case 's':
  778. getnext(); /* ignore size change */
  779. return getnext();
  780. /* vertical movement */
  781. case 'v':
  782. r = getrune();
  783. if(r != '\''){
  784. ungetrune();
  785. return getnext();
  786. }
  787. r = getrune();
  788. if(r != '-')
  789. vert--;
  790. else
  791. vert++;
  792. while(r != '\'' && r != '\n')
  793. r = getrune();
  794. if(r != '\'')
  795. ungetrune();
  796. if(vert > 0)
  797. return "^";
  798. return getnext();
  799. /* horizontal line */
  800. case 'l':
  801. r = getrune();
  802. if(r != '\''){
  803. ungetrune();
  804. return "<HR>";
  805. }
  806. while(getrune() != '\'')
  807. ;
  808. return "<HR>";
  809. /* character height and slant */
  810. case 'S':
  811. case 'H':
  812. r = getrune();
  813. if(r != '\''){
  814. ungetrune();
  815. return "<HR>";
  816. }
  817. while(getrune() != '\'')
  818. ;
  819. return getnext();
  820. /* digit-width space */
  821. case '0':
  822. return " ";
  823. /*for .if, .ie, .el */
  824. case '{':
  825. return "\\{"; /*}*/
  826. case '}':
  827. return "";
  828. /* up and down */
  829. case 'u':
  830. if (isdown) {
  831. isdown = 0;
  832. return "</sub>";
  833. }
  834. isup = 1;
  835. return "<sup>";
  836. case 'd':
  837. if (isup) {
  838. isup = 0;
  839. return "</sup>";
  840. }
  841. isdown = 1;
  842. return "<sub>";
  843. }
  844. break;
  845. case '&':
  846. if(msp >= 0 || strsp >= 0)
  847. return "&";
  848. return "&amp;";
  849. case '<':
  850. if(msp >= 0 || strsp >= 0)
  851. return "<";
  852. return "&#60;";
  853. case '>':
  854. if(msp >= 0 || strsp >= 0)
  855. return ">";
  856. return "&#62;";
  857. }
  858. if (r < Runeself) {
  859. token[0] = r;
  860. token[1] = 0;
  861. }
  862. else {
  863. R = r;
  864. token[runetochar(token,&R)] = 0;
  865. }
  866. return token;
  867. }
  868. char*
  869. copyline(char *p, char *e, int arg0)
  870. {
  871. int c;
  872. Rune r;
  873. char *p1;
  874. while((c = getrune()) == ' ' || c == '\t')
  875. ;
  876. for(indirective = 1; p < e; c = getrune()) {
  877. if (c < 0)
  878. goto done;
  879. switch(c) {
  880. case '\\':
  881. break;
  882. case '\n':
  883. if (arg0)
  884. ungetrune();
  885. goto done;
  886. case ' ':
  887. case '\t':
  888. if (arg0)
  889. goto done;
  890. default:
  891. r = c;
  892. p += runetochar(p,&r);
  893. continue;
  894. }
  895. ungetrune();
  896. p1 = getnext();
  897. if (p1 == nil)
  898. goto done;
  899. if (*p1 == '\n') {
  900. if (arg0)
  901. ungetrune();
  902. break;
  903. }
  904. while((*p = *p1++) && p < e)
  905. p++;
  906. }
  907. done:
  908. indirective = 0;
  909. *p++ = 0;
  910. return p;
  911. }
  912. char*
  913. copyarg(char *p, char *e, int *nullarg)
  914. {
  915. int c, quoted;
  916. Rune r;
  917. char *p1;
  918. *nullarg = 0;
  919. quoted = 0;
  920. do{
  921. c = getrune();
  922. } while(c == ' ' || c == '\t');
  923. if(c == '"'){
  924. quoted = 1;
  925. *nullarg = 1;
  926. c = getrune();
  927. }
  928. if(c == '\n')
  929. goto done;
  930. for(; p < e; c = getrune()) {
  931. if (c < 0)
  932. break;
  933. switch(c) {
  934. case '\\':
  935. break;
  936. case '\n':
  937. ungetrune();
  938. goto done;
  939. case ' ':
  940. case '\t':
  941. if(!quoted)
  942. goto done;
  943. r = c;
  944. p += runetochar(p,&r);
  945. continue;
  946. case '"':
  947. if(quoted)
  948. goto done;
  949. r = c;
  950. p += runetochar(p,&r);
  951. continue;
  952. default:
  953. r = c;
  954. p += runetochar(p,&r);
  955. continue;
  956. }
  957. ungetrune();
  958. p1 = getnext();
  959. if(p1 == nil)
  960. break;
  961. if(*p1 == '\n')
  962. break;
  963. while((*p = *p1++) && p < e)
  964. p++;
  965. }
  966. done:
  967. *p++ = 0;
  968. return p;
  969. }
  970. int
  971. parseargs(char *p, char *e, char **argv)
  972. {
  973. int argc;
  974. char *np;
  975. int nullarg;
  976. indirective = 1;
  977. *p++ = 0;
  978. for(argc = 1; argc < Narg; argc++){
  979. np = copyarg(p, e, &nullarg);
  980. if(nullarg==0 && np == p+1)
  981. break;
  982. argv[argc] = p;
  983. p = np;
  984. }
  985. argv[argc] = nil;
  986. indirective = 0;
  987. return argc;
  988. }
  989. void
  990. dodirective(void)
  991. {
  992. char *p, *e;
  993. Goobie *g;
  994. Goobieif *gif;
  995. char line[Nline], *line1;
  996. int i, argc;
  997. char *argv[Narg];
  998. Mstack *m;
  999. /* read line, translate special bytes */
  1000. e = line + sizeof(line) - UTFmax - 1;
  1001. line1 = copyline(line, e, 1);
  1002. if (!line[0])
  1003. return;
  1004. argv[0] = line;
  1005. /* first look through user defined macros */
  1006. p = getmacro(argv[0]);
  1007. if(p != nil){
  1008. if(msp == Maxmstack-1){
  1009. fprint(2, "ms2html: macro stack overflow\n");
  1010. return;
  1011. }
  1012. argc = parseargs(line1, e, argv);
  1013. m = &mstack[++msp];
  1014. m->ptr = p;
  1015. memset(m->argv, 0, sizeof(m->argv));
  1016. for(i = 0; i < argc; i++)
  1017. m->argv[i] = strdup(argv[i]);
  1018. return;
  1019. }
  1020. /* check for .if or .ie */
  1021. for(gif = gtabif; gif->name; gif++)
  1022. if(strcmp(gif->name, argv[0]) == 0){
  1023. (*gif->f)(line1, e);
  1024. return;
  1025. }
  1026. argc = parseargs(line1, e, argv);
  1027. /* try standard ms macros */
  1028. for(g = gtab; g->name; g++)
  1029. if(strcmp(g->name, argv[0]) == 0){
  1030. (*g->f)(argc, argv);
  1031. return;
  1032. }
  1033. if(debug)
  1034. fprint(2, "stdin %d(%s:%d): unknown directive %s\n",
  1035. ssp->lno, ssp->filename, ssp->rlno, line);
  1036. }
  1037. void
  1038. printarg(char *a)
  1039. {
  1040. char *e, *p;
  1041. e = a + strlen(a);
  1042. pushstr(a);
  1043. while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){
  1044. p = getnext();
  1045. if(p == nil)
  1046. return;
  1047. Bprint(&bout, "%s", p);
  1048. }
  1049. }
  1050. void
  1051. printargs(int argc, char **argv)
  1052. {
  1053. argc--;
  1054. argv++;
  1055. while(--argc > 0){
  1056. printarg(*argv++);
  1057. Bprint(&bout, " ");
  1058. }
  1059. if(argc == 0)
  1060. printarg(*argv);
  1061. }
  1062. void
  1063. dohangingdt(void)
  1064. {
  1065. switch(hangingdt){
  1066. case 3:
  1067. hangingdt--;
  1068. break;
  1069. case 2:
  1070. Bprint(&bout, "<dd>");
  1071. hangingdt = 0;
  1072. break;
  1073. }
  1074. }
  1075. void
  1076. dohangingau(void)
  1077. {
  1078. if(hangingau == 0)
  1079. return;
  1080. Bprint(&bout, "</I></DL>\n");
  1081. hangingau = 0;
  1082. }
  1083. void
  1084. dohanginghead(void)
  1085. {
  1086. if(hanginghead == 0)
  1087. return;
  1088. Bprint(&bout, "</H%d>\n", hanginghead);
  1089. hanginghead = 0;
  1090. }
  1091. /*
  1092. * convert a man page to html and output
  1093. */
  1094. void
  1095. doconvert(void)
  1096. {
  1097. char c, *p;
  1098. Tm *t;
  1099. pushsrc(nil);
  1100. sol = 1;
  1101. Bprint(&bout, "<html>\n");
  1102. Bflush(&bout);
  1103. for(;;){
  1104. p = getnext();
  1105. if(p == nil)
  1106. break;
  1107. c = *p;
  1108. if(c == '.' && sol){
  1109. dodirective();
  1110. dohangingdt();
  1111. ssp->lno++;
  1112. ssp->rlno++;
  1113. sol = 1;
  1114. } else if(c == '\n'){
  1115. if (ignore_nl)
  1116. ignore_nl = 0;
  1117. else {
  1118. if(hangingau)
  1119. Bprint(&bout, "<br>\n");
  1120. else
  1121. Bprint(&bout, "%s", p);
  1122. dohangingdt();
  1123. }
  1124. ssp->lno++;
  1125. ssp->rlno++;
  1126. sol = 1;
  1127. } else{
  1128. Bprint(&bout, "%s", p);
  1129. ignore_nl = sol = 0;
  1130. }
  1131. }
  1132. dohanginghead();
  1133. dohangingdt();
  1134. closel();
  1135. if(curfont)
  1136. Bprint(&bout, "%s", curfont->end);
  1137. Bprint(&bout, "<br>&#32;<br>\n");
  1138. Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n");
  1139. t = localtime(time(nil));
  1140. Bprint(&bout, "Copyright</A> &#169; %d Lucent Technologies Inc. All rights reserved.\n",
  1141. t->year+1900);
  1142. Bprint(&bout, "</body></html>\n");
  1143. }
  1144. void
  1145. main(int argc, char **argv)
  1146. {
  1147. quiet = 1;
  1148. if(argc > 1)
  1149. if(strcmp(argv[1], "-v") == 0)
  1150. quiet = 0;
  1151. Binit(&bout, 1, OWRITE);
  1152. ds("R", "&#174;");
  1153. doconvert();
  1154. exits(nil);
  1155. }
  1156. void
  1157. g_notyet(int, char **argv)
  1158. {
  1159. fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
  1160. }
  1161. void
  1162. g_ignore(int, char **argv)
  1163. {
  1164. if(quiet)
  1165. return;
  1166. fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
  1167. }
  1168. void
  1169. g_PP(int, char**)
  1170. {
  1171. dohanginghead();
  1172. closel();
  1173. closefont();
  1174. Bprint(&bout, "<P>\n");
  1175. paragraph = 1;
  1176. }
  1177. void
  1178. g_LP(int, char**)
  1179. {
  1180. dohanginghead();
  1181. closel();
  1182. closefont();
  1183. Bprint(&bout, "<br>&#32;<br>\n");
  1184. }
  1185. /* close a list */
  1186. void
  1187. closel(void)
  1188. {
  1189. g_P2(1, nil);
  1190. dohangingau();
  1191. if(paragraph){
  1192. Bprint(&bout, "</P>\n");
  1193. paragraph = 0;
  1194. }
  1195. switch(list){
  1196. case Lordered:
  1197. Bprint(&bout, "</ol>\n");
  1198. break;
  1199. case Lunordered:
  1200. Bprint(&bout, "</ul>\n");
  1201. break;
  1202. case Lother:
  1203. case Ldef:
  1204. Bprint(&bout, "</dl>\n");
  1205. break;
  1206. }
  1207. list = 0;
  1208. }
  1209. void
  1210. g_IP(int argc, char **argv)
  1211. {
  1212. switch(list){
  1213. default:
  1214. closel();
  1215. if(argc > 1){
  1216. if(strcmp(argv[1], "1") == 0){
  1217. list = Lordered;
  1218. listnum = 1;
  1219. Bprint(&bout, "<OL>\n");
  1220. } else if(strcmp(argv[1], "\\(bu") == 0){
  1221. list = Lunordered;
  1222. Bprint(&bout, "<UL>\n");
  1223. } else {
  1224. list = Lother;
  1225. Bprint(&bout, "<DL COMPACT>\n");
  1226. }
  1227. } else {
  1228. list = Lother;
  1229. Bprint(&bout, "<DL>\n");
  1230. }
  1231. break;
  1232. case Lother:
  1233. case Lordered:
  1234. case Lunordered:
  1235. break;
  1236. }
  1237. switch(list){
  1238. case Lother:
  1239. Bprint(&bout, "<DT>");
  1240. if(argc > 1)
  1241. printarg(argv[1]);
  1242. else
  1243. Bprint(&bout, "<DT>&#32;");
  1244. Bprint(&bout, "<DD>\n");
  1245. break;
  1246. case Lordered:
  1247. case Lunordered:
  1248. Bprint(&bout, "<LI>\n");
  1249. break;
  1250. }
  1251. }
  1252. /*
  1253. * .5i is one <DL><DT><DD>
  1254. */
  1255. void
  1256. g_in(int argc, char **argv)
  1257. {
  1258. float f;
  1259. int delta, x;
  1260. char *p;
  1261. f = indent/0.5;
  1262. delta = f;
  1263. if(argc <= 1){
  1264. indent = 0.0;
  1265. } else {
  1266. f = strtod(argv[1], &p);
  1267. switch(*p){
  1268. case 'i':
  1269. break;
  1270. case 'c':
  1271. f = f / 2.54;
  1272. break;
  1273. case 'P':
  1274. f = f / 6;
  1275. break;
  1276. default:
  1277. case 'u':
  1278. case 'm':
  1279. f = f * (12 / 72);
  1280. break;
  1281. case 'n':
  1282. f = f * (6 / 72);
  1283. break;
  1284. case 'p':
  1285. f = f / 72.0;
  1286. break;
  1287. }
  1288. switch(argv[1][0]){
  1289. case '+':
  1290. case '-':
  1291. indent += f;
  1292. break;
  1293. default:
  1294. indent = f;
  1295. break;
  1296. }
  1297. }
  1298. if(indent < 0.0)
  1299. indent = 0.0;
  1300. f = (indent/0.5);
  1301. x = f;
  1302. delta = x - delta;
  1303. while(delta < 0){
  1304. Bprint(&bout, "</DL>\n");
  1305. delta++;
  1306. }
  1307. while(delta > 0){
  1308. Bprint(&bout, "<DL><DT><DD>\n");
  1309. delta--;
  1310. }
  1311. }
  1312. void
  1313. g_HP(int, char**)
  1314. {
  1315. switch(list){
  1316. default:
  1317. closel();
  1318. list = Ldef;
  1319. hangingdt = 1;
  1320. Bprint(&bout, "<DL><DT>\n");
  1321. break;
  1322. case Ldef:
  1323. if(hangingdt)
  1324. Bprint(&bout, "<DD>");
  1325. Bprint(&bout, "<DT>");
  1326. hangingdt = 1;
  1327. break;
  1328. }
  1329. }
  1330. void
  1331. g_SH(int, char**)
  1332. {
  1333. dohanginghead();
  1334. closel();
  1335. closefont();
  1336. Bprint(&bout, "<H%d>", HH);
  1337. hanginghead = HH;
  1338. }
  1339. void
  1340. g_NH(int argc, char **argv)
  1341. {
  1342. int i, level;
  1343. closel();
  1344. closefont();
  1345. if(argc == 1)
  1346. level = 0;
  1347. else {
  1348. level = atoi(argv[1])-1;
  1349. if(level < 0 || level >= Maxnh)
  1350. level = Maxnh - 1;
  1351. }
  1352. nh[level]++;
  1353. Bprint(&bout, "<H%d>", HH);
  1354. hanginghead = HH;
  1355. Bprint(&bout, "%d", nh[0]);
  1356. for(i = 1; i <= level; i++)
  1357. Bprint(&bout, ".%d", nh[i]);
  1358. Bprint(&bout, " ");
  1359. for(i = level+1; i < Maxnh; i++)
  1360. nh[i] = 0;
  1361. }
  1362. void
  1363. g_TL(int, char**)
  1364. {
  1365. char *p, *np;
  1366. char name[128];
  1367. closefont();
  1368. if(!titleseen){
  1369. /* get base part of filename */
  1370. p = strrchr(ssp->filename, '/');
  1371. if(p == nil)
  1372. p = ssp->filename;
  1373. else
  1374. p++;
  1375. strncpy(name, p, sizeof(name));
  1376. name[sizeof(name)-1] = 0;
  1377. /* dump any extensions */
  1378. np = strchr(name, '.');
  1379. if(np)
  1380. *np = 0;
  1381. Bprint(&bout, "<title>\n");
  1382. Bprint(&bout, "%s\n", p);
  1383. Bprint(&bout, "</title>\n");
  1384. Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n");
  1385. titleseen = 1;
  1386. }
  1387. Bprint(&bout, "<H%d>", 1);
  1388. hanginghead = 1;
  1389. }
  1390. void
  1391. g_AU(int, char**)
  1392. {
  1393. closel();
  1394. dohanginghead();
  1395. Bprint(&bout, "<DL><DD><I>");
  1396. hangingau = 1;
  1397. }
  1398. void
  1399. setfont(Font *f)
  1400. {
  1401. if(curfont != nil)
  1402. Bprint(&bout, "%s", curfont->end);
  1403. prevfont = curfont;
  1404. if(f != nil)
  1405. Bprint(&bout, "%s", f->start);
  1406. curfont = f;
  1407. }
  1408. /*
  1409. * for 3 args print arg3 \fxarg1\fP arg2
  1410. * for 2 args print arg1 \fxarg2\fP
  1411. * for 1 args print \fxarg1\fP
  1412. */
  1413. void
  1414. font(Font *f, int argc, char **argv)
  1415. {
  1416. Font *prev;
  1417. if(argc == 1){
  1418. setfont(nil);
  1419. return;
  1420. }
  1421. if(argc > 3)
  1422. printarg(argv[3]);
  1423. prev = prevfont;
  1424. setfont(f);
  1425. printarg(argv[1]);
  1426. setfont(prevfont);
  1427. prevfont = prev;
  1428. if(argc > 2)
  1429. printarg(argv[2]);
  1430. Bprint(&bout, "\n");
  1431. }
  1432. void
  1433. closefont(void)
  1434. {
  1435. if(curfont != nil)
  1436. Bprint(&bout, "%s", curfont->end);
  1437. curfont = nil;
  1438. prevfont = nil;
  1439. }
  1440. void
  1441. g_B(int argc, char **argv)
  1442. {
  1443. font(&bfont, argc, argv);
  1444. }
  1445. void
  1446. g_R(int argc, char **argv)
  1447. {
  1448. font(nil, argc, argv);
  1449. }
  1450. void
  1451. g_BI(int argc, char **argv)
  1452. {
  1453. font(&bifont, argc, argv);
  1454. }
  1455. void
  1456. g_CW(int argc, char **argv)
  1457. {
  1458. font(&cwfont, argc, argv);
  1459. }
  1460. char*
  1461. lower(char *p)
  1462. {
  1463. char *x;
  1464. for(x = p; *x; x++)
  1465. if(*x >= 'A' && *x <= 'Z')
  1466. *x -= 'A' - 'a';
  1467. return p;
  1468. }
  1469. void
  1470. g_I(int argc, char **argv)
  1471. {
  1472. int anchor;
  1473. char *p;
  1474. anchor = 0;
  1475. if(argc > 2){
  1476. p = argv[2];
  1477. if(p[0] == '(')
  1478. if(p[1] >= '0' && p[1] <= '9')
  1479. if(p[2] == ')'){
  1480. anchor = 1;
  1481. Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">",
  1482. p[1], lower(argv[1]));
  1483. }
  1484. }
  1485. font(&ifont, argc, argv);
  1486. if(anchor)
  1487. Bprint(&bout, "</A>");
  1488. }
  1489. void
  1490. g_br(int, char**)
  1491. {
  1492. if(hangingdt){
  1493. Bprint(&bout, "<dd>");
  1494. hangingdt = 0;
  1495. }else
  1496. Bprint(&bout, "<br>\n");
  1497. }
  1498. void
  1499. g_P1(int, char**)
  1500. {
  1501. if(example == 0){
  1502. example = 1;
  1503. Bprint(&bout, "<DL><DT><DD><TT><PRE>\n");
  1504. }
  1505. }
  1506. void
  1507. g_P2(int, char**)
  1508. {
  1509. if(example){
  1510. example = 0;
  1511. Bprint(&bout, "</PRE></TT></DL>\n");
  1512. }
  1513. }
  1514. void
  1515. g_SM(int, char **argv)
  1516. {
  1517. Bprint(&bout, "%s", argv[1]);
  1518. }
  1519. void
  1520. g_ft(int argc, char **argv)
  1521. {
  1522. if(argc < 2){
  1523. setfont(nil);
  1524. return;
  1525. }
  1526. switch(argv[1][0]){
  1527. case '3':
  1528. case 'B':
  1529. setfont(&bfont);
  1530. break;
  1531. case '2':
  1532. case 'I':
  1533. setfont(&ifont);
  1534. break;
  1535. case '4':
  1536. setfont(&bifont);
  1537. break;
  1538. case '5':
  1539. setfont(&cwfont);
  1540. break;
  1541. case 'P':
  1542. setfont(prevfont);
  1543. break;
  1544. case 'R':
  1545. default:
  1546. setfont(nil);
  1547. break;
  1548. }
  1549. }
  1550. void
  1551. g_sp(int argc, char **argv)
  1552. {
  1553. int n;
  1554. n = 1;
  1555. if(argc > 1){
  1556. n = atoi(argv[1]);
  1557. if(n < 1)
  1558. n = 1;
  1559. if(argv[1][strlen(argv[1])-1] == 'i')
  1560. n *= 4;
  1561. }
  1562. if(n > 5){
  1563. Bprint(&bout, "<br>&#32;<br>\n");
  1564. Bprint(&bout, "<HR>\n");
  1565. Bprint(&bout, "<br>&#32;<br>\n");
  1566. } else
  1567. for(; n > 0; n--)
  1568. Bprint(&bout, "<br>&#32;<br>\n");
  1569. }
  1570. void
  1571. rm_loop(char *name, String **l)
  1572. {
  1573. String *s;
  1574. for(s = *l; s != nil; s = *l){
  1575. if(strcmp(name, s->name) == 0){
  1576. *l = s->next;
  1577. free(s->name);
  1578. free(s->val);
  1579. free(s);
  1580. break;
  1581. }
  1582. l = &s->next;
  1583. }
  1584. }
  1585. void
  1586. g_rm(int argc, char **argv)
  1587. {
  1588. Goobie *g;
  1589. char *name;
  1590. int i;
  1591. for(i = 1; i < argc; i++) {
  1592. name = argv[i];
  1593. rm_loop(name, &strings);
  1594. rm_loop(name, &macros);
  1595. for(g = gtab; g->name; g++)
  1596. if (strcmp(g->name, name) == 0) {
  1597. g->f = g_ignore;
  1598. break;
  1599. }
  1600. }
  1601. }
  1602. void
  1603. g_AB(int, char**)
  1604. {
  1605. closel();
  1606. Bprint(&bout, "<DL><DD><H4>ABSTRACT</H4>\n");
  1607. }
  1608. void
  1609. g_AE(int, char**)
  1610. {
  1611. Bprint(&bout, "</DL>\n");
  1612. }
  1613. void
  1614. g_FS(int, char **)
  1615. {
  1616. char *argv[3];
  1617. argv[0] = "IP";
  1618. argv[1] = nil;
  1619. argv[2] = nil;
  1620. g_IP(1, argv);
  1621. Bprint(&bout, "NOTE:<I> ");
  1622. }
  1623. void
  1624. g_FE(int, char **)
  1625. {
  1626. Bprint(&bout, "</I><DT>&#32;<DD>");
  1627. closel();
  1628. Bprint(&bout, "<br>\n");
  1629. }
  1630. void
  1631. g_de(int argc, char **argv)
  1632. {
  1633. int r;
  1634. char *p, *cp;
  1635. String *m;
  1636. int len;
  1637. if(argc < 2)
  1638. return;
  1639. m = nil;
  1640. len = 0;
  1641. if(strcmp(argv[0], "am") == 0){
  1642. for(m = macros; m != nil; m = m->next)
  1643. if(strcmp(argv[1], m->name) == 0){
  1644. len = strlen(m->val);
  1645. break;
  1646. }
  1647. if(m == nil){
  1648. /* nothing to append to */
  1649. for(;;){
  1650. p = Brdline(&ssp->in, '\n');
  1651. if(p == nil)
  1652. break;
  1653. p[Blinelen(&ssp->in)-1] = 0;
  1654. if(strcmp(p, "..") == 0)
  1655. break;
  1656. }
  1657. return;
  1658. }
  1659. }
  1660. if(m == nil){
  1661. m = emalloc(sizeof(*m));
  1662. m->next = macros;
  1663. macros = m;
  1664. m->name = strdup(argv[1]);
  1665. m->val = nil;
  1666. len = 0;
  1667. }
  1668. /* read up to a .. removing double backslashes */
  1669. for(;;){
  1670. p = Brdline(&ssp->in, '\n');
  1671. if(p == nil)
  1672. break;
  1673. p[Blinelen(&ssp->in)-1] = 0;
  1674. if(strcmp(p, "..") == 0)
  1675. break;
  1676. m->val = realloc(m->val, len + Blinelen(&ssp->in)+1);
  1677. cp = m->val + len;
  1678. while(*p){
  1679. r = *p++;
  1680. if(r == '\\' && *p == '\\')
  1681. p++;
  1682. *cp++ = r;
  1683. }
  1684. *cp++ = '\n';
  1685. len = cp - m->val;
  1686. *cp = 0;
  1687. }
  1688. }
  1689. void
  1690. g_hrule(int, char**)
  1691. {
  1692. Bprint(&bout, "<HR>\n");
  1693. }
  1694. void
  1695. g_BX(int argc, char **argv)
  1696. {
  1697. Bprint(&bout, "<HR>\n");
  1698. printargs(argc, argv);
  1699. Bprint(&bout, "<HR>\n");
  1700. }
  1701. void
  1702. g_IH(int, char**)
  1703. {
  1704. Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
  1705. }
  1706. void
  1707. g_MH(int, char**)
  1708. {
  1709. Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
  1710. }
  1711. void
  1712. g_PY(int, char**)
  1713. {
  1714. Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
  1715. }
  1716. void
  1717. g_HO(int, char**)
  1718. {
  1719. Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
  1720. }
  1721. void
  1722. g_QS(int, char**)
  1723. {
  1724. Bprint(&bout, "<BLOCKQUOTE>\n");
  1725. }
  1726. void
  1727. g_QE(int, char**)
  1728. {
  1729. Bprint(&bout, "</BLOCKQUOTE>\n");
  1730. }
  1731. void
  1732. g_RS(int, char**)
  1733. {
  1734. Bprint(&bout, "<DL><DD>\n");
  1735. }
  1736. void
  1737. g_RE(int, char**)
  1738. {
  1739. Bprint(&bout, "</DL>\n");
  1740. }
  1741. int gif;
  1742. void
  1743. g_startgif(int, char **argv)
  1744. {
  1745. int fd;
  1746. int pfd[2];
  1747. char *e, *p;
  1748. char name[32];
  1749. Dir *d;
  1750. if(strcmp(argv[0], "EQ") == 0)
  1751. e = ".EN";
  1752. else if(strcmp(argv[0], "TS") == 0)
  1753. e = ".TE";
  1754. else if(strcmp(argv[0], "PS") == 0)
  1755. e = ".PE";
  1756. else
  1757. return;
  1758. p = strrchr(sstack[0].filename, '/');
  1759. if(p != nil)
  1760. p++;
  1761. else
  1762. p = sstack[0].filename;
  1763. snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
  1764. fd = create(name, OWRITE, 0664);
  1765. if(fd < 0){
  1766. fprint(2, "ms2html: can't create %s: %r\n", name);
  1767. return;
  1768. }
  1769. if(pipe(pfd) < 0){
  1770. fprint(2, "ms2html: can't create pipe: %r\n");
  1771. close(fd);
  1772. return;
  1773. }
  1774. switch(rfork(RFFDG|RFPROC)){
  1775. case -1:
  1776. fprint(2, "ms2html: can't fork: %r\n");
  1777. close(fd);
  1778. return;
  1779. case 0:
  1780. dup(fd, 1);
  1781. close(fd);
  1782. dup(pfd[0], 0);
  1783. close(pfd[0]);
  1784. close(pfd[1]);
  1785. execl("/bin/troff2gif", "troff2gif", 0);
  1786. fprint(2, "ms2html: couldn't exec troff2gif: %r\n");
  1787. _exits(nil);
  1788. default:
  1789. close(fd);
  1790. close(pfd[0]);
  1791. fprint(pfd[1], ".ll 7i\n");
  1792. for(;;){
  1793. p = Brdline(&ssp->in, '\n');
  1794. if(p == nil)
  1795. break;
  1796. ssp->lno++;
  1797. ssp->rlno++;
  1798. if(strncmp(p, e, 3) == 0)
  1799. break;
  1800. if(write(pfd[1], p, Blinelen(&ssp->in)) < 0)
  1801. break;
  1802. }
  1803. close(pfd[1]);
  1804. waitpid();
  1805. d = dirstat(name);
  1806. if(d == nil)
  1807. break;
  1808. if(d->length == 0){
  1809. remove(name);
  1810. free(d);
  1811. break;
  1812. }
  1813. free(d);
  1814. fprint(2, "ms2html: created auxiliary file %s\n", name);
  1815. Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
  1816. break;
  1817. }
  1818. }
  1819. void
  1820. g_lf(int argc, char **argv)
  1821. {
  1822. if(argc > 2)
  1823. snprint(ssp->filename, sizeof(ssp->filename), argv[2]);
  1824. if(argc > 1)
  1825. ssp->rlno = atoi(argv[1]);
  1826. }
  1827. void
  1828. g_so(int argc, char **argv)
  1829. {
  1830. ssp->lno++;
  1831. ssp->rlno++;
  1832. if(argc > 1)
  1833. pushsrc(argv[1]);
  1834. }
  1835. void
  1836. g_BP(int argc, char **argv)
  1837. {
  1838. int fd;
  1839. char *p, *ext;
  1840. char name[32];
  1841. Dir *d;
  1842. if(argc < 2)
  1843. return;
  1844. p = strrchr(argv[1], '/');
  1845. if(p != nil)
  1846. p++;
  1847. else
  1848. p = argv[1];
  1849. ext = strrchr(p, '.');
  1850. if(ext){
  1851. if(strcmp(ext, ".jpeg") == 0
  1852. || strcmp(ext, ".gif") == 0){
  1853. Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]);
  1854. return;
  1855. }
  1856. }
  1857. snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
  1858. fd = create(name, OWRITE, 0664);
  1859. if(fd < 0){
  1860. fprint(2, "ms2html: can't create %s: %r\n", name);
  1861. return;
  1862. }
  1863. switch(rfork(RFFDG|RFPROC)){
  1864. case -1:
  1865. fprint(2, "ms2html: can't fork: %r\n");
  1866. close(fd);
  1867. return;
  1868. case 0:
  1869. dup(fd, 1);
  1870. close(fd);
  1871. execl("/bin/ps2gif", "ps2gif", argv[1], 0);
  1872. fprint(2, "ms2html: couldn't exec ps2gif: %r\n");
  1873. _exits(nil);
  1874. default:
  1875. close(fd);
  1876. waitpid();
  1877. d = dirstat(name);
  1878. if(d == nil)
  1879. break;
  1880. if(d->length == 0){
  1881. remove(name);
  1882. free(d);
  1883. break;
  1884. }
  1885. free(d);
  1886. fprint(2, "ms2html: created auxiliary file %s\n", name);
  1887. Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
  1888. break;
  1889. }
  1890. }
  1891. /* insert straight HTML into output */
  1892. void
  1893. g__H(int argc, char **argv)
  1894. {
  1895. int i;
  1896. for(i = 1; i < argc; i++)
  1897. Bprint(&bout, "%s ", argv[i]);
  1898. Bprint(&bout, "\n");
  1899. }
  1900. /* HTML page title */
  1901. void
  1902. g__T(int argc, char **argv)
  1903. {
  1904. if(titleseen)
  1905. return;
  1906. Bprint(&bout, "<title>\n");
  1907. printargs(argc, argv);
  1908. Bprint(&bout, "</title></head><body>\n");
  1909. titleseen = 1;
  1910. }
  1911. void
  1912. g_nr(int argc, char **argv)
  1913. {
  1914. char *val;
  1915. if (argc > 1) {
  1916. if (argc == 2)
  1917. val = "0";
  1918. else
  1919. val = argv[2];
  1920. dsnr(argv[1], val, &numregs);
  1921. }
  1922. }
  1923. void
  1924. zerodivide(void)
  1925. {
  1926. fprint(2, "stdin %d(%s:%d): division by 0\n",
  1927. ssp->lno, ssp->filename, ssp->rlno);
  1928. }
  1929. int
  1930. numval(char **pline, int recur)
  1931. {
  1932. char *p;
  1933. int neg, x, y;
  1934. x = neg = 0;
  1935. p = *pline;
  1936. while(*p == '-') {
  1937. neg = 1 - neg;
  1938. p++;
  1939. }
  1940. if (*p == '(') {
  1941. p++;
  1942. x = numval(&p, 1);
  1943. if (*p != ')')
  1944. goto done;
  1945. p++;
  1946. }
  1947. else while(*p >= '0' && *p <= '9')
  1948. x = 10*x + *p++ - '0';
  1949. if (neg)
  1950. x = -x;
  1951. if (recur)
  1952. for(;;) {
  1953. switch(*p++) {
  1954. case '+':
  1955. x += numval(&p, 0);
  1956. continue;
  1957. case '-':
  1958. x -= numval(&p, 0);
  1959. continue;
  1960. case '*':
  1961. x *= numval(&p, 0);
  1962. continue;
  1963. case '/':
  1964. y = numval(&p, 0);
  1965. if (y == 0) {
  1966. zerodivide();
  1967. x = 0;
  1968. goto done;
  1969. }
  1970. x /= y;
  1971. continue;
  1972. case '<':
  1973. if (*p == '=') {
  1974. p++;
  1975. x = x <= numval(&p, 0);
  1976. continue;
  1977. }
  1978. x = x < numval(&p, 0);
  1979. continue;
  1980. case '>':
  1981. if (*p == '=') {
  1982. p++;
  1983. x = x >= numval(&p, 0);
  1984. continue;
  1985. }
  1986. x = x > numval(&p, 0);
  1987. continue;
  1988. case '=':
  1989. if (*p == '=')
  1990. p++;
  1991. x = x == numval(&p, 0);
  1992. continue;
  1993. case '&':
  1994. x &= numval(&p, 0);
  1995. continue;
  1996. case ':':
  1997. x |= numval(&p, 0);
  1998. continue;
  1999. case '%':
  2000. y = numval(&p, 0);
  2001. if (!y) {
  2002. zerodivide();
  2003. goto done;
  2004. }
  2005. x %= y;
  2006. continue;
  2007. }
  2008. --p;
  2009. break;
  2010. }
  2011. done:
  2012. *pline = p;
  2013. return x;
  2014. }
  2015. int
  2016. iftest(char *p, char **bp)
  2017. {
  2018. char *p1;
  2019. int c, neg, rv;
  2020. rv = neg = 0;
  2021. if (*p == '!') {
  2022. neg = 1;
  2023. p++;
  2024. }
  2025. c = *p;
  2026. if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) {
  2027. if (numval(&p,1) >= 1)
  2028. rv = 1;
  2029. goto done;
  2030. }
  2031. switch(c) {
  2032. case 't':
  2033. case 'o':
  2034. rv = 1;
  2035. case 'n':
  2036. case 'e':
  2037. p++;
  2038. goto done;
  2039. }
  2040. for(p1 = ++p; *p != c; p++)
  2041. if (!*p)
  2042. goto done;
  2043. for(p++;;) {
  2044. if (*p != *p1++) {
  2045. while(*p && *p++ != c);
  2046. goto done;
  2047. }
  2048. if (*p++ == c)
  2049. break;
  2050. }
  2051. rv = 1;
  2052. done:
  2053. if (neg)
  2054. rv = 1 - rv;
  2055. while(*p == ' ' || *p == '\t')
  2056. p++;
  2057. *bp = p;
  2058. return rv;
  2059. }
  2060. void
  2061. scanline(char *p, char *e, int wantnl)
  2062. {
  2063. int c;
  2064. Rune r;
  2065. while((c = getrune()) == ' ' || c == '\t') ;
  2066. while(p < e) {
  2067. if (c < 0)
  2068. break;
  2069. if (c < Runeself) {
  2070. if (c == '\n') {
  2071. if (wantnl)
  2072. *p++ = c;
  2073. break;
  2074. }
  2075. *p++ = c;
  2076. }
  2077. else {
  2078. r = c;
  2079. p += runetochar(p, &r);
  2080. }
  2081. c = getrune();
  2082. }
  2083. *p = 0;
  2084. }
  2085. void
  2086. pushbody(char *line)
  2087. {
  2088. char *b;
  2089. if (line[0] == '\\' && line[1] == '{' /*}*/ )
  2090. line += 2;
  2091. if (strsp < Maxmstack - 1) {
  2092. pushstr(b = strdup(line));
  2093. mustfree[strsp] = b;
  2094. }
  2095. }
  2096. void
  2097. skipbody(char *line)
  2098. {
  2099. int c, n;
  2100. if (line[0] != '\\' || line[1] != '{' /*}*/ )
  2101. return;
  2102. for(n = 1;;) {
  2103. while((c = getrune()) != '\\')
  2104. if (c < 0)
  2105. return;
  2106. c = getrune();
  2107. if (c == '{')
  2108. n++;
  2109. else if ((c == '}' && (c = getrune()) == '\n' && !--n)
  2110. || c < 0)
  2111. return;
  2112. }
  2113. }
  2114. int
  2115. ifstart(char *line, char *e, char **bp)
  2116. {
  2117. int it;
  2118. char *b;
  2119. b = copyline(line, e, 1);
  2120. ungetrune();
  2121. b[-1] = getrune();
  2122. scanline(b, e, 1);
  2123. it = iftest(line, bp);
  2124. return it;
  2125. }
  2126. void
  2127. g_ie(char *line, char *e)
  2128. {
  2129. char *b;
  2130. if (elsetop >= Maxif-1) {
  2131. fprint(2, "ms2html: .ie's too deep\n");
  2132. return;
  2133. }
  2134. if (ifwastrue[++elsetop] = ifstart(line, e, &b))
  2135. pushbody(b);
  2136. else
  2137. skipbody(b);
  2138. }
  2139. void
  2140. g_if(char *line, char *e)
  2141. {
  2142. char *b;
  2143. if (ifstart(line, e, &b))
  2144. pushbody(b);
  2145. else
  2146. skipbody(b);
  2147. }
  2148. void
  2149. g_el(char *line, char *e)
  2150. {
  2151. if (elsetop < 0)
  2152. return;
  2153. scanline(line, e, 1);
  2154. if (ifwastrue[elsetop--])
  2155. skipbody(line);
  2156. else
  2157. pushbody(line);
  2158. }
  2159. void
  2160. g_ig(int argc, char **argv)
  2161. {
  2162. char *p, *s;
  2163. s = "..";
  2164. if (argc > 1)
  2165. s = argv[1];
  2166. for(;;) {
  2167. p = Brdline(&ssp->in, '\n');
  2168. if(p == nil)
  2169. break;
  2170. p[Blinelen(&ssp->in)-1] = 0;
  2171. if(strcmp(p, s) == 0)
  2172. break;
  2173. }
  2174. }
  2175. void
  2176. g_ds(char *line, char *e)
  2177. {
  2178. char *b;
  2179. b = copyline(line, e, 1);
  2180. if (b > line) {
  2181. copyline(b, e, 0);
  2182. if (*b == '"')
  2183. b++;
  2184. ds(line, b);
  2185. }
  2186. }
  2187. void
  2188. g_as(char *line, char *e)
  2189. {
  2190. String *s;
  2191. char *b;
  2192. b = copyline(line, e, 1);
  2193. if (b == line)
  2194. return;
  2195. copyline(b, e, 0);
  2196. if (*b == '"')
  2197. b++;
  2198. for(s = strings; s != nil; s = s->next)
  2199. if(strcmp(line, s->name) == 0)
  2200. break;
  2201. if(s == nil){
  2202. ds(line, b);
  2203. return;
  2204. }
  2205. s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1);
  2206. strcat(s->val, b);
  2207. }
  2208. void
  2209. g_BS(int argc, char **argv)
  2210. {
  2211. int i;
  2212. if (argc > 1 && !weBref) {
  2213. Bprint(&bout, "<a href=\"%s\"", argv[1]);
  2214. for(i = 2; i < argc; i++)
  2215. Bprint(&bout, " %s", argv[i]);
  2216. Bprint(&bout, ">");
  2217. weBref = 1;
  2218. }
  2219. }
  2220. void
  2221. g_BE(int, char**)
  2222. {
  2223. if (weBref) {
  2224. Bprint(&bout, "</a>");
  2225. weBref = 0;
  2226. }
  2227. }
  2228. void
  2229. g_LB(int argc, char **argv)
  2230. {
  2231. if (argc > 1) {
  2232. if (weBref)
  2233. g_BE(0,nil);
  2234. Bprint(&bout, "<a name=\"%s\"></a>", argv[1]);
  2235. }
  2236. }
  2237. void
  2238. g_RT(int, char**)
  2239. {
  2240. g_BE(0,nil);
  2241. dohanginghead();
  2242. closel();
  2243. closefont();
  2244. }