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