Annotation of mandoc/mdocterm.c, Revision 1.22
1.22 ! kristaps 1: /* $Id: mdocterm.c,v 1.21 2009/02/28 21:31:13 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 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
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
1.18 kristaps 19: #include <sys/utsname.h>
20:
1.1 kristaps 21: #include <assert.h>
1.3 kristaps 22: #include <ctype.h>
1.1 kristaps 23: #include <err.h>
24: #include <getopt.h>
1.3 kristaps 25: #include <stdio.h>
1.1 kristaps 26: #include <stdlib.h>
1.3 kristaps 27: #include <string.h>
1.1 kristaps 28:
1.7 kristaps 29: #ifndef __OpenBSD__
1.5 kristaps 30: #include <time.h>
31: #endif
32:
1.2 kristaps 33: #include "mmain.h"
1.1 kristaps 34: #include "term.h"
35:
1.16 kristaps 36: #define TERMSYM_RBRACK "]"
37: #define TERMSYM_LBRACK "["
38: #define TERMSYM_LARROW "<-"
39: #define TERMSYM_RARROW "->"
40: #define TERMSYM_UARROW "^"
1.17 kristaps 41: #define TERMSYM_DARROW "v"
1.16 kristaps 42: #define TERMSYM_LSQUOTE "`"
43: #define TERMSYM_RSQUOTE "\'"
44: #define TERMSYM_SQUOTE "\'"
45: #define TERMSYM_LDQUOTE "``"
46: #define TERMSYM_RDQUOTE "\'\'"
47: #define TERMSYM_DQUOTE "\""
48: #define TERMSYM_LT "<"
49: #define TERMSYM_GT ">"
50: #define TERMSYM_LE "<="
51: #define TERMSYM_GE ">="
52: #define TERMSYM_EQ "=="
53: #define TERMSYM_NEQ "!="
54: #define TERMSYM_ACUTE "\'"
55: #define TERMSYM_GRAVE "`"
56: #define TERMSYM_PI "pi"
57: #define TERMSYM_PLUSMINUS "+="
1.17 kristaps 58: #define TERMSYM_INF "oo"
59: #define TERMSYM_INF2 "infinity"
1.16 kristaps 60: #define TERMSYM_NAN "NaN"
61: #define TERMSYM_BAR "|"
62: #define TERMSYM_BULLET "o"
63:
1.7 kristaps 64: #ifdef __NetBSD__
65: #define xisspace(x) isspace((int)(x))
66: #else
67: #define xisspace(x) isspace((x))
68: #endif
69:
1.3 kristaps 70: enum termstyle {
71: STYLE_CLEAR,
72: STYLE_BOLD,
73: STYLE_UNDERLINE
74: };
75:
76: static void body(struct termp *,
1.12 kristaps 77: struct termpair *,
1.3 kristaps 78: const struct mdoc_meta *,
79: const struct mdoc_node *);
80: static void header(struct termp *,
81: const struct mdoc_meta *);
82: static void footer(struct termp *,
83: const struct mdoc_meta *);
84:
85: static void pword(struct termp *, const char *, size_t);
86: static void pescape(struct termp *,
87: const char *, size_t *, size_t);
1.12 kristaps 88: static void nescape(struct termp *,
89: const char *, size_t);
1.3 kristaps 90: static void chara(struct termp *, char);
1.4 kristaps 91: static void stringa(struct termp *, const char *);
1.3 kristaps 92: static void style(struct termp *, enum termstyle);
93:
94: #ifdef __linux__
95: extern size_t strlcat(char *, const char *, size_t);
96: extern size_t strlcpy(char *, const char *, size_t);
97: #endif
98:
99:
1.1 kristaps 100: int
101: main(int argc, char *argv[])
102: {
1.2 kristaps 103: struct mmain *p;
104: const struct mdoc *mdoc;
1.3 kristaps 105: struct termp termp;
1.2 kristaps 106:
107: p = mmain_alloc();
1.1 kristaps 108:
1.3 kristaps 109: if ( ! mmain_getopt(p, argc, argv, NULL, NULL, NULL, NULL))
1.2 kristaps 110: mmain_exit(p, 1);
1.1 kristaps 111:
1.3 kristaps 112: if (NULL == (mdoc = mmain_mdoc(p)))
113: mmain_exit(p, 1);
114:
1.19 kristaps 115: termp.maxrmargin = 78; /* XXX */
1.3 kristaps 116: termp.rmargin = termp.maxrmargin;
117: termp.maxcols = 1024;
118: termp.offset = termp.col = 0;
119: termp.flags = TERMP_NOSPACE;
120:
121: if (NULL == (termp.buf = malloc(termp.maxcols)))
122: err(1, "malloc");
123:
1.15 kristaps 124: header(&termp, mdoc_meta(mdoc));
1.12 kristaps 125: body(&termp, NULL, mdoc_meta(mdoc), mdoc_node(mdoc));
1.3 kristaps 126: footer(&termp, mdoc_meta(mdoc));
127:
128: free(termp.buf);
129:
130: mmain_exit(p, 0);
131: /* NOTREACHED */
132: }
133:
134:
135: void
136: flushln(struct termp *p)
137: {
138: size_t i, j, vsz, vis, maxvis;
139:
140: /*
141: * First, establish the maximum columns of "visible" content.
142: * This is usually the difference between the right-margin and
143: * an indentation, but can be, for tagged lists or columns, a
144: * small set of values.
145: */
146:
147: assert(p->offset < p->rmargin);
148: maxvis = p->rmargin - p->offset;
149: vis = 0;
150:
151: /*
152: * If in the standard case (left-justified), then begin with our
153: * indentation, otherwise (columns, etc.) just start spitting
154: * out text.
155: */
156:
157: if ( ! (p->flags & TERMP_NOLPAD))
158: /* LINTED */
159: for (j = 0; j < p->offset; j++)
160: putchar(' ');
161:
162: /*
163: * If we're literal, print out verbatim.
164: */
165: if (p->flags & TERMP_LITERAL) {
166: for (i = 0; i < p->col; i++)
167: putchar(p->buf[i]);
168: putchar('\n');
169: p->col = 0;
170: return;
171: }
172:
173: for (i = 0; i < p->col; i++) {
174: /*
175: * Count up visible word characters. Control sequences
176: * (starting with the CSI) aren't counted.
177: */
1.7 kristaps 178: assert( ! xisspace(p->buf[i]));
1.3 kristaps 179:
180: /* LINTED */
181: for (j = i, vsz = 0; j < p->col; j++) {
1.7 kristaps 182: if (xisspace(p->buf[j]))
1.3 kristaps 183: break;
184: else if (27 == p->buf[j]) {
185: assert(j + 4 <= p->col);
186: j += 3;
187: } else
188: vsz++;
189: }
190: assert(vsz > 0);
191:
192: /*
1.22 ! kristaps 193: * If we're breaking normally...
! 194: *
1.3 kristaps 195: * If a word is too long and we're within a line, put it
196: * on the next line. Puke if we're being asked to write
197: * something that will exceed the right margin (i.e.,
1.22 ! kristaps 198: * from a fresh line).
! 199: *
! 200: * If we're not breaking...
! 201: *
! 202: * Don't let the visible size exceed the full margin.
1.3 kristaps 203: */
204:
1.22 ! kristaps 205: if ( ! (TERMP_NOBREAK & p->flags)) {
! 206: if (vis && vis + vsz > maxvis) {
! 207: putchar('\n');
! 208: for (j = 0; j < p->offset; j++)
! 209: putchar(' ');
! 210: vis = 0;
! 211: } else if (vis + vsz > maxvis)
1.3 kristaps 212: errx(1, "word breaks right margin");
1.22 ! kristaps 213: } else if (vis + vsz > p->maxrmargin)
1.3 kristaps 214: errx(1, "word breaks right margin");
215:
216: /*
217: * Write out the word and a trailing space. Omit the
218: * space if we're the last word in the line.
219: */
220:
221: for ( ; i < p->col; i++) {
1.7 kristaps 222: if (xisspace(p->buf[i]))
1.3 kristaps 223: break;
224: putchar(p->buf[i]);
225: }
226: vis += vsz;
227: if (i < p->col) {
228: putchar(' ');
229: vis++;
230: }
231: }
232:
1.22 ! kristaps 233: if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) {
! 234: putchar('\n');
! 235: for (i = 0; i < p->rmargin; i++)
! 236: putchar(' ');
! 237: p->col = 0;
! 238: return;
! 239: }
! 240:
1.3 kristaps 241: /*
242: * If we're not to right-marginalise it (newline), then instead
243: * pad to the right margin and stay off.
244: */
245:
246: if (p->flags & TERMP_NOBREAK) {
1.22 ! kristaps 247: for ( ; vis < maxvis; vis++)
! 248: putchar(' ');
1.3 kristaps 249: } else
250: putchar('\n');
251:
252: p->col = 0;
253: }
254:
255:
256: void
257: newln(struct termp *p)
258: {
1.1 kristaps 259:
1.3 kristaps 260: /*
261: * A newline only breaks an existing line; it won't assert
262: * vertical space.
263: */
264: p->flags |= TERMP_NOSPACE;
1.12 kristaps 265: if (0 == p->col) {
266: p->flags &= ~TERMP_NOLPAD;
1.3 kristaps 267: return;
1.12 kristaps 268: }
1.3 kristaps 269: flushln(p);
1.11 kristaps 270: p->flags &= ~TERMP_NOLPAD;
1.3 kristaps 271: }
272:
273:
274: void
275: vspace(struct termp *p)
276: {
277:
278: /*
279: * Asserts a vertical space (a full, empty line-break between
280: * lines).
281: */
282: newln(p);
283: putchar('\n');
284: }
285:
286:
287: static void
1.4 kristaps 288: stringa(struct termp *p, const char *s)
289: {
290:
291: /* XXX - speed up if not passing to chara. */
292: for ( ; *s; s++)
293: chara(p, *s);
294: }
295:
296:
297: static void
1.3 kristaps 298: chara(struct termp *p, char c)
299: {
300:
1.16 kristaps 301: /*
302: * Insert a single character into the line-buffer. If the
303: * buffer's space is exceeded, then allocate more space.
304: */
305: if (p->col + 1 >= p->maxcols) {
306: p->buf = realloc(p->buf, p->maxcols * 2);
307: if (NULL == p->buf)
308: err(1, "malloc");
309: p->maxcols *= 2;
310: }
1.3 kristaps 311: p->buf[(p->col)++] = c;
312: }
313:
314:
315: static void
316: style(struct termp *p, enum termstyle esc)
317: {
318:
319: if (p->col + 4 >= p->maxcols)
320: errx(1, "line overrun");
321:
322: p->buf[(p->col)++] = 27;
323: p->buf[(p->col)++] = '[';
324: switch (esc) {
325: case (STYLE_CLEAR):
326: p->buf[(p->col)++] = '0';
327: break;
328: case (STYLE_BOLD):
329: p->buf[(p->col)++] = '1';
330: break;
331: case (STYLE_UNDERLINE):
332: p->buf[(p->col)++] = '4';
333: break;
334: default:
335: abort();
336: /* NOTREACHED */
337: }
338: p->buf[(p->col)++] = 'm';
339: }
340:
341:
342: static void
1.12 kristaps 343: nescape(struct termp *p, const char *word, size_t len)
344: {
345:
346: switch (len) {
1.16 kristaps 347: case (1):
348: if ('q' == word[0])
349: stringa(p, TERMSYM_DQUOTE);
350: break;
1.12 kristaps 351: case (2):
352: if ('r' == word[0] && 'B' == word[1])
1.16 kristaps 353: stringa(p, TERMSYM_RBRACK);
1.12 kristaps 354: else if ('l' == word[0] && 'B' == word[1])
1.16 kristaps 355: stringa(p, TERMSYM_LBRACK);
1.17 kristaps 356: else if ('l' == word[0] && 'q' == word[1])
357: stringa(p, TERMSYM_LDQUOTE);
358: else if ('r' == word[0] && 'q' == word[1])
359: stringa(p, TERMSYM_RDQUOTE);
360: else if ('o' == word[0] && 'q' == word[1])
361: stringa(p, TERMSYM_LSQUOTE);
362: else if ('a' == word[0] && 'q' == word[1])
363: stringa(p, TERMSYM_RSQUOTE);
1.12 kristaps 364: else if ('<' == word[0] && '-' == word[1])
1.16 kristaps 365: stringa(p, TERMSYM_LARROW);
1.12 kristaps 366: else if ('-' == word[0] && '>' == word[1])
1.16 kristaps 367: stringa(p, TERMSYM_RARROW);
1.12 kristaps 368: else if ('b' == word[0] && 'u' == word[1])
1.16 kristaps 369: stringa(p, TERMSYM_BULLET);
370: else if ('<' == word[0] && '=' == word[1])
371: stringa(p, TERMSYM_LE);
372: else if ('>' == word[0] && '=' == word[1])
373: stringa(p, TERMSYM_GE);
1.17 kristaps 374: else if ('=' == word[0] && '=' == word[1])
375: stringa(p, TERMSYM_EQ);
376: else if ('+' == word[0] && '-' == word[1])
377: stringa(p, TERMSYM_PLUSMINUS);
1.16 kristaps 378: else if ('u' == word[0] && 'a' == word[1])
379: stringa(p, TERMSYM_UARROW);
1.17 kristaps 380: else if ('d' == word[0] && 'a' == word[1])
381: stringa(p, TERMSYM_DARROW);
1.16 kristaps 382: else if ('a' == word[0] && 'a' == word[1])
383: stringa(p, TERMSYM_ACUTE);
384: else if ('g' == word[0] && 'a' == word[1])
385: stringa(p, TERMSYM_GRAVE);
1.17 kristaps 386: else if ('!' == word[0] && '=' == word[1])
1.16 kristaps 387: stringa(p, TERMSYM_NEQ);
1.17 kristaps 388: else if ('i' == word[0] && 'f' == word[1])
389: stringa(p, TERMSYM_INF);
390: else if ('n' == word[0] && 'a' == word[1])
391: stringa(p, TERMSYM_NAN);
392: else if ('b' == word[0] && 'a' == word[1])
393: stringa(p, TERMSYM_BAR);
394:
395: /* Deprecated forms. */
396: else if ('B' == word[0] && 'a' == word[1])
397: stringa(p, TERMSYM_BAR);
398: else if ('I' == word[0] && 'f' == word[1])
399: stringa(p, TERMSYM_INF2);
400: else if ('G' == word[0] && 'e' == word[1])
401: stringa(p, TERMSYM_GE);
402: else if ('G' == word[0] && 't' == word[1])
403: stringa(p, TERMSYM_GT);
404: else if ('L' == word[0] && 'e' == word[1])
405: stringa(p, TERMSYM_LE);
406: else if ('L' == word[0] && 'q' == word[1])
407: stringa(p, TERMSYM_LDQUOTE);
1.16 kristaps 408: else if ('L' == word[0] && 't' == word[1])
409: stringa(p, TERMSYM_LT);
1.17 kristaps 410: else if ('N' == word[0] && 'a' == word[1])
411: stringa(p, TERMSYM_NAN);
412: else if ('N' == word[0] && 'e' == word[1])
413: stringa(p, TERMSYM_NEQ);
414: else if ('P' == word[0] && 'i' == word[1])
415: stringa(p, TERMSYM_PI);
1.16 kristaps 416: else if ('P' == word[0] && 'm' == word[1])
417: stringa(p, TERMSYM_PLUSMINUS);
1.17 kristaps 418: else if ('R' == word[0] && 'q' == word[1])
419: stringa(p, TERMSYM_RDQUOTE);
1.12 kristaps 420: break;
421: default:
422: break;
423: }
424: }
425:
426:
427: static void
1.3 kristaps 428: pescape(struct termp *p, const char *word, size_t *i, size_t len)
429: {
1.12 kristaps 430: size_t j;
1.3 kristaps 431:
432: (*i)++;
433: assert(*i < len);
434:
1.16 kristaps 435: /*
436: * Handle an escape sequence. This must manage both groff-style
437: * escapes and mdoc-style escapes.
438: */
439:
1.3 kristaps 440: if ('(' == word[*i]) {
441: /* Two-character escapes. */
442: (*i)++;
443: assert(*i + 1 < len);
1.12 kristaps 444: nescape(p, &word[*i], 2);
1.3 kristaps 445: (*i)++;
446: return;
447:
1.16 kristaps 448: } else if ('*' == word[*i]) {
449: (*i)++;
450: assert(*i < len);
451: switch (word[*i]) {
452: case ('('):
453: (*i)++;
454: assert(*i + 1 < len);
455: nescape(p, &word[*i], 2);
456: (*i)++;
457: return;
458: default:
459: break;
460: }
461: nescape(p, &word[*i], 1);
462: return;
463:
1.3 kristaps 464: } else if ('[' != word[*i]) {
465: /* One-character escapes. */
466: switch (word[*i]) {
467: case ('\\'):
468: /* FALLTHROUGH */
469: case ('\''):
470: /* FALLTHROUGH */
471: case ('`'):
472: /* FALLTHROUGH */
473: case ('-'):
474: /* FALLTHROUGH */
1.13 kristaps 475: case (' '):
476: /* FALLTHROUGH */
1.3 kristaps 477: case ('.'):
478: chara(p, word[*i]);
1.21 kristaps 479: break;
480: case ('e'):
481: chara(p, '\\');
482: break;
1.1 kristaps 483: default:
1.3 kristaps 484: break;
485: }
486: return;
487: }
1.12 kristaps 488:
489: (*i)++;
490: for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++)
491: /* Loop... */ ;
492:
493: nescape(p, &word[*i - j], j);
1.3 kristaps 494: }
495:
496:
497: static void
498: pword(struct termp *p, const char *word, size_t len)
499: {
500: size_t i;
501:
502: /*assert(len > 0);*/ /* Can be, if literal. */
503:
1.16 kristaps 504: /*
505: * Handle pwords, partial words, which may be either a single
506: * word or a phrase that cannot be broken down (such as a
507: * literal string). This handles word styling.
508: */
509:
1.3 kristaps 510: if ( ! (p->flags & TERMP_NOSPACE) &&
511: ! (p->flags & TERMP_LITERAL))
512: chara(p, ' ');
513:
1.13 kristaps 514: if ( ! (p->flags & TERMP_NONOSPACE))
515: p->flags &= ~TERMP_NOSPACE;
1.3 kristaps 516:
1.16 kristaps 517: /*
518: * XXX - if literal and underlining, this will underline the
519: * spaces between literal words.
520: */
521:
1.3 kristaps 522: if (p->flags & TERMP_BOLD)
523: style(p, STYLE_BOLD);
524: if (p->flags & TERMP_UNDERLINE)
525: style(p, STYLE_UNDERLINE);
526:
527: for (i = 0; i < len; i++) {
528: if ('\\' == word[i]) {
529: pescape(p, word, &i, len);
530: continue;
531: }
532: chara(p, word[i]);
533: }
534:
535: if (p->flags & TERMP_BOLD ||
536: p->flags & TERMP_UNDERLINE)
537: style(p, STYLE_CLEAR);
538: }
539:
540:
541: void
542: word(struct termp *p, const char *word)
543: {
544: size_t i, j, len;
545:
1.16 kristaps 546: /*
547: * Break apart a word into tokens. If we're a literal word,
548: * then don't. This doesn't handle zero-length words (there
549: * should be none) and makes sure that pword doesn't get spaces
550: * or nil words unless literal.
551: */
552:
1.3 kristaps 553: if (p->flags & TERMP_LITERAL) {
554: pword(p, word, strlen(word));
555: return;
556: }
557:
558: len = strlen(word);
559: assert(len > 0);
560:
561: if (mdoc_isdelim(word)) {
562: if ( ! (p->flags & TERMP_IGNDELIM))
563: p->flags |= TERMP_NOSPACE;
564: p->flags &= ~TERMP_IGNDELIM;
565: }
566:
567: /* LINTED */
568: for (j = i = 0; i < len; i++) {
1.7 kristaps 569: if ( ! xisspace(word[i])) {
1.3 kristaps 570: j++;
571: continue;
1.20 kristaps 572: }
573:
574: /* Escaped spaces don't delimit... */
575: if (i > 0 && xisspace(word[i]) && '\\' == word[i - 1]) {
576: j++;
577: continue;
1.1 kristaps 578: }
1.20 kristaps 579:
1.3 kristaps 580: if (0 == j)
581: continue;
582: assert(i >= j);
583: pword(p, &word[i - j], j);
584: j = 0;
585: }
586: if (j > 0) {
587: assert(i >= j);
588: pword(p, &word[i - j], j);
589: }
590: }
591:
592:
593: static void
1.12 kristaps 594: body(struct termp *p, struct termpair *ppair,
595: const struct mdoc_meta *meta,
1.3 kristaps 596: const struct mdoc_node *node)
597: {
598: int dochild;
1.9 kristaps 599: struct termpair pair;
1.3 kristaps 600:
1.16 kristaps 601: /*
602: * This is the main function for printing out nodes. It's
603: * constituted of PRE and POST functions, which correspond to
604: * prefix and infix processing.
605: */
606:
1.3 kristaps 607: /* Pre-processing. */
608:
609: dochild = 1;
1.12 kristaps 610: pair.ppair = ppair;
1.9 kristaps 611: pair.type = 0;
1.11 kristaps 612: pair.offset = pair.rmargin = 0;
1.10 kristaps 613: pair.flag = 0;
1.12 kristaps 614: pair.count = 0;
1.3 kristaps 615:
616: if (MDOC_TEXT != node->type) {
617: if (termacts[node->tok].pre)
1.9 kristaps 618: if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node))
1.3 kristaps 619: dochild = 0;
620: } else /* MDOC_TEXT == node->type */
621: word(p, node->data.text.string);
622:
623: /* Children. */
624:
1.10 kristaps 625: if (TERMPAIR_FLAG & pair.type)
626: p->flags |= pair.flag;
1.9 kristaps 627:
1.3 kristaps 628: if (dochild && node->child)
1.12 kristaps 629: body(p, &pair, meta, node->child);
1.3 kristaps 630:
1.10 kristaps 631: if (TERMPAIR_FLAG & pair.type)
632: p->flags &= ~pair.flag;
1.9 kristaps 633:
1.3 kristaps 634: /* Post-processing. */
635:
636: if (MDOC_TEXT != node->type)
637: if (termacts[node->tok].post)
1.9 kristaps 638: (*termacts[node->tok].post)(p, &pair, meta, node);
1.3 kristaps 639:
640: /* Siblings. */
1.1 kristaps 641:
1.3 kristaps 642: if (node->next)
1.12 kristaps 643: body(p, ppair, meta, node->next);
1.3 kristaps 644: }
645:
646:
647: static void
648: footer(struct termp *p, const struct mdoc_meta *meta)
649: {
650: struct tm *tm;
651: char *buf, *os;
652:
653: if (NULL == (buf = malloc(p->rmargin)))
654: err(1, "malloc");
655: if (NULL == (os = malloc(p->rmargin)))
656: err(1, "malloc");
657:
658: tm = localtime(&meta->date);
659:
1.7 kristaps 660: #ifdef __OpenBSD__
661: if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
662: #else
1.3 kristaps 663: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
664: #endif
665: err(1, "strftime");
666:
1.15 kristaps 667: (void)strlcpy(os, meta->os, p->rmargin);
1.3 kristaps 668:
1.16 kristaps 669: /*
670: * This is /slightly/ different from regular groff output
671: * because we don't have page numbers. Print the following:
672: *
673: * OS MDOCDATE
674: */
675:
1.15 kristaps 676: vspace(p);
1.3 kristaps 677:
1.15 kristaps 678: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
679: p->rmargin = p->maxrmargin - strlen(buf);
680: p->offset = 0;
1.3 kristaps 681:
1.15 kristaps 682: word(p, os);
683: flushln(p);
1.3 kristaps 684:
1.15 kristaps 685: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
686: p->offset = p->rmargin;
687: p->rmargin = p->maxrmargin;
688: p->flags &= ~TERMP_NOBREAK;
689:
690: word(p, buf);
691: flushln(p);
1.1 kristaps 692:
1.3 kristaps 693: free(buf);
694: free(os);
1.1 kristaps 695: }
696:
697:
1.3 kristaps 698: static void
699: header(struct termp *p, const struct mdoc_meta *meta)
700: {
1.18 kristaps 701: char *buf, *title, *bufp, *vbuf;
1.13 kristaps 702: const char *pp;
1.18 kristaps 703: struct utsname uts;
704:
705: p->rmargin = p->maxrmargin;
706: p->offset = 0;
1.3 kristaps 707:
708: if (NULL == (buf = malloc(p->rmargin)))
709: err(1, "malloc");
710: if (NULL == (title = malloc(p->rmargin)))
711: err(1, "malloc");
1.18 kristaps 712: if (NULL == (vbuf = malloc(p->rmargin)))
713: err(1, "malloc");
1.3 kristaps 714:
1.18 kristaps 715: if (NULL == (pp = mdoc_vol2a(meta->vol))) {
1.3 kristaps 716: switch (meta->msec) {
717: case (MSEC_1):
718: /* FALLTHROUGH */
719: case (MSEC_6):
720: /* FALLTHROUGH */
721: case (MSEC_7):
722: pp = mdoc_vol2a(VOL_URM);
723: break;
724: case (MSEC_8):
725: pp = mdoc_vol2a(VOL_SMM);
726: break;
727: case (MSEC_2):
728: /* FALLTHROUGH */
729: case (MSEC_3):
730: /* FALLTHROUGH */
731: case (MSEC_4):
732: /* FALLTHROUGH */
733: case (MSEC_5):
734: pp = mdoc_vol2a(VOL_PRM);
735: break;
736: case (MSEC_9):
737: pp = mdoc_vol2a(VOL_KM);
738: break;
739: default:
740: break;
741: }
1.18 kristaps 742: }
743: vbuf[0] = 0;
744:
745: if (pp) {
746: if (-1 == uname(&uts))
747: err(1, "uname");
748: (void)strlcat(vbuf, uts.sysname, p->rmargin);
749: (void)strlcat(vbuf, " ", p->rmargin);
750: } else if (NULL == (pp = mdoc_msec2a(meta->msec)))
751: pp = mdoc_msec2a(MSEC_local);
752:
753: (void)strlcat(vbuf, pp, p->rmargin);
1.3 kristaps 754:
1.16 kristaps 755: /*
756: * The header is strange. It has three components, which are
757: * really two with the first duplicated. It goes like this:
758: *
759: * IDENTIFIER TITLE IDENTIFIER
760: *
761: * The IDENTIFIER is NAME(SECTION), which is the command-name
762: * (if given, or "unknown" if not) followed by the manual page
763: * section. These are given in `Dt'. The TITLE is a free-form
764: * string depending on the manual volume. If not specified, it
765: * switches on the manual section.
766: */
767:
1.13 kristaps 768: if (mdoc_arch2a(meta->arch))
1.16 kristaps 769: (void)snprintf(buf, p->rmargin, "%s (%s)",
1.18 kristaps 770: vbuf, mdoc_arch2a(meta->arch));
1.13 kristaps 771: else
1.18 kristaps 772: (void)strlcpy(buf, vbuf, p->rmargin);
1.13 kristaps 773:
774: pp = mdoc_msec2a(meta->msec);
775:
776: (void)snprintf(title, p->rmargin, "%s(%s)",
777: meta->title, pp ? pp : "");
778:
1.16 kristaps 779: for (bufp = title; *bufp; bufp++)
780: *bufp = toupper(*bufp);
781:
1.13 kristaps 782: p->offset = 0;
783: p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
1.15 kristaps 784: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.3 kristaps 785:
1.13 kristaps 786: word(p, title);
787: flushln(p);
1.3 kristaps 788:
1.15 kristaps 789: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.13 kristaps 790: p->offset = p->rmargin;
1.15 kristaps 791: p->rmargin = p->maxrmargin - strlen(title);
1.3 kristaps 792:
1.13 kristaps 793: word(p, buf);
794: flushln(p);
1.3 kristaps 795:
1.13 kristaps 796: p->offset = p->rmargin;
797: p->rmargin = p->maxrmargin;
798: p->flags &= ~TERMP_NOBREAK;
1.15 kristaps 799: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.3 kristaps 800:
1.13 kristaps 801: word(p, title);
802: flushln(p);
1.3 kristaps 803:
1.13 kristaps 804: p->rmargin = p->maxrmargin;
805: p->offset = 0;
806: p->flags &= ~TERMP_NOSPACE;
1.3 kristaps 807:
808: free(title);
1.18 kristaps 809: free(vbuf);
1.3 kristaps 810: free(buf);
811: }
CVSweb