Annotation of mandoc/mlg.c, Revision 1.12
1.12 ! kristaps 1: /* $Id: mlg.c,v 1.11 2008/12/05 19:45:15 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):
1.12 ! kristaps 450: abort(); /* TODO */
! 451: break;
! 452: case (ROFF_Nm):
! 453: assert(*more);
! 454: if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
! 455: return(0);
! 456: if ( ! ml_puts(p->mbuf, *more++, &p->pos))
! 457: return(0);
! 458: assert(NULL == *more);
! 459: if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
! 460: return(0);
1.8 kristaps 461: break;
1.1 kristaps 462: case (ROFF_Ns):
463: p->flags |= ML_OVERRIDE_ONE;
464: break;
465: case (ROFF_Sm):
466: assert(*more);
467: if (0 == strcmp(*more, "on"))
468: p->flags |= ML_OVERRIDE_ALL;
469: else
470: p->flags &= ~ML_OVERRIDE_ALL;
471: break;
472: default:
473: break;
474: }
475:
476: return(1);
477: }
478:
479:
480: static int
481: mlg_roffblkin(void *arg, int tok, int *argc, char **argv)
482: {
483:
1.8 kristaps 484: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 485: MD_NS_BLOCK, tok, argc, argv));
486: }
487:
488:
489: static int
490: mlg_roffblkout(void *arg, int tok)
491: {
492:
1.9 kristaps 493: return(mlg_endtag((struct md_mlg *)arg, MD_NS_BLOCK, tok));
1.2 kristaps 494: }
495:
496:
497: static int
498: mlg_roffblkbodyin(void *arg, int tok, int *argc, char **argv)
499: {
500:
1.8 kristaps 501: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 502: MD_NS_BODY, tok, argc, argv));
503: }
1.1 kristaps 504:
505:
1.2 kristaps 506: static int
507: mlg_roffblkbodyout(void *arg, int tok)
508: {
1.1 kristaps 509:
1.9 kristaps 510: return(mlg_endtag((struct md_mlg *)arg, MD_NS_BODY, tok));
1.1 kristaps 511: }
512:
513:
514: static int
1.2 kristaps 515: mlg_roffblkheadin(void *arg, int tok, int *argc, char **argv)
1.1 kristaps 516: {
517:
1.8 kristaps 518: return(mlg_begintag((struct md_mlg *)arg,
1.2 kristaps 519: MD_NS_HEAD, tok, argc, argv));
520: }
1.1 kristaps 521:
522:
1.2 kristaps 523: static int
524: mlg_roffblkheadout(void *arg, int tok)
525: {
1.1 kristaps 526:
1.9 kristaps 527: return(mlg_endtag((struct md_mlg *)arg, MD_NS_HEAD, tok));
1.1 kristaps 528: }
529:
530:
531: static int
532: mlg_roffin(void *arg, int tok, int *argc, char **argv)
533: {
534:
1.8 kristaps 535: return(mlg_begintag((struct md_mlg *)arg,
536: MD_NS_INLINE, tok, argc, argv));
1.1 kristaps 537: }
538:
539:
540: static int
541: mlg_roffout(void *arg, int tok)
542: {
543:
1.9 kristaps 544: return(mlg_endtag((struct md_mlg *)arg, MD_NS_INLINE, tok));
1.1 kristaps 545: }
546:
547:
548: static void
549: mlg_roffmsg(void *arg, enum roffmsg lvl,
550: const char *buf, const char *pos, char *msg)
551: {
1.5 kristaps 552:
553: mlg_msg((struct md_mlg *)arg, lvl, buf, pos, msg);
554: }
555:
556:
557: static int
558: mlg_roffdata(void *arg, int space, const char *start, char *buf)
559: {
1.1 kristaps 560: struct md_mlg *p;
561:
562: assert(arg);
563: p = (struct md_mlg *)arg;
564:
1.5 kristaps 565: if ( ! mlg_data(p, space, start, buf))
566: return(0);
567:
568: mlg_mode(p, MD_TEXT);
1.11 kristaps 569:
1.5 kristaps 570: return(1);
571: }
572:
573:
574: static void
575: mlg_err(struct md_mlg *p, const char *buf, const char *pos, char *msg)
576: {
577:
578: mlg_msg(p, ROFF_ERROR, buf, pos, msg);
579: }
580:
581:
582: static void
583: mlg_warn(struct md_mlg *p, const char *buf, const char *pos, char *msg)
584: {
585:
586: mlg_msg(p, ROFF_WARN, buf, pos, msg);
587: }
588:
589:
590: static void
591: mlg_msg(struct md_mlg *p, enum roffmsg lvl,
592: const char *buf, const char *pos, char *msg)
593: {
594: char *level;
595:
1.1 kristaps 596: switch (lvl) {
597: case (ROFF_WARN):
598: if ( ! (MD_WARN_ALL & p->args->warnings))
599: return;
600: level = "warning";
601: break;
602: case (ROFF_ERROR):
603: level = "error";
604: break;
605: default:
606: abort();
607: }
608:
609: if (pos)
610: (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
611: p->rbuf->name, p->rbuf->line, level,
612: msg, pos - buf);
613: else
614: (void)fprintf(stderr, "%s: %s: %s\n",
615: p->rbuf->name, level, msg);
616:
617: }
CVSweb