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