1 |
|
|
/* $OpenBSD: man_term.c,v 1.140 2016/01/08 17:48:04 schwarze Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> |
4 |
|
|
* Copyright (c) 2010-2015 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 <sys/types.h> |
19 |
|
|
|
20 |
|
|
#include <assert.h> |
21 |
|
|
#include <ctype.h> |
22 |
|
|
#include <limits.h> |
23 |
|
|
#include <stdio.h> |
24 |
|
|
#include <stdlib.h> |
25 |
|
|
#include <string.h> |
26 |
|
|
|
27 |
|
|
#include "mandoc_aux.h" |
28 |
|
|
#include "mandoc.h" |
29 |
|
|
#include "roff.h" |
30 |
|
|
#include "man.h" |
31 |
|
|
#include "out.h" |
32 |
|
|
#include "term.h" |
33 |
|
|
#include "main.h" |
34 |
|
|
|
35 |
|
|
#define MAXMARGINS 64 /* maximum number of indented scopes */ |
36 |
|
|
|
37 |
|
|
struct mtermp { |
38 |
|
|
int fl; |
39 |
|
|
#define MANT_LITERAL (1 << 0) |
40 |
|
|
int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */ |
41 |
|
|
int lmargincur; /* index of current margin */ |
42 |
|
|
int lmarginsz; /* actual number of nested margins */ |
43 |
|
|
size_t offset; /* default offset to visible page */ |
44 |
|
|
int pardist; /* vert. space before par., unit: [v] */ |
45 |
|
|
}; |
46 |
|
|
|
47 |
|
|
#define DECL_ARGS struct termp *p, \ |
48 |
|
|
struct mtermp *mt, \ |
49 |
|
|
struct roff_node *n, \ |
50 |
|
|
const struct roff_meta *meta |
51 |
|
|
|
52 |
|
|
struct termact { |
53 |
|
|
int (*pre)(DECL_ARGS); |
54 |
|
|
void (*post)(DECL_ARGS); |
55 |
|
|
int flags; |
56 |
|
|
#define MAN_NOTEXT (1 << 0) /* Never has text children. */ |
57 |
|
|
}; |
58 |
|
|
|
59 |
|
|
static void print_man_nodelist(DECL_ARGS); |
60 |
|
|
static void print_man_node(DECL_ARGS); |
61 |
|
|
static void print_man_head(struct termp *, |
62 |
|
|
const struct roff_meta *); |
63 |
|
|
static void print_man_foot(struct termp *, |
64 |
|
|
const struct roff_meta *); |
65 |
|
|
static void print_bvspace(struct termp *, |
66 |
|
|
const struct roff_node *, int); |
67 |
|
|
|
68 |
|
|
static int pre_B(DECL_ARGS); |
69 |
|
|
static int pre_HP(DECL_ARGS); |
70 |
|
|
static int pre_I(DECL_ARGS); |
71 |
|
|
static int pre_IP(DECL_ARGS); |
72 |
|
|
static int pre_OP(DECL_ARGS); |
73 |
|
|
static int pre_PD(DECL_ARGS); |
74 |
|
|
static int pre_PP(DECL_ARGS); |
75 |
|
|
static int pre_RS(DECL_ARGS); |
76 |
|
|
static int pre_SH(DECL_ARGS); |
77 |
|
|
static int pre_SS(DECL_ARGS); |
78 |
|
|
static int pre_TP(DECL_ARGS); |
79 |
|
|
static int pre_UR(DECL_ARGS); |
80 |
|
|
static int pre_alternate(DECL_ARGS); |
81 |
|
|
static int pre_ft(DECL_ARGS); |
82 |
|
|
static int pre_ign(DECL_ARGS); |
83 |
|
|
static int pre_in(DECL_ARGS); |
84 |
|
|
static int pre_literal(DECL_ARGS); |
85 |
|
|
static int pre_ll(DECL_ARGS); |
86 |
|
|
static int pre_sp(DECL_ARGS); |
87 |
|
|
|
88 |
|
|
static void post_IP(DECL_ARGS); |
89 |
|
|
static void post_HP(DECL_ARGS); |
90 |
|
|
static void post_RS(DECL_ARGS); |
91 |
|
|
static void post_SH(DECL_ARGS); |
92 |
|
|
static void post_SS(DECL_ARGS); |
93 |
|
|
static void post_TP(DECL_ARGS); |
94 |
|
|
static void post_UR(DECL_ARGS); |
95 |
|
|
|
96 |
|
|
static const struct termact termacts[MAN_MAX] = { |
97 |
|
|
{ pre_sp, NULL, MAN_NOTEXT }, /* br */ |
98 |
|
|
{ NULL, NULL, 0 }, /* TH */ |
99 |
|
|
{ pre_SH, post_SH, 0 }, /* SH */ |
100 |
|
|
{ pre_SS, post_SS, 0 }, /* SS */ |
101 |
|
|
{ pre_TP, post_TP, 0 }, /* TP */ |
102 |
|
|
{ pre_PP, NULL, 0 }, /* LP */ |
103 |
|
|
{ pre_PP, NULL, 0 }, /* PP */ |
104 |
|
|
{ pre_PP, NULL, 0 }, /* P */ |
105 |
|
|
{ pre_IP, post_IP, 0 }, /* IP */ |
106 |
|
|
{ pre_HP, post_HP, 0 }, /* HP */ |
107 |
|
|
{ NULL, NULL, 0 }, /* SM */ |
108 |
|
|
{ pre_B, NULL, 0 }, /* SB */ |
109 |
|
|
{ pre_alternate, NULL, 0 }, /* BI */ |
110 |
|
|
{ pre_alternate, NULL, 0 }, /* IB */ |
111 |
|
|
{ pre_alternate, NULL, 0 }, /* BR */ |
112 |
|
|
{ pre_alternate, NULL, 0 }, /* RB */ |
113 |
|
|
{ NULL, NULL, 0 }, /* R */ |
114 |
|
|
{ pre_B, NULL, 0 }, /* B */ |
115 |
|
|
{ pre_I, NULL, 0 }, /* I */ |
116 |
|
|
{ pre_alternate, NULL, 0 }, /* IR */ |
117 |
|
|
{ pre_alternate, NULL, 0 }, /* RI */ |
118 |
|
|
{ pre_sp, NULL, MAN_NOTEXT }, /* sp */ |
119 |
|
|
{ pre_literal, NULL, 0 }, /* nf */ |
120 |
|
|
{ pre_literal, NULL, 0 }, /* fi */ |
121 |
|
|
{ NULL, NULL, 0 }, /* RE */ |
122 |
|
|
{ pre_RS, post_RS, 0 }, /* RS */ |
123 |
|
|
{ pre_ign, NULL, 0 }, /* DT */ |
124 |
|
|
{ pre_ign, NULL, MAN_NOTEXT }, /* UC */ |
125 |
|
|
{ pre_PD, NULL, MAN_NOTEXT }, /* PD */ |
126 |
|
|
{ pre_ign, NULL, 0 }, /* AT */ |
127 |
|
|
{ pre_in, NULL, MAN_NOTEXT }, /* in */ |
128 |
|
|
{ pre_ft, NULL, MAN_NOTEXT }, /* ft */ |
129 |
|
|
{ pre_OP, NULL, 0 }, /* OP */ |
130 |
|
|
{ pre_literal, NULL, 0 }, /* EX */ |
131 |
|
|
{ pre_literal, NULL, 0 }, /* EE */ |
132 |
|
|
{ pre_UR, post_UR, 0 }, /* UR */ |
133 |
|
|
{ NULL, NULL, 0 }, /* UE */ |
134 |
|
|
{ pre_ll, NULL, MAN_NOTEXT }, /* ll */ |
135 |
|
|
}; |
136 |
|
|
|
137 |
|
|
|
138 |
|
|
void |
139 |
|
|
terminal_man(void *arg, const struct roff_man *man) |
140 |
|
|
{ |
141 |
|
|
struct termp *p; |
142 |
|
|
struct roff_node *n; |
143 |
|
|
struct mtermp mt; |
144 |
|
|
|
145 |
|
|
p = (struct termp *)arg; |
146 |
|
|
p->overstep = 0; |
147 |
|
|
p->rmargin = p->maxrmargin = p->defrmargin; |
148 |
|
|
p->tabwidth = term_len(p, 5); |
149 |
|
|
|
150 |
|
|
memset(&mt, 0, sizeof(struct mtermp)); |
151 |
|
|
mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); |
152 |
|
|
mt.offset = term_len(p, p->defindent); |
153 |
|
|
mt.pardist = 1; |
154 |
|
|
|
155 |
|
|
n = man->first->child; |
156 |
|
|
if (p->synopsisonly) { |
157 |
|
|
while (n != NULL) { |
158 |
|
|
if (n->tok == MAN_SH && |
159 |
|
|
n->child->child->type == ROFFT_TEXT && |
160 |
|
|
!strcmp(n->child->child->string, "SYNOPSIS")) { |
161 |
|
|
if (n->child->next->child != NULL) |
162 |
|
|
print_man_nodelist(p, &mt, |
163 |
|
|
n->child->next->child, |
164 |
|
|
&man->meta); |
165 |
|
|
term_newln(p); |
166 |
|
|
break; |
167 |
|
|
} |
168 |
|
|
n = n->next; |
169 |
|
|
} |
170 |
|
|
} else { |
171 |
|
|
if (p->defindent == 0) |
172 |
|
|
p->defindent = 7; |
173 |
|
|
term_begin(p, print_man_head, print_man_foot, &man->meta); |
174 |
|
|
p->flags |= TERMP_NOSPACE; |
175 |
|
|
if (n != NULL) |
176 |
|
|
print_man_nodelist(p, &mt, n, &man->meta); |
177 |
|
|
term_end(p); |
178 |
|
|
} |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
/* |
182 |
|
|
* Printing leading vertical space before a block. |
183 |
|
|
* This is used for the paragraph macros. |
184 |
|
|
* The rules are pretty simple, since there's very little nesting going |
185 |
|
|
* on here. Basically, if we're the first within another block (SS/SH), |
186 |
|
|
* then don't emit vertical space. If we are (RS), then do. If not the |
187 |
|
|
* first, print it. |
188 |
|
|
*/ |
189 |
|
|
static void |
190 |
|
|
print_bvspace(struct termp *p, const struct roff_node *n, int pardist) |
191 |
|
|
{ |
192 |
|
|
int i; |
193 |
|
|
|
194 |
|
|
term_newln(p); |
195 |
|
|
|
196 |
|
|
if (n->body && n->body->child) |
197 |
|
|
if (n->body->child->type == ROFFT_TBL) |
198 |
|
|
return; |
199 |
|
|
|
200 |
|
|
if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) |
201 |
|
|
if (NULL == n->prev) |
202 |
|
|
return; |
203 |
|
|
|
204 |
|
|
for (i = 0; i < pardist; i++) |
205 |
|
|
term_vspace(p); |
206 |
|
|
} |
207 |
|
|
|
208 |
|
|
|
209 |
|
|
static int |
210 |
|
|
pre_ign(DECL_ARGS) |
211 |
|
|
{ |
212 |
|
|
|
213 |
|
|
return 0; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
static int |
217 |
|
|
pre_ll(DECL_ARGS) |
218 |
|
|
{ |
219 |
|
|
|
220 |
|
|
term_setwidth(p, n->child != NULL ? n->child->string : NULL); |
221 |
|
|
return 0; |
222 |
|
|
} |
223 |
|
|
|
224 |
|
|
static int |
225 |
|
|
pre_I(DECL_ARGS) |
226 |
|
|
{ |
227 |
|
|
|
228 |
|
|
term_fontrepl(p, TERMFONT_UNDER); |
229 |
|
|
return 1; |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
static int |
233 |
|
|
pre_literal(DECL_ARGS) |
234 |
|
|
{ |
235 |
|
|
|
236 |
|
|
term_newln(p); |
237 |
|
|
|
238 |
|
|
if (MAN_nf == n->tok || MAN_EX == n->tok) |
239 |
|
|
mt->fl |= MANT_LITERAL; |
240 |
|
|
else |
241 |
|
|
mt->fl &= ~MANT_LITERAL; |
242 |
|
|
|
243 |
|
|
/* |
244 |
|
|
* Unlike .IP and .TP, .HP does not have a HEAD. |
245 |
|
|
* So in case a second call to term_flushln() is needed, |
246 |
|
|
* indentation has to be set up explicitly. |
247 |
|
|
*/ |
248 |
|
|
if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { |
249 |
|
|
p->offset = p->rmargin; |
250 |
|
|
p->rmargin = p->maxrmargin; |
251 |
|
|
p->trailspace = 0; |
252 |
|
|
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); |
253 |
|
|
p->flags |= TERMP_NOSPACE; |
254 |
|
|
} |
255 |
|
|
|
256 |
|
|
return 0; |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
static int |
260 |
|
|
pre_PD(DECL_ARGS) |
261 |
|
|
{ |
262 |
|
|
struct roffsu su; |
263 |
|
|
|
264 |
|
|
n = n->child; |
265 |
|
|
if (n == NULL) { |
266 |
|
|
mt->pardist = 1; |
267 |
|
|
return 0; |
268 |
|
|
} |
269 |
|
|
assert(n->type == ROFFT_TEXT); |
270 |
|
|
if (a2roffsu(n->string, &su, SCALE_VS)) |
271 |
|
|
mt->pardist = term_vspan(p, &su); |
272 |
|
|
return 0; |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
static int |
276 |
|
|
pre_alternate(DECL_ARGS) |
277 |
|
|
{ |
278 |
|
|
enum termfont font[2]; |
279 |
|
|
struct roff_node *nn; |
280 |
|
|
int savelit, i; |
281 |
|
|
|
282 |
|
|
switch (n->tok) { |
283 |
|
|
case MAN_RB: |
284 |
|
|
font[0] = TERMFONT_NONE; |
285 |
|
|
font[1] = TERMFONT_BOLD; |
286 |
|
|
break; |
287 |
|
|
case MAN_RI: |
288 |
|
|
font[0] = TERMFONT_NONE; |
289 |
|
|
font[1] = TERMFONT_UNDER; |
290 |
|
|
break; |
291 |
|
|
case MAN_BR: |
292 |
|
|
font[0] = TERMFONT_BOLD; |
293 |
|
|
font[1] = TERMFONT_NONE; |
294 |
|
|
break; |
295 |
|
|
case MAN_BI: |
296 |
|
|
font[0] = TERMFONT_BOLD; |
297 |
|
|
font[1] = TERMFONT_UNDER; |
298 |
|
|
break; |
299 |
|
|
case MAN_IR: |
300 |
|
|
font[0] = TERMFONT_UNDER; |
301 |
|
|
font[1] = TERMFONT_NONE; |
302 |
|
|
break; |
303 |
|
|
case MAN_IB: |
304 |
|
|
font[0] = TERMFONT_UNDER; |
305 |
|
|
font[1] = TERMFONT_BOLD; |
306 |
|
|
break; |
307 |
|
|
default: |
308 |
|
|
abort(); |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
savelit = MANT_LITERAL & mt->fl; |
312 |
|
|
mt->fl &= ~MANT_LITERAL; |
313 |
|
|
|
314 |
|
|
for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { |
315 |
|
|
term_fontrepl(p, font[i]); |
316 |
|
|
if (savelit && NULL == nn->next) |
317 |
|
|
mt->fl |= MANT_LITERAL; |
318 |
|
|
assert(nn->type == ROFFT_TEXT); |
319 |
|
|
term_word(p, nn->string); |
320 |
|
|
if (nn->flags & MAN_EOS) |
321 |
|
|
p->flags |= TERMP_SENTENCE; |
322 |
|
|
if (nn->next) |
323 |
|
|
p->flags |= TERMP_NOSPACE; |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
return 0; |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
static int |
330 |
|
|
pre_B(DECL_ARGS) |
331 |
|
|
{ |
332 |
|
|
|
333 |
|
|
term_fontrepl(p, TERMFONT_BOLD); |
334 |
|
|
return 1; |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
static int |
338 |
|
|
pre_OP(DECL_ARGS) |
339 |
|
|
{ |
340 |
|
|
|
341 |
|
|
term_word(p, "["); |
342 |
|
|
p->flags |= TERMP_NOSPACE; |
343 |
|
|
|
344 |
|
|
if (NULL != (n = n->child)) { |
345 |
|
|
term_fontrepl(p, TERMFONT_BOLD); |
346 |
|
|
term_word(p, n->string); |
347 |
|
|
} |
348 |
|
|
if (NULL != n && NULL != n->next) { |
349 |
|
|
term_fontrepl(p, TERMFONT_UNDER); |
350 |
|
|
term_word(p, n->next->string); |
351 |
|
|
} |
352 |
|
|
|
353 |
|
|
term_fontrepl(p, TERMFONT_NONE); |
354 |
|
|
p->flags |= TERMP_NOSPACE; |
355 |
|
|
term_word(p, "]"); |
356 |
|
|
return 0; |
357 |
|
|
} |
358 |
|
|
|
359 |
|
|
static int |
360 |
|
|
pre_ft(DECL_ARGS) |
361 |
|
|
{ |
362 |
|
|
const char *cp; |
363 |
|
|
|
364 |
|
|
if (NULL == n->child) { |
365 |
|
|
term_fontlast(p); |
366 |
|
|
return 0; |
367 |
|
|
} |
368 |
|
|
|
369 |
|
|
cp = n->child->string; |
370 |
|
|
switch (*cp) { |
371 |
|
|
case '4': |
372 |
|
|
case '3': |
373 |
|
|
case 'B': |
374 |
|
|
term_fontrepl(p, TERMFONT_BOLD); |
375 |
|
|
break; |
376 |
|
|
case '2': |
377 |
|
|
case 'I': |
378 |
|
|
term_fontrepl(p, TERMFONT_UNDER); |
379 |
|
|
break; |
380 |
|
|
case 'P': |
381 |
|
|
term_fontlast(p); |
382 |
|
|
break; |
383 |
|
|
case '1': |
384 |
|
|
case 'C': |
385 |
|
|
case 'R': |
386 |
|
|
term_fontrepl(p, TERMFONT_NONE); |
387 |
|
|
break; |
388 |
|
|
default: |
389 |
|
|
break; |
390 |
|
|
} |
391 |
|
|
return 0; |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
static int |
395 |
|
|
pre_in(DECL_ARGS) |
396 |
|
|
{ |
397 |
|
|
struct roffsu su; |
398 |
|
|
const char *cp; |
399 |
|
|
size_t v; |
400 |
|
|
int less; |
401 |
|
|
|
402 |
|
|
term_newln(p); |
403 |
|
|
|
404 |
|
|
if (NULL == n->child) { |
405 |
|
|
p->offset = mt->offset; |
406 |
|
|
return 0; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
cp = n->child->string; |
410 |
|
|
less = 0; |
411 |
|
|
|
412 |
|
|
if ('-' == *cp) |
413 |
|
|
less = -1; |
414 |
|
|
else if ('+' == *cp) |
415 |
|
|
less = 1; |
416 |
|
|
else |
417 |
|
|
cp--; |
418 |
|
|
|
419 |
|
|
if ( ! a2roffsu(++cp, &su, SCALE_EN)) |
420 |
|
|
return 0; |
421 |
|
|
|
422 |
|
|
v = (term_hspan(p, &su) + 11) / 24; |
423 |
|
|
|
424 |
|
|
if (less < 0) |
425 |
|
|
p->offset -= p->offset > v ? v : p->offset; |
426 |
|
|
else if (less > 0) |
427 |
|
|
p->offset += v; |
428 |
|
|
else |
429 |
|
|
p->offset = v; |
430 |
|
|
if (p->offset > SHRT_MAX) |
431 |
|
|
p->offset = term_len(p, p->defindent); |
432 |
|
|
|
433 |
|
|
return 0; |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
static int |
437 |
|
|
pre_sp(DECL_ARGS) |
438 |
|
|
{ |
439 |
|
|
struct roffsu su; |
440 |
|
|
int i, len; |
441 |
|
|
|
442 |
|
|
if ((NULL == n->prev && n->parent)) { |
443 |
|
|
switch (n->parent->tok) { |
444 |
|
|
case MAN_SH: |
445 |
|
|
case MAN_SS: |
446 |
|
|
case MAN_PP: |
447 |
|
|
case MAN_LP: |
448 |
|
|
case MAN_P: |
449 |
|
|
return 0; |
450 |
|
|
default: |
451 |
|
|
break; |
452 |
|
|
} |
453 |
|
|
} |
454 |
|
|
|
455 |
|
|
if (n->tok == MAN_br) |
456 |
|
|
len = 0; |
457 |
|
|
else if (n->child == NULL) |
458 |
|
|
len = 1; |
459 |
|
|
else { |
460 |
|
|
if ( ! a2roffsu(n->child->string, &su, SCALE_VS)) |
461 |
|
|
su.scale = 1.0; |
462 |
|
|
len = term_vspan(p, &su); |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
if (len == 0) |
466 |
|
|
term_newln(p); |
467 |
|
|
else if (len < 0) |
468 |
|
|
p->skipvsp -= len; |
469 |
|
|
else |
470 |
|
|
for (i = 0; i < len; i++) |
471 |
|
|
term_vspace(p); |
472 |
|
|
|
473 |
|
|
/* |
474 |
|
|
* Handle an explicit break request in the same way |
475 |
|
|
* as an overflowing line. |
476 |
|
|
*/ |
477 |
|
|
|
478 |
|
|
if (p->flags & TERMP_BRIND) { |
479 |
|
|
p->offset = p->rmargin; |
480 |
|
|
p->rmargin = p->maxrmargin; |
481 |
|
|
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); |
482 |
|
|
} |
483 |
|
|
|
484 |
|
|
return 0; |
485 |
|
|
} |
486 |
|
|
|
487 |
|
|
static int |
488 |
|
|
pre_HP(DECL_ARGS) |
489 |
|
|
{ |
490 |
|
|
struct roffsu su; |
491 |
|
|
const struct roff_node *nn; |
492 |
|
|
int len; |
493 |
|
|
|
494 |
|
|
switch (n->type) { |
495 |
|
|
case ROFFT_BLOCK: |
496 |
|
|
print_bvspace(p, n, mt->pardist); |
497 |
|
|
return 1; |
498 |
|
|
case ROFFT_BODY: |
499 |
|
|
break; |
500 |
|
|
default: |
501 |
|
|
return 0; |
502 |
|
|
} |
503 |
|
|
|
504 |
|
|
if ( ! (MANT_LITERAL & mt->fl)) { |
505 |
|
|
p->flags |= TERMP_NOBREAK | TERMP_BRIND; |
506 |
|
|
p->trailspace = 2; |
507 |
|
|
} |
508 |
|
|
|
509 |
|
|
/* Calculate offset. */ |
510 |
|
|
|
511 |
|
|
if ((nn = n->parent->head->child) != NULL && |
512 |
|
|
a2roffsu(nn->string, &su, SCALE_EN)) { |
513 |
|
|
len = term_hspan(p, &su) / 24; |
514 |
|
|
if (len < 0 && (size_t)(-len) > mt->offset) |
515 |
|
|
len = -mt->offset; |
516 |
|
|
else if (len > SHRT_MAX) |
517 |
|
|
len = term_len(p, p->defindent); |
518 |
|
|
mt->lmargin[mt->lmargincur] = len; |
519 |
|
|
} else |
520 |
|
|
len = mt->lmargin[mt->lmargincur]; |
521 |
|
|
|
522 |
|
|
p->offset = mt->offset; |
523 |
|
|
p->rmargin = mt->offset + len; |
524 |
|
|
return 1; |
525 |
|
|
} |
526 |
|
|
|
527 |
|
|
static void |
528 |
|
|
post_HP(DECL_ARGS) |
529 |
|
|
{ |
530 |
|
|
|
531 |
|
|
switch (n->type) { |
532 |
|
|
case ROFFT_BODY: |
533 |
|
|
term_newln(p); |
534 |
|
|
|
535 |
|
|
/* |
536 |
|
|
* Compatibility with a groff bug. |
537 |
|
|
* The .HP macro uses the undocumented .tag request |
538 |
|
|
* which causes a line break and cancels no-space |
539 |
|
|
* mode even if there isn't any output. |
540 |
|
|
*/ |
541 |
|
|
|
542 |
|
|
if (n->child == NULL) |
543 |
|
|
term_vspace(p); |
544 |
|
|
|
545 |
|
|
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); |
546 |
|
|
p->trailspace = 0; |
547 |
|
|
p->offset = mt->offset; |
548 |
|
|
p->rmargin = p->maxrmargin; |
549 |
|
|
break; |
550 |
|
|
default: |
551 |
|
|
break; |
552 |
|
|
} |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
static int |
556 |
|
|
pre_PP(DECL_ARGS) |
557 |
|
|
{ |
558 |
|
|
|
559 |
|
|
switch (n->type) { |
560 |
|
|
case ROFFT_BLOCK: |
561 |
|
|
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); |
562 |
|
|
print_bvspace(p, n, mt->pardist); |
563 |
|
|
break; |
564 |
|
|
default: |
565 |
|
|
p->offset = mt->offset; |
566 |
|
|
break; |
567 |
|
|
} |
568 |
|
|
|
569 |
|
|
return n->type != ROFFT_HEAD; |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
static int |
573 |
|
|
pre_IP(DECL_ARGS) |
574 |
|
|
{ |
575 |
|
|
struct roffsu su; |
576 |
|
|
const struct roff_node *nn; |
577 |
|
|
int len, savelit; |
578 |
|
|
|
579 |
|
|
switch (n->type) { |
580 |
|
|
case ROFFT_BODY: |
581 |
|
|
p->flags |= TERMP_NOSPACE; |
582 |
|
|
break; |
583 |
|
|
case ROFFT_HEAD: |
584 |
|
|
p->flags |= TERMP_NOBREAK; |
585 |
|
|
p->trailspace = 1; |
586 |
|
|
break; |
587 |
|
|
case ROFFT_BLOCK: |
588 |
|
|
print_bvspace(p, n, mt->pardist); |
589 |
|
|
/* FALLTHROUGH */ |
590 |
|
|
default: |
591 |
|
|
return 1; |
592 |
|
|
} |
593 |
|
|
|
594 |
|
|
/* Calculate the offset from the optional second argument. */ |
595 |
|
|
if ((nn = n->parent->head->child) != NULL && |
596 |
|
|
(nn = nn->next) != NULL && |
597 |
|
|
a2roffsu(nn->string, &su, SCALE_EN)) { |
598 |
|
|
len = term_hspan(p, &su) / 24; |
599 |
|
|
if (len < 0 && (size_t)(-len) > mt->offset) |
600 |
|
|
len = -mt->offset; |
601 |
|
|
else if (len > SHRT_MAX) |
602 |
|
|
len = term_len(p, p->defindent); |
603 |
|
|
mt->lmargin[mt->lmargincur] = len; |
604 |
|
|
} else |
605 |
|
|
len = mt->lmargin[mt->lmargincur]; |
606 |
|
|
|
607 |
|
|
switch (n->type) { |
608 |
|
|
case ROFFT_HEAD: |
609 |
|
|
p->offset = mt->offset; |
610 |
|
|
p->rmargin = mt->offset + len; |
611 |
|
|
|
612 |
|
|
savelit = MANT_LITERAL & mt->fl; |
613 |
|
|
mt->fl &= ~MANT_LITERAL; |
614 |
|
|
|
615 |
|
|
if (n->child) |
616 |
|
|
print_man_node(p, mt, n->child, meta); |
617 |
|
|
|
618 |
|
|
if (savelit) |
619 |
|
|
mt->fl |= MANT_LITERAL; |
620 |
|
|
|
621 |
|
|
return 0; |
622 |
|
|
case ROFFT_BODY: |
623 |
|
|
p->offset = mt->offset + len; |
624 |
|
|
p->rmargin = p->maxrmargin; |
625 |
|
|
break; |
626 |
|
|
default: |
627 |
|
|
break; |
628 |
|
|
} |
629 |
|
|
|
630 |
|
|
return 1; |
631 |
|
|
} |
632 |
|
|
|
633 |
|
|
static void |
634 |
|
|
post_IP(DECL_ARGS) |
635 |
|
|
{ |
636 |
|
|
|
637 |
|
|
switch (n->type) { |
638 |
|
|
case ROFFT_HEAD: |
639 |
|
|
term_flushln(p); |
640 |
|
|
p->flags &= ~TERMP_NOBREAK; |
641 |
|
|
p->trailspace = 0; |
642 |
|
|
p->rmargin = p->maxrmargin; |
643 |
|
|
break; |
644 |
|
|
case ROFFT_BODY: |
645 |
|
|
term_newln(p); |
646 |
|
|
p->offset = mt->offset; |
647 |
|
|
break; |
648 |
|
|
default: |
649 |
|
|
break; |
650 |
|
|
} |
651 |
|
|
} |
652 |
|
|
|
653 |
|
|
static int |
654 |
|
|
pre_TP(DECL_ARGS) |
655 |
|
|
{ |
656 |
|
|
struct roffsu su; |
657 |
|
|
struct roff_node *nn; |
658 |
|
|
int len, savelit; |
659 |
|
|
|
660 |
|
|
switch (n->type) { |
661 |
|
|
case ROFFT_HEAD: |
662 |
|
|
p->flags |= TERMP_NOBREAK | TERMP_BRTRSP; |
663 |
|
|
p->trailspace = 1; |
664 |
|
|
break; |
665 |
|
|
case ROFFT_BODY: |
666 |
|
|
p->flags |= TERMP_NOSPACE; |
667 |
|
|
break; |
668 |
|
|
case ROFFT_BLOCK: |
669 |
|
|
print_bvspace(p, n, mt->pardist); |
670 |
|
|
/* FALLTHROUGH */ |
671 |
|
|
default: |
672 |
|
|
return 1; |
673 |
|
|
} |
674 |
|
|
|
675 |
|
|
/* Calculate offset. */ |
676 |
|
|
|
677 |
|
|
if ((nn = n->parent->head->child) != NULL && |
678 |
|
|
nn->string != NULL && ! (MAN_LINE & nn->flags) && |
679 |
|
|
a2roffsu(nn->string, &su, SCALE_EN)) { |
680 |
|
|
len = term_hspan(p, &su) / 24; |
681 |
|
|
if (len < 0 && (size_t)(-len) > mt->offset) |
682 |
|
|
len = -mt->offset; |
683 |
|
|
else if (len > SHRT_MAX) |
684 |
|
|
len = term_len(p, p->defindent); |
685 |
|
|
mt->lmargin[mt->lmargincur] = len; |
686 |
|
|
} else |
687 |
|
|
len = mt->lmargin[mt->lmargincur]; |
688 |
|
|
|
689 |
|
|
switch (n->type) { |
690 |
|
|
case ROFFT_HEAD: |
691 |
|
|
p->offset = mt->offset; |
692 |
|
|
p->rmargin = mt->offset + len; |
693 |
|
|
|
694 |
|
|
savelit = MANT_LITERAL & mt->fl; |
695 |
|
|
mt->fl &= ~MANT_LITERAL; |
696 |
|
|
|
697 |
|
|
/* Don't print same-line elements. */ |
698 |
|
|
nn = n->child; |
699 |
|
|
while (NULL != nn && 0 == (MAN_LINE & nn->flags)) |
700 |
|
|
nn = nn->next; |
701 |
|
|
|
702 |
|
|
while (NULL != nn) { |
703 |
|
|
print_man_node(p, mt, nn, meta); |
704 |
|
|
nn = nn->next; |
705 |
|
|
} |
706 |
|
|
|
707 |
|
|
if (savelit) |
708 |
|
|
mt->fl |= MANT_LITERAL; |
709 |
|
|
return 0; |
710 |
|
|
case ROFFT_BODY: |
711 |
|
|
p->offset = mt->offset + len; |
712 |
|
|
p->rmargin = p->maxrmargin; |
713 |
|
|
p->trailspace = 0; |
714 |
|
|
p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP); |
715 |
|
|
break; |
716 |
|
|
default: |
717 |
|
|
break; |
718 |
|
|
} |
719 |
|
|
|
720 |
|
|
return 1; |
721 |
|
|
} |
722 |
|
|
|
723 |
|
|
static void |
724 |
|
|
post_TP(DECL_ARGS) |
725 |
|
|
{ |
726 |
|
|
|
727 |
|
|
switch (n->type) { |
728 |
|
|
case ROFFT_HEAD: |
729 |
|
|
term_flushln(p); |
730 |
|
|
break; |
731 |
|
|
case ROFFT_BODY: |
732 |
|
|
term_newln(p); |
733 |
|
|
p->offset = mt->offset; |
734 |
|
|
break; |
735 |
|
|
default: |
736 |
|
|
break; |
737 |
|
|
} |
738 |
|
|
} |
739 |
|
|
|
740 |
|
|
static int |
741 |
|
|
pre_SS(DECL_ARGS) |
742 |
|
|
{ |
743 |
|
|
int i; |
744 |
|
|
|
745 |
|
|
switch (n->type) { |
746 |
|
|
case ROFFT_BLOCK: |
747 |
|
|
mt->fl &= ~MANT_LITERAL; |
748 |
|
|
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); |
749 |
|
|
mt->offset = term_len(p, p->defindent); |
750 |
|
|
|
751 |
|
|
/* |
752 |
|
|
* No vertical space before the first subsection |
753 |
|
|
* and after an empty subsection. |
754 |
|
|
*/ |
755 |
|
|
|
756 |
|
|
do { |
757 |
|
|
n = n->prev; |
758 |
|
|
} while (n != NULL && n->tok != TOKEN_NONE && |
759 |
|
|
termacts[n->tok].flags & MAN_NOTEXT); |
760 |
|
|
if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL)) |
761 |
|
|
break; |
762 |
|
|
|
763 |
|
|
for (i = 0; i < mt->pardist; i++) |
764 |
|
|
term_vspace(p); |
765 |
|
|
break; |
766 |
|
|
case ROFFT_HEAD: |
767 |
|
|
term_fontrepl(p, TERMFONT_BOLD); |
768 |
|
|
p->offset = term_len(p, 3); |
769 |
|
|
p->rmargin = mt->offset; |
770 |
|
|
p->trailspace = mt->offset; |
771 |
|
|
p->flags |= TERMP_NOBREAK | TERMP_BRIND; |
772 |
|
|
break; |
773 |
|
|
case ROFFT_BODY: |
774 |
|
|
p->offset = mt->offset; |
775 |
|
|
p->rmargin = p->maxrmargin; |
776 |
|
|
p->trailspace = 0; |
777 |
|
|
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); |
778 |
|
|
break; |
779 |
|
|
default: |
780 |
|
|
break; |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
return 1; |
784 |
|
|
} |
785 |
|
|
|
786 |
|
|
static void |
787 |
|
|
post_SS(DECL_ARGS) |
788 |
|
|
{ |
789 |
|
|
|
790 |
|
|
switch (n->type) { |
791 |
|
|
case ROFFT_HEAD: |
792 |
|
|
term_newln(p); |
793 |
|
|
break; |
794 |
|
|
case ROFFT_BODY: |
795 |
|
|
term_newln(p); |
796 |
|
|
break; |
797 |
|
|
default: |
798 |
|
|
break; |
799 |
|
|
} |
800 |
|
|
} |
801 |
|
|
|
802 |
|
|
static int |
803 |
|
|
pre_SH(DECL_ARGS) |
804 |
|
|
{ |
805 |
|
|
int i; |
806 |
|
|
|
807 |
|
|
switch (n->type) { |
808 |
|
|
case ROFFT_BLOCK: |
809 |
|
|
mt->fl &= ~MANT_LITERAL; |
810 |
|
|
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); |
811 |
|
|
mt->offset = term_len(p, p->defindent); |
812 |
|
|
|
813 |
|
|
/* |
814 |
|
|
* No vertical space before the first section |
815 |
|
|
* and after an empty section. |
816 |
|
|
*/ |
817 |
|
|
|
818 |
|
|
do { |
819 |
|
|
n = n->prev; |
820 |
|
|
} while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT); |
821 |
|
|
if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL)) |
822 |
|
|
break; |
823 |
|
|
|
824 |
|
|
for (i = 0; i < mt->pardist; i++) |
825 |
|
|
term_vspace(p); |
826 |
|
|
break; |
827 |
|
|
case ROFFT_HEAD: |
828 |
|
|
term_fontrepl(p, TERMFONT_BOLD); |
829 |
|
|
p->offset = 0; |
830 |
|
|
p->rmargin = mt->offset; |
831 |
|
|
p->trailspace = mt->offset; |
832 |
|
|
p->flags |= TERMP_NOBREAK | TERMP_BRIND; |
833 |
|
|
break; |
834 |
|
|
case ROFFT_BODY: |
835 |
|
|
p->offset = mt->offset; |
836 |
|
|
p->rmargin = p->maxrmargin; |
837 |
|
|
p->trailspace = 0; |
838 |
|
|
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); |
839 |
|
|
break; |
840 |
|
|
default: |
841 |
|
|
break; |
842 |
|
|
} |
843 |
|
|
|
844 |
|
|
return 1; |
845 |
|
|
} |
846 |
|
|
|
847 |
|
|
static void |
848 |
|
|
post_SH(DECL_ARGS) |
849 |
|
|
{ |
850 |
|
|
|
851 |
|
|
switch (n->type) { |
852 |
|
|
case ROFFT_HEAD: |
853 |
|
|
term_newln(p); |
854 |
|
|
break; |
855 |
|
|
case ROFFT_BODY: |
856 |
|
|
term_newln(p); |
857 |
|
|
break; |
858 |
|
|
default: |
859 |
|
|
break; |
860 |
|
|
} |
861 |
|
|
} |
862 |
|
|
|
863 |
|
|
static int |
864 |
|
|
pre_RS(DECL_ARGS) |
865 |
|
|
{ |
866 |
|
|
struct roffsu su; |
867 |
|
|
|
868 |
|
|
switch (n->type) { |
869 |
|
|
case ROFFT_BLOCK: |
870 |
|
|
term_newln(p); |
871 |
|
|
return 1; |
872 |
|
|
case ROFFT_HEAD: |
873 |
|
|
return 0; |
874 |
|
|
default: |
875 |
|
|
break; |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
n = n->parent->head; |
879 |
|
|
n->aux = SHRT_MAX + 1; |
880 |
|
|
if (n->child == NULL) |
881 |
|
|
n->aux = mt->lmargin[mt->lmargincur]; |
882 |
|
|
else if (a2roffsu(n->child->string, &su, SCALE_EN)) |
883 |
|
|
n->aux = term_hspan(p, &su) / 24; |
884 |
|
|
if (n->aux < 0 && (size_t)(-n->aux) > mt->offset) |
885 |
|
|
n->aux = -mt->offset; |
886 |
|
|
else if (n->aux > SHRT_MAX) |
887 |
|
|
n->aux = term_len(p, p->defindent); |
888 |
|
|
|
889 |
|
|
mt->offset += n->aux; |
890 |
|
|
p->offset = mt->offset; |
891 |
|
|
p->rmargin = p->maxrmargin; |
892 |
|
|
|
893 |
|
|
if (++mt->lmarginsz < MAXMARGINS) |
894 |
|
|
mt->lmargincur = mt->lmarginsz; |
895 |
|
|
|
896 |
|
|
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); |
897 |
|
|
return 1; |
898 |
|
|
} |
899 |
|
|
|
900 |
|
|
static void |
901 |
|
|
post_RS(DECL_ARGS) |
902 |
|
|
{ |
903 |
|
|
|
904 |
|
|
switch (n->type) { |
905 |
|
|
case ROFFT_BLOCK: |
906 |
|
|
return; |
907 |
|
|
case ROFFT_HEAD: |
908 |
|
|
return; |
909 |
|
|
default: |
910 |
|
|
term_newln(p); |
911 |
|
|
break; |
912 |
|
|
} |
913 |
|
|
|
914 |
|
|
mt->offset -= n->parent->head->aux; |
915 |
|
|
p->offset = mt->offset; |
916 |
|
|
|
917 |
|
|
if (--mt->lmarginsz < MAXMARGINS) |
918 |
|
|
mt->lmargincur = mt->lmarginsz; |
919 |
|
|
} |
920 |
|
|
|
921 |
|
|
static int |
922 |
|
|
pre_UR(DECL_ARGS) |
923 |
|
|
{ |
924 |
|
|
|
925 |
|
|
return n->type != ROFFT_HEAD; |
926 |
|
|
} |
927 |
|
|
|
928 |
|
|
static void |
929 |
|
|
post_UR(DECL_ARGS) |
930 |
|
|
{ |
931 |
|
|
|
932 |
|
|
if (n->type != ROFFT_BLOCK) |
933 |
|
|
return; |
934 |
|
|
|
935 |
|
|
term_word(p, "<"); |
936 |
|
|
p->flags |= TERMP_NOSPACE; |
937 |
|
|
|
938 |
|
|
if (NULL != n->child->child) |
939 |
|
|
print_man_node(p, mt, n->child->child, meta); |
940 |
|
|
|
941 |
|
|
p->flags |= TERMP_NOSPACE; |
942 |
|
|
term_word(p, ">"); |
943 |
|
|
} |
944 |
|
|
|
945 |
|
|
static void |
946 |
|
|
print_man_node(DECL_ARGS) |
947 |
|
|
{ |
948 |
|
|
size_t rm, rmax; |
949 |
|
|
int c; |
950 |
|
|
|
951 |
|
|
switch (n->type) { |
952 |
|
|
case ROFFT_TEXT: |
953 |
|
|
/* |
954 |
|
|
* If we have a blank line, output a vertical space. |
955 |
|
|
* If we have a space as the first character, break |
956 |
|
|
* before printing the line's data. |
957 |
|
|
*/ |
958 |
|
|
if ('\0' == *n->string) { |
959 |
|
|
term_vspace(p); |
960 |
|
|
return; |
961 |
|
|
} else if (' ' == *n->string && MAN_LINE & n->flags) |
962 |
|
|
term_newln(p); |
963 |
|
|
|
964 |
|
|
term_word(p, n->string); |
965 |
|
|
goto out; |
966 |
|
|
|
967 |
|
|
case ROFFT_EQN: |
968 |
|
|
if ( ! (n->flags & MAN_LINE)) |
969 |
|
|
p->flags |= TERMP_NOSPACE; |
970 |
|
|
term_eqn(p, n->eqn); |
971 |
|
|
if (n->next != NULL && ! (n->next->flags & MAN_LINE)) |
972 |
|
|
p->flags |= TERMP_NOSPACE; |
973 |
|
|
return; |
974 |
|
|
case ROFFT_TBL: |
975 |
|
|
if (p->tbl.cols == NULL) |
976 |
|
|
term_vspace(p); |
977 |
|
|
term_tbl(p, n->span); |
978 |
|
|
return; |
979 |
|
|
default: |
980 |
|
|
break; |
981 |
|
|
} |
982 |
|
|
|
983 |
|
|
if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) |
984 |
|
|
term_fontrepl(p, TERMFONT_NONE); |
985 |
|
|
|
986 |
|
|
c = 1; |
987 |
|
|
if (termacts[n->tok].pre) |
988 |
|
|
c = (*termacts[n->tok].pre)(p, mt, n, meta); |
989 |
|
|
|
990 |
|
|
if (c && n->child) |
991 |
|
|
print_man_nodelist(p, mt, n->child, meta); |
992 |
|
|
|
993 |
|
|
if (termacts[n->tok].post) |
994 |
|
|
(*termacts[n->tok].post)(p, mt, n, meta); |
995 |
|
|
if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) |
996 |
|
|
term_fontrepl(p, TERMFONT_NONE); |
997 |
|
|
|
998 |
|
|
out: |
999 |
|
|
/* |
1000 |
|
|
* If we're in a literal context, make sure that words |
1001 |
|
|
* together on the same line stay together. This is a |
1002 |
|
|
* POST-printing call, so we check the NEXT word. Since |
1003 |
|
|
* -man doesn't have nested macros, we don't need to be |
1004 |
|
|
* more specific than this. |
1005 |
|
|
*/ |
1006 |
|
|
if (mt->fl & MANT_LITERAL && |
1007 |
|
|
! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) && |
1008 |
|
|
(n->next == NULL || n->next->flags & MAN_LINE)) { |
1009 |
|
|
rm = p->rmargin; |
1010 |
|
|
rmax = p->maxrmargin; |
1011 |
|
|
p->rmargin = p->maxrmargin = TERM_MAXMARGIN; |
1012 |
|
|
p->flags |= TERMP_NOSPACE; |
1013 |
|
|
if (n->string != NULL && *n->string != '\0') |
1014 |
|
|
term_flushln(p); |
1015 |
|
|
else |
1016 |
|
|
term_newln(p); |
1017 |
|
|
if (rm < rmax && n->parent->tok == MAN_HP) { |
1018 |
|
|
p->offset = rm; |
1019 |
|
|
p->rmargin = rmax; |
1020 |
|
|
} else |
1021 |
|
|
p->rmargin = rm; |
1022 |
|
|
p->maxrmargin = rmax; |
1023 |
|
|
} |
1024 |
|
|
if (MAN_EOS & n->flags) |
1025 |
|
|
p->flags |= TERMP_SENTENCE; |
1026 |
|
|
} |
1027 |
|
|
|
1028 |
|
|
|
1029 |
|
|
static void |
1030 |
|
|
print_man_nodelist(DECL_ARGS) |
1031 |
|
|
{ |
1032 |
|
|
|
1033 |
|
|
while (n != NULL) { |
1034 |
|
|
print_man_node(p, mt, n, meta); |
1035 |
|
|
n = n->next; |
1036 |
|
|
} |
1037 |
|
|
} |
1038 |
|
|
|
1039 |
|
|
static void |
1040 |
|
|
print_man_foot(struct termp *p, const struct roff_meta *meta) |
1041 |
|
|
{ |
1042 |
|
|
char *title; |
1043 |
|
|
size_t datelen, titlen; |
1044 |
|
|
|
1045 |
|
|
assert(meta->title); |
1046 |
|
|
assert(meta->msec); |
1047 |
|
|
assert(meta->date); |
1048 |
|
|
|
1049 |
|
|
term_fontrepl(p, TERMFONT_NONE); |
1050 |
|
|
|
1051 |
|
|
if (meta->hasbody) |
1052 |
|
|
term_vspace(p); |
1053 |
|
|
|
1054 |
|
|
/* |
1055 |
|
|
* Temporary, undocumented option to imitate mdoc(7) output. |
1056 |
|
|
* In the bottom right corner, use the operating system |
1057 |
|
|
* instead of the title. |
1058 |
|
|
*/ |
1059 |
|
|
|
1060 |
|
|
if ( ! p->mdocstyle) { |
1061 |
|
|
if (meta->hasbody) { |
1062 |
|
|
term_vspace(p); |
1063 |
|
|
term_vspace(p); |
1064 |
|
|
} |
1065 |
|
|
mandoc_asprintf(&title, "%s(%s)", |
1066 |
|
|
meta->title, meta->msec); |
1067 |
|
|
} else if (meta->os) { |
1068 |
|
|
title = mandoc_strdup(meta->os); |
1069 |
|
|
} else { |
1070 |
|
|
title = mandoc_strdup(""); |
1071 |
|
|
} |
1072 |
|
|
datelen = term_strlen(p, meta->date); |
1073 |
|
|
|
1074 |
|
|
/* Bottom left corner: operating system. */ |
1075 |
|
|
|
1076 |
|
|
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; |
1077 |
|
|
p->trailspace = 1; |
1078 |
|
|
p->offset = 0; |
1079 |
|
|
p->rmargin = p->maxrmargin > datelen ? |
1080 |
|
|
(p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0; |
1081 |
|
|
|
1082 |
|
|
if (meta->os) |
1083 |
|
|
term_word(p, meta->os); |
1084 |
|
|
term_flushln(p); |
1085 |
|
|
|
1086 |
|
|
/* At the bottom in the middle: manual date. */ |
1087 |
|
|
|
1088 |
|
|
p->offset = p->rmargin; |
1089 |
|
|
titlen = term_strlen(p, title); |
1090 |
|
|
p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0; |
1091 |
|
|
p->flags |= TERMP_NOSPACE; |
1092 |
|
|
|
1093 |
|
|
term_word(p, meta->date); |
1094 |
|
|
term_flushln(p); |
1095 |
|
|
|
1096 |
|
|
/* Bottom right corner: manual title and section. */ |
1097 |
|
|
|
1098 |
|
|
p->flags &= ~TERMP_NOBREAK; |
1099 |
|
|
p->flags |= TERMP_NOSPACE; |
1100 |
|
|
p->trailspace = 0; |
1101 |
|
|
p->offset = p->rmargin; |
1102 |
|
|
p->rmargin = p->maxrmargin; |
1103 |
|
|
|
1104 |
|
|
term_word(p, title); |
1105 |
|
|
term_flushln(p); |
1106 |
|
|
free(title); |
1107 |
|
|
} |
1108 |
|
|
|
1109 |
|
|
static void |
1110 |
|
|
print_man_head(struct termp *p, const struct roff_meta *meta) |
1111 |
|
|
{ |
1112 |
|
|
const char *volume; |
1113 |
|
|
char *title; |
1114 |
|
|
size_t vollen, titlen; |
1115 |
|
|
|
1116 |
|
|
assert(meta->title); |
1117 |
|
|
assert(meta->msec); |
1118 |
|
|
|
1119 |
|
|
volume = NULL == meta->vol ? "" : meta->vol; |
1120 |
|
|
vollen = term_strlen(p, volume); |
1121 |
|
|
|
1122 |
|
|
/* Top left corner: manual title and section. */ |
1123 |
|
|
|
1124 |
|
|
mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); |
1125 |
|
|
titlen = term_strlen(p, title); |
1126 |
|
|
|
1127 |
|
|
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; |
1128 |
|
|
p->trailspace = 1; |
1129 |
|
|
p->offset = 0; |
1130 |
|
|
p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? |
1131 |
|
|
(p->maxrmargin - vollen + term_len(p, 1)) / 2 : |
1132 |
|
|
vollen < p->maxrmargin ? p->maxrmargin - vollen : 0; |
1133 |
|
|
|
1134 |
|
|
term_word(p, title); |
1135 |
|
|
term_flushln(p); |
1136 |
|
|
|
1137 |
|
|
/* At the top in the middle: manual volume. */ |
1138 |
|
|
|
1139 |
|
|
p->flags |= TERMP_NOSPACE; |
1140 |
|
|
p->offset = p->rmargin; |
1141 |
|
|
p->rmargin = p->offset + vollen + titlen < p->maxrmargin ? |
1142 |
|
|
p->maxrmargin - titlen : p->maxrmargin; |
1143 |
|
|
|
1144 |
|
|
term_word(p, volume); |
1145 |
|
|
term_flushln(p); |
1146 |
|
|
|
1147 |
|
|
/* Top right corner: title and section, again. */ |
1148 |
|
|
|
1149 |
|
|
p->flags &= ~TERMP_NOBREAK; |
1150 |
|
|
p->trailspace = 0; |
1151 |
|
|
if (p->rmargin + titlen <= p->maxrmargin) { |
1152 |
|
|
p->flags |= TERMP_NOSPACE; |
1153 |
|
|
p->offset = p->rmargin; |
1154 |
|
|
p->rmargin = p->maxrmargin; |
1155 |
|
|
term_word(p, title); |
1156 |
|
|
term_flushln(p); |
1157 |
|
|
} |
1158 |
|
|
|
1159 |
|
|
p->flags &= ~TERMP_NOSPACE; |
1160 |
|
|
p->offset = 0; |
1161 |
|
|
p->rmargin = p->maxrmargin; |
1162 |
|
|
|
1163 |
|
|
/* |
1164 |
|
|
* Groff prints three blank lines before the content. |
1165 |
|
|
* Do the same, except in the temporary, undocumented |
1166 |
|
|
* mode imitating mdoc(7) output. |
1167 |
|
|
*/ |
1168 |
|
|
|
1169 |
|
|
term_vspace(p); |
1170 |
|
|
if ( ! p->mdocstyle) { |
1171 |
|
|
term_vspace(p); |
1172 |
|
|
term_vspace(p); |
1173 |
|
|
} |
1174 |
|
|
free(title); |
1175 |
|
|
} |