Annotation of mandoc/mlg.c, Revision 1.9
1.9 ! kristaps 1: /* $Id: mlg.c,v 1.8 2008/12/04 23:10:51 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:
1.1 kristaps 85: static int mlg_begintag(struct md_mlg *, enum md_ns,
86: int, int *, char **);
87: static int mlg_endtag(struct md_mlg *, enum md_ns, int);
88: static int mlg_indent(struct md_mlg *);
89: static int mlg_newline(struct md_mlg *);
90: static void mlg_mode(struct md_mlg *, enum md_tok);
1.5 kristaps 91: static int mlg_data(struct md_mlg *, int,
92: const char *, char *);
93: static void mlg_err(struct md_mlg *, const char *,
94: const char *, char *);
95: static void mlg_warn(struct md_mlg *, const char *,
96: const char *, char *);
97: static void mlg_msg(struct md_mlg *, enum roffmsg,
98: const char *, const char *, char *);
1.1 kristaps 99:
100: #ifdef __linux__
101: extern size_t strlcat(char *, const char *, size_t);
102: extern size_t strlcpy(char *, const char *, size_t);
103: #endif
104:
105:
106: static int
107: mlg_begintag(struct md_mlg *p, enum md_ns ns, int tok,
108: int *argc, char **argv)
109: {
110: ssize_t res;
111:
1.8 kristaps 112: assert(MD_NS_DEFAULT != ns);
113:
114: switch (ns) {
115: case (MD_NS_INLINE):
116: if ( ! (ML_OVERRIDE_ONE & p->flags) &&
117: ! (ML_OVERRIDE_ALL & p->flags) &&
1.9 ! kristaps 118: p->pos + 11 >= COLUMNS)
1.8 kristaps 119: if ( ! mlg_newline(p))
120: return(0);
121: if (0 != p->pos && (MD_TEXT == p->last ||
122: MD_INLINE_OUT == p->last)
123: && ! (ML_OVERRIDE_ONE & p->flags)
124: && ! (ML_OVERRIDE_ALL & p->flags))
125: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
126: return(0);
127: if (0 == p->pos && ! mlg_indent(p))
128: return(0);
129: mlg_mode(p, MD_INLINE_IN);
130: break;
131: default:
132: if (0 != p->pos) {
133: if ( ! mlg_newline(p))
134: return(0);
135: if ( ! mlg_indent(p))
136: return(0);
137: } else if ( ! mlg_indent(p))
138: return(0);
139: p->indent++;
140: mlg_mode(p, MD_BLK_IN);
141: break;
142: }
1.1 kristaps 143:
144: if ( ! ml_nputs(p->mbuf, "<", 1, &p->pos))
145: return(0);
146:
147: res = (*p->begintag)(p->mbuf, p->args, ns, tok,
148: argc, (const char **)argv);
149: if (-1 == res)
150: return(0);
151:
152: assert(res >= 0);
153: p->pos += (size_t)res;
154:
1.8 kristaps 155: if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
156: return(0);
157:
158: switch (ns) {
159: case (MD_NS_INLINE):
160: break;
161: default:
162: if ( ! mlg_newline(p))
163: return(0);
164: break;
165: }
1.1 kristaps 166:
1.8 kristaps 167: return(1);
1.1 kristaps 168: }
169:
170:
171: static int
172: mlg_endtag(struct md_mlg *p, enum md_ns ns, int tok)
173: {
174: ssize_t res;
175:
1.9 ! kristaps 176: assert(MD_NS_DEFAULT != ns);
! 177:
! 178: switch (ns) {
! 179: case (MD_NS_INLINE):
! 180: break;
! 181: default:
! 182: p->indent--;
! 183: if (0 != p->pos) {
! 184: if ( ! mlg_newline(p))
! 185: return(0);
! 186: if ( ! mlg_indent(p))
! 187: return(0);
! 188: } else if ( ! mlg_indent(p))
! 189: return(0);
! 190: break;
! 191: }
1.1 kristaps 192:
193: if ( ! ml_nputs(p->mbuf, "</", 2, &p->pos))
194: return(0);
195:
196: res = (*p->endtag)(p->mbuf, p->args, ns, tok);
197: if (-1 == res)
198: return(0);
199:
200: assert(res >= 0);
201: p->pos += (size_t)res;
202:
1.9 ! kristaps 203: if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
! 204: return(0);
! 205:
! 206: switch (ns) {
! 207: case (MD_NS_INLINE):
! 208: mlg_mode(p, MD_INLINE_OUT);
! 209: break;
! 210: default:
! 211: mlg_mode(p, MD_BLK_OUT);
! 212: break;
! 213: }
1.1 kristaps 214:
1.9 ! kristaps 215: return(1);
1.1 kristaps 216: }
217:
218:
219: static int
220: mlg_indent(struct md_mlg *p)
221: {
222: size_t count;
223:
1.9 ! kristaps 224: count = p->indent > MAXINDENT ?
! 225: (size_t)MAXINDENT : p->indent;
1.1 kristaps 226: count *= INDENT;
227:
228: assert(0 == p->pos);
229: return(ml_putchars(p->mbuf, ' ', count, &p->pos));
230: }
231:
232:
233: static int
234: mlg_newline(struct md_mlg *p)
235: {
236: size_t dummy;
237:
238: if ( ! ml_nputs(p->mbuf, "\n", 1, &dummy))
239: return(0);
240: p->pos = 0;
241: return(1);
242: }
243:
244:
245: static void
246: mlg_mode(struct md_mlg *p, enum md_tok ns)
247: {
1.3 kristaps 248:
1.1 kristaps 249: p->flags &= ~ML_OVERRIDE_ONE;
250: p->last = ns;
251: }
252:
253:
254: static int
1.5 kristaps 255: mlg_data(struct md_mlg *p, int space, const char *start, char *buf)
1.1 kristaps 256: {
257: size_t sz;
1.5 kristaps 258: int c;
1.1 kristaps 259:
260: assert(p->mbuf);
261: assert(0 != p->indent);
262:
263: if (ML_OVERRIDE_ONE & p->flags ||
264: ML_OVERRIDE_ALL & p->flags)
265: space = 0;
266:
1.8 kristaps 267: sz = strlen(buf);
1.1 kristaps 268:
1.8 kristaps 269: if (0 == p->pos) {
270: if ( ! mlg_indent(p))
271: return(0);
1.1 kristaps 272:
1.8 kristaps 273: c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
1.1 kristaps 274:
1.8 kristaps 275: if (0 == c) {
276: mlg_err(p, start, buf, "bad char sequence");
277: return(0);
278: } else if (c > 1) {
279: mlg_warn(p, start, buf, "bogus char sequence");
280: return(0);
281: } else if (-1 == c)
282: return(0);
1.1 kristaps 283:
1.8 kristaps 284: if (p->indent * INDENT + sz >= COLUMNS)
285: if ( ! mlg_newline(p))
1.1 kristaps 286: return(0);
1.5 kristaps 287:
1.8 kristaps 288: return(1);
289: }
1.5 kristaps 290:
1.8 kristaps 291: if (space && sz + p->pos >= COLUMNS) {
292: if ( ! mlg_newline(p))
1.5 kristaps 293: return(0);
1.8 kristaps 294: if ( ! mlg_indent(p))
1.5 kristaps 295: return(0);
1.8 kristaps 296: } else if (space) {
297: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
1.1 kristaps 298: return(0);
1.8 kristaps 299: }
1.1 kristaps 300:
1.8 kristaps 301: c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
302:
303: if (0 == c) {
304: mlg_err(p, start, buf, "bad char sequence");
305: return(0);
306: } else if (c > 1) {
307: mlg_warn(p, start, buf, "bogus char sequence");
308: return(0);
309: } else if (-1 == c)
310: return(0);
1.1 kristaps 311:
312: return(1);
313: }
314:
315:
316: int
317: mlg_line(struct md_mlg *p, char *buf)
318: {
319:
320: return(roff_engine(p->tree, buf));
321: }
322:
323:
324: int
325: mlg_exit(struct md_mlg *p, int flush)
326: {
327: int c;
328:
329: c = roff_free(p->tree, flush);
330: free(p);
331: return(c);
332: }
333:
334:
335: struct md_mlg *
336: mlg_alloc(const struct md_args *args,
337: const struct md_rbuf *rbuf,
338: struct md_mbuf *mbuf,
1.2 kristaps 339: ml_begintag begintag, ml_endtag endtag,
340: ml_begin begin, ml_end end)
1.1 kristaps 341: {
342: struct roffcb cb;
343: struct md_mlg *p;
344:
345: cb.roffhead = mlg_roffhead;
346: cb.rofftail = mlg_rofftail;
347: cb.roffin = mlg_roffin;
348: cb.roffout = mlg_roffout;
349: cb.roffblkin = mlg_roffblkin;
1.2 kristaps 350: cb.roffblkheadin = mlg_roffblkheadin;
351: cb.roffblkheadout = mlg_roffblkheadout;
352: cb.roffblkbodyin = mlg_roffblkbodyin;
353: cb.roffblkbodyout = mlg_roffblkbodyout;
1.1 kristaps 354: cb.roffblkout = mlg_roffblkout;
355: cb.roffspecial = mlg_roffspecial;
356: cb.roffmsg = mlg_roffmsg;
357: cb.roffdata = mlg_roffdata;
358:
359: if (NULL == (p = calloc(1, sizeof(struct md_mlg))))
360: err(1, "calloc");
361:
362: p->args = args;
363: p->mbuf = mbuf;
364: p->rbuf = rbuf;
365: p->begintag = begintag;
366: p->endtag = endtag;
1.2 kristaps 367: p->begin = begin;
368: p->end = end;
1.1 kristaps 369:
370: if (NULL == (p->tree = roff_alloc(&cb, p))) {
371: free(p);
372: return(NULL);
373: }
374:
375: return(p);
376: }
377:
378:
379: static int
1.4 kristaps 380: mlg_roffhead(void *arg, const struct tm *tm, const char *os,
381: const char *title, const char *sec, const char *vol)
1.1 kristaps 382: {
383: struct md_mlg *p;
384:
385: assert(arg);
386: p = (struct md_mlg *)arg;
387:
388: mlg_mode(p, MD_BLK_IN);
1.4 kristaps 389: if ( ! (*p->begin)(p->mbuf, p->args, tm, os, title, sec, vol))
1.1 kristaps 390: return(0);
391:
392: p->indent++;
393: return(mlg_newline(p));
394: }
395:
396:
397: static int
398: mlg_rofftail(void *arg)
399: {
400: struct md_mlg *p;
401:
402: assert(arg);
403: p = (struct md_mlg *)arg;
404:
405: if (0 != p->pos && ! mlg_newline(p))
406: return(0);
407:
408: mlg_mode(p, MD_BLK_OUT);
1.2 kristaps 409: if ( ! (*p->end)(p->mbuf, p->args))
1.1 kristaps 410: return(0);
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);
558: return(1);
559: }
560:
561:
562: static void
563: mlg_err(struct md_mlg *p, const char *buf, const char *pos, char *msg)
564: {
565:
566: mlg_msg(p, ROFF_ERROR, buf, pos, msg);
567: }
568:
569:
570: static void
571: mlg_warn(struct md_mlg *p, const char *buf, const char *pos, char *msg)
572: {
573:
574: mlg_msg(p, ROFF_WARN, buf, pos, msg);
575: }
576:
577:
578: static void
579: mlg_msg(struct md_mlg *p, enum roffmsg lvl,
580: const char *buf, const char *pos, char *msg)
581: {
582: char *level;
583:
1.1 kristaps 584: switch (lvl) {
585: case (ROFF_WARN):
586: if ( ! (MD_WARN_ALL & p->args->warnings))
587: return;
588: level = "warning";
589: break;
590: case (ROFF_ERROR):
591: level = "error";
592: break;
593: default:
594: abort();
595: }
596:
597: if (pos)
598: (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
599: p->rbuf->name, p->rbuf->line, level,
600: msg, pos - buf);
601: else
602: (void)fprintf(stderr, "%s: %s: %s\n",
603: p->rbuf->name, level, msg);
604:
605: }
CVSweb