Annotation of mandoc/mlg.c, Revision 1.13
1.13 ! kristaps 1: /* $Id: mlg.c,v 1.12 2008/12/05 22:34:30 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: */
19: #include <assert.h>
20: #include <ctype.h>
21: #include <err.h>
1.13 ! kristaps 22: #include <stdarg.h>
1.1 kristaps 23: #include <stdlib.h>
24: #include <stdio.h>
25: #include <string.h>
26:
27: #include "libmdocml.h"
28: #include "private.h"
29: #include "ml.h"
30:
31: /* TODO: literal tokens. */
32:
33: #define COLUMNS 72
34: #define INDENT 4
1.2 kristaps 35: #define MAXINDENT 10
1.1 kristaps 36:
37: enum md_tok {
38: MD_TEXT,
39: MD_INLINE_IN,
40: MD_INLINE_OUT,
41: MD_BLK_IN,
42: MD_BLK_OUT,
43: };
44:
45: struct md_mlg {
46: const struct md_args *args;
47: const struct md_rbuf *rbuf;
48:
49: struct md_mbuf *mbuf;
50: struct rofftree *tree;
51: size_t indent;
52: size_t pos;
53: enum md_tok last;
54: void *arg;
1.11 kristaps 55: struct ml_cbs cbs;
1.1 kristaps 56: int flags;
57: #define ML_OVERRIDE_ONE (1 << 0)
58: #define ML_OVERRIDE_ALL (1 << 1)
1.10 kristaps 59: void *data;
1.1 kristaps 60: };
61:
62:
63: static void mlg_roffmsg(void *arg, enum roffmsg,
64: const char *, const char *, char *);
1.4 kristaps 65: static int mlg_roffhead(void *, const struct tm *,
66: const char *, const char *,
67: const char *, const char *);
1.1 kristaps 68: static int mlg_rofftail(void *);
69: static int mlg_roffin(void *, int, int *, char **);
1.5 kristaps 70: static int mlg_roffdata(void *, int,
71: const char *, char *);
1.1 kristaps 72: static int mlg_roffout(void *, int);
73: static int mlg_roffblkin(void *, int, int *, char **);
74: static int mlg_roffblkout(void *, int);
1.8 kristaps 75: static int mlg_roffspecial(void *, int,
76: const char *, char **);
1.5 kristaps 77: static int mlg_roffblkheadin(void *, int,
78: int *, char **);
1.2 kristaps 79: static int mlg_roffblkheadout(void *, int);
1.5 kristaps 80: static int mlg_roffblkbodyin(void *, int,
81: int *, char **);
1.2 kristaps 82: static int mlg_roffblkbodyout(void *, int);
83:
1.1 kristaps 84: static int mlg_begintag(struct md_mlg *, enum md_ns,
85: int, int *, char **);
86: static int mlg_endtag(struct md_mlg *, enum md_ns, int);
87: static int mlg_indent(struct md_mlg *);
88: static int mlg_newline(struct md_mlg *);
89: static void mlg_mode(struct md_mlg *, enum md_tok);
1.5 kristaps 90: static int mlg_data(struct md_mlg *, int,
91: const char *, char *);
92: static void mlg_err(struct md_mlg *, const char *,
1.13 ! kristaps 93: const char *, const char *, ...);
1.5 kristaps 94: static void mlg_warn(struct md_mlg *, const char *,
1.13 ! kristaps 95: const char *, const char *, ...);
1.5 kristaps 96: static void mlg_msg(struct md_mlg *, enum roffmsg,
97: const char *, const char *, char *);
1.13 ! kristaps 98: static void mlg_vmsg(struct md_mlg *, enum roffmsg,
! 99: const char *, const char *,
! 100: const char *, va_list);
1.1 kristaps 101:
102: #ifdef __linux__
103: extern size_t strlcat(char *, const char *, size_t);
104: extern size_t strlcpy(char *, const char *, size_t);
105: #endif
106:
107:
108: static int
109: mlg_begintag(struct md_mlg *p, enum md_ns ns, int tok,
110: int *argc, char **argv)
111: {
112: ssize_t res;
113:
1.8 kristaps 114: assert(MD_NS_DEFAULT != ns);
115:
116: switch (ns) {
117: case (MD_NS_INLINE):
118: if ( ! (ML_OVERRIDE_ONE & p->flags) &&
119: ! (ML_OVERRIDE_ALL & p->flags) &&
1.9 kristaps 120: p->pos + 11 >= COLUMNS)
1.8 kristaps 121: if ( ! mlg_newline(p))
122: return(0);
123: if (0 != p->pos && (MD_TEXT == p->last ||
124: MD_INLINE_OUT == p->last)
125: && ! (ML_OVERRIDE_ONE & p->flags)
126: && ! (ML_OVERRIDE_ALL & p->flags))
127: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
128: return(0);
129: if (0 == p->pos && ! mlg_indent(p))
130: return(0);
131: mlg_mode(p, MD_INLINE_IN);
132: break;
133: default:
134: if (0 != p->pos) {
135: if ( ! mlg_newline(p))
136: return(0);
137: if ( ! mlg_indent(p))
138: return(0);
139: } else if ( ! mlg_indent(p))
140: return(0);
141: p->indent++;
142: mlg_mode(p, MD_BLK_IN);
143: break;
144: }
1.1 kristaps 145:
146: if ( ! ml_nputs(p->mbuf, "<", 1, &p->pos))
147: return(0);
148:
1.11 kristaps 149: res = (*p->cbs.ml_begintag)(p->mbuf, p->data, p->args, ns, tok,
1.1 kristaps 150: argc, (const char **)argv);
151: if (-1 == res)
152: return(0);
153:
154: assert(res >= 0);
155: p->pos += (size_t)res;
156:
1.8 kristaps 157: if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
158: return(0);
159:
160: switch (ns) {
161: case (MD_NS_INLINE):
162: break;
163: default:
164: if ( ! mlg_newline(p))
165: return(0);
166: break;
167: }
1.1 kristaps 168:
1.8 kristaps 169: return(1);
1.1 kristaps 170: }
171:
172:
173: static int
174: mlg_endtag(struct md_mlg *p, enum md_ns ns, int tok)
175: {
176: ssize_t res;
177:
1.9 kristaps 178: assert(MD_NS_DEFAULT != ns);
179:
180: switch (ns) {
181: case (MD_NS_INLINE):
182: break;
183: default:
184: p->indent--;
185: if (0 != p->pos) {
186: if ( ! mlg_newline(p))
187: return(0);
188: if ( ! mlg_indent(p))
189: return(0);
190: } else if ( ! mlg_indent(p))
191: return(0);
192: break;
193: }
1.1 kristaps 194:
195: if ( ! ml_nputs(p->mbuf, "</", 2, &p->pos))
196: return(0);
197:
1.11 kristaps 198: res = (*p->cbs.ml_endtag)(p->mbuf, p->data, p->args, ns, tok);
1.1 kristaps 199: if (-1 == res)
200: return(0);
201:
202: assert(res >= 0);
203: p->pos += (size_t)res;
204:
1.9 kristaps 205: if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
206: return(0);
207:
208: switch (ns) {
209: case (MD_NS_INLINE):
210: mlg_mode(p, MD_INLINE_OUT);
211: break;
212: default:
213: mlg_mode(p, MD_BLK_OUT);
214: break;
215: }
1.1 kristaps 216:
1.9 kristaps 217: return(1);
1.1 kristaps 218: }
219:
220:
221: static int
222: mlg_indent(struct md_mlg *p)
223: {
224: size_t count;
225:
1.9 kristaps 226: count = p->indent > MAXINDENT ?
227: (size_t)MAXINDENT : p->indent;
1.1 kristaps 228: count *= INDENT;
229:
230: assert(0 == p->pos);
231: return(ml_putchars(p->mbuf, ' ', count, &p->pos));
232: }
233:
234:
235: static int
236: mlg_newline(struct md_mlg *p)
237: {
238:
239: p->pos = 0;
1.11 kristaps 240: return(ml_nputs(p->mbuf, "\n", 1, NULL));
1.1 kristaps 241: }
242:
243:
244: static void
245: mlg_mode(struct md_mlg *p, enum md_tok ns)
246: {
1.3 kristaps 247:
1.1 kristaps 248: p->flags &= ~ML_OVERRIDE_ONE;
249: p->last = ns;
250: }
251:
252:
253: static int
1.5 kristaps 254: mlg_data(struct md_mlg *p, int space, const char *start, char *buf)
1.1 kristaps 255: {
256: size_t sz;
1.5 kristaps 257: int c;
1.1 kristaps 258:
259: assert(p->mbuf);
260: assert(0 != p->indent);
261:
262: if (ML_OVERRIDE_ONE & p->flags ||
263: ML_OVERRIDE_ALL & p->flags)
264: space = 0;
265:
1.8 kristaps 266: sz = strlen(buf);
1.1 kristaps 267:
1.8 kristaps 268: if (0 == p->pos) {
269: if ( ! mlg_indent(p))
270: return(0);
1.1 kristaps 271:
1.8 kristaps 272: c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
1.1 kristaps 273:
1.8 kristaps 274: if (0 == c) {
275: mlg_err(p, start, buf, "bad char sequence");
276: return(0);
277: } else if (c > 1) {
278: mlg_warn(p, start, buf, "bogus char sequence");
279: return(0);
280: } else if (-1 == c)
281: return(0);
1.1 kristaps 282:
1.8 kristaps 283: if (p->indent * INDENT + sz >= COLUMNS)
284: if ( ! mlg_newline(p))
1.1 kristaps 285: return(0);
1.5 kristaps 286:
1.8 kristaps 287: return(1);
288: }
1.5 kristaps 289:
1.8 kristaps 290: if (space && sz + p->pos >= COLUMNS) {
291: if ( ! mlg_newline(p))
1.5 kristaps 292: return(0);
1.8 kristaps 293: if ( ! mlg_indent(p))
1.5 kristaps 294: return(0);
1.8 kristaps 295: } else if (space) {
296: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
1.1 kristaps 297: return(0);
1.8 kristaps 298: }
1.1 kristaps 299:
1.8 kristaps 300: c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
301:
302: if (0 == c) {
303: mlg_err(p, start, buf, "bad char sequence");
304: return(0);
305: } else if (c > 1) {
306: mlg_warn(p, start, buf, "bogus char sequence");
307: return(0);
308: } else if (-1 == c)
309: return(0);
1.1 kristaps 310:
311: return(1);
312: }
313:
314:
315: int
316: mlg_line(struct md_mlg *p, char *buf)
317: {
318:
319: return(roff_engine(p->tree, buf));
320: }
321:
322:
323: int
324: mlg_exit(struct md_mlg *p, int flush)
325: {
326: int c;
327:
328: c = roff_free(p->tree, flush);
329: free(p);
1.11 kristaps 330:
331: (*p->cbs.ml_free)(p->data);
332:
1.1 kristaps 333: return(c);
334: }
335:
336:
337: struct md_mlg *
1.11 kristaps 338: mlg_alloc(const struct md_args *args,
1.1 kristaps 339: const struct md_rbuf *rbuf,
340: struct md_mbuf *mbuf,
1.11 kristaps 341: const struct ml_cbs *cbs)
1.1 kristaps 342: {
343: struct roffcb cb;
344: struct md_mlg *p;
345:
346: cb.roffhead = mlg_roffhead;
347: cb.rofftail = mlg_rofftail;
348: cb.roffin = mlg_roffin;
349: cb.roffout = mlg_roffout;
350: cb.roffblkin = mlg_roffblkin;
1.2 kristaps 351: cb.roffblkheadin = mlg_roffblkheadin;
352: cb.roffblkheadout = mlg_roffblkheadout;
353: cb.roffblkbodyin = mlg_roffblkbodyin;
354: cb.roffblkbodyout = mlg_roffblkbodyout;
1.1 kristaps 355: cb.roffblkout = mlg_roffblkout;
356: cb.roffspecial = mlg_roffspecial;
357: cb.roffmsg = mlg_roffmsg;
358: cb.roffdata = mlg_roffdata;
359:
360: if (NULL == (p = calloc(1, sizeof(struct md_mlg))))
361: err(1, "calloc");
362:
363: p->args = args;
364: p->mbuf = mbuf;
365: p->rbuf = rbuf;
366:
1.11 kristaps 367: (void)memcpy(&p->cbs, cbs, sizeof(struct ml_cbs));
368:
369: if (NULL == (p->tree = roff_alloc(&cb, p)))
370: free(p);
371: else if ( ! (*p->cbs.ml_alloc)(&p->data))
1.1 kristaps 372: free(p);
1.11 kristaps 373: else
374: return(p);
1.1 kristaps 375:
1.11 kristaps 376: return(NULL);
1.1 kristaps 377: }
378:
379:
380: static int
1.4 kristaps 381: mlg_roffhead(void *arg, const struct tm *tm, const char *os,
382: const char *title, const char *sec, const char *vol)
1.1 kristaps 383: {
384: struct md_mlg *p;
385:
386: assert(arg);
387: p = (struct md_mlg *)arg;
388:
389: mlg_mode(p, MD_BLK_IN);
1.11 kristaps 390:
391: if ( ! (*p->cbs.ml_begin)(p->mbuf, p->args, tm, os, title, sec, vol))
1.1 kristaps 392: return(0);
393:
394: p->indent++;
395: return(mlg_newline(p));
396: }
397:
398:
399: static int
400: mlg_rofftail(void *arg)
401: {
402: struct md_mlg *p;
403:
404: assert(arg);
405: p = (struct md_mlg *)arg;
406:
1.11 kristaps 407: if (0 != p->pos)
408: if ( ! mlg_newline(p))
409: return(0);
410:
411: if ( ! (*p->cbs.ml_end)(p->mbuf, p->args))
1.1 kristaps 412: return(0);
413:
414: mlg_mode(p, MD_BLK_OUT);
415:
416: return(mlg_newline(p));
417: }
418:
419:
420: static int
1.8 kristaps 421: mlg_roffspecial(void *arg, int tok, const char *start, char **more)
1.1 kristaps 422: {
423: struct md_mlg *p;
424:
425: assert(arg);
426: p = (struct md_mlg *)arg;
427:
428: switch (tok) {
1.8 kristaps 429: case (ROFF_Xr):
430: if ( ! *more) {
1.9 kristaps 431: mlg_err(p, start, start, "missing argument");
1.8 kristaps 432: return(0);
433: }
434: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
435: return(0);
436: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
437: return(0);
438: if (*more) {
439: if ( ! ml_nputs(p->mbuf, "(", 1, &p->pos))
440: return(0);
1.9 kristaps 441: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
1.8 kristaps 442: return(0);
443: if ( ! ml_nputs(p->mbuf, ")", 1, &p->pos))
444: return(0);
445: }
446: if (*more) {
447: mlg_err(p, start, start, "too many arguments");
448: return(0);
449: }
450: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
451: return(0);
452: break;
1.13 ! kristaps 453:
1.12 kristaps 454: case (ROFF_Nm):
455: assert(*more);
456: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
457: return(0);
458: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
459: return(0);
460: assert(NULL == *more);
461: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
462: return(0);
1.8 kristaps 463: break;
1.13 ! kristaps 464:
1.1 kristaps 465: case (ROFF_Ns):
466: p->flags |= ML_OVERRIDE_ONE;
467: break;
1.13 ! kristaps 468:
1.1 kristaps 469: case (ROFF_Sm):
470: assert(*more);
471: if (0 == strcmp(*more, "on"))
472: p->flags |= ML_OVERRIDE_ALL;
473: else
474: p->flags &= ~ML_OVERRIDE_ALL;
475: break;
1.13 ! kristaps 476:
! 477: case (ROFF_Ex):
! 478: assert(*more);
! 479: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
! 480: return(0);
! 481: if ( ! ml_puts(p->mbuf, "The ", &p->pos))
! 482: return(0);
! 483: if ( ! mlg_begintag(p, MD_NS_INLINE, ROFF_Xr, NULL, NULL))
! 484: return(0);
! 485: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
! 486: return(0);
! 487: if ( ! mlg_endtag(p, MD_NS_INLINE, ROFF_Xr))
! 488: return(0);
! 489: if ( ! ml_puts(p->mbuf, " utility exits 0 on success, "
! 490: "and >0 if an error "
! 491: "occurs.", &p->pos))
! 492: return(0);
! 493: assert(NULL == *more);
! 494: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
! 495: return(0);
! 496: break;
! 497:
1.1 kristaps 498: default:
1.13 ! kristaps 499: mlg_err(p, start, start, "`%s' not yet supported",
! 500: toknames[tok]);
! 501: return(0);
1.1 kristaps 502: }
503:
504: return(1);
505: }
506:
507:
508: static int
509: mlg_roffblkin(void *arg, int tok, int *argc, char **argv)
510: {
511:
1.8 kristaps 512: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 513: MD_NS_BLOCK, tok, argc, argv));
514: }
515:
516:
517: static int
518: mlg_roffblkout(void *arg, int tok)
519: {
520:
1.9 kristaps 521: return(mlg_endtag((struct md_mlg *)arg, MD_NS_BLOCK, tok));
1.2 kristaps 522: }
523:
524:
525: static int
526: mlg_roffblkbodyin(void *arg, int tok, int *argc, char **argv)
527: {
528:
1.8 kristaps 529: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 530: MD_NS_BODY, tok, argc, argv));
531: }
1.1 kristaps 532:
533:
1.2 kristaps 534: static int
535: mlg_roffblkbodyout(void *arg, int tok)
536: {
1.1 kristaps 537:
1.9 kristaps 538: return(mlg_endtag((struct md_mlg *)arg, MD_NS_BODY, tok));
1.1 kristaps 539: }
540:
541:
542: static int
1.2 kristaps 543: mlg_roffblkheadin(void *arg, int tok, int *argc, char **argv)
1.1 kristaps 544: {
545:
1.8 kristaps 546: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 547: MD_NS_HEAD, tok, argc, argv));
548: }
1.1 kristaps 549:
550:
1.2 kristaps 551: static int
552: mlg_roffblkheadout(void *arg, int tok)
553: {
1.1 kristaps 554:
1.9 kristaps 555: return(mlg_endtag((struct md_mlg *)arg, MD_NS_HEAD, tok));
1.1 kristaps 556: }
557:
558:
559: static int
560: mlg_roffin(void *arg, int tok, int *argc, char **argv)
561: {
562:
1.8 kristaps 563: return(mlg_begintag((struct md_mlg *)arg,
564: MD_NS_INLINE, tok, argc, argv));
1.1 kristaps 565: }
566:
567:
568: static int
569: mlg_roffout(void *arg, int tok)
570: {
571:
1.9 kristaps 572: return(mlg_endtag((struct md_mlg *)arg, MD_NS_INLINE, tok));
1.1 kristaps 573: }
574:
575:
576: static void
577: mlg_roffmsg(void *arg, enum roffmsg lvl,
578: const char *buf, const char *pos, char *msg)
579: {
1.5 kristaps 580:
581: mlg_msg((struct md_mlg *)arg, lvl, buf, pos, msg);
582: }
583:
584:
585: static int
586: mlg_roffdata(void *arg, int space, const char *start, char *buf)
587: {
1.1 kristaps 588: struct md_mlg *p;
589:
590: assert(arg);
591: p = (struct md_mlg *)arg;
592:
1.5 kristaps 593: if ( ! mlg_data(p, space, start, buf))
594: return(0);
595:
596: mlg_mode(p, MD_TEXT);
1.11 kristaps 597:
1.5 kristaps 598: return(1);
599: }
600:
601:
602: static void
1.13 ! kristaps 603: mlg_vmsg(struct md_mlg *p, enum roffmsg lvl, const char *start,
! 604: const char *pos, const char *fmt, va_list ap)
1.5 kristaps 605: {
1.13 ! kristaps 606: char buf[128];
1.5 kristaps 607:
1.13 ! kristaps 608: (void)vsnprintf(buf, sizeof(buf), fmt, ap);
! 609: mlg_msg(p, lvl, start, pos, buf);
1.5 kristaps 610: }
611:
612:
613: static void
1.13 ! kristaps 614: mlg_warn(struct md_mlg *p, const char *start,
! 615: const char *pos, const char *fmt, ...)
1.5 kristaps 616: {
1.13 ! kristaps 617: va_list ap;
1.5 kristaps 618:
1.13 ! kristaps 619: va_start(ap, fmt);
! 620: mlg_vmsg(p, ROFF_WARN, start, pos, fmt, ap);
! 621: va_end(ap);
! 622: }
! 623:
! 624:
! 625: static void
! 626: mlg_err(struct md_mlg *p, const char *start,
! 627: const char *pos, const char *fmt, ...)
! 628: {
! 629: va_list ap;
! 630:
! 631: va_start(ap, fmt);
! 632: mlg_vmsg(p, ROFF_ERROR, start, pos, fmt, ap);
! 633: va_end(ap);
1.5 kristaps 634: }
635:
636:
637: static void
638: mlg_msg(struct md_mlg *p, enum roffmsg lvl,
639: const char *buf, const char *pos, char *msg)
640: {
641: char *level;
642:
1.1 kristaps 643: switch (lvl) {
644: case (ROFF_WARN):
645: if ( ! (MD_WARN_ALL & p->args->warnings))
646: return;
647: level = "warning";
648: break;
649: case (ROFF_ERROR):
650: level = "error";
651: break;
652: default:
653: abort();
654: }
655:
656: if (pos)
657: (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
658: p->rbuf->name, p->rbuf->line, level,
659: msg, pos - buf);
660: else
661: (void)fprintf(stderr, "%s: %s: %s\n",
662: p->rbuf->name, level, msg);
663: }
CVSweb