mandoc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/COPYING +674 -0
  4. data/README.md +117 -0
  5. data/ext/mandoc/extconf.rb +59 -0
  6. data/ext/mandoc/rb_mandoc.c +548 -0
  7. data/ext/mandoc/rb_mandoc.h +22 -0
  8. data/lib/mandoc/version.rb +19 -0
  9. data/lib/mandoc.rb +26 -0
  10. data/mandoc-1.14.6/LICENSE +55 -0
  11. data/mandoc-1.14.6/arch.c +54 -0
  12. data/mandoc-1.14.6/att.c +49 -0
  13. data/mandoc-1.14.6/catman.c +260 -0
  14. data/mandoc-1.14.6/cgi.c +1279 -0
  15. data/mandoc-1.14.6/chars.c +507 -0
  16. data/mandoc-1.14.6/compat_err.c +103 -0
  17. data/mandoc-1.14.6/compat_fts.c +696 -0
  18. data/mandoc-1.14.6/compat_fts.h +106 -0
  19. data/mandoc-1.14.6/compat_getline.c +59 -0
  20. data/mandoc-1.14.6/compat_getsubopt.c +87 -0
  21. data/mandoc-1.14.6/compat_isblank.c +23 -0
  22. data/mandoc-1.14.6/compat_mkdtemp.c +50 -0
  23. data/mandoc-1.14.6/compat_mkstemps.c +63 -0
  24. data/mandoc-1.14.6/compat_ohash.c +330 -0
  25. data/mandoc-1.14.6/compat_ohash.h +72 -0
  26. data/mandoc-1.14.6/compat_progname.c +31 -0
  27. data/mandoc-1.14.6/compat_reallocarray.c +40 -0
  28. data/mandoc-1.14.6/compat_recallocarray.c +99 -0
  29. data/mandoc-1.14.6/compat_strcasestr.c +64 -0
  30. data/mandoc-1.14.6/compat_stringlist.c +135 -0
  31. data/mandoc-1.14.6/compat_stringlist.h +48 -0
  32. data/mandoc-1.14.6/compat_strlcat.c +57 -0
  33. data/mandoc-1.14.6/compat_strlcpy.c +52 -0
  34. data/mandoc-1.14.6/compat_strndup.c +42 -0
  35. data/mandoc-1.14.6/compat_strsep.c +70 -0
  36. data/mandoc-1.14.6/compat_strtonum.c +67 -0
  37. data/mandoc-1.14.6/compat_vasprintf.c +47 -0
  38. data/mandoc-1.14.6/config.h +52 -0
  39. data/mandoc-1.14.6/dba.c +508 -0
  40. data/mandoc-1.14.6/dba.h +50 -0
  41. data/mandoc-1.14.6/dba_array.c +190 -0
  42. data/mandoc-1.14.6/dba_array.h +47 -0
  43. data/mandoc-1.14.6/dba_read.c +74 -0
  44. data/mandoc-1.14.6/dba_write.c +127 -0
  45. data/mandoc-1.14.6/dba_write.h +30 -0
  46. data/mandoc-1.14.6/dbm.c +480 -0
  47. data/mandoc-1.14.6/dbm.h +68 -0
  48. data/mandoc-1.14.6/dbm_map.c +194 -0
  49. data/mandoc-1.14.6/dbm_map.h +29 -0
  50. data/mandoc-1.14.6/demandoc.c +260 -0
  51. data/mandoc-1.14.6/eqn.c +1132 -0
  52. data/mandoc-1.14.6/eqn.h +72 -0
  53. data/mandoc-1.14.6/eqn_html.c +246 -0
  54. data/mandoc-1.14.6/eqn_parse.h +48 -0
  55. data/mandoc-1.14.6/eqn_term.c +174 -0
  56. data/mandoc-1.14.6/html.c +1102 -0
  57. data/mandoc-1.14.6/html.h +142 -0
  58. data/mandoc-1.14.6/lib.c +35 -0
  59. data/mandoc-1.14.6/libman.h +42 -0
  60. data/mandoc-1.14.6/libmandoc.h +85 -0
  61. data/mandoc-1.14.6/libmdoc.h +87 -0
  62. data/mandoc-1.14.6/main.c +1375 -0
  63. data/mandoc-1.14.6/main.h +53 -0
  64. data/mandoc-1.14.6/man.c +345 -0
  65. data/mandoc-1.14.6/man.h +21 -0
  66. data/mandoc-1.14.6/man_html.c +640 -0
  67. data/mandoc-1.14.6/man_macro.c +470 -0
  68. data/mandoc-1.14.6/man_term.c +1143 -0
  69. data/mandoc-1.14.6/man_validate.c +660 -0
  70. data/mandoc-1.14.6/manconf.h +58 -0
  71. data/mandoc-1.14.6/mandoc.c +669 -0
  72. data/mandoc-1.14.6/mandoc.h +329 -0
  73. data/mandoc-1.14.6/mandoc_aux.c +118 -0
  74. data/mandoc-1.14.6/mandoc_aux.h +27 -0
  75. data/mandoc-1.14.6/mandoc_msg.c +375 -0
  76. data/mandoc-1.14.6/mandoc_ohash.c +65 -0
  77. data/mandoc-1.14.6/mandoc_ohash.h +23 -0
  78. data/mandoc-1.14.6/mandoc_parse.h +44 -0
  79. data/mandoc-1.14.6/mandoc_xr.c +123 -0
  80. data/mandoc-1.14.6/mandoc_xr.h +31 -0
  81. data/mandoc-1.14.6/mandocd.c +282 -0
  82. data/mandoc-1.14.6/mandocdb.c +2448 -0
  83. data/mandoc-1.14.6/manpath.c +363 -0
  84. data/mandoc-1.14.6/mansearch.c +851 -0
  85. data/mandoc-1.14.6/mansearch.h +118 -0
  86. data/mandoc-1.14.6/mdoc.c +433 -0
  87. data/mandoc-1.14.6/mdoc.h +158 -0
  88. data/mandoc-1.14.6/mdoc_argv.c +682 -0
  89. data/mandoc-1.14.6/mdoc_html.c +1762 -0
  90. data/mandoc-1.14.6/mdoc_macro.c +1600 -0
  91. data/mandoc-1.14.6/mdoc_man.c +1850 -0
  92. data/mandoc-1.14.6/mdoc_markdown.c +1610 -0
  93. data/mandoc-1.14.6/mdoc_state.c +256 -0
  94. data/mandoc-1.14.6/mdoc_term.c +1964 -0
  95. data/mandoc-1.14.6/mdoc_validate.c +3062 -0
  96. data/mandoc-1.14.6/msec.c +37 -0
  97. data/mandoc-1.14.6/out.c +544 -0
  98. data/mandoc-1.14.6/out.h +70 -0
  99. data/mandoc-1.14.6/preconv.c +179 -0
  100. data/mandoc-1.14.6/read.c +732 -0
  101. data/mandoc-1.14.6/roff.c +4390 -0
  102. data/mandoc-1.14.6/roff.h +561 -0
  103. data/mandoc-1.14.6/roff_html.c +119 -0
  104. data/mandoc-1.14.6/roff_int.h +94 -0
  105. data/mandoc-1.14.6/roff_term.c +266 -0
  106. data/mandoc-1.14.6/roff_validate.c +151 -0
  107. data/mandoc-1.14.6/soelim.c +182 -0
  108. data/mandoc-1.14.6/st.c +82 -0
  109. data/mandoc-1.14.6/tag.c +327 -0
  110. data/mandoc-1.14.6/tag.h +35 -0
  111. data/mandoc-1.14.6/tbl.c +183 -0
  112. data/mandoc-1.14.6/tbl.h +121 -0
  113. data/mandoc-1.14.6/tbl_data.c +323 -0
  114. data/mandoc-1.14.6/tbl_html.c +293 -0
  115. data/mandoc-1.14.6/tbl_int.h +47 -0
  116. data/mandoc-1.14.6/tbl_layout.c +376 -0
  117. data/mandoc-1.14.6/tbl_opts.c +173 -0
  118. data/mandoc-1.14.6/tbl_parse.h +30 -0
  119. data/mandoc-1.14.6/tbl_term.c +948 -0
  120. data/mandoc-1.14.6/term.c +1113 -0
  121. data/mandoc-1.14.6/term.h +158 -0
  122. data/mandoc-1.14.6/term_ascii.c +424 -0
  123. data/mandoc-1.14.6/term_ps.c +1362 -0
  124. data/mandoc-1.14.6/term_tab.c +130 -0
  125. data/mandoc-1.14.6/term_tag.c +227 -0
  126. data/mandoc-1.14.6/term_tag.h +34 -0
  127. data/mandoc-1.14.6/tree.c +536 -0
  128. metadata +170 -0
