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