groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / preproc / eqn / text.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548
JL
2/* Copyright (C) 1989, 1990, 1991, 1992, 2003, 2007, 2009
3 Free Software Foundation, Inc.
92d0a6a6
JR
4 Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
4d3e9548
JL
10Software Foundation, either version 3 of the License, or
11(at your option) any later version.
92d0a6a6
JR
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
4d3e9548
JL
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6 20
4d3e9548 21#include <ctype.h>
92d0a6a6
JR
22#include "eqn.h"
23#include "pbox.h"
24#include "ptable.h"
25
4d3e9548
JL
26struct map {
27 const char *from;
28 const char *to;
29};
30
31struct map entity_table[] = {
32 // Classic troff special characters
33 {"%", "&shy;"}, // ISOnum
34 {"'", "&acute;"}, // ISOdia
35 {"!=", "&ne;"}, // ISOtech
36 {"**", "&lowast;"}, // ISOtech
37 {"*a", "&alpha;"}, // ISOgrk3
38 {"*A", "A"},
39 {"*b", "&beta;"}, // ISOgrk3
40 {"*B", "B"},
41 {"*d", "&delta;"}, // ISOgrk3
42 {"*D", "&Delta;"}, // ISOgrk3
43 {"*e", "&epsilon;"}, // ISOgrk3
44 {"*E", "E"},
45 {"*f", "&phi;"}, // ISOgrk3
46 {"*F", "&Phi;"}, // ISOgrk3
47 {"*g", "&gamma;"}, // ISOgrk3
48 {"*G", "&Gamma;"}, // ISOgrk3
49 {"*h", "&theta;"}, // ISOgrk3
50 {"*H", "&Theta;"}, // ISOgrk3
51 {"*i", "&iota;"}, // ISOgrk3
52 {"*I", "I"},
53 {"*k", "&kappa;"}, // ISOgrk3
54 {"*K", "K;"},
55 {"*l", "&lamda;"}, // ISOgrk3
56 {"*L", "&Lambda;"}, // ISOgrk3
57 {"*m", "&mu;"}, // ISOgrk3
58 {"*M", "M"},
59 {"*n", "&nu;"}, // ISOgrk3
60 {"*N", "N"},
61 {"*o", "o"},
62 {"*O", "O"},
63 {"*p", "&pi;"}, // ISOgrk3
64 {"*P", "&Pi;"}, // ISOgrk3
65 {"*q", "&psi;"}, // ISOgrk3
66 {"*Q", "&PSI;"}, // ISOgrk3
67 {"*r", "&rho;"}, // ISOgrk3
68 {"*R", "R"},
69 {"*s", "&sigma;"}, // ISOgrk3
70 {"*S", "&Sigma;"}, // ISOgrk3
71 {"*t", "&tau;"}, // ISOgrk3
72 {"*T", "&Tau;"}, // ISOgrk3
73 {"*u", "&upsilon;"}, // ISOgrk3
74 {"*U", "&Upsilon;"}, // ISOgrk3
75 {"*w", "&omega;"}, // ISOgrk3
76 {"*W", "&Omega;"}, // ISOgrk3
77 {"*x", "&chi;"}, // ISOgrk3
78 {"*X", "&Chi;"}, // ISOgrk3
79 {"*y", "&eta;"}, // ISOgrk3
80 {"*Y", "&Eta;"}, // ISOgrk3
81 {"*z", "&zeta;"}, // ISOgrk3
82 {"*Z", "&Zeta;"}, // ISOgrk3
83 {"+-", "&plusmn;"}, // ISOnum
84 {"->", "&rarr;"}, // ISOnum
85 {"12", "&frac12;"}, // ISOnum
86 {"14", "&frac14;"}, // ISOnum
87 {"34", "&frac34;"}, // ISOnum
88 {"<-", "&larr;"}, // ISOnum
89 {"==", "&equiv;"}, // ISOtech
90 {"Fi", "&ffilig;"}, // ISOpub
91 {"Fl", "&ffllig;"}, // ISOpub
92 {"aa", "&acute;"}, // ISOdia
93 {"ap", "&sim;"}, // ISOtech
94 {"bl", "&phonexb;"}, // ISOpub
95 {"br", "&boxv;"}, // ISObox
96 {"bs", "&phone;"}, // ISOpub (for the Bell logo)
97 {"bu", "&bull;"}, // ISOpub
98 {"bv", "&verbar;"}, // ISOnum
99 {"ca", "&cap;"}, // ISOtech
100 {"ci", "&cir;"}, // ISOpub
101 {"co", "&copy;"}, // ISOnum
102 {"ct", "&cent;"}, // ISOnum
103 {"cu", "&cup;"}, // ISOtech
104 {"da", "&darr;"}, // ISOnum
105 {"de", "&deg;"}, // ISOnum
106 {"dg", "&dagger;"}, // ISOpub
107 {"dd", "&Dagger;"}, // ISOpub
108 {"di", "&divide;"}, // ISOnum
109 {"em", "&mdash;"}, // ISOpub
110 {"eq", "&equals;"}, // ISOnum
111 {"es", "&empty;"}, // ISOamso
112 {"ff", "&fflig;"}, // ISOpub
113 {"fi", "&filig;"}, // ISOpub
114 {"fl", "&fllig;"}, // ISOpub
115 {"fm", "&prime;"}, // ISOtech
116 {"ge", "&ge;"}, // ISOtech
117 {"gr", "&nabla;"}, // ISOtech
118 {"hy", "&hyphen;"}, // ISOnum
119 {"ib", "&sube;"}, // ISOtech
120 {"if", "&infin;"}, // ISOtech
121 {"ip", "&supe;"}, // ISOtech
122 {"is", "&int;"}, // ISOtech
123 {"le", "&le;"}, // ISOtech
124 // Some pile characters go here
125 {"mi", "&minus;"}, // ISOtech
126 {"mo", "&isin;"}, // ISOtech
127 {"mu", "&times;"}, // ISOnum
128 {"no", "&not;"}, // ISOnum
129 {"or", "&verbar;"}, // ISOnum
130 {"pl", "&plus;"}, // ISOnum
131 {"pt", "&prop;"}, // ISOtech
132 {"rg", "&trade;"}, // ISOnum
133 // More pile characters go here
134 {"rn", "&macr;"}, // ISOdia
135 {"ru", "&lowbar;"}, // ISOnum
136 {"sb", "&sub;"}, // ISOtech
137 {"sc", "&sect;"}, // ISOnum
138 {"sl", "/"},
139 {"sp", "&sup;"}, // ISOtech
140 {"sq", "&squf;"}, // ISOpub
141 {"sr", "&radic;"}, // ISOtech
142 {"ts", "&sigmav;"}, // ISOgrk3
143 {"ua", "&uarr;"}, // ISOnum
144 {"ul", "_"},
145 {"~=", "&cong;"}, // ISOtech
146 // Extended specials supported by groff; see groff_char(7).
147 // These are listed in the order they occur on that man page.
148 {"-D", "&ETH;"}, // ISOlat: Icelandic uppercase eth
149 {"Sd", "&eth;"}, // ISOlat1: Icelandic lowercase eth
150 {"TP", "&THORN;"}, // ISOlat1: Icelandic uppercase thorn
151 {"Tp", "&thorn;"}, // ISOlat1: Icelandic lowercase thorn
152 {"ss", "&szlig;"}, // ISOlat1
153 // Ligatures
154 // ff, fi, fl, ffi, ffl from old troff go here
155 {"AE", "&AElig;"}, // ISOlat1
156 {"ae", "&aelig;"}, // ISOlat1
157 {"OE", "&OElig;"}, // ISOlat2
158 {"oe", "&oelig;"}, // ISOlat2
159 {"IJ", "&ijlig;"}, // ISOlat2: Dutch IJ ligature
160 {"ij", "&IJlig;"}, // ISOlat2: Dutch ij ligature
161 {".i", "&inodot;"}, // ISOlat2,ISOamso
162 {".j", "&jnodot;"}, // ISOamso (undocumented but in 1.19)
163 // Accented characters
164 {"'A", "&Aacute;"}, // ISOlat1
165 {"'C", "&Cacute;"}, // ISOlat2
166 {"'E", "&Eacute;"}, // ISOlat1
167 {"'I", "&Iacute;"}, // ISOlat1
168 {"'O", "&Oacute;"}, // ISOlat1
169 {"'U", "&Uacute;"}, // ISOlat1
170 {"'Y", "&Yacute;"}, // ISOlat1
171 {"'a", "&aacute;"}, // ISOlat1
172 {"'c", "&cacute;"}, // ISOlat2
173 {"'e", "&eacute;"}, // ISOlat1
174 {"'i", "&iacute;"}, // ISOlat1
175 {"'o", "&oacute;"}, // ISOlat1
176 {"'u", "&uacute;"}, // ISOlat1
177 {"'y", "&yacute;"}, // ISOlat1
178 {":A", "&Auml;"}, // ISOlat1
179 {":E", "&Euml;"}, // ISOlat1
180 {":I", "&Iuml;"}, // ISOlat1
181 {":O", "&Ouml;"}, // ISOlat1
182 {":U", "&Uuml;"}, // ISOlat1
183 {":Y", "&Yuml;"}, // ISOlat2
184 {":a", "&auml;"}, // ISOlat1
185 {":e", "&euml;"}, // ISOlat1
186 {":i", "&iuml;"}, // ISOlat1
187 {":o", "&ouml;"}, // ISOlat1
188 {":u", "&uuml;"}, // ISOlat1
189 {":y", "&yuml;"}, // ISOlat1
190 {"^A", "&Acirc;"}, // ISOlat1
191 {"^E", "&Ecirc;"}, // ISOlat1
192 {"^I", "&Icirc;"}, // ISOlat1
193 {"^O", "&Ocirc;"}, // ISOlat1
194 {"^U", "&Ucirc;"}, // ISOlat1
195 {"^a", "&acirc;"}, // ISOlat1
196 {"^e", "&ecirc;"}, // ISOlat1
197 {"^i", "&icirc;"}, // ISOlat1
198 {"^o", "&ocirc;"}, // ISOlat1
199 {"^u", "&ucirc;"}, // ISOlat1
200 {"`A", "&Agrave;"}, // ISOlat1
201 {"`E", "&Egrave;"}, // ISOlat1
202 {"`I", "&Igrave;"}, // ISOlat1
203 {"`O", "&Ograve;"}, // ISOlat1
204 {"`U", "&Ugrave;"}, // ISOlat1
205 {"`a", "&agrave;"}, // ISOlat1
206 {"`e", "&egrave;"}, // ISOlat1
207 {"`i", "&igrave;"}, // ISOlat1
208 {"`o", "&ograve;"}, // ISOlat1
209 {"`u", "&ugrave;"}, // ISOlat1
210 {"~A", "&Atilde;"}, // ISOlat1
211 {"~N", "&Ntilde;"}, // ISOlat1
212 {"~O", "&Otilde;"}, // ISOlat1
213 {"~a", "&atilde;"}, // ISOlat1
214 {"~n", "&ntilde;"}, // ISOlat1
215 {"~o", "&otilde;"}, // ISOlat1
216 {"vS", "&Scaron;"}, // ISOlat2
217 {"vs", "&scaron;"}, // ISOlat2
218 {"vZ", "&Zcaron;"}, // ISOlat2
219 {"vz", "&zcaron;"}, // ISOlat2
220 {",C", "&Ccedil;"}, // ISOlat1
221 {",c", "&ccedil;"}, // ISOlat1
222 {"/L", "&Lstrok;"}, // ISOlat2: Polish L with a slash
223 {"/l", "&lstrok;"}, // ISOlat2: Polish l with a slash
224 {"/O", "&Oslash;"}, // ISOlat1
225 {"/o", "&oslash;"}, // ISOlat1
226 {"oA", "&Aring;"}, // ISOlat1
227 {"oa", "&aring;"}, // ISOlat1
228 // Accents
229 {"a\"","&dblac;"}, // ISOdia: double acute accent (Hungarian umlaut)
230 {"a-", "&macr;"}, // ISOdia: macron or bar accent
231 {"a.", "&dot;"}, // ISOdia: dot above
232 {"a^", "&circ;"}, // ISOdia: circumflex accent
233 {"aa", "&acute;"}, // ISOdia: acute accent
234 {"ga", "&grave;"}, // ISOdia: grave accent
235 {"ab", "&breve;"}, // ISOdia: breve accent
236 {"ac", "&cedil;"}, // ISOdia: cedilla accent
237 {"ad", "&uml;"}, // ISOdia: umlaut or dieresis
238 {"ah", "&caron;"}, // ISOdia: caron (aka hacek accent)
239 {"ao", "&ring;"}, // ISOdia: ring or circle accent
240 {"a~", "&tilde;"}, // ISOdia: tilde accent
241 {"ho", "&ogon;"}, // ISOdia: hook or ogonek accent
242 {"ha", "^"}, // ASCII circumflex, hat, caret
243 {"ti", "~"}, // ASCII tilde, large tilde
244 // Quotes
245 {"Bq", "&lsquor;"}, // ISOpub: low double comma quote
246 {"bq", "&ldquor;"}, // ISOpub: low single comma quote
247 {"lq", "&ldquo;"}, // ISOnum
248 {"rq", "&rdquo;"}, // ISOpub
249 {"oq", "&lsquo;"}, // ISOnum: single open quote
250 {"cq", "&rsquo;"}, // ISOnum: single closing quote (ASCII 39)
251 {"aq", "&zerosp;'"}, // apostrophe quote
252 {"dq", "\""}, // double quote (ASCII 34)
253 {"Fo", "&laquo;"}, // ISOnum
254 {"Fc", "&raquo;"}, // ISOnum
255 //{"fo", "&fo;"},
256 //{"fc", "&fc;"},
257 // Punctuation
258 {"r!", "&iexcl;"}, // ISOnum
259 {"r?", "&iquest;"}, // ISOnum
260 // Old troff \(em goes here
261 {"en", "&ndash;"}, // ISOpub: en dash
262 // Old troff \(hy goes here
263 // Brackets
264 {"lB", "&lsqb;"}, // ISOnum: left (square) bracket
265 {"rB", "&rsqb;"}, // ISOnum: right (square) bracket
266 {"lC", "&lcub;"}, // ISOnum: left (curly) brace
267 {"rC", "&rcub;"}, // ISOnum: right (curly) brace
268 {"la", "&lang;"}, // ISOtech: left angle bracket
269 {"ra", "&rang;"}, // ISOtech: right angle bracket
270 // Old troff \(bv goes here
271 // Bracket-pile characters could go here.
272 // Arrows
273 // Old troff \(<- and \(-> go here
274 {"<>", "&harr;"}, // ISOamsa
275 {"da", "&darr;"}, // ISOnum
276 {"ua", "&uarr;"}, // ISOnum
277 {"lA", "&lArr;"}, // ISOtech
278 {"rA", "&rArr;"}, // ISOtech
279 {"hA", "&iff;"}, // ISOtech: horizontal double-headed arrow
280 {"dA", "&dArr;"}, // ISOamsa
281 {"uA", "&uArr;"}, // ISOamsa
282 {"vA", "&vArr;"}, // ISOamsa: vertical double-headed double arrow
283 //{"an", "&an;"},
284 // Lines
285 {"-h", "&planck;"}, // ISOamso: h-bar (Planck's constant)
286 // Old troff \(or goes here
287 {"ba", "&verbar;"}, // ISOnum
288 // Old troff \(br, \{u, \(ul, \(bv go here
289 {"bb", "&brvbar;"}, // ISOnum
290 {"sl", "/"},
291 {"rs", "&bsol;"}, // ISOnum
292 // Text markers
293 // Old troff \(ci, \(bu, \(dd, \(dg go here
294 {"lz", "&loz;"}, // ISOpub
295 // Old troff sq goes here
296 {"ps", "&para;"}, // ISOnum: paragraph or pilcrow sign
297 {"sc", "&sect;"}, // ISOnum (in old troff)
298 // Old troff \(lh, \{h go here
299 {"at", "&commat;"}, // ISOnum
300 {"sh", "&num;"}, // ISOnum
301 //{"CR", "&CR;"},
302 {"OK", "&check;"}, // ISOpub
303 // Legalize
304 // Old troff \(co, \{g go here
305 {"tm", "&trade;"}, // ISOnum
306 // Currency symbols
307 {"Do", "&dollar;"}, // ISOnum
308 {"ct", "&cent;"}, // ISOnum
309 {"eu", "&euro;"},
310 {"Eu", "&euro;"},
311 {"Ye", "&yen;"}, // ISOnum
312 {"Po", "&pound;"}, // ISOnum
313 {"Cs", "&curren;"}, // ISOnum: currency sign
314 {"Fn", "&fnof"}, // ISOtech
315 // Units
316 // Old troff de goes here
317 {"%0", "&permil;"}, // ISOtech: per thousand, per mille sign
318 // Old troff \(fm goes here
319 {"sd", "&Prime;"}, // ISOtech
320 {"mc", "&micro;"}, // ISOnum
321 {"Of", "&ordf;"}, // ISOnum
322 {"Om", "&ordm;"}, // ISOnum
323 // Logical symbols
324 {"AN", "&and;"}, // ISOtech
325 {"OR", "&or;"}, // ISOtech
326 // Old troff \(no goes here
327 {"te", "&exist;"}, // ISOtech: there exists, existential quantifier
328 {"fa", "&forall;"}, // ISOtech: for all, universal quantifier
329 {"st", "&bepsi"}, // ISOamsr: such that
330 {"3d", "&there4;"}, // ISOtech
331 {"tf", "&there4;"}, // ISOtech
332 // Mathematical symbols
333 // Old troff "12", "14", "34" goes here
334 {"S1", "&sup1;"}, // ISOnum
335 {"S2", "&sup2;"}, // ISOnum
336 {"S3", "&sup3;"}, // ISOnum
337 // Old troff \(pl", \-, \(+- go here
338 {"t+-", "&plusmn;"}, // ISOnum
339 {"-+", "&mnplus;"}, // ISOtech
340 {"pc", "&middot;"}, // ISOnum
341 {"md", "&middot;"}, // ISOnum
342 // Old troff \(mu goes here
343 {"tmu", "&times;"}, // ISOnum
344 {"c*", "&otimes;"}, // ISOamsb: multiply sign in a circle
345 {"c+", "&oplus;"}, // ISOamsb: plus sign in a circle
346 // Old troff \(di goes here
347 {"tdi", "&divide;"}, // ISOnum
348 {"f/", "&horbar;"}, // ISOnum: horizintal bar for fractions
349 // Old troff \(** goes here
350 {"<=", "&le;"}, // ISOtech
351 {">=", "&ge;"}, // ISOtech
352 {"<<", "&Lt;"}, // ISOamsr
353 {">>", "&Gt;"}, // ISOamsr
354 {"!=", "&ne;"}, // ISOtech
355 // Old troff \(eq and \(== go here
356 {"=~", "&cong;"}, // ISOamsr
357 // Old troff \(ap goes here
358 {"~~", "&ap;"}, // ISOtech
359 // This appears to be an error in the groff table.
360 // It clashes with the Bell Labs use of ~= for a congruence sign
361 // {"~=", "&ap;"}, // ISOamsr
362 // Old troff \(pt, \(es, \(mo go here
363 {"nm", "&notin;"}, // ISOtech
364 {"nb", "&nsub;"}, // ISOamsr
365 {"nc", "&nsup;"}, // ISOamsn
366 {"ne", "&nequiv;"}, // ISOamsn
367 // Old troff \(sb, \(sp, \(ib, \(ip, \(ca, \(cu go here
368 {"/_", "&ang;"}, // ISOamso
369 {"pp", "&perp;"}, // ISOtech
370 // Old troff \(is goes here
371 {"sum", "&sum;"}, // ISOamsb
372 {"product", "&prod;"}, // ISOamsb
373 {"gr", "&nabla;"}, // ISOtech
374 // Old troff \(sr. \{n, \(if go here
375 {"Ah", "&aleph;"}, // ISOtech
376 {"Im", "&image;"}, // ISOamso: Fraktur I, imaginary
377 {"Re", "&real;"}, // ISOamso: Fraktur R, real
378 {"wp", "&weierp;"}, // ISOamso
379 {"pd", "&part;"}, // ISOtech: partial differentiation sign
380 // Their table duplicates the Greek letters here.
381 // We list only the variant forms here, mapping them into
382 // the ISO Greek 4 variants (which may or may not be correct :-()
383 {"+f", "&b.phiv;"}, // ISOgrk4: variant phi
384 {"+h", "&b.thetas;"}, // ISOgrk4: variant theta
385 {"+p", "&b.omega;"}, // ISOgrk4: variant pi, looking like omega
386 // Card symbols
387 {"CL", "&clubs;"}, // ISOpub: club suit
388 {"SP", "&spades;"}, // ISOpub: spade suit
389 {"HE", "&hearts;"}, // ISOpub: heart suit
390 {"DI", "&diams;"}, // ISOpub: diamond suit
391};
392
393const char *special_to_entity(const char *sp)
394{
395 struct map *mp;
396 for (mp = entity_table;
397 mp < entity_table + sizeof(entity_table)/sizeof(entity_table[0]);
398 mp++) {
399 if (strcmp(mp->from, sp) == 0)
400 return mp->to;
401 }
402 return NULL;
403}
404
92d0a6a6
JR
405class char_box : public simple_box {
406 unsigned char c;
407 char next_is_italic;
408 char prev_is_italic;
409public:
410 char_box(unsigned char);
411 void debug_print();
412 void output();
413 int is_char();
414 int left_is_italic();
415 int right_is_italic();
416 void hint(unsigned);
417 void handle_char_type(int, int);
418};
419
420class special_char_box : public simple_box {
421 char *s;
422public:
423 special_char_box(const char *);
424 ~special_char_box();
425 void output();
426 void debug_print();
427 int is_char();
428 void handle_char_type(int, int);
429};
430
4d3e9548
JL
431enum spacing_type {
432 s_ordinary,
433 s_operator,
434 s_binary,
435 s_relation,
436 s_opening,
437 s_closing,
438 s_punctuation,
439 s_inner,
440 s_suppress
441};
442
92d0a6a6
JR
443const char *spacing_type_table[] = {
444 "ordinary",
445 "operator",
446 "binary",
447 "relation",
448 "opening",
449 "closing",
450 "punctuation",
451 "inner",
452 "suppress",
453 0,
454};
455
456const int DIGIT_TYPE = 0;
457const int LETTER_TYPE = 1;
458
459const char *font_type_table[] = {
460 "digit",
461 "letter",
462 0,
463};
464
465struct char_info {
466 int spacing_type;
467 int font_type;
468 char_info();
469};
470
471char_info::char_info()
472: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE)
473{
474}
475
476static char_info char_table[256];
477
478declare_ptable(char_info)
479implement_ptable(char_info)
480
481PTABLE(char_info) special_char_table;
482
483static int get_special_char_spacing_type(const char *ch)
484{
485 char_info *p = special_char_table.lookup(ch);
486 return p ? p->spacing_type : ORDINARY_TYPE;
487}
488
489static int get_special_char_font_type(const char *ch)
490{
491 char_info *p = special_char_table.lookup(ch);
492 return p ? p->font_type : DIGIT_TYPE;
493}
494
495static void set_special_char_type(const char *ch, int st, int ft)
496{
497 char_info *p = special_char_table.lookup(ch);
498 if (!p) {
499 p = new char_info[1];
500 special_char_table.define(ch, p);
501 }
502 if (st >= 0)
503 p->spacing_type = st;
504 if (ft >= 0)
505 p->font_type = ft;
506}
507
508void init_char_table()
509{
4d3e9548
JL
510 set_special_char_type("pl", s_binary, -1);
511 set_special_char_type("mi", s_binary, -1);
512 set_special_char_type("eq", s_relation, -1);
513 set_special_char_type("<=", s_relation, -1);
514 set_special_char_type(">=", s_relation, -1);
515 char_table['}'].spacing_type = s_closing;
516 char_table[')'].spacing_type = s_closing;
517 char_table[']'].spacing_type = s_closing;
518 char_table['{'].spacing_type = s_opening;
519 char_table['('].spacing_type = s_opening;
520 char_table['['].spacing_type = s_opening;
521 char_table[','].spacing_type = s_punctuation;
522 char_table[';'].spacing_type = s_punctuation;
523 char_table[':'].spacing_type = s_punctuation;
524 char_table['.'].spacing_type = s_punctuation;
525 char_table['>'].spacing_type = s_relation;
526 char_table['<'].spacing_type = s_relation;
527 char_table['*'].spacing_type = s_binary;
92d0a6a6
JR
528 for (int i = 0; i < 256; i++)
529 if (csalpha(i))
530 char_table[i].font_type = LETTER_TYPE;
531}
532
533static int lookup_spacing_type(const char *type)
534{
535 for (int i = 0; spacing_type_table[i] != 0; i++)
536 if (strcmp(spacing_type_table[i], type) == 0)
537 return i;
538 return -1;
539}
540
541static int lookup_font_type(const char *type)
542{
543 for (int i = 0; font_type_table[i] != 0; i++)
544 if (strcmp(font_type_table[i], type) == 0)
545 return i;
546 return -1;
547}
548
549void box::set_spacing_type(char *type)
550{
551 int t = lookup_spacing_type(type);
552 if (t < 0)
553 error("unrecognised type `%1'", type);
554 else
555 spacing_type = t;
556 a_delete type;
557}
558
559char_box::char_box(unsigned char cc)
560: c(cc), next_is_italic(0), prev_is_italic(0)
561{
562 spacing_type = char_table[c].spacing_type;
563}
564
565void char_box::hint(unsigned flags)
566{
567 if (flags & HINT_PREV_IS_ITALIC)
568 prev_is_italic = 1;
569 if (flags & HINT_NEXT_IS_ITALIC)
570 next_is_italic = 1;
571}
572
573void char_box::output()
574{
4d3e9548
JL
575 if (output_format == troff) {
576 int font_type = char_table[c].font_type;
577 if (font_type != LETTER_TYPE)
578 printf("\\f[%s]", current_roman_font);
579 if (!prev_is_italic)
580 fputs("\\,", stdout);
581 if (c == '\\')
582 fputs("\\e", stdout);
583 else
584 putchar(c);
585 if (!next_is_italic)
586 fputs("\\/", stdout);
587 else
588 fputs("\\&", stdout); // suppress ligaturing and kerning
589 if (font_type != LETTER_TYPE)
590 fputs("\\fP", stdout);
591 }
592 else if (output_format == mathml) {
593 if (isdigit(c))
594 printf("<mn>");
595 else if (char_table[c].spacing_type)
596 printf("<mo>");
597 else
598 printf("<mi>");
599 if (c == '<')
600 printf("&lt;");
601 else if (c == '>')
602 printf("&gt;");
603 else if (c == '&')
604 printf("&amp;");
605 else
606 putchar(c);
607 if (isdigit(c))
608 printf("</mn>");
609 else if (char_table[c].spacing_type)
610 printf("</mo>");
611 else
612 printf("</mi>");
613 }
92d0a6a6
JR
614}
615
616int char_box::left_is_italic()
617{
618 int font_type = char_table[c].font_type;
619 return font_type == LETTER_TYPE;
620}
621
622int char_box::right_is_italic()
623{
624 int font_type = char_table[c].font_type;
625 return font_type == LETTER_TYPE;
626}
627
628int char_box::is_char()
629{
630 return 1;
631}
632
633void char_box::debug_print()
634{
635 if (c == '\\') {
636 putc('\\', stderr);
637 putc('\\', stderr);
638 }
639 else
640 putc(c, stderr);
641}
642
643special_char_box::special_char_box(const char *t)
644{
645 s = strsave(t);
646 spacing_type = get_special_char_spacing_type(s);
647}
648
649special_char_box::~special_char_box()
650{
651 a_delete s;
652}
653
654void special_char_box::output()
655{
4d3e9548
JL
656 if (output_format == troff) {
657 int font_type = get_special_char_font_type(s);
658 if (font_type != LETTER_TYPE)
659 printf("\\f[%s]", current_roman_font);
660 printf("\\,\\[%s]\\/", s);
661 if (font_type != LETTER_TYPE)
662 printf("\\fP");
663 }
664 else if (output_format == mathml) {
665 const char *entity = special_to_entity(s);
666 if (entity != NULL)
667 printf("<mo>%s</mo>", entity);
668 else
669 printf("<merror>unknown eqn/troff special char %s</merror>", s);
670 }
92d0a6a6
JR
671}
672
673int special_char_box::is_char()
674{
675 return 1;
676}
677
678void special_char_box::debug_print()
679{
680 fprintf(stderr, "\\[%s]", s);
681}
682
683
684void char_box::handle_char_type(int st, int ft)
685{
686 if (st >= 0)
687 char_table[c].spacing_type = st;
688 if (ft >= 0)
689 char_table[c].font_type = ft;
690}
691
692void special_char_box::handle_char_type(int st, int ft)
693{
694 set_special_char_type(s, st, ft);
695}
696
697void set_char_type(const char *type, char *ch)
698{
699 assert(ch != 0);
700 int st = lookup_spacing_type(type);
701 int ft = lookup_font_type(type);
702 if (st < 0 && ft < 0) {
703 error("bad character type `%1'", type);
704 a_delete ch;
705 return;
706 }
707 box *b = split_text(ch);
708 b->handle_char_type(st, ft);
709 delete b;
710}
711
712/* We give primes special treatment so that in ``x' sub 2'', the ``2''
713will be tucked under the prime */
714
715class prime_box : public pointer_box {
716 box *pb;
717public:
718 prime_box(box *);
719 ~prime_box();
720 int compute_metrics(int style);
721 void output();
722 void compute_subscript_kern();
723 void debug_print();
724 void handle_char_type(int, int);
725};
726
727box *make_prime_box(box *pp)
728{
729 return new prime_box(pp);
730}
731
732prime_box::prime_box(box *pp) : pointer_box(pp)
733{
734 pb = new special_char_box("fm");
735}
736
737prime_box::~prime_box()
738{
739 delete pb;
740}
741
742int prime_box::compute_metrics(int style)
743{
744 int res = p->compute_metrics(style);
745 pb->compute_metrics(style);
746 printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]"
747 "+\\n[" WIDTH_FORMAT "]\n",
748 uid, p->uid, pb->uid);
749 printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
750 ">?\\n[" HEIGHT_FORMAT "]\n",
751 uid, p->uid, pb->uid);
752 printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
753 ">?\\n[" DEPTH_FORMAT "]\n",
754 uid, p->uid, pb->uid);
755 return res;
756}
757
758void prime_box::compute_subscript_kern()
759{
760 p->compute_subscript_kern();
761 printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]"
762 "+\\n[" SUB_KERN_FORMAT "]>?0\n",
763 uid, pb->uid, p->uid);
764}
765
766void prime_box::output()
767{
768 p->output();
769 pb->output();
770}
771
772void prime_box::handle_char_type(int st, int ft)
773{
774 p->handle_char_type(st, ft);
775 pb->handle_char_type(st, ft);
776}
777
778void prime_box::debug_print()
779{
780 p->debug_print();
781 putc('\'', stderr);
782}
783
784box *split_text(char *text)
785{
786 list_box *lb = 0;
787 box *fb = 0;
788 char *s = text;
789 while (*s != '\0') {
790 char c = *s++;
791 box *b = 0;
792 switch (c) {
793 case '+':
794 b = new special_char_box("pl");
795 break;
796 case '-':
797 b = new special_char_box("mi");
798 break;
799 case '=':
800 b = new special_char_box("eq");
801 break;
802 case '\'':
803 b = new special_char_box("fm");
804 break;
805 case '<':
806 if (*s == '=') {
807 b = new special_char_box("<=");
808 s++;
809 break;
810 }
811 goto normal_char;
812 case '>':
813 if (*s == '=') {
814 b = new special_char_box(">=");
815 s++;
816 break;
817 }
818 goto normal_char;
819 case '\\':
820 if (*s == '\0') {
821 lex_error("bad escape");
822 break;
823 }
824 c = *s++;
825 switch (c) {
826 case '(':
827 {
828 char buf[3];
829 if (*s != '\0') {
830 buf[0] = *s++;
831 if (*s != '\0') {
832 buf[1] = *s++;
833 buf[2] = '\0';
834 b = new special_char_box(buf);
835 }
836 else {
837 lex_error("bad escape");
838 }
839 }
840 else {
841 lex_error("bad escape");
842 }
843 }
844 break;
845 case '[':
846 {
847 char *ch = s;
848 while (*s != ']' && *s != '\0')
849 s++;
850 if (*s == '\0')
851 lex_error("bad escape");
852 else {
853 *s++ = '\0';
854 b = new special_char_box(ch);
855 }
856 }
857 break;
858 case 'f':
859 case 'g':
860 case 'k':
861 case 'n':
862 case '*':
863 {
864 char *escape_start = s - 2;
865 switch (*s) {
866 case '(':
867 if (*++s != '\0')
868 ++s;
869 break;
870 case '[':
871 for (++s; *s != '\0' && *s != ']'; s++)
872 ;
873 break;
874 }
875 if (*s == '\0')
876 lex_error("bad escape");
877 else {
878 ++s;
879 char *buf = new char[s - escape_start + 1];
880 memcpy(buf, escape_start, s - escape_start);
881 buf[s - escape_start] = '\0';
882 b = new quoted_text_box(buf);
883 }
884 }
885 break;
886 case '-':
887 case '_':
888 {
889 char buf[2];
890 buf[0] = c;
891 buf[1] = '\0';
892 b = new special_char_box(buf);
893 }
894 break;
895 case '`':
896 b = new special_char_box("ga");
897 break;
898 case '\'':
899 b = new special_char_box("aa");
900 break;
901 case 'e':
902 case '\\':
903 b = new char_box('\\');
904 break;
905 case '^':
906 case '|':
907 case '0':
908 {
909 char buf[3];
910 buf[0] = '\\';
911 buf[1] = c;
912 buf[2] = '\0';
913 b = new quoted_text_box(strsave(buf));
914 break;
915 }
916 default:
917 lex_error("unquoted escape");
918 b = new quoted_text_box(strsave(s - 2));
919 s = strchr(s, '\0');
920 break;
921 }
922 break;
923 default:
924 normal_char:
925 b = new char_box(c);
926 break;
927 }
928 while (*s == '\'') {
929 if (b == 0)
930 b = new quoted_text_box(0);
931 b = new prime_box(b);
932 s++;
933 }
934 if (b != 0) {
935 if (lb != 0)
936 lb->append(b);
937 else if (fb != 0) {
938 lb = new list_box(fb);
939 lb->append(b);
940 }
941 else
942 fb = b;
943 }
944 }
945 a_delete text;
946 if (lb != 0)
947 return lb;
948 else if (fb != 0)
949 return fb;
950 else
951 return new quoted_text_box(0);
952}
953