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