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,1113 @@
1
+ /* $Id: term.c,v 1.283 2021/08/10 12:55:04 schwarze Exp $ */
2
+ /*
3
+ * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4
+ * Copyright (c) 2010-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 AUTHORS DISCLAIM ALL WARRANTIES
11
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <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 "out.h"
32
+ #include "term.h"
33
+ #include "main.h"
34
+
35
+ static size_t cond_width(const struct termp *, int, int *);
36
+ static void adjbuf(struct termp_col *, size_t);
37
+ static void bufferc(struct termp *, char);
38
+ static void encode(struct termp *, const char *, size_t);
39
+ static void encode1(struct termp *, int);
40
+ static void endline(struct termp *);
41
+ static void term_field(struct termp *, size_t, size_t);
42
+ static void term_fill(struct termp *, size_t *, size_t *,
43
+ size_t);
44
+
45
+
46
+ void
47
+ term_setcol(struct termp *p, size_t maxtcol)
48
+ {
49
+ if (maxtcol > p->maxtcol) {
50
+ p->tcols = mandoc_recallocarray(p->tcols,
51
+ p->maxtcol, maxtcol, sizeof(*p->tcols));
52
+ p->maxtcol = maxtcol;
53
+ }
54
+ p->lasttcol = maxtcol - 1;
55
+ p->tcol = p->tcols;
56
+ }
57
+
58
+ void
59
+ term_free(struct termp *p)
60
+ {
61
+ for (p->tcol = p->tcols; p->tcol < p->tcols + p->maxtcol; p->tcol++)
62
+ free(p->tcol->buf);
63
+ free(p->tcols);
64
+ free(p->fontq);
65
+ free(p);
66
+ }
67
+
68
+ void
69
+ term_begin(struct termp *p, term_margin head,
70
+ term_margin foot, const struct roff_meta *arg)
71
+ {
72
+
73
+ p->headf = head;
74
+ p->footf = foot;
75
+ p->argf = arg;
76
+ (*p->begin)(p);
77
+ }
78
+
79
+ void
80
+ term_end(struct termp *p)
81
+ {
82
+
83
+ (*p->end)(p);
84
+ }
85
+
86
+ /*
87
+ * Flush a chunk of text. By default, break the output line each time
88
+ * the right margin is reached, and continue output on the next line
89
+ * at the same offset as the chunk itself. By default, also break the
90
+ * output line at the end of the chunk. There are many flags modifying
91
+ * this behaviour, see the comments in the body of the function.
92
+ */
93
+ void
94
+ term_flushln(struct termp *p)
95
+ {
96
+ size_t vbl; /* Number of blanks to prepend to the output. */
97
+ size_t vbr; /* Actual visual position of the end of field. */
98
+ size_t vfield; /* Desired visual field width. */
99
+ size_t vtarget; /* Desired visual position of the right margin. */
100
+ size_t ic; /* Character position in the input buffer. */
101
+ size_t nbr; /* Number of characters to print in this field. */
102
+
103
+ /*
104
+ * Normally, start writing at the left margin, but with the
105
+ * NOPAD flag, start writing at the current position instead.
106
+ */
107
+
108
+ vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ?
109
+ 0 : p->tcol->offset - p->viscol;
110
+ if (p->minbl && vbl < p->minbl)
111
+ vbl = p->minbl;
112
+
113
+ if ((p->flags & TERMP_MULTICOL) == 0)
114
+ p->tcol->col = 0;
115
+
116
+ /* Loop over output lines. */
117
+
118
+ for (;;) {
119
+ vfield = p->tcol->rmargin > p->viscol + vbl ?
120
+ p->tcol->rmargin - p->viscol - vbl : 0;
121
+
122
+ /*
123
+ * Normally, break the line at the the right margin
124
+ * of the field, but with the NOBREAK flag, only
125
+ * break it at the max right margin of the screen,
126
+ * and with the BRNEVER flag, never break it at all.
127
+ */
128
+
129
+ vtarget = (p->flags & TERMP_NOBREAK) == 0 ? vfield :
130
+ p->maxrmargin > p->viscol + vbl ?
131
+ p->maxrmargin - p->viscol - vbl : 0;
132
+
133
+ /*
134
+ * Figure out how much text will fit in the field.
135
+ * If there is whitespace only, print nothing.
136
+ */
137
+
138
+ term_fill(p, &nbr, &vbr,
139
+ p->flags & TERMP_BRNEVER ? SIZE_MAX : vtarget);
140
+ if (nbr == 0)
141
+ break;
142
+
143
+ /*
144
+ * With the CENTER or RIGHT flag, increase the indentation
145
+ * to center the text between the left and right margins
146
+ * or to adjust it to the right margin, respectively.
147
+ */
148
+
149
+ if (vbr < vtarget) {
150
+ if (p->flags & TERMP_CENTER)
151
+ vbl += (vtarget - vbr) / 2;
152
+ else if (p->flags & TERMP_RIGHT)
153
+ vbl += vtarget - vbr;
154
+ }
155
+
156
+ /* Finally, print the field content. */
157
+
158
+ term_field(p, vbl, nbr);
159
+
160
+ /*
161
+ * If there is no text left in the field, exit the loop.
162
+ * If the BRTRSP flag is set, consider trailing
163
+ * whitespace significant when deciding whether
164
+ * the field fits or not.
165
+ */
166
+
167
+ for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
168
+ switch (p->tcol->buf[ic]) {
169
+ case '\t':
170
+ if (p->flags & TERMP_BRTRSP)
171
+ vbr = term_tab_next(vbr);
172
+ continue;
173
+ case ' ':
174
+ if (p->flags & TERMP_BRTRSP)
175
+ vbr += (*p->width)(p, ' ');
176
+ continue;
177
+ case '\n':
178
+ case ASCII_BREAK:
179
+ continue;
180
+ default:
181
+ break;
182
+ }
183
+ break;
184
+ }
185
+ if (ic == p->tcol->lastcol)
186
+ break;
187
+
188
+ /*
189
+ * At the location of an automtic line break, input
190
+ * space characters are consumed by the line break.
191
+ */
192
+
193
+ while (p->tcol->col < p->tcol->lastcol &&
194
+ p->tcol->buf[p->tcol->col] == ' ')
195
+ p->tcol->col++;
196
+
197
+ /*
198
+ * In multi-column mode, leave the rest of the text
199
+ * in the buffer to be handled by a subsequent
200
+ * invocation, such that the other columns of the
201
+ * table can be handled first.
202
+ * In single-column mode, simply break the line.
203
+ */
204
+
205
+ if (p->flags & TERMP_MULTICOL)
206
+ return;
207
+
208
+ endline(p);
209
+ p->viscol = 0;
210
+
211
+ /*
212
+ * Normally, start the next line at the same indentation
213
+ * as this one, but with the BRIND flag, start it at the
214
+ * right margin instead. This is used together with
215
+ * NOBREAK for the tags in various kinds of tagged lists.
216
+ */
217
+
218
+ vbl = p->flags & TERMP_BRIND ?
219
+ p->tcol->rmargin : p->tcol->offset;
220
+ }
221
+
222
+ /* Reset output state in preparation for the next field. */
223
+
224
+ p->col = p->tcol->col = p->tcol->lastcol = 0;
225
+ p->minbl = p->trailspace;
226
+ p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD);
227
+
228
+ if (p->flags & TERMP_MULTICOL)
229
+ return;
230
+
231
+ /*
232
+ * The HANG flag means that the next field
233
+ * always follows on the same line.
234
+ * The NOBREAK flag means that the next field
235
+ * follows on the same line unless the field was overrun.
236
+ * Normally, break the line at the end of each field.
237
+ */
238
+
239
+ if ((p->flags & TERMP_HANG) == 0 &&
240
+ ((p->flags & TERMP_NOBREAK) == 0 ||
241
+ vbr + term_len(p, p->trailspace) > vfield))
242
+ endline(p);
243
+ }
244
+
245
+ /*
246
+ * Store the number of input characters to print in this field in *nbr
247
+ * and their total visual width to print in *vbr.
248
+ * If there is only whitespace in the field, both remain zero.
249
+ * The desired visual width of the field is provided by vtarget.
250
+ * If the first word is longer, the field will be overrun.
251
+ */
252
+ static void
253
+ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
254
+ {
255
+ size_t ic; /* Character position in the input buffer. */
256
+ size_t vis; /* Visual position of the current character. */
257
+ size_t vn; /* Visual position of the next character. */
258
+ int breakline; /* Break at the end of this word. */
259
+ int graph; /* Last character was non-blank. */
260
+
261
+ *nbr = *vbr = vis = 0;
262
+ breakline = graph = 0;
263
+ for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
264
+ switch (p->tcol->buf[ic]) {
265
+ case '\b': /* Escape \o (overstrike) or backspace markup. */
266
+ assert(ic > 0);
267
+ vis -= (*p->width)(p, p->tcol->buf[ic - 1]);
268
+ continue;
269
+
270
+ case '\t': /* Normal ASCII whitespace. */
271
+ case ' ':
272
+ case ASCII_BREAK: /* Escape \: (breakpoint). */
273
+ switch (p->tcol->buf[ic]) {
274
+ case '\t':
275
+ vn = term_tab_next(vis);
276
+ break;
277
+ case ' ':
278
+ vn = vis + (*p->width)(p, ' ');
279
+ break;
280
+ case ASCII_BREAK:
281
+ vn = vis;
282
+ break;
283
+ default:
284
+ abort();
285
+ }
286
+ /* Can break at the end of a word. */
287
+ if (breakline || vn > vtarget)
288
+ break;
289
+ if (graph) {
290
+ *nbr = ic;
291
+ *vbr = vis;
292
+ graph = 0;
293
+ }
294
+ vis = vn;
295
+ continue;
296
+
297
+ case '\n': /* Escape \p (break at the end of the word). */
298
+ breakline = 1;
299
+ continue;
300
+
301
+ case ASCII_HYPH: /* Breakable hyphen. */
302
+ graph = 1;
303
+ /*
304
+ * We are about to decide whether to break the
305
+ * line or not, so we no longer need this hyphen
306
+ * to be marked as breakable. Put back a real
307
+ * hyphen such that we get the correct width.
308
+ */
309
+ p->tcol->buf[ic] = '-';
310
+ vis += (*p->width)(p, '-');
311
+ if (vis > vtarget) {
312
+ ic++;
313
+ break;
314
+ }
315
+ *nbr = ic + 1;
316
+ *vbr = vis;
317
+ continue;
318
+
319
+ case ASCII_NBRSP: /* Non-breakable space. */
320
+ p->tcol->buf[ic] = ' ';
321
+ /* FALLTHROUGH */
322
+ default: /* Printable character. */
323
+ graph = 1;
324
+ vis += (*p->width)(p, p->tcol->buf[ic]);
325
+ if (vis > vtarget && *nbr > 0)
326
+ return;
327
+ continue;
328
+ }
329
+ break;
330
+ }
331
+
332
+ /*
333
+ * If the last word extends to the end of the field without any
334
+ * trailing whitespace, the loop could not check yet whether it
335
+ * can remain on this line. So do the check now.
336
+ */
337
+
338
+ if (graph && (vis <= vtarget || *nbr == 0)) {
339
+ *nbr = ic;
340
+ *vbr = vis;
341
+ }
342
+ }
343
+
344
+ /*
345
+ * Print the contents of one field
346
+ * with an indentation of vbl visual columns,
347
+ * and an input string length of nbr characters.
348
+ */
349
+ static void
350
+ term_field(struct termp *p, size_t vbl, size_t nbr)
351
+ {
352
+ size_t ic; /* Character position in the input buffer. */
353
+ size_t vis; /* Visual position of the current character. */
354
+ size_t dv; /* Visual width of the current character. */
355
+ size_t vn; /* Visual position of the next character. */
356
+
357
+ vis = 0;
358
+ for (ic = p->tcol->col; ic < nbr; ic++) {
359
+
360
+ /*
361
+ * To avoid the printing of trailing whitespace,
362
+ * do not print whitespace right away, only count it.
363
+ */
364
+
365
+ switch (p->tcol->buf[ic]) {
366
+ case '\n':
367
+ case ASCII_BREAK:
368
+ continue;
369
+ case '\t':
370
+ vn = term_tab_next(vis);
371
+ vbl += vn - vis;
372
+ vis = vn;
373
+ continue;
374
+ case ' ':
375
+ case ASCII_NBRSP:
376
+ dv = (*p->width)(p, ' ');
377
+ vbl += dv;
378
+ vis += dv;
379
+ continue;
380
+ default:
381
+ break;
382
+ }
383
+
384
+ /*
385
+ * We found a non-blank character to print,
386
+ * so write preceding white space now.
387
+ */
388
+
389
+ if (vbl > 0) {
390
+ (*p->advance)(p, vbl);
391
+ p->viscol += vbl;
392
+ vbl = 0;
393
+ }
394
+
395
+ /* Print the character and adjust the visual position. */
396
+
397
+ (*p->letter)(p, p->tcol->buf[ic]);
398
+ if (p->tcol->buf[ic] == '\b') {
399
+ dv = (*p->width)(p, p->tcol->buf[ic - 1]);
400
+ p->viscol -= dv;
401
+ vis -= dv;
402
+ } else {
403
+ dv = (*p->width)(p, p->tcol->buf[ic]);
404
+ p->viscol += dv;
405
+ vis += dv;
406
+ }
407
+ }
408
+ p->tcol->col = nbr;
409
+ }
410
+
411
+ static void
412
+ endline(struct termp *p)
413
+ {
414
+ if ((p->flags & (TERMP_NEWMC | TERMP_ENDMC)) == TERMP_ENDMC) {
415
+ p->mc = NULL;
416
+ p->flags &= ~TERMP_ENDMC;
417
+ }
418
+ if (p->mc != NULL) {
419
+ if (p->viscol && p->maxrmargin >= p->viscol)
420
+ (*p->advance)(p, p->maxrmargin - p->viscol + 1);
421
+ p->flags |= TERMP_NOBUF | TERMP_NOSPACE;
422
+ term_word(p, p->mc);
423
+ p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC);
424
+ }
425
+ p->viscol = 0;
426
+ p->minbl = 0;
427
+ (*p->endline)(p);
428
+ }
429
+
430
+ /*
431
+ * A newline only breaks an existing line; it won't assert vertical
432
+ * space. All data in the output buffer is flushed prior to the newline
433
+ * assertion.
434
+ */
435
+ void
436
+ term_newln(struct termp *p)
437
+ {
438
+
439
+ p->flags |= TERMP_NOSPACE;
440
+ if (p->tcol->lastcol || p->viscol)
441
+ term_flushln(p);
442
+ }
443
+
444
+ /*
445
+ * Asserts a vertical space (a full, empty line-break between lines).
446
+ * Note that if used twice, this will cause two blank spaces and so on.
447
+ * All data in the output buffer is flushed prior to the newline
448
+ * assertion.
449
+ */
450
+ void
451
+ term_vspace(struct termp *p)
452
+ {
453
+
454
+ term_newln(p);
455
+ p->viscol = 0;
456
+ p->minbl = 0;
457
+ if (0 < p->skipvsp)
458
+ p->skipvsp--;
459
+ else
460
+ (*p->endline)(p);
461
+ }
462
+
463
+ /* Swap current and previous font; for \fP and .ft P */
464
+ void
465
+ term_fontlast(struct termp *p)
466
+ {
467
+ enum termfont f;
468
+
469
+ f = p->fontl;
470
+ p->fontl = p->fontq[p->fonti];
471
+ p->fontq[p->fonti] = f;
472
+ }
473
+
474
+ /* Set font, save current, discard previous; for \f, .ft, .B etc. */
475
+ void
476
+ term_fontrepl(struct termp *p, enum termfont f)
477
+ {
478
+
479
+ p->fontl = p->fontq[p->fonti];
480
+ p->fontq[p->fonti] = f;
481
+ }
482
+
483
+ /* Set font, save previous. */
484
+ void
485
+ term_fontpush(struct termp *p, enum termfont f)
486
+ {
487
+
488
+ p->fontl = p->fontq[p->fonti];
489
+ if (++p->fonti == p->fontsz) {
490
+ p->fontsz += 8;
491
+ p->fontq = mandoc_reallocarray(p->fontq,
492
+ p->fontsz, sizeof(*p->fontq));
493
+ }
494
+ p->fontq[p->fonti] = f;
495
+ }
496
+
497
+ /* Flush to make the saved pointer current again. */
498
+ void
499
+ term_fontpopq(struct termp *p, int i)
500
+ {
501
+
502
+ assert(i >= 0);
503
+ if (p->fonti > i)
504
+ p->fonti = i;
505
+ }
506
+
507
+ /* Pop one font off the stack. */
508
+ void
509
+ term_fontpop(struct termp *p)
510
+ {
511
+
512
+ assert(p->fonti);
513
+ p->fonti--;
514
+ }
515
+
516
+ /*
517
+ * Handle pwords, partial words, which may be either a single word or a
518
+ * phrase that cannot be broken down (such as a literal string). This
519
+ * handles word styling.
520
+ */
521
+ void
522
+ term_word(struct termp *p, const char *word)
523
+ {
524
+ struct roffsu su;
525
+ const char nbrsp[2] = { ASCII_NBRSP, 0 };
526
+ const char *seq, *cp;
527
+ int sz, uc;
528
+ size_t csz, lsz, ssz;
529
+ enum mandoc_esc esc;
530
+
531
+ if ((p->flags & TERMP_NOBUF) == 0) {
532
+ if ((p->flags & TERMP_NOSPACE) == 0) {
533
+ if ((p->flags & TERMP_KEEP) == 0) {
534
+ bufferc(p, ' ');
535
+ if (p->flags & TERMP_SENTENCE)
536
+ bufferc(p, ' ');
537
+ } else
538
+ bufferc(p, ASCII_NBRSP);
539
+ }
540
+ if (p->flags & TERMP_PREKEEP)
541
+ p->flags |= TERMP_KEEP;
542
+ if (p->flags & TERMP_NONOSPACE)
543
+ p->flags |= TERMP_NOSPACE;
544
+ else
545
+ p->flags &= ~TERMP_NOSPACE;
546
+ p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE);
547
+ p->skipvsp = 0;
548
+ }
549
+
550
+ while ('\0' != *word) {
551
+ if ('\\' != *word) {
552
+ if (TERMP_NBRWORD & p->flags) {
553
+ if (' ' == *word) {
554
+ encode(p, nbrsp, 1);
555
+ word++;
556
+ continue;
557
+ }
558
+ ssz = strcspn(word, "\\ ");
559
+ } else
560
+ ssz = strcspn(word, "\\");
561
+ encode(p, word, ssz);
562
+ word += (int)ssz;
563
+ continue;
564
+ }
565
+
566
+ word++;
567
+ esc = mandoc_escape(&word, &seq, &sz);
568
+ switch (esc) {
569
+ case ESCAPE_UNICODE:
570
+ uc = mchars_num2uc(seq + 1, sz - 1);
571
+ break;
572
+ case ESCAPE_NUMBERED:
573
+ uc = mchars_num2char(seq, sz);
574
+ if (uc < 0)
575
+ continue;
576
+ break;
577
+ case ESCAPE_SPECIAL:
578
+ if (p->enc == TERMENC_ASCII) {
579
+ cp = mchars_spec2str(seq, sz, &ssz);
580
+ if (cp != NULL)
581
+ encode(p, cp, ssz);
582
+ } else {
583
+ uc = mchars_spec2cp(seq, sz);
584
+ if (uc > 0)
585
+ encode1(p, uc);
586
+ }
587
+ continue;
588
+ case ESCAPE_UNDEF:
589
+ uc = *seq;
590
+ break;
591
+ case ESCAPE_FONTBOLD:
592
+ case ESCAPE_FONTCB:
593
+ term_fontrepl(p, TERMFONT_BOLD);
594
+ continue;
595
+ case ESCAPE_FONTITALIC:
596
+ case ESCAPE_FONTCI:
597
+ term_fontrepl(p, TERMFONT_UNDER);
598
+ continue;
599
+ case ESCAPE_FONTBI:
600
+ term_fontrepl(p, TERMFONT_BI);
601
+ continue;
602
+ case ESCAPE_FONT:
603
+ case ESCAPE_FONTCR:
604
+ case ESCAPE_FONTROMAN:
605
+ term_fontrepl(p, TERMFONT_NONE);
606
+ continue;
607
+ case ESCAPE_FONTPREV:
608
+ term_fontlast(p);
609
+ continue;
610
+ case ESCAPE_BREAK:
611
+ bufferc(p, '\n');
612
+ continue;
613
+ case ESCAPE_NOSPACE:
614
+ if (p->flags & TERMP_BACKAFTER)
615
+ p->flags &= ~TERMP_BACKAFTER;
616
+ else if (*word == '\0')
617
+ p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE);
618
+ continue;
619
+ case ESCAPE_DEVICE:
620
+ if (p->type == TERMTYPE_PDF)
621
+ encode(p, "pdf", 3);
622
+ else if (p->type == TERMTYPE_PS)
623
+ encode(p, "ps", 2);
624
+ else if (p->enc == TERMENC_ASCII)
625
+ encode(p, "ascii", 5);
626
+ else
627
+ encode(p, "utf8", 4);
628
+ continue;
629
+ case ESCAPE_HORIZ:
630
+ if (*seq == '|') {
631
+ seq++;
632
+ uc = -p->col;
633
+ } else
634
+ uc = 0;
635
+ if (a2roffsu(seq, &su, SCALE_EM) == NULL)
636
+ continue;
637
+ uc += term_hen(p, &su);
638
+ if (uc > 0)
639
+ while (uc-- > 0)
640
+ bufferc(p, ASCII_NBRSP);
641
+ else if (p->col > (size_t)(-uc))
642
+ p->col += uc;
643
+ else {
644
+ uc += p->col;
645
+ p->col = 0;
646
+ if (p->tcol->offset > (size_t)(-uc)) {
647
+ p->ti += uc;
648
+ p->tcol->offset += uc;
649
+ } else {
650
+ p->ti -= p->tcol->offset;
651
+ p->tcol->offset = 0;
652
+ }
653
+ }
654
+ continue;
655
+ case ESCAPE_HLINE:
656
+ if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL)
657
+ continue;
658
+ uc = term_hen(p, &su);
659
+ if (uc <= 0) {
660
+ if (p->tcol->rmargin <= p->tcol->offset)
661
+ continue;
662
+ lsz = p->tcol->rmargin - p->tcol->offset;
663
+ } else
664
+ lsz = uc;
665
+ if (*cp == seq[-1])
666
+ uc = -1;
667
+ else if (*cp == '\\') {
668
+ seq = cp + 1;
669
+ esc = mandoc_escape(&seq, &cp, &sz);
670
+ switch (esc) {
671
+ case ESCAPE_UNICODE:
672
+ uc = mchars_num2uc(cp + 1, sz - 1);
673
+ break;
674
+ case ESCAPE_NUMBERED:
675
+ uc = mchars_num2char(cp, sz);
676
+ break;
677
+ case ESCAPE_SPECIAL:
678
+ uc = mchars_spec2cp(cp, sz);
679
+ break;
680
+ case ESCAPE_UNDEF:
681
+ uc = *seq;
682
+ break;
683
+ default:
684
+ uc = -1;
685
+ break;
686
+ }
687
+ } else
688
+ uc = *cp;
689
+ if (uc < 0x20 || (uc > 0x7E && uc < 0xA0))
690
+ uc = '_';
691
+ if (p->enc == TERMENC_ASCII) {
692
+ cp = ascii_uc2str(uc);
693
+ csz = term_strlen(p, cp);
694
+ ssz = strlen(cp);
695
+ } else
696
+ csz = (*p->width)(p, uc);
697
+ while (lsz >= csz) {
698
+ if (p->enc == TERMENC_ASCII)
699
+ encode(p, cp, ssz);
700
+ else
701
+ encode1(p, uc);
702
+ lsz -= csz;
703
+ }
704
+ continue;
705
+ case ESCAPE_SKIPCHAR:
706
+ p->flags |= TERMP_BACKAFTER;
707
+ continue;
708
+ case ESCAPE_OVERSTRIKE:
709
+ cp = seq + sz;
710
+ while (seq < cp) {
711
+ if (*seq == '\\') {
712
+ mandoc_escape(&seq, NULL, NULL);
713
+ continue;
714
+ }
715
+ encode1(p, *seq++);
716
+ if (seq < cp) {
717
+ if (p->flags & TERMP_BACKBEFORE)
718
+ p->flags |= TERMP_BACKAFTER;
719
+ else
720
+ p->flags |= TERMP_BACKBEFORE;
721
+ }
722
+ }
723
+ /* Trim trailing backspace/blank pair. */
724
+ if (p->tcol->lastcol > 2 &&
725
+ (p->tcol->buf[p->tcol->lastcol - 1] == ' ' ||
726
+ p->tcol->buf[p->tcol->lastcol - 1] == '\t'))
727
+ p->tcol->lastcol -= 2;
728
+ if (p->col > p->tcol->lastcol)
729
+ p->col = p->tcol->lastcol;
730
+ continue;
731
+ default:
732
+ continue;
733
+ }
734
+
735
+ /*
736
+ * Common handling for Unicode and numbered
737
+ * character escape sequences.
738
+ */
739
+
740
+ if (p->enc == TERMENC_ASCII) {
741
+ cp = ascii_uc2str(uc);
742
+ encode(p, cp, strlen(cp));
743
+ } else {
744
+ if ((uc < 0x20 && uc != 0x09) ||
745
+ (uc > 0x7E && uc < 0xA0))
746
+ uc = 0xFFFD;
747
+ encode1(p, uc);
748
+ }
749
+ }
750
+ p->flags &= ~TERMP_NBRWORD;
751
+ }
752
+
753
+ static void
754
+ adjbuf(struct termp_col *c, size_t sz)
755
+ {
756
+ if (c->maxcols == 0)
757
+ c->maxcols = 1024;
758
+ while (c->maxcols <= sz)
759
+ c->maxcols <<= 2;
760
+ c->buf = mandoc_reallocarray(c->buf, c->maxcols, sizeof(*c->buf));
761
+ }
762
+
763
+ static void
764
+ bufferc(struct termp *p, char c)
765
+ {
766
+ if (p->flags & TERMP_NOBUF) {
767
+ (*p->letter)(p, c);
768
+ return;
769
+ }
770
+ if (p->col + 1 >= p->tcol->maxcols)
771
+ adjbuf(p->tcol, p->col + 1);
772
+ if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP))
773
+ p->tcol->buf[p->col] = c;
774
+ if (p->tcol->lastcol < ++p->col)
775
+ p->tcol->lastcol = p->col;
776
+ }
777
+
778
+ /*
779
+ * See encode().
780
+ * Do this for a single (probably unicode) value.
781
+ * Does not check for non-decorated glyphs.
782
+ */
783
+ static void
784
+ encode1(struct termp *p, int c)
785
+ {
786
+ enum termfont f;
787
+
788
+ if (p->flags & TERMP_NOBUF) {
789
+ (*p->letter)(p, c);
790
+ return;
791
+ }
792
+
793
+ if (p->col + 7 >= p->tcol->maxcols)
794
+ adjbuf(p->tcol, p->col + 7);
795
+
796
+ f = (c == ASCII_HYPH || c > 127 || isgraph(c)) ?
797
+ p->fontq[p->fonti] : TERMFONT_NONE;
798
+
799
+ if (p->flags & TERMP_BACKBEFORE) {
800
+ if (p->tcol->buf[p->col - 1] == ' ' ||
801
+ p->tcol->buf[p->col - 1] == '\t')
802
+ p->col--;
803
+ else
804
+ p->tcol->buf[p->col++] = '\b';
805
+ p->flags &= ~TERMP_BACKBEFORE;
806
+ }
807
+ if (f == TERMFONT_UNDER || f == TERMFONT_BI) {
808
+ p->tcol->buf[p->col++] = '_';
809
+ p->tcol->buf[p->col++] = '\b';
810
+ }
811
+ if (f == TERMFONT_BOLD || f == TERMFONT_BI) {
812
+ if (c == ASCII_HYPH)
813
+ p->tcol->buf[p->col++] = '-';
814
+ else
815
+ p->tcol->buf[p->col++] = c;
816
+ p->tcol->buf[p->col++] = '\b';
817
+ }
818
+ if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP))
819
+ p->tcol->buf[p->col] = c;
820
+ if (p->tcol->lastcol < ++p->col)
821
+ p->tcol->lastcol = p->col;
822
+ if (p->flags & TERMP_BACKAFTER) {
823
+ p->flags |= TERMP_BACKBEFORE;
824
+ p->flags &= ~TERMP_BACKAFTER;
825
+ }
826
+ }
827
+
828
+ static void
829
+ encode(struct termp *p, const char *word, size_t sz)
830
+ {
831
+ size_t i;
832
+
833
+ if (p->flags & TERMP_NOBUF) {
834
+ for (i = 0; i < sz; i++)
835
+ (*p->letter)(p, word[i]);
836
+ return;
837
+ }
838
+
839
+ if (p->col + 2 + (sz * 5) >= p->tcol->maxcols)
840
+ adjbuf(p->tcol, p->col + 2 + (sz * 5));
841
+
842
+ for (i = 0; i < sz; i++) {
843
+ if (ASCII_HYPH == word[i] ||
844
+ isgraph((unsigned char)word[i]))
845
+ encode1(p, word[i]);
846
+ else {
847
+ if (p->tcol->lastcol <= p->col ||
848
+ (word[i] != ' ' && word[i] != ASCII_NBRSP))
849
+ p->tcol->buf[p->col] = word[i];
850
+ p->col++;
851
+
852
+ /*
853
+ * Postpone the effect of \z while handling
854
+ * an overstrike sequence from ascii_uc2str().
855
+ */
856
+
857
+ if (word[i] == '\b' &&
858
+ (p->flags & TERMP_BACKBEFORE)) {
859
+ p->flags &= ~TERMP_BACKBEFORE;
860
+ p->flags |= TERMP_BACKAFTER;
861
+ }
862
+ }
863
+ }
864
+ if (p->tcol->lastcol < p->col)
865
+ p->tcol->lastcol = p->col;
866
+ }
867
+
868
+ void
869
+ term_setwidth(struct termp *p, const char *wstr)
870
+ {
871
+ struct roffsu su;
872
+ int iop, width;
873
+
874
+ iop = 0;
875
+ width = 0;
876
+ if (NULL != wstr) {
877
+ switch (*wstr) {
878
+ case '+':
879
+ iop = 1;
880
+ wstr++;
881
+ break;
882
+ case '-':
883
+ iop = -1;
884
+ wstr++;
885
+ break;
886
+ default:
887
+ break;
888
+ }
889
+ if (a2roffsu(wstr, &su, SCALE_MAX) != NULL)
890
+ width = term_hspan(p, &su);
891
+ else
892
+ iop = 0;
893
+ }
894
+ (*p->setwidth)(p, iop, width);
895
+ }
896
+
897
+ size_t
898
+ term_len(const struct termp *p, size_t sz)
899
+ {
900
+
901
+ return (*p->width)(p, ' ') * sz;
902
+ }
903
+
904
+ static size_t
905
+ cond_width(const struct termp *p, int c, int *skip)
906
+ {
907
+
908
+ if (*skip) {
909
+ (*skip) = 0;
910
+ return 0;
911
+ } else
912
+ return (*p->width)(p, c);
913
+ }
914
+
915
+ size_t
916
+ term_strlen(const struct termp *p, const char *cp)
917
+ {
918
+ size_t sz, rsz, i;
919
+ int ssz, skip, uc;
920
+ const char *seq, *rhs;
921
+ enum mandoc_esc esc;
922
+ static const char rej[] = { '\\', ASCII_NBRSP, ASCII_HYPH,
923
+ ASCII_BREAK, '\0' };
924
+
925
+ /*
926
+ * Account for escaped sequences within string length
927
+ * calculations. This follows the logic in term_word() as we
928
+ * must calculate the width of produced strings.
929
+ */
930
+
931
+ sz = 0;
932
+ skip = 0;
933
+ while ('\0' != *cp) {
934
+ rsz = strcspn(cp, rej);
935
+ for (i = 0; i < rsz; i++)
936
+ sz += cond_width(p, *cp++, &skip);
937
+
938
+ switch (*cp) {
939
+ case '\\':
940
+ cp++;
941
+ rhs = NULL;
942
+ esc = mandoc_escape(&cp, &seq, &ssz);
943
+ switch (esc) {
944
+ case ESCAPE_UNICODE:
945
+ uc = mchars_num2uc(seq + 1, ssz - 1);
946
+ break;
947
+ case ESCAPE_NUMBERED:
948
+ uc = mchars_num2char(seq, ssz);
949
+ if (uc < 0)
950
+ continue;
951
+ break;
952
+ case ESCAPE_SPECIAL:
953
+ if (p->enc == TERMENC_ASCII) {
954
+ rhs = mchars_spec2str(seq, ssz, &rsz);
955
+ if (rhs != NULL)
956
+ break;
957
+ } else {
958
+ uc = mchars_spec2cp(seq, ssz);
959
+ if (uc > 0)
960
+ sz += cond_width(p, uc, &skip);
961
+ }
962
+ continue;
963
+ case ESCAPE_UNDEF:
964
+ uc = *seq;
965
+ break;
966
+ case ESCAPE_DEVICE:
967
+ if (p->type == TERMTYPE_PDF) {
968
+ rhs = "pdf";
969
+ rsz = 3;
970
+ } else if (p->type == TERMTYPE_PS) {
971
+ rhs = "ps";
972
+ rsz = 2;
973
+ } else if (p->enc == TERMENC_ASCII) {
974
+ rhs = "ascii";
975
+ rsz = 5;
976
+ } else {
977
+ rhs = "utf8";
978
+ rsz = 4;
979
+ }
980
+ break;
981
+ case ESCAPE_SKIPCHAR:
982
+ skip = 1;
983
+ continue;
984
+ case ESCAPE_OVERSTRIKE:
985
+ rsz = 0;
986
+ rhs = seq + ssz;
987
+ while (seq < rhs) {
988
+ if (*seq == '\\') {
989
+ mandoc_escape(&seq, NULL, NULL);
990
+ continue;
991
+ }
992
+ i = (*p->width)(p, *seq++);
993
+ if (rsz < i)
994
+ rsz = i;
995
+ }
996
+ sz += rsz;
997
+ continue;
998
+ default:
999
+ continue;
1000
+ }
1001
+
1002
+ /*
1003
+ * Common handling for Unicode and numbered
1004
+ * character escape sequences.
1005
+ */
1006
+
1007
+ if (rhs == NULL) {
1008
+ if (p->enc == TERMENC_ASCII) {
1009
+ rhs = ascii_uc2str(uc);
1010
+ rsz = strlen(rhs);
1011
+ } else {
1012
+ if ((uc < 0x20 && uc != 0x09) ||
1013
+ (uc > 0x7E && uc < 0xA0))
1014
+ uc = 0xFFFD;
1015
+ sz += cond_width(p, uc, &skip);
1016
+ continue;
1017
+ }
1018
+ }
1019
+
1020
+ if (skip) {
1021
+ skip = 0;
1022
+ break;
1023
+ }
1024
+
1025
+ /*
1026
+ * Common handling for all escape sequences
1027
+ * printing more than one character.
1028
+ */
1029
+
1030
+ for (i = 0; i < rsz; i++)
1031
+ sz += (*p->width)(p, *rhs++);
1032
+ break;
1033
+ case ASCII_NBRSP:
1034
+ sz += cond_width(p, ' ', &skip);
1035
+ cp++;
1036
+ break;
1037
+ case ASCII_HYPH:
1038
+ sz += cond_width(p, '-', &skip);
1039
+ cp++;
1040
+ break;
1041
+ default:
1042
+ break;
1043
+ }
1044
+ }
1045
+
1046
+ return sz;
1047
+ }
1048
+
1049
+ int
1050
+ term_vspan(const struct termp *p, const struct roffsu *su)
1051
+ {
1052
+ double r;
1053
+ int ri;
1054
+
1055
+ switch (su->unit) {
1056
+ case SCALE_BU:
1057
+ r = su->scale / 40.0;
1058
+ break;
1059
+ case SCALE_CM:
1060
+ r = su->scale * 6.0 / 2.54;
1061
+ break;
1062
+ case SCALE_FS:
1063
+ r = su->scale * 65536.0 / 40.0;
1064
+ break;
1065
+ case SCALE_IN:
1066
+ r = su->scale * 6.0;
1067
+ break;
1068
+ case SCALE_MM:
1069
+ r = su->scale * 0.006;
1070
+ break;
1071
+ case SCALE_PC:
1072
+ r = su->scale;
1073
+ break;
1074
+ case SCALE_PT:
1075
+ r = su->scale / 12.0;
1076
+ break;
1077
+ case SCALE_EN:
1078
+ case SCALE_EM:
1079
+ r = su->scale * 0.6;
1080
+ break;
1081
+ case SCALE_VS:
1082
+ r = su->scale;
1083
+ break;
1084
+ default:
1085
+ abort();
1086
+ }
1087
+ ri = r > 0.0 ? r + 0.4995 : r - 0.4995;
1088
+ return ri < 66 ? ri : 1;
1089
+ }
1090
+
1091
+ /*
1092
+ * Convert a scaling width to basic units, rounding towards 0.
1093
+ */
1094
+ int
1095
+ term_hspan(const struct termp *p, const struct roffsu *su)
1096
+ {
1097
+
1098
+ return (*p->hspan)(p, su);
1099
+ }
1100
+
1101
+ /*
1102
+ * Convert a scaling width to basic units, rounding to closest.
1103
+ */
1104
+ int
1105
+ term_hen(const struct termp *p, const struct roffsu *su)
1106
+ {
1107
+ int bu;
1108
+
1109
+ if ((bu = (*p->hspan)(p, su)) >= 0)
1110
+ return (bu + 11) / 24;
1111
+ else
1112
+ return -((-bu + 11) / 24);
1113
+ }