@@ -0,0 +1,1132 @@
1
+ /* $Id: eqn.c,v 1.84 2020/01/08 12:16:24 schwarze Exp $ */
2
+ /*
3
+ * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4
+ * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org>
5
+ *
6
+ * Permission to use, copy, modify, and distribute this software for any
7
+ * purpose with or without fee is hereby granted, provided that the above
8
+ * copyright notice and this permission notice appear in all copies.
9
+ *
10
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
+ */
18
+ #include "config.h"
19
+
20
+ #include <sys/types.h>
21
+
22
+ #include <assert.h>
23
+ #include <ctype.h>
24
+ #include <limits.h>
25
+ #include <stdio.h>
26
+ #include <stdlib.h>
27
+ #include <string.h>
28
+ #include <time.h>
29
+
30
+ #include "mandoc_aux.h"
31
+ #include "mandoc.h"
32
+ #include "roff.h"
33
+ #include "eqn.h"
34
+ #include "libmandoc.h"
35
+ #include "eqn_parse.h"
36
+
37
+ #define EQN_NEST_MAX 128 /* maximum nesting of defines */
38
+ #define STRNEQ(p1, sz1, p2, sz2) \
39
+ ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
40
+
41
+ enum eqn_tok {
42
+ EQN_TOK_DYAD = 0,
43
+ EQN_TOK_VEC,
44
+ EQN_TOK_UNDER,
45
+ EQN_TOK_BAR,
46
+ EQN_TOK_TILDE,
47
+ EQN_TOK_HAT,
48
+ EQN_TOK_DOT,
49
+ EQN_TOK_DOTDOT,
50
+ EQN_TOK_FWD,
51
+ EQN_TOK_BACK,
52
+ EQN_TOK_DOWN,
53
+ EQN_TOK_UP,
54
+ EQN_TOK_FAT,
55
+ EQN_TOK_ROMAN,
56
+ EQN_TOK_ITALIC,
57
+ EQN_TOK_BOLD,
58
+ EQN_TOK_SIZE,
59
+ EQN_TOK_SUB,
60
+ EQN_TOK_SUP,
61
+ EQN_TOK_SQRT,
62
+ EQN_TOK_OVER,
63
+ EQN_TOK_FROM,
64
+ EQN_TOK_TO,
65
+ EQN_TOK_BRACE_OPEN,
66
+ EQN_TOK_BRACE_CLOSE,
67
+ EQN_TOK_GSIZE,
68
+ EQN_TOK_GFONT,
69
+ EQN_TOK_MARK,
70
+ EQN_TOK_LINEUP,
71
+ EQN_TOK_LEFT,
72
+ EQN_TOK_RIGHT,
73
+ EQN_TOK_PILE,
74
+ EQN_TOK_LPILE,
75
+ EQN_TOK_RPILE,
76
+ EQN_TOK_CPILE,
77
+ EQN_TOK_MATRIX,
78
+ EQN_TOK_CCOL,
79
+ EQN_TOK_LCOL,
80
+ EQN_TOK_RCOL,
81
+ EQN_TOK_DELIM,
82
+ EQN_TOK_DEFINE,
83
+ EQN_TOK_TDEFINE,
84
+ EQN_TOK_NDEFINE,
85
+ EQN_TOK_UNDEF,
86
+ EQN_TOK_ABOVE,
87
+ EQN_TOK__MAX,
88
+ EQN_TOK_FUNC,
89
+ EQN_TOK_QUOTED,
90
+ EQN_TOK_SYM,
91
+ EQN_TOK_EOF
92
+ };
93
+
94
+ static const char *eqn_toks[EQN_TOK__MAX] = {
95
+ "dyad", /* EQN_TOK_DYAD */
96
+ "vec", /* EQN_TOK_VEC */
97
+ "under", /* EQN_TOK_UNDER */
98
+ "bar", /* EQN_TOK_BAR */
99
+ "tilde", /* EQN_TOK_TILDE */
100
+ "hat", /* EQN_TOK_HAT */
101
+ "dot", /* EQN_TOK_DOT */
102
+ "dotdot", /* EQN_TOK_DOTDOT */
103
+ "fwd", /* EQN_TOK_FWD * */
104
+ "back", /* EQN_TOK_BACK */
105
+ "down", /* EQN_TOK_DOWN */
106
+ "up", /* EQN_TOK_UP */
107
+ "fat", /* EQN_TOK_FAT */
108
+ "roman", /* EQN_TOK_ROMAN */
109
+ "italic", /* EQN_TOK_ITALIC */
110
+ "bold", /* EQN_TOK_BOLD */
111
+ "size", /* EQN_TOK_SIZE */
112
+ "sub", /* EQN_TOK_SUB */
113
+ "sup", /* EQN_TOK_SUP */
114
+ "sqrt", /* EQN_TOK_SQRT */
115
+ "over", /* EQN_TOK_OVER */
116
+ "from", /* EQN_TOK_FROM */
117
+ "to", /* EQN_TOK_TO */
118
+ "{", /* EQN_TOK_BRACE_OPEN */
119
+ "}", /* EQN_TOK_BRACE_CLOSE */
120
+ "gsize", /* EQN_TOK_GSIZE */
121
+ "gfont", /* EQN_TOK_GFONT */
122
+ "mark", /* EQN_TOK_MARK */
123
+ "lineup", /* EQN_TOK_LINEUP */
124
+ "left", /* EQN_TOK_LEFT */
125
+ "right", /* EQN_TOK_RIGHT */
126
+ "pile", /* EQN_TOK_PILE */
127
+ "lpile", /* EQN_TOK_LPILE */
128
+ "rpile", /* EQN_TOK_RPILE */
129
+ "cpile", /* EQN_TOK_CPILE */
130
+ "matrix", /* EQN_TOK_MATRIX */
131
+ "ccol", /* EQN_TOK_CCOL */
132
+ "lcol", /* EQN_TOK_LCOL */
133
+ "rcol", /* EQN_TOK_RCOL */
134
+ "delim", /* EQN_TOK_DELIM */
135
+ "define", /* EQN_TOK_DEFINE */
136
+ "tdefine", /* EQN_TOK_TDEFINE */
137
+ "ndefine", /* EQN_TOK_NDEFINE */
138
+ "undef", /* EQN_TOK_UNDEF */
139
+ "above", /* EQN_TOK_ABOVE */
140
+ };
141
+
142
+ static const char *const eqn_func[] = {
143
+ "acos", "acsc", "and", "arc", "asec", "asin", "atan",
144
+ "cos", "cosh", "coth", "csc", "det", "exp", "for",
145
+ "if", "lim", "ln", "log", "max", "min",
146
+ "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
147
+ };
148
+
149
+ enum eqn_symt {
150
+ EQNSYM_alpha = 0,
151
+ EQNSYM_beta,
152
+ EQNSYM_chi,
153
+ EQNSYM_delta,
154
+ EQNSYM_epsilon,
155
+ EQNSYM_eta,
156
+ EQNSYM_gamma,
157
+ EQNSYM_iota,
158
+ EQNSYM_kappa,
159
+ EQNSYM_lambda,
160
+ EQNSYM_mu,
161
+ EQNSYM_nu,
162
+ EQNSYM_omega,
163
+ EQNSYM_omicron,
164
+ EQNSYM_phi,
165
+ EQNSYM_pi,
166
+ EQNSYM_ps,
167
+ EQNSYM_rho,
168
+ EQNSYM_sigma,
169
+ EQNSYM_tau,
170
+ EQNSYM_theta,
171
+ EQNSYM_upsilon,
172
+ EQNSYM_xi,
173
+ EQNSYM_zeta,
174
+ EQNSYM_DELTA,
175
+ EQNSYM_GAMMA,
176
+ EQNSYM_LAMBDA,
177
+ EQNSYM_OMEGA,
178
+ EQNSYM_PHI,
179
+ EQNSYM_PI,
180
+ EQNSYM_PSI,
181
+ EQNSYM_SIGMA,
182
+ EQNSYM_THETA,
183
+ EQNSYM_UPSILON,
184
+ EQNSYM_XI,
185
+ EQNSYM_inter,
186
+ EQNSYM_union,
187
+ EQNSYM_prod,
188
+ EQNSYM_int,
189
+ EQNSYM_sum,
190
+ EQNSYM_grad,
191
+ EQNSYM_del,
192
+ EQNSYM_times,
193
+ EQNSYM_cdot,
194
+ EQNSYM_nothing,
195
+ EQNSYM_approx,
196
+ EQNSYM_prime,
197
+ EQNSYM_half,
198
+ EQNSYM_partial,
199
+ EQNSYM_inf,
200
+ EQNSYM_muchgreat,
201
+ EQNSYM_muchless,
202
+ EQNSYM_larrow,
203
+ EQNSYM_rarrow,
204
+ EQNSYM_pm,
205
+ EQNSYM_nequal,
206
+ EQNSYM_equiv,
207
+ EQNSYM_lessequal,
208
+ EQNSYM_moreequal,
209
+ EQNSYM_minus,
210
+ EQNSYM__MAX
211
+ };
212
+
213
+ struct eqnsym {
214
+ const char *str;
215
+ const char *sym;
216
+ };
217
+
218
+ static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
219
+ { "alpha", "*a" }, /* EQNSYM_alpha */
220
+ { "beta", "*b" }, /* EQNSYM_beta */
221
+ { "chi", "*x" }, /* EQNSYM_chi */
222
+ { "delta", "*d" }, /* EQNSYM_delta */
223
+ { "epsilon", "*e" }, /* EQNSYM_epsilon */
224
+ { "eta", "*y" }, /* EQNSYM_eta */
225
+ { "gamma", "*g" }, /* EQNSYM_gamma */
226
+ { "iota", "*i" }, /* EQNSYM_iota */
227
+ { "kappa", "*k" }, /* EQNSYM_kappa */
228
+ { "lambda", "*l" }, /* EQNSYM_lambda */
229
+ { "mu", "*m" }, /* EQNSYM_mu */
230
+ { "nu", "*n" }, /* EQNSYM_nu */
231
+ { "omega", "*w" }, /* EQNSYM_omega */
232
+ { "omicron", "*o" }, /* EQNSYM_omicron */
233
+ { "phi", "*f" }, /* EQNSYM_phi */
234
+ { "pi", "*p" }, /* EQNSYM_pi */
235
+ { "psi", "*q" }, /* EQNSYM_psi */
236
+ { "rho", "*r" }, /* EQNSYM_rho */
237
+ { "sigma", "*s" }, /* EQNSYM_sigma */
238
+ { "tau", "*t" }, /* EQNSYM_tau */
239
+ { "theta", "*h" }, /* EQNSYM_theta */
240
+ { "upsilon", "*u" }, /* EQNSYM_upsilon */
241
+ { "xi", "*c" }, /* EQNSYM_xi */
242
+ { "zeta", "*z" }, /* EQNSYM_zeta */
243
+ { "DELTA", "*D" }, /* EQNSYM_DELTA */
244
+ { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
245
+ { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
246
+ { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
247
+ { "PHI", "*F" }, /* EQNSYM_PHI */
248
+ { "PI", "*P" }, /* EQNSYM_PI */
249
+ { "PSI", "*Q" }, /* EQNSYM_PSI */
250
+ { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
251
+ { "THETA", "*H" }, /* EQNSYM_THETA */
252
+ { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
253
+ { "XI", "*C" }, /* EQNSYM_XI */
254
+ { "inter", "ca" }, /* EQNSYM_inter */
255
+ { "union", "cu" }, /* EQNSYM_union */
256
+ { "prod", "product" }, /* EQNSYM_prod */
257
+ { "int", "integral" }, /* EQNSYM_int */
258
+ { "sum", "sum" }, /* EQNSYM_sum */
259
+ { "grad", "gr" }, /* EQNSYM_grad */
260
+ { "del", "gr" }, /* EQNSYM_del */
261
+ { "times", "mu" }, /* EQNSYM_times */
262
+ { "cdot", "pc" }, /* EQNSYM_cdot */
263
+ { "nothing", "&" }, /* EQNSYM_nothing */
264
+ { "approx", "~~" }, /* EQNSYM_approx */
265
+ { "prime", "fm" }, /* EQNSYM_prime */
266
+ { "half", "12" }, /* EQNSYM_half */
267
+ { "partial", "pd" }, /* EQNSYM_partial */
268
+ { "inf", "if" }, /* EQNSYM_inf */
269
+ { ">>", ">>" }, /* EQNSYM_muchgreat */
270
+ { "<<", "<<" }, /* EQNSYM_muchless */
271
+ { "<-", "<-" }, /* EQNSYM_larrow */
272
+ { "->", "->" }, /* EQNSYM_rarrow */
273
+ { "+-", "+-" }, /* EQNSYM_pm */
274
+ { "!=", "!=" }, /* EQNSYM_nequal */
275
+ { "==", "==" }, /* EQNSYM_equiv */
276
+ { "<=", "<=" }, /* EQNSYM_lessequal */
277
+ { ">=", ">=" }, /* EQNSYM_moreequal */
278
+ { "-", "mi" }, /* EQNSYM_minus */
279
+ };
280
+
281
+ enum parse_mode {
282
+ MODE_QUOTED,
283
+ MODE_NOSUB,
284
+ MODE_SUB,
285
+ MODE_TOK
286
+ };
287
+
288
+ struct eqn_def {
289
+ char *key;
290
+ size_t keysz;
291
+ char *val;
292
+ size_t valsz;
293
+ };
294
+
295
+ static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *);
296
+ static struct eqn_box *eqn_box_makebinary(struct eqn_node *,
297
+ struct eqn_box *);
298
+ static void eqn_def(struct eqn_node *);
299
+ static struct eqn_def *eqn_def_find(struct eqn_node *);
300
+ static void eqn_delim(struct eqn_node *);
301
+ static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode);
302
+ static void eqn_undef(struct eqn_node *);
303
+
304
+
305
+ struct eqn_node *
306
+ eqn_alloc(void)
307
+ {
308
+ struct eqn_node *ep;
309
+
310
+ ep = mandoc_calloc(1, sizeof(*ep));
311
+ ep->gsize = EQN_DEFSIZE;
312
+ return ep;
313
+ }
314
+
315
+ void
316
+ eqn_reset(struct eqn_node *ep)
317
+ {
318
+ free(ep->data);
319
+ ep->data = ep->start = ep->end = NULL;
320
+ ep->sz = ep->toksz = 0;
321
+ }
322
+
323
+ void
324
+ eqn_read(struct eqn_node *ep, const char *p)
325
+ {
326
+ char *cp;
327
+
328
+ if (ep->data == NULL) {
329
+ ep->sz = strlen(p);
330
+ ep->data = mandoc_strdup(p);
331
+ } else {
332
+ ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
333
+ free(ep->data);
334
+ ep->data = cp;
335
+ }
336
+ ep->sz += 1;
337
+ }
338
+
339
+ /*
340
+ * Find the key "key" of the give size within our eqn-defined values.
341
+ */
342
+ static struct eqn_def *
343
+ eqn_def_find(struct eqn_node *ep)
344
+ {
345
+ int i;
346
+
347
+ for (i = 0; i < (int)ep->defsz; i++)
348
+ if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
349
+ ep->defs[i].keysz, ep->start, ep->toksz))
350
+ return &ep->defs[i];
351
+
352
+ return NULL;
353
+ }
354
+
355
+ /*
356
+ * Parse a token from the input text. The modes are:
357
+ * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
358
+ * before its next occurence. Do not interpret the token in any
359
+ * way and return EQN_TOK_QUOTED. All other modes behave like
360
+ * MODE_QUOTED when *ep->start is '"'.
361
+ * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
362
+ * otherwise, it ends before the next whitespace or brace.
363
+ * Do not interpret the token and return EQN_TOK__MAX.
364
+ * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
365
+ * alias created with define. If it is an alias, replace it with
366
+ * its string value and reparse.
367
+ * MODE_TOK: Like MODE_SUB, but also check the token against the list
368
+ * of tokens, and if there is a match, return that token. Otherwise,
369
+ * if the token matches a symbol, return EQN_TOK_SYM; if it matches
370
+ * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for
371
+ * a token match, *ep->start is set to an allocated string that the
372
+ * caller is expected to free.
373
+ * All modes skip whitespace following the end of the token.
374
+ */
375
+ static enum eqn_tok
376
+ eqn_next(struct eqn_node *ep, enum parse_mode mode)
377
+ {
378
+ static int last_len, lim;
379
+
380
+ struct eqn_def *def;
381
+ size_t start;
382
+ int diff, i, quoted;
383
+ enum eqn_tok tok;
384
+
385
+ /*
386
+ * Reset the recursion counter after advancing
387
+ * beyond the end of the previous substitution.
388
+ */
389
+ if (ep->end - ep->data >= last_len)
390
+ lim = 0;
391
+
392
+ ep->start = ep->end;
393
+ quoted = mode == MODE_QUOTED;
394
+ for (;;) {
395
+ switch (*ep->start) {
396
+ case '\0':
397
+ ep->toksz = 0;
398
+ return EQN_TOK_EOF;
399
+ case '"':
400
+ quoted = 1;
401
+ break;
402
+ case ' ':
403
+ case '\t':
404
+ case '~':
405
+ case '^':
406
+ if (quoted)
407
+ break;
408
+ ep->start++;
409
+ continue;
410
+ default:
411
+ break;
412
+ }
413
+ if (quoted) {
414
+ ep->end = strchr(ep->start + 1, *ep->start);
415
+ ep->start++; /* Skip opening quote. */
416
+ if (ep->end == NULL) {
417
+ mandoc_msg(MANDOCERR_ARG_QUOTE,
418
+ ep->node->line, ep->node->pos, NULL);
419
+ ep->end = strchr(ep->start, '\0');
420
+ }
421
+ } else {
422
+ ep->end = ep->start + 1;
423
+ if (*ep->start != '{' && *ep->start != '}')
424
+ ep->end += strcspn(ep->end, " ^~\"{}\t");
425
+ }
426
+ ep->toksz = ep->end - ep->start;
427
+ if (quoted && *ep->end != '\0')
428
+ ep->end++; /* Skip closing quote. */
429
+ while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
430
+ ep->end++;
431
+ if (quoted) /* Cannot return, may have to strndup. */
432
+ break;
433
+ if (mode == MODE_NOSUB)
434
+ return EQN_TOK__MAX;
435
+ if ((def = eqn_def_find(ep)) == NULL)
436
+ break;
437
+ if (++lim > EQN_NEST_MAX) {
438
+ mandoc_msg(MANDOCERR_ROFFLOOP,
439
+ ep->node->line, ep->node->pos, NULL);
440
+ return EQN_TOK_EOF;
441
+ }
442
+
443
+ /* Replace a defined name with its string value. */
444
+ if ((diff = def->valsz - ep->toksz) > 0) {
445
+ start = ep->start - ep->data;
446
+ ep->sz += diff;
447
+ ep->data = mandoc_realloc(ep->data, ep->sz + 1);
448
+ ep->start = ep->data + start;
449
+ }
450
+ if (diff)
451
+ memmove(ep->start + def->valsz, ep->start + ep->toksz,
452
+ strlen(ep->start + ep->toksz) + 1);
453
+ memcpy(ep->start, def->val, def->valsz);
454
+ last_len = ep->start - ep->data + def->valsz;
455
+ }
456
+ if (mode != MODE_TOK)
457
+ return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
458
+ if (quoted) {
459
+ ep->start = mandoc_strndup(ep->start, ep->toksz);
460
+ return EQN_TOK_QUOTED;
461
+ }
462
+ for (tok = 0; tok < EQN_TOK__MAX; tok++)
463
+ if (STRNEQ(ep->start, ep->toksz,
464
+ eqn_toks[tok], strlen(eqn_toks[tok])))
465
+ return tok;
466
+
467
+ for (i = 0; i < EQNSYM__MAX; i++) {
468
+ if (STRNEQ(ep->start, ep->toksz,
469
+ eqnsyms[i].str, strlen(eqnsyms[i].str))) {
470
+ mandoc_asprintf(&ep->start,
471
+ "\\[%s]", eqnsyms[i].sym);
472
+ return EQN_TOK_SYM;
473
+ }
474
+ }
475
+ ep->start = mandoc_strndup(ep->start, ep->toksz);
476
+ for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
477
+ if (STRNEQ(ep->start, ep->toksz,
478
+ eqn_func[i], strlen(eqn_func[i])))
479
+ return EQN_TOK_FUNC;
480
+ return EQN_TOK__MAX;
481
+ }
482
+
483
+ void
484
+ eqn_box_free(struct eqn_box *bp)
485
+ {
486
+ if (bp == NULL)
487
+ return;
488
+
489
+ if (bp->first)
490
+ eqn_box_free(bp->first);
491
+ if (bp->next)
492
+ eqn_box_free(bp->next);
493
+
494
+ free(bp->text);
495
+ free(bp->left);
496
+ free(bp->right);
497
+ free(bp->top);
498
+ free(bp->bottom);
499
+ free(bp);
500
+ }
501
+
502
+ struct eqn_box *
503
+ eqn_box_new(void)
504
+ {
505
+ struct eqn_box *bp;
506
+
507
+ bp = mandoc_calloc(1, sizeof(*bp));
508
+ bp->expectargs = UINT_MAX;
509
+ return bp;
510
+ }
511
+
512
+ /*
513
+ * Allocate a box as the last child of the parent node.
514
+ */
515
+ static struct eqn_box *
516
+ eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
517
+ {
518
+ struct eqn_box *bp;
519
+
520
+ bp = eqn_box_new();
521
+ bp->parent = parent;
522
+ bp->parent->args++;
523
+ bp->font = bp->parent->font;
524
+ bp->size = ep->gsize;
525
+
526
+ if (NULL != parent->first) {
527
+ parent->last->next = bp;
528
+ bp->prev = parent->last;
529
+ } else
530
+ parent->first = bp;
531
+
532
+ parent->last = bp;
533
+ return bp;
534
+ }
535
+
536
+ /*
537
+ * Reparent the current last node (of the current parent) under a new
538
+ * EQN_SUBEXPR as the first element.
539
+ * Then return the new parent.
540
+ * The new EQN_SUBEXPR will have a two-child limit.
541
+ */
542
+ static struct eqn_box *
543
+ eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
544
+ {
545
+ struct eqn_box *b, *newb;
546
+
547
+ assert(NULL != parent->last);
548
+ b = parent->last;
549
+ if (parent->last == parent->first)
550
+ parent->first = NULL;
551
+ parent->args--;
552
+ parent->last = b->prev;
553
+ b->prev = NULL;
554
+ newb = eqn_box_alloc(ep, parent);
555
+ newb->type = EQN_SUBEXPR;
556
+ newb->expectargs = 2;
557
+ newb->args = 1;
558
+ newb->first = newb->last = b;
559
+ newb->first->next = NULL;
560
+ b->parent = newb;
561
+ return newb;
562
+ }
563
+
564
+ /*
565
+ * Parse the "delim" control statement.
566
+ */
567
+ static void
568
+ eqn_delim(struct eqn_node *ep)
569
+ {
570
+ if (ep->end[0] == '\0' || ep->end[1] == '\0') {
571
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
572
+ ep->node->line, ep->node->pos, "delim");
573
+ if (ep->end[0] != '\0')
574
+ ep->end++;
575
+ } else if (strncmp(ep->end, "off", 3) == 0) {
576
+ ep->delim = 0;
577
+ ep->end += 3;
578
+ } else if (strncmp(ep->end, "on", 2) == 0) {
579
+ if (ep->odelim && ep->cdelim)
580
+ ep->delim = 1;
581
+ ep->end += 2;
582
+ } else {
583
+ ep->odelim = *ep->end++;
584
+ ep->cdelim = *ep->end++;
585
+ ep->delim = 1;
586
+ }
587
+ }
588
+
589
+ /*
590
+ * Undefine a previously-defined string.
591
+ */
592
+ static void
593
+ eqn_undef(struct eqn_node *ep)
594
+ {
595
+ struct eqn_def *def;
596
+
597
+ if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
598
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
599
+ ep->node->line, ep->node->pos, "undef");
600
+ return;
601
+ }
602
+ if ((def = eqn_def_find(ep)) == NULL)
603
+ return;
604
+ free(def->key);
605
+ free(def->val);
606
+ def->key = def->val = NULL;
607
+ def->keysz = def->valsz = 0;
608
+ }
609
+
610
+ static void
611
+ eqn_def(struct eqn_node *ep)
612
+ {
613
+ struct eqn_def *def;
614
+ int i;
615
+
616
+ if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
617
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
618
+ ep->node->line, ep->node->pos, "define");
619
+ return;
620
+ }
621
+
622
+ /*
623
+ * Search for a key that already exists.
624
+ * Create a new key if none is found.
625
+ */
626
+ if ((def = eqn_def_find(ep)) == NULL) {
627
+ /* Find holes in string array. */
628
+ for (i = 0; i < (int)ep->defsz; i++)
629
+ if (0 == ep->defs[i].keysz)
630
+ break;
631
+
632
+ if (i == (int)ep->defsz) {
633
+ ep->defsz++;
634
+ ep->defs = mandoc_reallocarray(ep->defs,
635
+ ep->defsz, sizeof(struct eqn_def));
636
+ ep->defs[i].key = ep->defs[i].val = NULL;
637
+ }
638
+
639
+ def = ep->defs + i;
640
+ free(def->key);
641
+ def->key = mandoc_strndup(ep->start, ep->toksz);
642
+ def->keysz = ep->toksz;
643
+ }
644
+
645
+ if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
646
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
647
+ ep->node->line, ep->node->pos, "define %s", def->key);
648
+ free(def->key);
649
+ free(def->val);
650
+ def->key = def->val = NULL;
651
+ def->keysz = def->valsz = 0;
652
+ return;
653
+ }
654
+ free(def->val);
655
+ def->val = mandoc_strndup(ep->start, ep->toksz);
656
+ def->valsz = ep->toksz;
657
+ }
658
+
659
+ void
660
+ eqn_parse(struct eqn_node *ep)
661
+ {
662
+ struct eqn_box *cur, *nbox, *parent, *split;
663
+ const char *cp, *cpn;
664
+ char *p;
665
+ enum eqn_tok tok;
666
+ enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
667
+ int size;
668
+
669
+ parent = ep->node->eqn;
670
+ assert(parent != NULL);
671
+
672
+ /*
673
+ * Empty equation.
674
+ * Do not add it to the high-level syntax tree.
675
+ */
676
+
677
+ if (ep->data == NULL)
678
+ return;
679
+
680
+ ep->start = ep->end = ep->data;
681
+
682
+ next_tok:
683
+ tok = eqn_next(ep, MODE_TOK);
684
+ switch (tok) {
685
+ case EQN_TOK_UNDEF:
686
+ eqn_undef(ep);
687
+ break;
688
+ case EQN_TOK_NDEFINE:
689
+ case EQN_TOK_DEFINE:
690
+ eqn_def(ep);
691
+ break;
692
+ case EQN_TOK_TDEFINE:
693
+ if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
694
+ eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
695
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
696
+ ep->node->line, ep->node->pos, "tdefine");
697
+ break;
698
+ case EQN_TOK_DELIM:
699
+ eqn_delim(ep);
700
+ break;
701
+ case EQN_TOK_GFONT:
702
+ if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
703
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
704
+ ep->node->pos, "%s", eqn_toks[tok]);
705
+ break;
706
+ case EQN_TOK_MARK:
707
+ case EQN_TOK_LINEUP:
708
+ /* Ignore these. */
709
+ break;
710
+ case EQN_TOK_DYAD:
711
+ case EQN_TOK_VEC:
712
+ case EQN_TOK_UNDER:
713
+ case EQN_TOK_BAR:
714
+ case EQN_TOK_TILDE:
715
+ case EQN_TOK_HAT:
716
+ case EQN_TOK_DOT:
717
+ case EQN_TOK_DOTDOT:
718
+ if (parent->last == NULL) {
719
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
720
+ ep->node->pos, "%s", eqn_toks[tok]);
721
+ cur = eqn_box_alloc(ep, parent);
722
+ cur->type = EQN_TEXT;
723
+ cur->text = mandoc_strdup("");
724
+ }
725
+ parent = eqn_box_makebinary(ep, parent);
726
+ parent->type = EQN_LIST;
727
+ parent->expectargs = 1;
728
+ parent->font = EQNFONT_ROMAN;
729
+ switch (tok) {
730
+ case EQN_TOK_DOTDOT:
731
+ parent->top = mandoc_strdup("\\[ad]");
732
+ break;
733
+ case EQN_TOK_VEC:
734
+ parent->top = mandoc_strdup("\\[->]");
735
+ break;
736
+ case EQN_TOK_DYAD:
737
+ parent->top = mandoc_strdup("\\[<>]");
738
+ break;
739
+ case EQN_TOK_TILDE:
740
+ parent->top = mandoc_strdup("\\[a~]");
741
+ break;
742
+ case EQN_TOK_UNDER:
743
+ parent->bottom = mandoc_strdup("\\[ul]");
744
+ break;
745
+ case EQN_TOK_BAR:
746
+ parent->top = mandoc_strdup("\\[rn]");
747
+ break;
748
+ case EQN_TOK_DOT:
749
+ parent->top = mandoc_strdup("\\[a.]");
750
+ break;
751
+ case EQN_TOK_HAT:
752
+ parent->top = mandoc_strdup("\\[ha]");
753
+ break;
754
+ default:
755
+ abort();
756
+ }
757
+ parent = parent->parent;
758
+ break;
759
+ case EQN_TOK_FWD:
760
+ case EQN_TOK_BACK:
761
+ case EQN_TOK_DOWN:
762
+ case EQN_TOK_UP:
763
+ if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
764
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
765
+ ep->node->pos, "%s", eqn_toks[tok]);
766
+ break;
767
+ case EQN_TOK_FAT:
768
+ case EQN_TOK_ROMAN:
769
+ case EQN_TOK_ITALIC:
770
+ case EQN_TOK_BOLD:
771
+ while (parent->args == parent->expectargs)
772
+ parent = parent->parent;
773
+ /*
774
+ * These values apply to the next word or sequence of
775
+ * words; thus, we mark that we'll have a child with
776
+ * exactly one of those.
777
+ */
778
+ parent = eqn_box_alloc(ep, parent);
779
+ parent->type = EQN_LIST;
780
+ parent->expectargs = 1;
781
+ switch (tok) {
782
+ case EQN_TOK_FAT:
783
+ parent->font = EQNFONT_FAT;
784
+ break;
785
+ case EQN_TOK_ROMAN:
786
+ parent->font = EQNFONT_ROMAN;
787
+ break;
788
+ case EQN_TOK_ITALIC:
789
+ parent->font = EQNFONT_ITALIC;
790
+ break;
791
+ case EQN_TOK_BOLD:
792
+ parent->font = EQNFONT_BOLD;
793
+ break;
794
+ default:
795
+ abort();
796
+ }
797
+ break;
798
+ case EQN_TOK_SIZE:
799
+ case EQN_TOK_GSIZE:
800
+ /* Accept two values: integral size and a single. */
801
+ if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
802
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
803
+ ep->node->pos, "%s", eqn_toks[tok]);
804
+ break;
805
+ }
806
+ size = mandoc_strntoi(ep->start, ep->toksz, 10);
807
+ if (-1 == size) {
808
+ mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line,
809
+ ep->node->pos, "%s", eqn_toks[tok]);
810
+ break;
811
+ }
812
+ if (EQN_TOK_GSIZE == tok) {
813
+ ep->gsize = size;
814
+ break;
815
+ }
816
+ while (parent->args == parent->expectargs)
817
+ parent = parent->parent;
818
+ parent = eqn_box_alloc(ep, parent);
819
+ parent->type = EQN_LIST;
820
+ parent->expectargs = 1;
821
+ parent->size = size;
822
+ break;
823
+ case EQN_TOK_FROM:
824
+ case EQN_TOK_TO:
825
+ case EQN_TOK_SUB:
826
+ case EQN_TOK_SUP:
827
+ /*
828
+ * We have a left-right-associative expression.
829
+ * Repivot under a positional node, open a child scope
830
+ * and keep on reading.
831
+ */
832
+ if (parent->last == NULL) {
833
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
834
+ ep->node->pos, "%s", eqn_toks[tok]);
835
+ cur = eqn_box_alloc(ep, parent);
836
+ cur->type = EQN_TEXT;
837
+ cur->text = mandoc_strdup("");
838
+ }
839
+ while (parent->expectargs == 1 && parent->args == 1)
840
+ parent = parent->parent;
841
+ if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) {
842
+ for (cur = parent; cur != NULL; cur = cur->parent)
843
+ if (cur->pos == EQNPOS_SUB ||
844
+ cur->pos == EQNPOS_SUP ||
845
+ cur->pos == EQNPOS_SUBSUP ||
846
+ cur->pos == EQNPOS_SQRT ||
847
+ cur->pos == EQNPOS_OVER)
848
+ break;
849
+ if (cur != NULL)
850
+ parent = cur->parent;
851
+ }
852
+ if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
853
+ parent->expectargs = 3;
854
+ parent->pos = EQNPOS_SUBSUP;
855
+ break;
856
+ }
857
+ if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
858
+ parent->expectargs = 3;
859
+ parent->pos = EQNPOS_FROMTO;
860
+ break;
861
+ }
862
+ parent = eqn_box_makebinary(ep, parent);
863
+ switch (tok) {
864
+ case EQN_TOK_FROM:
865
+ parent->pos = EQNPOS_FROM;
866
+ break;
867
+ case EQN_TOK_TO:
868
+ parent->pos = EQNPOS_TO;
869
+ break;
870
+ case EQN_TOK_SUP:
871
+ parent->pos = EQNPOS_SUP;
872
+ break;
873
+ case EQN_TOK_SUB:
874
+ parent->pos = EQNPOS_SUB;
875
+ break;
876
+ default:
877
+ abort();
878
+ }
879
+ break;
880
+ case EQN_TOK_SQRT:
881
+ while (parent->args == parent->expectargs)
882
+ parent = parent->parent;
883
+ /*
884
+ * Accept a left-right-associative set of arguments just
885
+ * like sub and sup and friends but without rebalancing
886
+ * under a pivot.
887
+ */
888
+ parent = eqn_box_alloc(ep, parent);
889
+ parent->type = EQN_SUBEXPR;
890
+ parent->pos = EQNPOS_SQRT;
891
+ parent->expectargs = 1;
892
+ break;
893
+ case EQN_TOK_OVER:
894
+ /*
895
+ * We have a right-left-associative fraction.
896
+ * Close out anything that's currently open, then
897
+ * rebalance and continue reading.
898
+ */
899
+ if (parent->last == NULL) {
900
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
901
+ ep->node->pos, "%s", eqn_toks[tok]);
902
+ cur = eqn_box_alloc(ep, parent);
903
+ cur->type = EQN_TEXT;
904
+ cur->text = mandoc_strdup("");
905
+ }
906
+ while (parent->args == parent->expectargs)
907
+ parent = parent->parent;
908
+ while (EQN_SUBEXPR == parent->type)
909
+ parent = parent->parent;
910
+ parent = eqn_box_makebinary(ep, parent);
911
+ parent->pos = EQNPOS_OVER;
912
+ break;
913
+ case EQN_TOK_RIGHT:
914
+ case EQN_TOK_BRACE_CLOSE:
915
+ /*
916
+ * Close out the existing brace.
917
+ * FIXME: this is a shitty sentinel: we should really
918
+ * have a native EQN_BRACE type or whatnot.
919
+ */
920
+ for (cur = parent; cur != NULL; cur = cur->parent)
921
+ if (cur->type == EQN_LIST &&
922
+ cur->expectargs > 1 &&
923
+ (tok == EQN_TOK_BRACE_CLOSE ||
924
+ cur->left != NULL))
925
+ break;
926
+ if (cur == NULL) {
927
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line,
928
+ ep->node->pos, "%s", eqn_toks[tok]);
929
+ break;
930
+ }
931
+ parent = cur;
932
+ if (EQN_TOK_RIGHT == tok) {
933
+ if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
934
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
935
+ ep->node->line, ep->node->pos,
936
+ "%s", eqn_toks[tok]);
937
+ break;
938
+ }
939
+ /* Handling depends on right/left. */
940
+ if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
941
+ parent->right = mandoc_strdup("\\[rc]");
942
+ else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
943
+ parent->right = mandoc_strdup("\\[rf]");
944
+ else
945
+ parent->right =
946
+ mandoc_strndup(ep->start, ep->toksz);
947
+ }
948
+ parent = parent->parent;
949
+ if (tok == EQN_TOK_BRACE_CLOSE &&
950
+ (parent->type == EQN_PILE ||
951
+ parent->type == EQN_MATRIX))
952
+ parent = parent->parent;
953
+ /* Close out any "singleton" lists. */
954
+ while (parent->type == EQN_LIST &&
955
+ parent->expectargs == 1 &&
956
+ parent->args == 1)
957
+ parent = parent->parent;
958
+ break;
959
+ case EQN_TOK_BRACE_OPEN:
960
+ case EQN_TOK_LEFT:
961
+ /*
962
+ * If we already have something in the stack and we're
963
+ * in an expression, then rewind til we're not any more
964
+ * (just like with the text node).
965
+ */
966
+ while (parent->args == parent->expectargs)
967
+ parent = parent->parent;
968
+ if (EQN_TOK_LEFT == tok &&
969
+ eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
970
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
971
+ ep->node->pos, "%s", eqn_toks[tok]);
972
+ break;
973
+ }
974
+ parent = eqn_box_alloc(ep, parent);
975
+ parent->type = EQN_LIST;
976
+ if (EQN_TOK_LEFT == tok) {
977
+ if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
978
+ parent->left = mandoc_strdup("\\[lc]");
979
+ else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
980
+ parent->left = mandoc_strdup("\\[lf]");
981
+ else
982
+ parent->left =
983
+ mandoc_strndup(ep->start, ep->toksz);
984
+ }
985
+ break;
986
+ case EQN_TOK_PILE:
987
+ case EQN_TOK_LPILE:
988
+ case EQN_TOK_RPILE:
989
+ case EQN_TOK_CPILE:
990
+ case EQN_TOK_CCOL:
991
+ case EQN_TOK_LCOL:
992
+ case EQN_TOK_RCOL:
993
+ while (parent->args == parent->expectargs)
994
+ parent = parent->parent;
995
+ parent = eqn_box_alloc(ep, parent);
996
+ parent->type = EQN_PILE;
997
+ parent->expectargs = 1;
998
+ break;
999
+ case EQN_TOK_ABOVE:
1000
+ for (cur = parent; cur != NULL; cur = cur->parent)
1001
+ if (cur->type == EQN_PILE)
1002
+ break;
1003
+ if (cur == NULL) {
1004
+ mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line,
1005
+ ep->node->pos, "%s", eqn_toks[tok]);
1006
+ break;
1007
+ }
1008
+ parent = eqn_box_alloc(ep, cur);
1009
+ parent->type = EQN_LIST;
1010
+ break;
1011
+ case EQN_TOK_MATRIX:
1012
+ while (parent->args == parent->expectargs)
1013
+ parent = parent->parent;
1014
+ parent = eqn_box_alloc(ep, parent);
1015
+ parent->type = EQN_MATRIX;
1016
+ parent->expectargs = 1;
1017
+ break;
1018
+ case EQN_TOK_EOF:
1019
+ return;
1020
+ case EQN_TOK__MAX:
1021
+ case EQN_TOK_FUNC:
1022
+ case EQN_TOK_QUOTED:
1023
+ case EQN_TOK_SYM:
1024
+ p = ep->start;
1025
+ assert(p != NULL);
1026
+ /*
1027
+ * If we already have something in the stack and we're
1028
+ * in an expression, then rewind til we're not any more.
1029
+ */
1030
+ while (parent->args == parent->expectargs)
1031
+ parent = parent->parent;
1032
+ cur = eqn_box_alloc(ep, parent);
1033
+ cur->type = EQN_TEXT;
1034
+ cur->text = p;
1035
+ switch (tok) {
1036
+ case EQN_TOK_FUNC:
1037
+ cur->font = EQNFONT_ROMAN;
1038
+ break;
1039
+ case EQN_TOK_QUOTED:
1040
+ if (cur->font == EQNFONT_NONE)
1041
+ cur->font = EQNFONT_ITALIC;
1042
+ break;
1043
+ case EQN_TOK_SYM:
1044
+ break;
1045
+ default:
1046
+ if (cur->font != EQNFONT_NONE || *p == '\0')
1047
+ break;
1048
+ cpn = p - 1;
1049
+ ccln = CCL_LET;
1050
+ split = NULL;
1051
+ for (;;) {
1052
+ /* Advance to next character. */
1053
+ cp = cpn++;
1054
+ ccl = ccln;
1055
+ ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
1056
+ isdigit((unsigned char)*cpn) ||
1057
+ (*cpn == '.' && (ccl == CCL_DIG ||
1058
+ isdigit((unsigned char)cpn[1]))) ?
1059
+ CCL_DIG : CCL_PUN;
1060
+ /* No boundary before first character. */
1061
+ if (cp < p)
1062
+ continue;
1063
+ cur->font = ccl == CCL_LET ?
1064
+ EQNFONT_ITALIC : EQNFONT_ROMAN;
1065
+ if (*cp == '\\')
1066
+ mandoc_escape(&cpn, NULL, NULL);
1067
+ /* No boundary after last character. */
1068
+ if (*cpn == '\0')
1069
+ break;
1070
+ if (ccln == ccl && *cp != ',' && *cpn != ',')
1071
+ continue;
1072
+ /* Boundary found, split the text. */
1073
+ if (parent->args == parent->expectargs) {
1074
+ /* Remove the text from the tree. */
1075
+ if (cur->prev == NULL)
1076
+ parent->first = cur->next;
1077
+ else
1078
+ cur->prev->next = NULL;
1079
+ parent->last = cur->prev;
1080
+ parent->args--;
1081
+ /* Set up a list instead. */
1082
+ split = eqn_box_alloc(ep, parent);
1083
+ split->type = EQN_LIST;
1084
+ /* Insert the word into the list. */
1085
+ split->first = split->last = cur;
1086
+ cur->parent = split;
1087
+ cur->prev = NULL;
1088
+ parent = split;
1089
+ }
1090
+ /* Append a new text box. */
1091
+ nbox = eqn_box_alloc(ep, parent);
1092
+ nbox->type = EQN_TEXT;
1093
+ nbox->text = mandoc_strdup(cpn);
1094
+ /* Truncate the old box. */
1095
+ p = mandoc_strndup(cur->text,
1096
+ cpn - cur->text);
1097
+ free(cur->text);
1098
+ cur->text = p;
1099
+ /* Setup to process the new box. */
1100
+ cur = nbox;
1101
+ p = nbox->text;
1102
+ cpn = p - 1;
1103
+ ccln = CCL_LET;
1104
+ }
1105
+ if (split != NULL)
1106
+ parent = split->parent;
1107
+ break;
1108
+ }
1109
+ break;
1110
+ default:
1111
+ abort();
1112
+ }
1113
+ goto next_tok;
1114
+ }
1115
+
1116
+ void
1117
+ eqn_free(struct eqn_node *p)
1118
+ {
1119
+ int i;
1120
+
1121
+ if (p == NULL)
1122
+ return;
1123
+
1124
+ for (i = 0; i < (int)p->defsz; i++) {
1125
+ free(p->defs[i].key);
1126
+ free(p->defs[i].val);
1127
+ }
1128
+
1129
+ free(p->data);
1130
+ free(p->defs);
1131
+ free(p);
1132
+ }