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