Annotation of mandoc/man_term.c, Revision 1.20
1.20 ! kristaps 1: /* $Id: man_term.c,v 1.19 2009/08/13 11:45:29 kristaps Exp $ */
1.1 kristaps 2: /*
1.9 kristaps 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.8 kristaps 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.8 kristaps 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.
1.1 kristaps 16: */
17: #include <assert.h>
1.18 kristaps 18: #include <ctype.h>
1.1 kristaps 19: #include <err.h>
20: #include <stdio.h>
21: #include <stdlib.h>
22: #include <string.h>
23:
24: #include "term.h"
25: #include "man.h"
26:
1.18 kristaps 27: #define INDENT 7
28: #define HALFINDENT 3
29:
1.2 kristaps 30: #ifdef __linux__
31: extern size_t strlcpy(char *, const char *, size_t);
32: extern size_t strlcat(char *, const char *, size_t);
33: #endif
34:
1.19 kristaps 35: #define MANT_LITERAL (1 << 0)
36:
1.1 kristaps 37: #define DECL_ARGS struct termp *p, \
1.19 kristaps 38: int *fl, \
1.1 kristaps 39: const struct man_node *n, \
40: const struct man_meta *m
41:
42: struct termact {
43: int (*pre)(DECL_ARGS);
44: void (*post)(DECL_ARGS);
45: };
46:
47: static int pre_B(DECL_ARGS);
1.3 kristaps 48: static int pre_BI(DECL_ARGS);
49: static int pre_BR(DECL_ARGS);
1.19 kristaps 50: static int pre_HP(DECL_ARGS);
1.1 kristaps 51: static int pre_I(DECL_ARGS);
1.3 kristaps 52: static int pre_IB(DECL_ARGS);
1.4 kristaps 53: static int pre_IP(DECL_ARGS);
1.3 kristaps 54: static int pre_IR(DECL_ARGS);
1.1 kristaps 55: static int pre_PP(DECL_ARGS);
1.3 kristaps 56: static int pre_RB(DECL_ARGS);
57: static int pre_RI(DECL_ARGS);
1.1 kristaps 58: static int pre_SH(DECL_ARGS);
59: static int pre_SS(DECL_ARGS);
60: static int pre_TP(DECL_ARGS);
1.19 kristaps 61: static int pre_br(DECL_ARGS);
62: static int pre_fi(DECL_ARGS);
63: static int pre_nf(DECL_ARGS);
64: static int pre_r(DECL_ARGS);
65: static int pre_sp(DECL_ARGS);
1.1 kristaps 66:
67: static void post_B(DECL_ARGS);
68: static void post_I(DECL_ARGS);
1.20 ! kristaps 69: static void post_HP(DECL_ARGS);
1.1 kristaps 70: static void post_SH(DECL_ARGS);
71: static void post_SS(DECL_ARGS);
1.19 kristaps 72: static void post_i(DECL_ARGS);
1.1 kristaps 73:
74: static const struct termact termacts[MAN_MAX] = {
1.15 kristaps 75: { pre_br, NULL }, /* br */
1.1 kristaps 76: { NULL, NULL }, /* TH */
77: { pre_SH, post_SH }, /* SH */
78: { pre_SS, post_SS }, /* SS */
79: { pre_TP, NULL }, /* TP */
80: { pre_PP, NULL }, /* LP */
81: { pre_PP, NULL }, /* PP */
82: { pre_PP, NULL }, /* P */
1.4 kristaps 83: { pre_IP, NULL }, /* IP */
1.20 ! kristaps 84: { pre_HP, post_HP }, /* HP */
1.1 kristaps 85: { NULL, NULL }, /* SM */
86: { pre_B, post_B }, /* SB */
1.3 kristaps 87: { pre_BI, NULL }, /* BI */
88: { pre_IB, NULL }, /* IB */
89: { pre_BR, NULL }, /* BR */
90: { pre_RB, NULL }, /* RB */
1.1 kristaps 91: { NULL, NULL }, /* R */
92: { pre_B, post_B }, /* B */
93: { pre_I, post_I }, /* I */
1.3 kristaps 94: { pre_IR, NULL }, /* IR */
95: { pre_RI, NULL }, /* RI */
1.19 kristaps 96: { NULL, NULL }, /* na */ /* TODO: document that has no effect */
97: { pre_I, post_i }, /* i */
98: { pre_sp, NULL }, /* sp */
99: { pre_nf, NULL }, /* nf */
100: { pre_fi, NULL }, /* fi */
101: { pre_r, NULL }, /* r */
1.1 kristaps 102: };
103:
104: static void print_head(struct termp *,
105: const struct man_meta *);
106: static void print_body(DECL_ARGS);
107: static void print_node(DECL_ARGS);
108: static void print_foot(struct termp *,
109: const struct man_meta *);
1.18 kristaps 110: static void fmt_block_vspace(struct termp *,
111: const struct man_node *);
112: static int arg_width(const struct man_node *);
1.1 kristaps 113:
114:
115: int
116: man_run(struct termp *p, const struct man *m)
117: {
1.19 kristaps 118: int fl;
1.1 kristaps 119:
120: print_head(p, man_meta(m));
121: p->flags |= TERMP_NOSPACE;
1.14 kristaps 122: assert(man_node(m));
123: assert(MAN_ROOT == man_node(m)->type);
1.19 kristaps 124:
125: fl = 0;
1.14 kristaps 126: if (man_node(m)->child)
1.19 kristaps 127: print_body(p, &fl, man_node(m)->child, man_meta(m));
1.1 kristaps 128: print_foot(p, man_meta(m));
129:
130: return(1);
131: }
132:
133:
1.18 kristaps 134: static void
135: fmt_block_vspace(struct termp *p, const struct man_node *n)
136: {
137: term_newln(p);
138:
139: if (NULL == n->prev)
140: return;
141:
142: if (MAN_SS == n->prev->tok)
143: return;
144: if (MAN_SH == n->prev->tok)
145: return;
146:
147: term_vspace(p);
148: }
149:
150:
151: static int
152: arg_width(const struct man_node *n)
153: {
154: int i, len;
155: const char *p;
156:
157: assert(MAN_TEXT == n->type);
158: assert(n->string);
159:
160: p = n->string;
161:
162: if (0 == (len = (int)strlen(p)))
163: return(-1);
164:
165: for (i = 0; i < len; i++)
166: if ( ! isdigit((u_char)p[i]))
167: break;
168:
169: if (i == len - 1) {
170: if ('n' == p[len - 1] || 'm' == p[len - 1])
171: return(atoi(p));
172: } else if (i == len)
173: return(atoi(p));
174:
175: return(-1);
176: }
177:
178:
1.3 kristaps 179: /* ARGSUSED */
1.1 kristaps 180: static int
181: pre_I(DECL_ARGS)
182: {
183:
184: p->flags |= TERMP_UNDER;
185: return(1);
186: }
187:
188:
1.3 kristaps 189: /* ARGSUSED */
1.19 kristaps 190: static int
191: pre_r(DECL_ARGS)
192: {
193:
194: p->flags &= ~TERMP_UNDER;
195: p->flags &= ~TERMP_BOLD;
196: return(1);
197: }
198:
199:
200: /* ARGSUSED */
201: static void
202: post_i(DECL_ARGS)
203: {
204:
205: if (n->nchild)
206: p->flags &= ~TERMP_UNDER;
207: }
208:
209:
210: /* ARGSUSED */
1.1 kristaps 211: static void
212: post_I(DECL_ARGS)
213: {
214:
215: p->flags &= ~TERMP_UNDER;
216: }
217:
218:
1.3 kristaps 219: /* ARGSUSED */
220: static int
1.19 kristaps 221: pre_fi(DECL_ARGS)
222: {
223:
224: *fl &= ~MANT_LITERAL;
225: return(1);
226: }
227:
228:
229: /* ARGSUSED */
230: static int
231: pre_nf(DECL_ARGS)
232: {
233:
234: term_newln(p);
235: *fl |= MANT_LITERAL;
236: return(1);
237: }
238:
239:
240: /* ARGSUSED */
241: static int
1.3 kristaps 242: pre_IR(DECL_ARGS)
243: {
244: const struct man_node *nn;
245: int i;
246:
247: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
248: if ( ! (i % 2))
249: p->flags |= TERMP_UNDER;
1.4 kristaps 250: if (i > 0)
251: p->flags |= TERMP_NOSPACE;
1.19 kristaps 252: print_node(p, fl, nn, m);
1.3 kristaps 253: if ( ! (i % 2))
254: p->flags &= ~TERMP_UNDER;
255: }
256: return(0);
257: }
258:
259:
260: /* ARGSUSED */
261: static int
262: pre_IB(DECL_ARGS)
263: {
264: const struct man_node *nn;
265: int i;
266:
267: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
268: p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
1.4 kristaps 269: if (i > 0)
270: p->flags |= TERMP_NOSPACE;
1.19 kristaps 271: print_node(p, fl, nn, m);
1.3 kristaps 272: p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
273: }
274: return(0);
275: }
276:
277:
278: /* ARGSUSED */
279: static int
280: pre_RB(DECL_ARGS)
281: {
282: const struct man_node *nn;
283: int i;
284:
285: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
286: if (i % 2)
287: p->flags |= TERMP_BOLD;
1.4 kristaps 288: if (i > 0)
289: p->flags |= TERMP_NOSPACE;
1.19 kristaps 290: print_node(p, fl, nn, m);
1.3 kristaps 291: if (i % 2)
292: p->flags &= ~TERMP_BOLD;
293: }
294: return(0);
295: }
296:
297:
298: /* ARGSUSED */
299: static int
300: pre_RI(DECL_ARGS)
301: {
302: const struct man_node *nn;
303: int i;
304:
305: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
306: if ( ! (i % 2))
307: p->flags |= TERMP_UNDER;
1.4 kristaps 308: if (i > 0)
309: p->flags |= TERMP_NOSPACE;
1.19 kristaps 310: print_node(p, fl, nn, m);
1.3 kristaps 311: if ( ! (i % 2))
312: p->flags &= ~TERMP_UNDER;
313: }
314: return(0);
315: }
316:
317:
318: /* ARGSUSED */
319: static int
320: pre_BR(DECL_ARGS)
321: {
322: const struct man_node *nn;
323: int i;
324:
325: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
326: if ( ! (i % 2))
327: p->flags |= TERMP_BOLD;
1.4 kristaps 328: if (i > 0)
329: p->flags |= TERMP_NOSPACE;
1.19 kristaps 330: print_node(p, fl, nn, m);
1.3 kristaps 331: if ( ! (i % 2))
332: p->flags &= ~TERMP_BOLD;
333: }
334: return(0);
335: }
336:
337:
338: /* ARGSUSED */
339: static int
340: pre_BI(DECL_ARGS)
341: {
342: const struct man_node *nn;
343: int i;
344:
345: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
346: p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
1.4 kristaps 347: if (i > 0)
348: p->flags |= TERMP_NOSPACE;
1.19 kristaps 349: print_node(p, fl, nn, m);
1.3 kristaps 350: p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
351: }
352: return(0);
353: }
354:
355:
356: /* ARGSUSED */
1.1 kristaps 357: static int
358: pre_B(DECL_ARGS)
359: {
360:
361: p->flags |= TERMP_BOLD;
362: return(1);
363: }
364:
365:
1.3 kristaps 366: /* ARGSUSED */
1.1 kristaps 367: static void
368: post_B(DECL_ARGS)
369: {
370:
371: p->flags &= ~TERMP_BOLD;
372: }
373:
374:
1.3 kristaps 375: /* ARGSUSED */
1.1 kristaps 376: static int
1.19 kristaps 377: pre_sp(DECL_ARGS)
378: {
379: int i, len;
380:
381: if (NULL == n->child) {
382: term_vspace(p);
383: return(0);
384: }
385:
386: len = atoi(n->child->string);
387: if (0 == len)
388: term_newln(p);
389: for (i = 0; i < len; i++)
390: term_vspace(p);
391:
392: return(0);
393: }
394:
395:
396: /* ARGSUSED */
397: static int
1.15 kristaps 398: pre_br(DECL_ARGS)
399: {
400:
401: term_newln(p);
402: return(0);
403: }
404:
405:
406: /* ARGSUSED */
407: static int
1.19 kristaps 408: pre_HP(DECL_ARGS)
409: {
410:
1.20 ! kristaps 411: switch (n->type) {
! 412: case (MAN_BLOCK):
! 413: fmt_block_vspace(p, n);
! 414: break;
! 415: case (MAN_BODY):
! 416: p->flags |= TERMP_NOBREAK;
! 417: p->flags |= TERMP_TWOSPACE;
! 418: p->offset = INDENT;
! 419: p->rmargin = INDENT * 2;
! 420: break;
! 421: default:
! 422: return(0);
! 423: }
! 424:
1.19 kristaps 425: return(1);
426: }
427:
428:
429: /* ARGSUSED */
1.20 ! kristaps 430: static void
! 431: post_HP(DECL_ARGS)
! 432: {
! 433:
! 434: switch (n->type) {
! 435: case (MAN_BODY):
! 436: term_flushln(p);
! 437: p->flags &= ~TERMP_NOBREAK;
! 438: p->flags &= ~TERMP_TWOSPACE;
! 439: p->offset = INDENT;
! 440: p->rmargin = p->maxrmargin;
! 441: break;
! 442: default:
! 443: break;
! 444: }
! 445: }
! 446:
! 447:
! 448: /* ARGSUSED */
1.19 kristaps 449: static int
1.1 kristaps 450: pre_PP(DECL_ARGS)
451: {
452:
1.19 kristaps 453: switch (n->type) {
454: case (MAN_BLOCK):
455: fmt_block_vspace(p, n);
456: break;
457: default:
458: p->offset = INDENT;
459: break;
460: }
461:
462: return(1);
1.1 kristaps 463: }
464:
465:
1.3 kristaps 466: /* ARGSUSED */
1.1 kristaps 467: static int
1.4 kristaps 468: pre_IP(DECL_ARGS)
469: {
1.19 kristaps 470: /* TODO */
471: #if 0
1.4 kristaps 472: const struct man_node *nn;
1.18 kristaps 473: size_t offs, sv;
474: int ival;
475:
476: fmt_block_vspace(p, n);
477:
478: p->flags |= TERMP_NOSPACE;
1.4 kristaps 479:
1.18 kristaps 480: sv = p->offset;
1.4 kristaps 481: p->offset = INDENT;
482:
1.18 kristaps 483: if (NULL == n->child)
1.4 kristaps 484: return(1);
485:
1.18 kristaps 486: p->flags |= TERMP_NOBREAK;
487:
488: offs = sv;
489:
490: /*
491: * If the last token is number-looking (3m, 3n, 3) then
492: * interpret it as the width specifier, else we stick with the
493: * prior saved offset. XXX - obviously not documented.
494: */
495: for (nn = n->child; nn; nn = nn->next) {
496: if (NULL == nn->next) {
497: ival = arg_width(nn);
498: if (ival >= 0) {
499: offs = (size_t)ival;
500: break;
501: }
502: }
1.19 kristaps 503: print_node(p, fl, nn, m);
1.18 kristaps 504: }
505:
506: p->rmargin = p->offset + offs;
507:
508: term_flushln(p);
509:
510: p->offset = offs;
511: p->rmargin = p->maxrmargin;
1.4 kristaps 512:
1.18 kristaps 513: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.12 kristaps 514:
1.4 kristaps 515: return(0);
1.19 kristaps 516: #endif
517: return(1);
1.4 kristaps 518: }
519:
520:
521: /* ARGSUSED */
522: static int
1.1 kristaps 523: pre_TP(DECL_ARGS)
524: {
1.19 kristaps 525: /* TODO */
526: #if 0
1.1 kristaps 527: const struct man_node *nn;
528: size_t offs;
529:
530: term_vspace(p);
1.16 kristaps 531:
1.1 kristaps 532: p->offset = INDENT;
533:
534: if (NULL == (nn = n->child))
535: return(1);
536:
537: if (nn->line == n->line) {
538: if (MAN_TEXT != nn->type)
539: errx(1, "expected text line argument");
1.3 kristaps 540: offs = (size_t)atoi(nn->string);
1.1 kristaps 541: nn = nn->next;
542: } else
543: offs = INDENT;
544:
545: for ( ; nn; nn = nn->next)
1.19 kristaps 546: print_node(p, fl, nn, m);
1.1 kristaps 547:
548: term_flushln(p);
549: p->flags |= TERMP_NOSPACE;
550: p->offset += offs;
551: return(0);
1.19 kristaps 552: #endif
553: return(1);
1.1 kristaps 554: }
555:
556:
1.3 kristaps 557: /* ARGSUSED */
1.1 kristaps 558: static int
559: pre_SS(DECL_ARGS)
560: {
561:
1.19 kristaps 562: switch (n->type) {
563: case (MAN_BLOCK):
564: term_newln(p);
565: if (n->prev)
566: term_vspace(p);
567: break;
568: case (MAN_HEAD):
569: p->flags |= TERMP_BOLD;
570: p->offset = HALFINDENT;
571: break;
572: default:
573: p->offset = INDENT;
574: break;
575: }
576:
1.1 kristaps 577: return(1);
578: }
579:
580:
1.3 kristaps 581: /* ARGSUSED */
1.1 kristaps 582: static void
583: post_SS(DECL_ARGS)
584: {
585:
1.19 kristaps 586: switch (n->type) {
587: case (MAN_HEAD):
588: term_newln(p);
589: p->flags &= ~TERMP_BOLD;
590: break;
591: default:
592: break;
593: }
1.1 kristaps 594: }
595:
596:
1.3 kristaps 597: /* ARGSUSED */
1.1 kristaps 598: static int
599: pre_SH(DECL_ARGS)
600: {
1.19 kristaps 601: /*
602: * XXX: undocumented: using two `SH' macros in sequence has no
603: * vspace between calls, only a newline.
604: */
605: switch (n->type) {
606: case (MAN_BLOCK):
607: if (n->prev && MAN_SH == n->prev->tok)
608: if (NULL == n->prev->body->child)
609: break;
610: term_vspace(p);
611: break;
612: case (MAN_HEAD):
613: p->flags |= TERMP_BOLD;
614: p->offset = 0;
615: break;
616: case (MAN_BODY):
617: p->offset = INDENT;
618: break;
619: default:
620: break;
621: }
1.1 kristaps 622:
623: return(1);
624: }
625:
626:
1.3 kristaps 627: /* ARGSUSED */
1.1 kristaps 628: static void
629: post_SH(DECL_ARGS)
630: {
631:
1.19 kristaps 632: switch (n->type) {
633: case (MAN_HEAD):
634: term_newln(p);
635: p->flags &= ~TERMP_BOLD;
636: break;
637: case (MAN_BODY):
638: term_newln(p);
639: break;
640: default:
641: break;
642: }
1.1 kristaps 643: }
644:
645:
646: static void
647: print_node(DECL_ARGS)
648: {
1.4 kristaps 649: int c, sz;
1.1 kristaps 650:
651: c = 1;
652:
653: switch (n->type) {
654: case(MAN_TEXT):
1.4 kristaps 655: if (0 == *n->string) {
656: term_vspace(p);
1.1 kristaps 657: break;
658: }
1.4 kristaps 659: /*
660: * Note! This is hacky. Here, we recognise the `\c'
661: * escape embedded in so many -man pages. It's supposed
662: * to remove the subsequent space, so we mark NOSPACE if
663: * it's encountered in the string.
664: */
665: sz = (int)strlen(n->string);
666: term_word(p, n->string);
667: if (sz >= 2 && n->string[sz - 1] == 'c' &&
668: n->string[sz - 2] == '\\')
669: p->flags |= TERMP_NOSPACE;
1.19 kristaps 670: /* FIXME: this means that macro lines are munged! */
671: if (MANT_LITERAL & *fl) {
672: p->flags |= TERMP_NOSPACE;
673: term_flushln(p);
674: }
1.1 kristaps 675: break;
676: default:
1.19 kristaps 677: if (termacts[n->tok].pre)
678: c = (*termacts[n->tok].pre)(p, fl, n, m);
1.1 kristaps 679: break;
680: }
681:
682: if (c && n->child)
1.19 kristaps 683: print_body(p, fl, n->child, m);
1.1 kristaps 684:
1.19 kristaps 685: if (MAN_TEXT != n->type)
1.1 kristaps 686: if (termacts[n->tok].post)
1.19 kristaps 687: (*termacts[n->tok].post)(p, fl, n, m);
1.1 kristaps 688: }
689:
690:
691: static void
692: print_body(DECL_ARGS)
693: {
1.19 kristaps 694:
695: print_node(p, fl, n, m);
1.1 kristaps 696: if ( ! n->next)
697: return;
1.19 kristaps 698: print_body(p, fl, n->next, m);
1.1 kristaps 699: }
700:
701:
702: static void
703: print_foot(struct termp *p, const struct man_meta *meta)
704: {
705: struct tm *tm;
706: char *buf;
707:
708: if (NULL == (buf = malloc(p->rmargin)))
709: err(1, "malloc");
710:
711: tm = localtime(&meta->date);
712:
713: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
714: err(1, "strftime");
715:
716: term_vspace(p);
717:
718: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
719: p->rmargin = p->maxrmargin - strlen(buf);
720: p->offset = 0;
721:
722: if (meta->source)
723: term_word(p, meta->source);
724: if (meta->source)
725: term_word(p, "");
726: term_flushln(p);
727:
728: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
729: p->offset = p->rmargin;
730: p->rmargin = p->maxrmargin;
731: p->flags &= ~TERMP_NOBREAK;
732:
733: term_word(p, buf);
734: term_flushln(p);
735:
736: free(buf);
737: }
738:
739:
740: static void
741: print_head(struct termp *p, const struct man_meta *meta)
742: {
743: char *buf, *title;
744:
745: p->rmargin = p->maxrmargin;
746: p->offset = 0;
747:
748: if (NULL == (buf = malloc(p->rmargin)))
749: err(1, "malloc");
750: if (NULL == (title = malloc(p->rmargin)))
751: err(1, "malloc");
752:
753: if (meta->vol)
754: (void)strlcpy(buf, meta->vol, p->rmargin);
755: else
756: *buf = 0;
757:
758: (void)snprintf(title, p->rmargin, "%s(%d)",
759: meta->title, meta->msec);
760:
761: p->offset = 0;
1.10 kristaps 762: p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
1.1 kristaps 763: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
764:
765: term_word(p, title);
766: term_flushln(p);
767:
768: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
769: p->offset = p->rmargin;
770: p->rmargin = p->maxrmargin - strlen(title);
771:
772: term_word(p, buf);
773: term_flushln(p);
774:
775: p->offset = p->rmargin;
776: p->rmargin = p->maxrmargin;
777: p->flags &= ~TERMP_NOBREAK;
778: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
779:
780: term_word(p, title);
781: term_flushln(p);
782:
783: p->rmargin = p->maxrmargin;
784: p->offset = 0;
785: p->flags &= ~TERMP_NOSPACE;
786:
787: free(title);
788: free(buf);
789: }
790:
CVSweb