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