1 /* $OpenBSD: chars.c,v 1.51 2022/06/26 20:30:00 schwarze Exp $ */
2 /*
3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2020
5 * Ingo Schwarze <schwarze@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <sys/types.h>
20
21 #include <assert.h>
22 #include <ctype.h>
23 #include <stddef.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "mandoc.h"
30 #include "mandoc_aux.h"
31 #include "mandoc_ohash.h"
32 #include "libmandoc.h"
33
34 struct ln {
35 const char roffcode[16];
36 const char *ascii;
37 int unicode;
38 };
39
40 /* Special break control characters. */
41 static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' };
42 static const char ascii_break[2] = { ASCII_BREAK, '\0' };
43
44 static struct ln lines[] = {
45
46 /* Spacing. */
47 { " ", ascii_nbrsp, 0x00a0 },
48 { "~", ascii_nbrsp, 0x00a0 },
49 { "0", ascii_nbrsp, 0x00a0 },
50 { ":", ascii_break, 0 },
51
52 /* Lines. */
53 { "ba", "|", 0x007c },
54 { "br", "|", 0x2502 },
55 { "ul", "_", 0x005f },
56 { "_", "_", 0x005f },
57 { "ru", "_", 0x005f },
58 { "rn", "-", 0x203e },
59 { "bb", "|", 0x00a6 },
60 { "sl", "/", 0x002f },
61 { "rs", "\\", 0x005c },
62
63 /* Text markers. */
64 { "ci", "O", 0x25cb },
65 { "bu", "+\bo", 0x2022 },
66 { "dd", "<**>", 0x2021 },
67 { "dg", "<*>", 0x2020 },
68 { "lz", "<>", 0x25ca },
69 { "sq", "[]", 0x25a1 },
70 { "ps", "<paragraph>", 0x00b6 },
71 { "sc", "<section>", 0x00a7 },
72 { "lh", "<=", 0x261c },
73 { "rh", "=>", 0x261e },
74 { "at", "@", 0x0040 },
75 { "sh", "#", 0x0023 },
76 { "CR", "<cr>", 0x21b5 },
77 { "OK", "\\/", 0x2713 },
78 { "CL", "C", 0x2663 },
79 { "SP", "S", 0x2660 },
80 { "HE", "H", 0x2665 },
81 { "DI", "D", 0x2666 },
82
83 /* Legal symbols. */
84 { "co", "(C)", 0x00a9 },
85 { "rg", "(R)", 0x00ae },
86 { "tm", "tm", 0x2122 },
87
88 /* Punctuation. */
89 { "em", "--", 0x2014 },
90 { "en", "-", 0x2013 },
91 { "hy", "-", 0x2010 },
92 { "e", "\\", 0x005c },
93 { "r!", "!", 0x00a1 },
94 { "r?", "?", 0x00bf },
95
96 /* Quotes. */
97 { "Bq", ",,", 0x201e },
98 { "bq", ",", 0x201a },
99 { "lq", "\"", 0x201c },
100 { "rq", "\"", 0x201d },
101 { "Lq", "\"", 0x201c },
102 { "Rq", "\"", 0x201d },
103 { "oq", "`", 0x2018 },
104 { "cq", "\'", 0x2019 },
105 { "aq", "\'", 0x0027 },
106 { "dq", "\"", 0x0022 },
107 { "Fo", "<<", 0x00ab },
108 { "Fc", ">>", 0x00bb },
109 { "fo", "<", 0x2039 },
110 { "fc", ">", 0x203a },
111
112 /* Brackets. */
113 { "lB", "[", 0x005b },
114 { "rB", "]", 0x005d },
115 { "lC", "{", 0x007b },
116 { "rC", "}", 0x007d },
117 { "la", "<", 0x27e8 },
118 { "ra", ">", 0x27e9 },
119 { "bv", "|", 0x23aa },
120 { "braceex", "|", 0x23aa },
121 { "bracketlefttp", "|", 0x23a1 },
122 { "bracketleftbt", "|", 0x23a3 },
123 { "bracketleftex", "|", 0x23a2 },
124 { "bracketrighttp", "|", 0x23a4 },
125 { "bracketrightbt", "|", 0x23a6 },
126 { "bracketrightex", "|", 0x23a5 },
127 { "lt", ",-", 0x23a7 },
128 { "bracelefttp", ",-", 0x23a7 },
129 { "lk", "{", 0x23a8 },
130 { "braceleftmid", "{", 0x23a8 },
131 { "lb", "`-", 0x23a9 },
132 { "braceleftbt", "`-", 0x23a9 },
133 { "braceleftex", "|", 0x23aa },
134 { "rt", "-.", 0x23ab },
135 { "bracerighttp", "-.", 0x23ab },
136 { "rk", "}", 0x23ac },
137 { "bracerightmid", "}", 0x23ac },
138 { "rb", "-\'", 0x23ad },
139 { "bracerightbt", "-\'", 0x23ad },
140 { "bracerightex", "|", 0x23aa },
141 { "parenlefttp", "/", 0x239b },
142 { "parenleftbt", "\\", 0x239d },
143 { "parenleftex", "|", 0x239c },
144 { "parenrighttp", "\\", 0x239e },
145 { "parenrightbt", "/", 0x23a0 },
146 { "parenrightex", "|", 0x239f },
147
148 /* Arrows and lines. */
149 { "<-", "<-", 0x2190 },
150 { "->", "->", 0x2192 },
151 { "<>", "<->", 0x2194 },
152 { "da", "|\bv", 0x2193 },
153 { "ua", "|\b^", 0x2191 },
154 { "va", "^v", 0x2195 },
155 { "lA", "<=", 0x21d0 },
156 { "rA", "=>", 0x21d2 },
157 { "hA", "<=>", 0x21d4 },
158 { "uA", "=\b^", 0x21d1 },
159 { "dA", "=\bv", 0x21d3 },
160 { "vA", "^=v", 0x21d5 },
161 { "an", "-", 0x23af },
162
163 /* Logic. */
164 { "AN", "^", 0x2227 },
165 { "OR", "v", 0x2228 },
166 { "no", "~", 0x00ac },
167 { "tno", "~", 0x00ac },
168 { "te", "<there\037exists>", 0x2203 },
169 { "fa", "<for\037all>", 0x2200 },
170 { "st", "<such\037that>", 0x220b },
171 { "tf", "<therefore>", 0x2234 },
172 { "3d", "<therefore>", 0x2234 },
173 { "or", "|", 0x007c },
174
175 /* Mathematicals. */
176 { "pl", "+", 0x002b },
177 { "mi", "-", 0x2212 },
178 { "-", "-", 0x002d },
179 { "-+", "-+", 0x2213 },
180 { "+-", "+-", 0x00b1 },
181 { "t+-", "+-", 0x00b1 },
182 { "pc", ".", 0x00b7 },
183 { "md", ".", 0x22c5 },
184 { "mu", "x", 0x00d7 },
185 { "tmu", "x", 0x00d7 },
186 { "c*", "O\bx", 0x2297 },
187 { "c+", "O\b+", 0x2295 },
188 { "di", "/", 0x00f7 },
189 { "tdi", "/", 0x00f7 },
190 { "f/", "/", 0x2044 },
191 { "**", "*", 0x2217 },
192 { "<=", "<=", 0x2264 },
193 { ">=", ">=", 0x2265 },
194 { "<<", "<<", 0x226a },
195 { ">>", ">>", 0x226b },
196 { "eq", "=", 0x003d },
197 { "!=", "!=", 0x2260 },
198 { "==", "==", 0x2261 },
199 { "ne", "!==", 0x2262 },
200 { "ap", "~", 0x223c },
201 { "|=", "-~", 0x2243 },
202 { "=~", "=~", 0x2245 },
203 { "~~", "~~", 0x2248 },
204 { "~=", "~=", 0x2248 },
205 { "pt", "<proportional\037to>", 0x221d },
206 { "es", "{}", 0x2205 },
207 { "mo", "<element\037of>", 0x2208 },
208 { "nm", "<not\037element\037of>", 0x2209 },
209 { "sb", "<proper\037subset>", 0x2282 },
210 { "nb", "<not\037subset>", 0x2284 },
211 { "sp", "<proper\037superset>", 0x2283 },
212 { "nc", "<not\037superset>", 0x2285 },
213 { "ib", "<subset\037or\037equal>", 0x2286 },
214 { "ip", "<superset\037or\037equal>", 0x2287 },
215 { "ca", "<intersection>", 0x2229 },
216 { "cu", "<union>", 0x222a },
217 { "/_", "<angle>", 0x2220 },
218 { "pp", "<perpendicular>", 0x22a5 },
219 { "is", "<integral>", 0x222b },
220 { "integral", "<integral>", 0x222b },
221 { "sum", "<sum>", 0x2211 },
222 { "product", "<product>", 0x220f },
223 { "coproduct", "<coproduct>", 0x2210 },
224 { "gr", "<nabla>", 0x2207 },
225 { "sr", "<sqrt>", 0x221a },
226 { "sqrt", "<sqrt>", 0x221a },
227 { "lc", "|~", 0x2308 },
228 { "rc", "~|", 0x2309 },
229 { "lf", "|_", 0x230a },
230 { "rf", "_|", 0x230b },
231 { "if", "<infinity>", 0x221e },
232 { "Ah", "<Aleph>", 0x2135 },
233 { "Im", "<Im>", 0x2111 },
234 { "Re", "<Re>", 0x211c },
235 { "wp", "p", 0x2118 },
236 { "pd", "<del>", 0x2202 },
237 { "-h", "/h", 0x210f },
238 { "hbar", "/h", 0x210f },
239 { "12", "1/2", 0x00bd },
240 { "14", "1/4", 0x00bc },
241 { "34", "3/4", 0x00be },
242 { "18", "1/8", 0x215B },
243 { "38", "3/8", 0x215C },
244 { "58", "5/8", 0x215D },
245 { "78", "7/8", 0x215E },
246 { "S1", "^1", 0x00B9 },
247 { "S2", "^2", 0x00B2 },
248 { "S3", "^3", 0x00B3 },
249
250 /* Ligatures. */
251 { "ff", "ff", 0xfb00 },
252 { "fi", "fi", 0xfb01 },
253 { "fl", "fl", 0xfb02 },
254 { "Fi", "ffi", 0xfb03 },
255 { "Fl", "ffl", 0xfb04 },
256 { "AE", "AE", 0x00c6 },
257 { "ae", "ae", 0x00e6 },
258 { "OE", "OE", 0x0152 },
259 { "oe", "oe", 0x0153 },
260 { "ss", "ss", 0x00df },
261 { "IJ", "IJ", 0x0132 },
262 { "ij", "ij", 0x0133 },
263
264 /* Accents. */
265 { "a\"", "\"", 0x02dd },
266 { "a-", "-", 0x00af },
267 { "a.", ".", 0x02d9 },
268 { "a^", "^", 0x005e },
269 { "aa", "\'", 0x00b4 },
270 { "\'", "\'", 0x00b4 },
271 { "ga", "`", 0x0060 },
272 { "`", "`", 0x0060 },
273 { "ab", "'\b`", 0x02d8 },
274 { "ac", ",", 0x00b8 },
275 { "ad", "\"", 0x00a8 },
276 { "ah", "v", 0x02c7 },
277 { "ao", "o", 0x02da },
278 { "a~", "~", 0x007e },
279 { "ho", ",", 0x02db },
280 { "ha", "^", 0x005e },
281 { "ti", "~", 0x007e },
282 { "u02DC", "~", 0x02dc },
283
284 /* Accented letters. */
285 { "'A", "'\bA", 0x00c1 },
286 { "'E", "'\bE", 0x00c9 },
287 { "'I", "'\bI", 0x00cd },
288 { "'O", "'\bO", 0x00d3 },
289 { "'U", "'\bU", 0x00da },
290 { "'Y", "'\bY", 0x00dd },
291 { "'a", "'\ba", 0x00e1 },
292 { "'e", "'\be", 0x00e9 },
293 { "'i", "'\bi", 0x00ed },
294 { "'o", "'\bo", 0x00f3 },
295 { "'u", "'\bu", 0x00fa },
296 { "'y", "'\by", 0x00fd },
297 { "`A", "`\bA", 0x00c0 },
298 { "`E", "`\bE", 0x00c8 },
299 { "`I", "`\bI", 0x00cc },
300 { "`O", "`\bO", 0x00d2 },
301 { "`U", "`\bU", 0x00d9 },
302 { "`a", "`\ba", 0x00e0 },
303 { "`e", "`\be", 0x00e8 },
304 { "`i", "`\bi", 0x00ec },
305 { "`o", "`\bo", 0x00f2 },
306 { "`u", "`\bu", 0x00f9 },
307 { "~A", "~\bA", 0x00c3 },
308 { "~N", "~\bN", 0x00d1 },
309 { "~O", "~\bO", 0x00d5 },
310 { "~a", "~\ba", 0x00e3 },
311 { "~n", "~\bn", 0x00f1 },
312 { "~o", "~\bo", 0x00f5 },
313 { ":A", "\"\bA", 0x00c4 },
314 { ":E", "\"\bE", 0x00cb },
315 { ":I", "\"\bI", 0x00cf },
316 { ":O", "\"\bO", 0x00d6 },
317 { ":U", "\"\bU", 0x00dc },
318 { ":a", "\"\ba", 0x00e4 },
319 { ":e", "\"\be", 0x00eb },
320 { ":i", "\"\bi", 0x00ef },
321 { ":o", "\"\bo", 0x00f6 },
322 { ":u", "\"\bu", 0x00fc },
323 { ":y", "\"\by", 0x00ff },
324 { "^A", "^\bA", 0x00c2 },
325 { "^E", "^\bE", 0x00ca },
326 { "^I", "^\bI", 0x00ce },
327 { "^O", "^\bO", 0x00d4 },
328 { "^U", "^\bU", 0x00db },
329 { "^a", "^\ba", 0x00e2 },
330 { "^e", "^\be", 0x00ea },
331 { "^i", "^\bi", 0x00ee },
332 { "^o", "^\bo", 0x00f4 },
333 { "^u", "^\bu", 0x00fb },
334 { ",C", ",\bC", 0x00c7 },
335 { ",c", ",\bc", 0x00e7 },
336 { "/L", "/\bL", 0x0141 },
337 { "/l", "/\bl", 0x0142 },
338 { "/O", "/\bO", 0x00d8 },
339 { "/o", "/\bo", 0x00f8 },
340 { "oA", "o\bA", 0x00c5 },
341 { "oa", "o\ba", 0x00e5 },
342
343 /* Special letters. */
344 { "-D", "Dh", 0x00d0 },
345 { "Sd", "dh", 0x00f0 },
346 { "TP", "Th", 0x00de },
347 { "Tp", "th", 0x00fe },
348 { ".i", "i", 0x0131 },
349 { ".j", "j", 0x0237 },
350
351 /* Currency. */
352 { "Do", "$", 0x0024 },
353 { "ct", "/\bc", 0x00a2 },
354 { "Eu", "EUR", 0x20ac },
355 { "eu", "EUR", 0x20ac },
356 { "Ye", "=\bY", 0x00a5 },
357 { "Po", "-\bL", 0x00a3 },
358 { "Cs", "o\bx", 0x00a4 },
359 { "Fn", ",\bf", 0x0192 },
360
361 /* Units. */
362 { "de", "<degree>", 0x00b0 },
363 { "%0", "<permille>", 0x2030 },
364 { "fm", "\'", 0x2032 },
365 { "sd", "\"", 0x2033 },
366 { "mc", "<micro>", 0x00b5 },
367 { "Of", "_\ba", 0x00aa },
368 { "Om", "_\bo", 0x00ba },
369
370 /* Greek characters. */
371 { "*A", "A", 0x0391 },
372 { "*B", "B", 0x0392 },
373 { "*G", "<Gamma>", 0x0393 },
374 { "*D", "<Delta>", 0x0394 },
375 { "*E", "E", 0x0395 },
376 { "*Z", "Z", 0x0396 },
377 { "*Y", "H", 0x0397 },
378 { "*H", "<Theta>", 0x0398 },
379 { "*I", "I", 0x0399 },
380 { "*K", "K", 0x039a },
381 { "*L", "<Lambda>", 0x039b },
382 { "*M", "M", 0x039c },
383 { "*N", "N", 0x039d },
384 { "*C", "<Xi>", 0x039e },
385 { "*O", "O", 0x039f },
386 { "*P", "<Pi>", 0x03a0 },
387 { "*R", "P", 0x03a1 },
388 { "*S", "<Sigma>", 0x03a3 },
389 { "*T", "T", 0x03a4 },
390 { "*U", "Y", 0x03a5 },
391 { "*F", "<Phi>", 0x03a6 },
392 { "*X", "X", 0x03a7 },
393 { "*Q", "<Psi>", 0x03a8 },
394 { "*W", "<Omega>", 0x03a9 },
395 { "*a", "<alpha>", 0x03b1 },
396 { "*b", "<beta>", 0x03b2 },
397 { "*g", "<gamma>", 0x03b3 },
398 { "*d", "<delta>", 0x03b4 },
399 { "*e", "<epsilon>", 0x03b5 },
400 { "*z", "<zeta>", 0x03b6 },
401 { "*y", "<eta>", 0x03b7 },
402 { "*h", "<theta>", 0x03b8 },
403 { "*i", "<iota>", 0x03b9 },
404 { "*k", "<kappa>", 0x03ba },
405 { "*l", "<lambda>", 0x03bb },
406 { "*m", "<mu>", 0x03bc },
407 { "*n", "<nu>", 0x03bd },
408 { "*c", "<xi>", 0x03be },
409 { "*o", "o", 0x03bf },
410 { "*p", "<pi>", 0x03c0 },
411 { "*r", "<rho>", 0x03c1 },
412 { "*s", "<sigma>", 0x03c3 },
413 { "*t", "<tau>", 0x03c4 },
414 { "*u", "<upsilon>", 0x03c5 },
415 { "*f", "<phi>", 0x03d5 },
416 { "*x", "<chi>", 0x03c7 },
417 { "*q", "<psi>", 0x03c8 },
418 { "*w", "<omega>", 0x03c9 },
419 { "+h", "<theta>", 0x03d1 },
420 { "+f", "<phi>", 0x03c6 },
421 { "+p", "<pi>", 0x03d6 },
422 { "+e", "<epsilon>", 0x03f5 },
423 { "ts", "<sigma>", 0x03c2 },
424 };
425
426 static struct ohash mchars;
427
428
429 void
mchars_free(void)430 mchars_free(void)
431 {
432
433 ohash_delete(&mchars);
434 }
435
436 void
mchars_alloc(void)437 mchars_alloc(void)
438 {
439 size_t i;
440 unsigned int slot;
441
442 mandoc_ohash_init(&mchars, 9, offsetof(struct ln, roffcode));
443 for (i = 0; i < sizeof(lines)/sizeof(lines[0]); i++) {
444 slot = ohash_qlookup(&mchars, lines[i].roffcode);
445 assert(ohash_find(&mchars, slot) == NULL);
446 ohash_insert(&mchars, slot, lines + i);
447 }
448 }
449
450 int
mchars_spec2cp(const char * p,size_t sz)451 mchars_spec2cp(const char *p, size_t sz)
452 {
453 const struct ln *ln;
454 const char *end;
455
456 end = p + sz;
457 ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end));
458 return ln != NULL ? ln->unicode : -1;
459 }
460
461 int
mchars_num2char(const char * p,size_t sz)462 mchars_num2char(const char *p, size_t sz)
463 {
464 int i;
465
466 i = mandoc_strntoi(p, sz, 10);
467 return i >= 0 && i < 256 ? i : -1;
468 }
469
470 int
mchars_num2uc(const char * p,size_t sz)471 mchars_num2uc(const char *p, size_t sz)
472 {
473 int i;
474
475 i = mandoc_strntoi(p, sz, 16);
476 assert(i >= 0 && i <= 0x10FFFF);
477 return i;
478 }
479
480 const char *
mchars_spec2str(const char * p,size_t sz,size_t * rsz)481 mchars_spec2str(const char *p, size_t sz, size_t *rsz)
482 {
483 const struct ln *ln;
484 const char *end;
485
486 end = p + sz;
487 ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end));
488 if (ln == NULL)
489 return NULL;
490
491 *rsz = strlen(ln->ascii);
492 return ln->ascii;
493 }
494
495 const char *
mchars_uc2str(int uc)496 mchars_uc2str(int uc)
497 {
498 size_t i;
499
500 for (i = 0; i < sizeof(lines)/sizeof(lines[0]); i++)
501 if (uc == lines[i].unicode)
502 return lines[i].ascii;
503 return "<?>";
504 }
505