]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Remove `Sp', `Vb', and `Ve' (as per schwarze@'s changes in OpenBSD),
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.86 2010/12/05 16:14:16 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "mandoc.h"
30 #include "out.h"
31 #include "man.h"
32 #include "term.h"
33 #include "chars.h"
34 #include "main.h"
35
36 #define INDENT 7
37 #define HALFINDENT 3
38
39 /* FIXME: have PD set the default vspace width. */
40
41 struct mtermp {
42 int fl;
43 #define MANT_LITERAL (1 << 0)
44 /*
45 * Default amount to indent the left margin after leading text
46 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
47 * indent). This needs to be saved because `HP' and so on, if
48 * not having a specified value, must default.
49 *
50 * Note that this is the indentation AFTER the left offset, so
51 * the total offset is usually offset + lmargin.
52 */
53 size_t lmargin;
54 /*
55 * The default offset, i.e., the amount between any text and the
56 * page boundary.
57 */
58 size_t offset;
59 };
60
61 #define DECL_ARGS struct termp *p, \
62 struct mtermp *mt, \
63 const struct man_node *n, \
64 const struct man_meta *m
65
66 struct termact {
67 int (*pre)(DECL_ARGS);
68 void (*post)(DECL_ARGS);
69 int flags;
70 #define MAN_NOTEXT (1 << 0) /* Never has text children. */
71 };
72
73 static int a2width(const struct termp *, const char *);
74 static size_t a2height(const struct termp *, const char *);
75
76 static void print_man_nodelist(DECL_ARGS);
77 static void print_man_node(DECL_ARGS);
78 static void print_man_head(struct termp *, const void *);
79 static void print_man_foot(struct termp *, const void *);
80 static void print_bvspace(struct termp *,
81 const struct man_node *);
82
83 static int pre_B(DECL_ARGS);
84 static int pre_BI(DECL_ARGS);
85 static int pre_HP(DECL_ARGS);
86 static int pre_I(DECL_ARGS);
87 static int pre_IP(DECL_ARGS);
88 static int pre_PP(DECL_ARGS);
89 static int pre_RB(DECL_ARGS);
90 static int pre_RI(DECL_ARGS);
91 static int pre_RS(DECL_ARGS);
92 static int pre_SH(DECL_ARGS);
93 static int pre_SS(DECL_ARGS);
94 static int pre_TP(DECL_ARGS);
95 static int pre_ign(DECL_ARGS);
96 static int pre_in(DECL_ARGS);
97 static int pre_literal(DECL_ARGS);
98 static int pre_sp(DECL_ARGS);
99
100 static void post_IP(DECL_ARGS);
101 static void post_HP(DECL_ARGS);
102 static void post_RS(DECL_ARGS);
103 static void post_SH(DECL_ARGS);
104 static void post_SS(DECL_ARGS);
105 static void post_TP(DECL_ARGS);
106
107 static const struct termact termacts[MAN_MAX] = {
108 { pre_sp, NULL, MAN_NOTEXT }, /* br */
109 { NULL, NULL, 0 }, /* TH */
110 { pre_SH, post_SH, 0 }, /* SH */
111 { pre_SS, post_SS, 0 }, /* SS */
112 { pre_TP, post_TP, 0 }, /* TP */
113 { pre_PP, NULL, 0 }, /* LP */
114 { pre_PP, NULL, 0 }, /* PP */
115 { pre_PP, NULL, 0 }, /* P */
116 { pre_IP, post_IP, 0 }, /* IP */
117 { pre_HP, post_HP, 0 }, /* HP */
118 { NULL, NULL, 0 }, /* SM */
119 { pre_B, NULL, 0 }, /* SB */
120 { pre_BI, NULL, 0 }, /* BI */
121 { pre_BI, NULL, 0 }, /* IB */
122 { pre_RB, NULL, 0 }, /* BR */
123 { pre_RB, NULL, 0 }, /* RB */
124 { NULL, NULL, 0 }, /* R */
125 { pre_B, NULL, 0 }, /* B */
126 { pre_I, NULL, 0 }, /* I */
127 { pre_RI, NULL, 0 }, /* IR */
128 { pre_RI, NULL, 0 }, /* RI */
129 { NULL, NULL, MAN_NOTEXT }, /* na */
130 { pre_I, NULL, 0 }, /* i */
131 { pre_sp, NULL, MAN_NOTEXT }, /* sp */
132 { pre_literal, NULL, 0 }, /* nf */
133 { pre_literal, NULL, 0 }, /* fi */
134 { NULL, NULL, 0 }, /* r */
135 { NULL, NULL, 0 }, /* RE */
136 { pre_RS, post_RS, 0 }, /* RS */
137 { pre_ign, NULL, 0 }, /* DT */
138 { pre_ign, NULL, 0 }, /* UC */
139 { pre_ign, NULL, 0 }, /* PD */
140 { pre_ign, NULL, 0 }, /* AT */
141 { pre_in, NULL, MAN_NOTEXT }, /* in */
142 };
143
144
145
146 void
147 terminal_man(void *arg, const struct man *man)
148 {
149 struct termp *p;
150 const struct man_node *n;
151 const struct man_meta *m;
152 struct mtermp mt;
153
154 p = (struct termp *)arg;
155
156 p->overstep = 0;
157 p->maxrmargin = p->defrmargin;
158 p->tabwidth = term_len(p, 5);
159
160 if (NULL == p->symtab)
161 switch (p->enc) {
162 case (TERMENC_ASCII):
163 p->symtab = chars_init(CHARS_ASCII);
164 break;
165 default:
166 abort();
167 /* NOTREACHED */
168 }
169
170 n = man_node(man);
171 m = man_meta(man);
172
173 term_begin(p, print_man_head, print_man_foot, m);
174 p->flags |= TERMP_NOSPACE;
175
176 mt.fl = 0;
177 mt.lmargin = term_len(p, INDENT);
178 mt.offset = term_len(p, INDENT);
179
180 if (n->child)
181 print_man_nodelist(p, &mt, n->child, m);
182
183 term_end(p);
184 }
185
186
187 static size_t
188 a2height(const struct termp *p, const char *cp)
189 {
190 struct roffsu su;
191
192 if ( ! a2roffsu(cp, &su, SCALE_VS))
193 SCALE_VS_INIT(&su, term_strlen(p, cp));
194
195 return(term_vspan(p, &su));
196 }
197
198
199 static int
200 a2width(const struct termp *p, const char *cp)
201 {
202 struct roffsu su;
203
204 if ( ! a2roffsu(cp, &su, SCALE_BU))
205 return(-1);
206
207 return((int)term_hspan(p, &su));
208 }
209
210
211 static void
212 print_bvspace(struct termp *p, const struct man_node *n)
213 {
214 term_newln(p);
215
216 if (NULL == n->prev)
217 return;
218
219 if (MAN_SS == n->prev->tok)
220 return;
221 if (MAN_SH == n->prev->tok)
222 return;
223
224 term_vspace(p);
225 }
226
227
228 /* ARGSUSED */
229 static int
230 pre_ign(DECL_ARGS)
231 {
232
233 return(0);
234 }
235
236
237 /* ARGSUSED */
238 static int
239 pre_I(DECL_ARGS)
240 {
241
242 term_fontrepl(p, TERMFONT_UNDER);
243 return(1);
244 }
245
246
247 /* ARGSUSED */
248 static int
249 pre_literal(DECL_ARGS)
250 {
251
252 term_newln(p);
253 switch (n->tok) {
254 case (MAN_nf):
255 mt->fl |= MANT_LITERAL;
256 break;
257 default:
258 mt->fl &= ~MANT_LITERAL;
259 break;
260 }
261
262 return(1);
263 }
264
265
266
267 /* ARGSUSED */
268 static int
269 pre_RB(DECL_ARGS)
270 {
271 const struct man_node *nn;
272 int i;
273
274 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
275 if (i % 2 && MAN_RB == n->tok)
276 term_fontrepl(p, TERMFONT_BOLD);
277 else if ( ! (i % 2) && MAN_RB != n->tok)
278 term_fontrepl(p, TERMFONT_BOLD);
279 else
280 term_fontrepl(p, TERMFONT_NONE);
281
282 if (i > 0)
283 p->flags |= TERMP_NOSPACE;
284
285 print_man_node(p, mt, nn, m);
286 }
287 return(0);
288 }
289
290
291 /* ARGSUSED */
292 static int
293 pre_RI(DECL_ARGS)
294 {
295 const struct man_node *nn;
296 int i;
297
298 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
299 if (i % 2 && MAN_RI == n->tok)
300 term_fontrepl(p, TERMFONT_UNDER);
301 else if ( ! (i % 2) && MAN_RI != n->tok)
302 term_fontrepl(p, TERMFONT_UNDER);
303 else
304 term_fontrepl(p, TERMFONT_NONE);
305
306 if (i > 0)
307 p->flags |= TERMP_NOSPACE;
308
309 print_man_node(p, mt, nn, m);
310 }
311 return(0);
312 }
313
314
315 /* ARGSUSED */
316 static int
317 pre_BI(DECL_ARGS)
318 {
319 const struct man_node *nn;
320 int i;
321
322 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
323 if (i % 2 && MAN_BI == n->tok)
324 term_fontrepl(p, TERMFONT_UNDER);
325 else if (i % 2)
326 term_fontrepl(p, TERMFONT_BOLD);
327 else if (MAN_BI == n->tok)
328 term_fontrepl(p, TERMFONT_BOLD);
329 else
330 term_fontrepl(p, TERMFONT_UNDER);
331
332 if (i)
333 p->flags |= TERMP_NOSPACE;
334
335 print_man_node(p, mt, nn, m);
336 }
337 return(0);
338 }
339
340
341 /* ARGSUSED */
342 static int
343 pre_B(DECL_ARGS)
344 {
345
346 term_fontrepl(p, TERMFONT_BOLD);
347 return(1);
348 }
349
350
351 /* ARGSUSED */
352 static int
353 pre_in(DECL_ARGS)
354 {
355 int len, less;
356 size_t v;
357 const char *cp;
358
359 term_newln(p);
360
361 if (NULL == n->child) {
362 p->offset = mt->offset;
363 return(0);
364 }
365
366 cp = n->child->string;
367 less = 0;
368
369 if ('-' == *cp)
370 less = -1;
371 else if ('+' == *cp)
372 less = 1;
373 else
374 cp--;
375
376 if ((len = a2width(p, ++cp)) < 0)
377 return(0);
378
379 v = (size_t)len;
380
381 if (less < 0)
382 p->offset -= p->offset > v ? v : p->offset;
383 else if (less > 0)
384 p->offset += v;
385 else
386 p->offset = v;
387
388 return(0);
389 }
390
391
392 /* ARGSUSED */
393 static int
394 pre_sp(DECL_ARGS)
395 {
396 size_t i, len;
397
398 switch (n->tok) {
399 case (MAN_br):
400 len = 0;
401 break;
402 default:
403 len = n->child ? a2height(p, n->child->string) : 1;
404 break;
405 }
406
407 if (0 == len)
408 term_newln(p);
409 for (i = 0; i < len; i++)
410 term_vspace(p);
411
412 return(0);
413 }
414
415
416 /* ARGSUSED */
417 static int
418 pre_HP(DECL_ARGS)
419 {
420 size_t len;
421 int ival;
422 const struct man_node *nn;
423
424 switch (n->type) {
425 case (MAN_BLOCK):
426 print_bvspace(p, n);
427 return(1);
428 case (MAN_BODY):
429 p->flags |= TERMP_NOBREAK;
430 p->flags |= TERMP_TWOSPACE;
431 break;
432 default:
433 return(0);
434 }
435
436 len = mt->lmargin;
437 ival = -1;
438
439 /* Calculate offset. */
440
441 if (NULL != (nn = n->parent->head->child))
442 if ((ival = a2width(p, nn->string)) >= 0)
443 len = (size_t)ival;
444
445 if (0 == len)
446 len = term_len(p, 1);
447
448 p->offset = mt->offset;
449 p->rmargin = mt->offset + len;
450
451 if (ival >= 0)
452 mt->lmargin = (size_t)ival;
453
454 return(1);
455 }
456
457
458 /* ARGSUSED */
459 static void
460 post_HP(DECL_ARGS)
461 {
462
463 switch (n->type) {
464 case (MAN_BLOCK):
465 term_flushln(p);
466 break;
467 case (MAN_BODY):
468 term_flushln(p);
469 p->flags &= ~TERMP_NOBREAK;
470 p->flags &= ~TERMP_TWOSPACE;
471 p->offset = mt->offset;
472 p->rmargin = p->maxrmargin;
473 break;
474 default:
475 break;
476 }
477 }
478
479
480 /* ARGSUSED */
481 static int
482 pre_PP(DECL_ARGS)
483 {
484
485 switch (n->type) {
486 case (MAN_BLOCK):
487 mt->lmargin = term_len(p, INDENT);
488 print_bvspace(p, n);
489 break;
490 default:
491 p->offset = mt->offset;
492 break;
493 }
494
495 return(1);
496 }
497
498
499 /* ARGSUSED */
500 static int
501 pre_IP(DECL_ARGS)
502 {
503 const struct man_node *nn;
504 size_t len;
505 int ival;
506
507 switch (n->type) {
508 case (MAN_BODY):
509 p->flags |= TERMP_NOLPAD;
510 p->flags |= TERMP_NOSPACE;
511 break;
512 case (MAN_HEAD):
513 p->flags |= TERMP_NOBREAK;
514 break;
515 case (MAN_BLOCK):
516 print_bvspace(p, n);
517 /* FALLTHROUGH */
518 default:
519 return(1);
520 }
521
522 len = mt->lmargin;
523 ival = -1;
524
525 /* Calculate offset. */
526
527 if (NULL != (nn = n->parent->head->child))
528 if (NULL != (nn = nn->next)) {
529 for ( ; nn->next; nn = nn->next)
530 /* Do nothing. */ ;
531 if ((ival = a2width(p, nn->string)) >= 0)
532 len = (size_t)ival;
533 }
534
535 switch (n->type) {
536 case (MAN_HEAD):
537 /* Handle zero-width lengths. */
538 if (0 == len)
539 len = term_len(p, 1);
540
541 p->offset = mt->offset;
542 p->rmargin = mt->offset + len;
543 if (ival < 0)
544 break;
545
546 /* Set the saved left-margin. */
547 mt->lmargin = (size_t)ival;
548
549 /* Don't print the length value. */
550 for (nn = n->child; nn->next; nn = nn->next)
551 print_man_node(p, mt, nn, m);
552 return(0);
553 case (MAN_BODY):
554 p->offset = mt->offset + len;
555 p->rmargin = p->maxrmargin;
556 break;
557 default:
558 break;
559 }
560
561 return(1);
562 }
563
564
565 /* ARGSUSED */
566 static void
567 post_IP(DECL_ARGS)
568 {
569
570 switch (n->type) {
571 case (MAN_HEAD):
572 term_flushln(p);
573 p->flags &= ~TERMP_NOBREAK;
574 p->rmargin = p->maxrmargin;
575 break;
576 case (MAN_BODY):
577 term_flushln(p);
578 p->flags &= ~TERMP_NOLPAD;
579 break;
580 default:
581 break;
582 }
583 }
584
585
586 /* ARGSUSED */
587 static int
588 pre_TP(DECL_ARGS)
589 {
590 const struct man_node *nn;
591 size_t len;
592 int ival;
593
594 switch (n->type) {
595 case (MAN_HEAD):
596 p->flags |= TERMP_NOBREAK;
597 p->flags |= TERMP_TWOSPACE;
598 break;
599 case (MAN_BODY):
600 p->flags |= TERMP_NOLPAD;
601 p->flags |= TERMP_NOSPACE;
602 break;
603 case (MAN_BLOCK):
604 print_bvspace(p, n);
605 /* FALLTHROUGH */
606 default:
607 return(1);
608 }
609
610 len = (size_t)mt->lmargin;
611 ival = -1;
612
613 /* Calculate offset. */
614
615 if (NULL != (nn = n->parent->head->child)) {
616 while (nn && MAN_TEXT != nn->type)
617 nn = nn->next;
618 if (nn && nn->next)
619 if ((ival = a2width(p, nn->string)) >= 0)
620 len = (size_t)ival;
621 }
622
623 switch (n->type) {
624 case (MAN_HEAD):
625 /* Handle zero-length properly. */
626 if (0 == len)
627 len = term_len(p, 1);
628
629 p->offset = mt->offset;
630 p->rmargin = mt->offset + len;
631
632 /* Don't print same-line elements. */
633 for (nn = n->child; nn; nn = nn->next)
634 if (nn->line > n->line)
635 print_man_node(p, mt, nn, m);
636
637 if (ival >= 0)
638 mt->lmargin = (size_t)ival;
639
640 return(0);
641 case (MAN_BODY):
642 p->offset = mt->offset + len;
643 p->rmargin = p->maxrmargin;
644 break;
645 default:
646 break;
647 }
648
649 return(1);
650 }
651
652
653 /* ARGSUSED */
654 static void
655 post_TP(DECL_ARGS)
656 {
657
658 switch (n->type) {
659 case (MAN_HEAD):
660 term_flushln(p);
661 p->flags &= ~TERMP_NOBREAK;
662 p->flags &= ~TERMP_TWOSPACE;
663 p->rmargin = p->maxrmargin;
664 break;
665 case (MAN_BODY):
666 term_flushln(p);
667 p->flags &= ~TERMP_NOLPAD;
668 break;
669 default:
670 break;
671 }
672 }
673
674
675 /* ARGSUSED */
676 static int
677 pre_SS(DECL_ARGS)
678 {
679
680 switch (n->type) {
681 case (MAN_BLOCK):
682 mt->lmargin = term_len(p, INDENT);
683 mt->offset = term_len(p, INDENT);
684 /* If following a prior empty `SS', no vspace. */
685 if (n->prev && MAN_SS == n->prev->tok)
686 if (NULL == n->prev->body->child)
687 break;
688 if (NULL == n->prev)
689 break;
690 term_vspace(p);
691 break;
692 case (MAN_HEAD):
693 term_fontrepl(p, TERMFONT_BOLD);
694 p->offset = term_len(p, HALFINDENT);
695 break;
696 case (MAN_BODY):
697 p->offset = mt->offset;
698 break;
699 default:
700 break;
701 }
702
703 return(1);
704 }
705
706
707 /* ARGSUSED */
708 static void
709 post_SS(DECL_ARGS)
710 {
711
712 switch (n->type) {
713 case (MAN_HEAD):
714 term_newln(p);
715 break;
716 case (MAN_BODY):
717 term_newln(p);
718 break;
719 default:
720 break;
721 }
722 }
723
724
725 /* ARGSUSED */
726 static int
727 pre_SH(DECL_ARGS)
728 {
729
730 switch (n->type) {
731 case (MAN_BLOCK):
732 mt->lmargin = term_len(p, INDENT);
733 mt->offset = term_len(p, INDENT);
734 /* If following a prior empty `SH', no vspace. */
735 if (n->prev && MAN_SH == n->prev->tok)
736 if (NULL == n->prev->body->child)
737 break;
738 /* If the first macro, no vspae. */
739 if (NULL == n->prev)
740 break;
741 term_vspace(p);
742 break;
743 case (MAN_HEAD):
744 term_fontrepl(p, TERMFONT_BOLD);
745 p->offset = 0;
746 break;
747 case (MAN_BODY):
748 p->offset = mt->offset;
749 break;
750 default:
751 break;
752 }
753
754 return(1);
755 }
756
757
758 /* ARGSUSED */
759 static void
760 post_SH(DECL_ARGS)
761 {
762
763 switch (n->type) {
764 case (MAN_HEAD):
765 term_newln(p);
766 break;
767 case (MAN_BODY):
768 term_newln(p);
769 break;
770 default:
771 break;
772 }
773 }
774
775
776 /* ARGSUSED */
777 static int
778 pre_RS(DECL_ARGS)
779 {
780 const struct man_node *nn;
781 int ival;
782
783 switch (n->type) {
784 case (MAN_BLOCK):
785 term_newln(p);
786 return(1);
787 case (MAN_HEAD):
788 return(0);
789 default:
790 break;
791 }
792
793 if (NULL == (nn = n->parent->head->child)) {
794 mt->offset = mt->lmargin + term_len(p, INDENT);
795 p->offset = mt->offset;
796 return(1);
797 }
798
799 if ((ival = a2width(p, nn->string)) < 0)
800 return(1);
801
802 mt->offset = term_len(p, INDENT) + (size_t)ival;
803 p->offset = mt->offset;
804
805 return(1);
806 }
807
808
809 /* ARGSUSED */
810 static void
811 post_RS(DECL_ARGS)
812 {
813
814 switch (n->type) {
815 case (MAN_BLOCK):
816 mt->offset = mt->lmargin = term_len(p, INDENT);
817 break;
818 case (MAN_HEAD):
819 break;
820 default:
821 term_newln(p);
822 p->offset = term_len(p, INDENT);
823 break;
824 }
825 }
826
827
828 static void
829 print_man_node(DECL_ARGS)
830 {
831 size_t rm, rmax;
832 int c;
833
834 c = 1;
835
836 switch (n->type) {
837 case(MAN_TEXT):
838 if (0 == *n->string) {
839 term_vspace(p);
840 break;
841 }
842
843 term_word(p, n->string);
844
845 /* FIXME: this means that macro lines are munged! */
846
847 if (MANT_LITERAL & mt->fl) {
848 rm = p->rmargin;
849 rmax = p->maxrmargin;
850 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
851 p->flags |= TERMP_NOSPACE;
852 term_flushln(p);
853 p->rmargin = rm;
854 p->maxrmargin = rmax;
855 }
856 break;
857 default:
858 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
859 term_fontrepl(p, TERMFONT_NONE);
860 if (termacts[n->tok].pre)
861 c = (*termacts[n->tok].pre)(p, mt, n, m);
862 break;
863 }
864
865 if (c && n->child)
866 print_man_nodelist(p, mt, n->child, m);
867
868 if (MAN_TEXT != n->type) {
869 if (termacts[n->tok].post)
870 (*termacts[n->tok].post)(p, mt, n, m);
871 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
872 term_fontrepl(p, TERMFONT_NONE);
873 }
874
875 if (MAN_EOS & n->flags)
876 p->flags |= TERMP_SENTENCE;
877 }
878
879
880 static void
881 print_man_nodelist(DECL_ARGS)
882 {
883
884 print_man_node(p, mt, n, m);
885 if ( ! n->next)
886 return;
887 print_man_nodelist(p, mt, n->next, m);
888 }
889
890
891 static void
892 print_man_foot(struct termp *p, const void *arg)
893 {
894 char buf[DATESIZ];
895 const struct man_meta *meta;
896
897 meta = (const struct man_meta *)arg;
898
899 term_fontrepl(p, TERMFONT_NONE);
900
901 if (meta->rawdate)
902 strlcpy(buf, meta->rawdate, DATESIZ);
903 else
904 time2a(meta->date, buf, DATESIZ);
905
906 term_vspace(p);
907 term_vspace(p);
908 term_vspace(p);
909
910 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
911 p->rmargin = p->maxrmargin - term_strlen(p, buf);
912 p->offset = 0;
913
914 /* term_strlen() can return zero. */
915 if (p->rmargin == p->maxrmargin)
916 p->rmargin--;
917
918 if (meta->source)
919 term_word(p, meta->source);
920 if (meta->source)
921 term_word(p, "");
922 term_flushln(p);
923
924 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
925 p->offset = p->rmargin;
926 p->rmargin = p->maxrmargin;
927 p->flags &= ~TERMP_NOBREAK;
928
929 term_word(p, buf);
930 term_flushln(p);
931 }
932
933
934 static void
935 print_man_head(struct termp *p, const void *arg)
936 {
937 char buf[BUFSIZ], title[BUFSIZ];
938 size_t buflen, titlen;
939 const struct man_meta *m;
940
941 m = (const struct man_meta *)arg;
942
943 /*
944 * Note that old groff would spit out some spaces before the
945 * header. We discontinue this strange behaviour, but at one
946 * point we did so here.
947 */
948
949 p->rmargin = p->maxrmargin;
950
951 p->offset = 0;
952 buf[0] = title[0] = '\0';
953
954 if (m->vol)
955 strlcpy(buf, m->vol, BUFSIZ);
956 buflen = term_strlen(p, buf);
957
958 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
959 titlen = term_strlen(p, title);
960
961 p->offset = 0;
962 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
963 (p->maxrmargin -
964 term_strlen(p, buf) + term_len(p, 1)) / 2 :
965 p->maxrmargin - buflen;
966 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
967
968 term_word(p, title);
969 term_flushln(p);
970
971 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
972 p->offset = p->rmargin;
973 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
974 p->maxrmargin - titlen : p->maxrmargin;
975
976 term_word(p, buf);
977 term_flushln(p);
978
979 p->flags &= ~TERMP_NOBREAK;
980 if (p->rmargin + titlen <= p->maxrmargin) {
981 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
982 p->offset = p->rmargin;
983 p->rmargin = p->maxrmargin;
984 term_word(p, title);
985 term_flushln(p);
986 }
987
988 p->rmargin = p->maxrmargin;
989 p->offset = 0;
990 p->flags &= ~TERMP_NOSPACE;
991
992 /*
993 * Groff likes to have some leading spaces before content. Well
994 * that's fine by me.
995 */
996
997 term_vspace(p);
998 term_vspace(p);
999 term_vspace(p);
1000 }