Annotation of mandoc/mlg.c, Revision 1.6
1.6 ! kristaps 1: /* $Id: mlg.c,v 1.5 2008/12/04 16:19:52 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 *);
73: static int mlg_rofftoken(void *, int, int);
1.1 kristaps 74: static int mlg_roffout(void *, int);
75: static int mlg_roffblkin(void *, int, int *, char **);
76: static int mlg_roffblkout(void *, int);
1.2 kristaps 77: static int mlg_roffspecial(void *, int, int *,
78: char **, char **);
1.5 kristaps 79: static int mlg_roffblkheadin(void *, int,
80: int *, char **);
1.2 kristaps 81: static int mlg_roffblkheadout(void *, int);
1.5 kristaps 82: static int mlg_roffblkbodyin(void *, int,
83: int *, char **);
1.2 kristaps 84: static int mlg_roffblkbodyout(void *, int);
85:
86: static int mlg_beginblk(struct md_mlg *, enum md_ns, int,
87: int *, char **);
88: static int mlg_endblk(struct md_mlg *, enum md_ns, int);
1.1 kristaps 89: static int mlg_begintag(struct md_mlg *, enum md_ns,
90: int, int *, char **);
91: static int mlg_endtag(struct md_mlg *, enum md_ns, int);
92: static int mlg_indent(struct md_mlg *);
93: static int mlg_newline(struct md_mlg *);
94: static void mlg_mode(struct md_mlg *, enum md_tok);
1.5 kristaps 95: static int mlg_data(struct md_mlg *, int,
96: const char *, char *);
97: static void mlg_err(struct md_mlg *, const char *,
98: const char *, char *);
99: static void mlg_warn(struct md_mlg *, const char *,
100: const char *, char *);
101: static void mlg_msg(struct md_mlg *, enum roffmsg,
102: const char *, const char *, char *);
1.1 kristaps 103:
104: #ifdef __linux__
105: extern size_t strlcat(char *, const char *, size_t);
106: extern size_t strlcpy(char *, const char *, size_t);
107: #endif
108:
109:
110: static int
1.2 kristaps 111: mlg_beginblk(struct md_mlg *p, enum md_ns ns, int tok,
112: int *argc, char **argv)
113: {
114: if (0 != p->pos) {
115: if ( ! mlg_newline(p))
116: return(0);
117: if ( ! mlg_indent(p))
118: return(0);
119: } else if ( ! mlg_indent(p))
120: return(0);
121:
122: p->indent++;
123: mlg_mode(p, MD_BLK_IN);
124:
125: if ( ! mlg_begintag(p, ns, tok, argc, argv))
126: return(0);
127: return(mlg_newline(p));
128: }
129:
130:
131: static int
132: mlg_endblk(struct md_mlg *p, enum md_ns ns, int tok)
133: {
134:
135: p->indent--;
136:
137: if (0 != p->pos) {
138: if ( ! mlg_newline(p))
139: return(0);
140: if ( ! mlg_indent(p))
141: return(0);
142: } else if ( ! mlg_indent(p))
143: return(0);
144:
145: mlg_mode(p, MD_BLK_OUT);
146: if ( ! mlg_endtag(p, ns, tok))
147: return(0);
148: return(mlg_newline(p));
149: }
150:
151:
152: static int
1.1 kristaps 153: mlg_begintag(struct md_mlg *p, enum md_ns ns, int tok,
154: int *argc, char **argv)
155: {
156: ssize_t res;
157:
158: /* TODO: extra rules for block/inline. */
159:
160: if ( ! ml_nputs(p->mbuf, "<", 1, &p->pos))
161: return(0);
162:
163: res = (*p->begintag)(p->mbuf, p->args, ns, tok,
164: argc, (const char **)argv);
165: if (-1 == res)
166: return(0);
167:
168: assert(res >= 0);
169: p->pos += (size_t)res;
170:
171: /* TODO: extra rules for block/inline. */
172:
173: return(ml_nputs(p->mbuf, ">", 1, &p->pos));
174: }
175:
176:
177: static int
178: mlg_endtag(struct md_mlg *p, enum md_ns ns, int tok)
179: {
180: ssize_t res;
181:
182: /* TODO: extra rules for block/inline. */
183:
184: if ( ! ml_nputs(p->mbuf, "</", 2, &p->pos))
185: return(0);
186:
187: res = (*p->endtag)(p->mbuf, p->args, ns, tok);
188: if (-1 == res)
189: return(0);
190:
191: assert(res >= 0);
192: p->pos += (size_t)res;
193:
194: /* TODO: extra rules for block/inline. */
195:
196: return(ml_nputs(p->mbuf, ">", 1, &p->pos));
197: }
198:
199:
200: static int
201: mlg_indent(struct md_mlg *p)
202: {
203: size_t count;
204:
1.3 kristaps 205: count = p->indent > MAXINDENT ? (size_t)MAXINDENT : p->indent;
1.1 kristaps 206: count *= INDENT;
207:
208: assert(0 == p->pos);
209: return(ml_putchars(p->mbuf, ' ', count, &p->pos));
210: }
211:
212:
213: static int
214: mlg_newline(struct md_mlg *p)
215: {
216: size_t dummy;
217:
218: if ( ! ml_nputs(p->mbuf, "\n", 1, &dummy))
219: return(0);
220: p->pos = 0;
221: return(1);
222: }
223:
224:
225: static void
226: mlg_mode(struct md_mlg *p, enum md_tok ns)
227: {
1.3 kristaps 228:
1.1 kristaps 229: p->flags &= ~ML_OVERRIDE_ONE;
230: p->last = ns;
231: }
232:
233:
234: static int
1.5 kristaps 235: mlg_data(struct md_mlg *p, int space, const char *start, char *buf)
1.1 kristaps 236: {
237: size_t sz;
238: char *bufp;
1.5 kristaps 239: int c;
1.1 kristaps 240:
241: assert(p->mbuf);
242: assert(0 != p->indent);
243:
244: if (ML_OVERRIDE_ONE & p->flags ||
245: ML_OVERRIDE_ALL & p->flags)
246: space = 0;
247:
248: while (*buf) {
249: while (*buf && isspace(*buf))
250: buf++;
251:
252: if (0 == *buf)
253: break;
254:
255: bufp = buf;
256: while (*buf && ! isspace(*buf))
257: buf++;
258:
259: if (0 != *buf)
260: *buf++ = 0;
261:
262: sz = strlen(bufp);
263:
264: if (0 == p->pos) {
265: if ( ! mlg_indent(p))
266: return(0);
1.5 kristaps 267:
268: c = ml_nputstring(p->mbuf, bufp, sz, &p->pos);
269: if (0 == c) {
270: mlg_err(p, start, bufp, "invalid "
271: "character sequence");
272: return(0);
273: } else if (c > 1) {
274: mlg_warn(p, start, bufp, "bogus "
275: "character sequence");
276: return(0);
277: } else if (-1 == c)
1.1 kristaps 278: return(0);
1.5 kristaps 279:
1.3 kristaps 280: if (p->indent * INDENT + sz >= COLUMNS)
1.1 kristaps 281: if ( ! mlg_newline(p))
282: return(0);
283: if ( ! (ML_OVERRIDE_ALL & p->flags))
284: space = 1;
285: continue;
286: }
287:
288: if (space && sz + p->pos >= COLUMNS) {
289: if ( ! mlg_newline(p))
290: return(0);
291: if ( ! mlg_indent(p))
292: return(0);
293: } else if (space) {
294: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
295: return(0);
296: }
297:
1.5 kristaps 298: c = ml_nputstring(p->mbuf, bufp, sz, &p->pos);
299: if (0 == c) {
300: mlg_err(p, start, bufp, "invalid "
301: "character sequence");
302: return(0);
303: } else if (c > 1) {
304: mlg_warn(p, start, bufp, "bogus "
305: "character sequence");
306: return(0);
307: } else if (-1 == c)
1.1 kristaps 308: return(0);
309:
310: if ( ! (ML_OVERRIDE_ALL & p->flags))
311: space = 1;
312: }
313:
314: return(1);
315: }
316:
317:
318: int
319: mlg_line(struct md_mlg *p, char *buf)
320: {
321:
322: return(roff_engine(p->tree, buf));
323: }
324:
325:
326: int
327: mlg_exit(struct md_mlg *p, int flush)
328: {
329: int c;
330:
331: c = roff_free(p->tree, flush);
332: free(p);
333: return(c);
334: }
335:
336:
337: struct md_mlg *
338: mlg_alloc(const struct md_args *args,
339: const struct md_rbuf *rbuf,
340: struct md_mbuf *mbuf,
1.2 kristaps 341: ml_begintag begintag, ml_endtag endtag,
342: ml_begin begin, ml_end end)
1.1 kristaps 343: {
344: struct roffcb cb;
345: struct md_mlg *p;
346:
347: cb.roffhead = mlg_roffhead;
348: cb.rofftail = mlg_rofftail;
349: cb.roffin = mlg_roffin;
350: cb.roffout = mlg_roffout;
351: cb.roffblkin = mlg_roffblkin;
1.2 kristaps 352: cb.roffblkheadin = mlg_roffblkheadin;
353: cb.roffblkheadout = mlg_roffblkheadout;
354: cb.roffblkbodyin = mlg_roffblkbodyin;
355: cb.roffblkbodyout = mlg_roffblkbodyout;
1.1 kristaps 356: cb.roffblkout = mlg_roffblkout;
357: cb.roffspecial = mlg_roffspecial;
358: cb.roffmsg = mlg_roffmsg;
359: cb.roffdata = mlg_roffdata;
1.5 kristaps 360: cb.rofftoken = mlg_rofftoken;
1.1 kristaps 361:
362: if (NULL == (p = calloc(1, sizeof(struct md_mlg))))
363: err(1, "calloc");
364:
365: p->args = args;
366: p->mbuf = mbuf;
367: p->rbuf = rbuf;
368: p->begintag = begintag;
369: p->endtag = endtag;
1.2 kristaps 370: p->begin = begin;
371: p->end = end;
1.1 kristaps 372:
373: if (NULL == (p->tree = roff_alloc(&cb, p))) {
374: free(p);
375: return(NULL);
376: }
377:
378: return(p);
379: }
380:
381:
382: static int
1.4 kristaps 383: mlg_roffhead(void *arg, const struct tm *tm, const char *os,
384: const char *title, const char *sec, const char *vol)
1.1 kristaps 385: {
386: struct md_mlg *p;
387:
388: assert(arg);
389: p = (struct md_mlg *)arg;
390:
391: mlg_mode(p, MD_BLK_IN);
1.4 kristaps 392: if ( ! (*p->begin)(p->mbuf, p->args, tm, os, title, sec, vol))
1.1 kristaps 393: return(0);
394:
395: p->indent++;
396: return(mlg_newline(p));
397: }
398:
399:
400: static int
401: mlg_rofftail(void *arg)
402: {
403: struct md_mlg *p;
404:
405: assert(arg);
406: p = (struct md_mlg *)arg;
407:
408: if (0 != p->pos && ! mlg_newline(p))
409: return(0);
410:
411: mlg_mode(p, MD_BLK_OUT);
1.2 kristaps 412: if ( ! (*p->end)(p->mbuf, p->args))
1.1 kristaps 413: return(0);
414:
415: return(mlg_newline(p));
416: }
417:
418:
419: /* ARGSUSED */
420: static int
421: mlg_roffspecial(void *arg, int tok, int *argc, char **argv, char **more)
422: {
423: struct md_mlg *p;
424:
425: assert(arg);
426: p = (struct md_mlg *)arg;
427:
428: switch (tok) {
429: case (ROFF_Ns):
430: p->flags |= ML_OVERRIDE_ONE;
431: break;
432: case (ROFF_Sm):
433: assert(*more);
434: if (0 == strcmp(*more, "on"))
435: p->flags |= ML_OVERRIDE_ALL;
436: else
437: p->flags &= ~ML_OVERRIDE_ALL;
438: break;
439: default:
440: break;
441: }
442:
443: return(1);
444: }
445:
446:
447: static int
448: mlg_roffblkin(void *arg, int tok, int *argc, char **argv)
449: {
450:
1.2 kristaps 451: return(mlg_beginblk((struct md_mlg *)arg,
452: MD_NS_BLOCK, tok, argc, argv));
453: }
454:
455:
456: static int
457: mlg_roffblkout(void *arg, int tok)
458: {
459:
460: return(mlg_endblk((struct md_mlg *)arg, MD_NS_BLOCK, tok));
461: }
462:
463:
464: static int
465: mlg_roffblkbodyin(void *arg, int tok, int *argc, char **argv)
466: {
467:
468: return(mlg_beginblk((struct md_mlg *)arg,
469: MD_NS_BODY, tok, argc, argv));
470: }
1.1 kristaps 471:
472:
1.2 kristaps 473: static int
474: mlg_roffblkbodyout(void *arg, int tok)
475: {
1.1 kristaps 476:
1.2 kristaps 477: return(mlg_endblk((struct md_mlg *)arg, MD_NS_BODY, tok));
1.1 kristaps 478: }
479:
480:
481: static int
1.2 kristaps 482: mlg_roffblkheadin(void *arg, int tok, int *argc, char **argv)
1.1 kristaps 483: {
484:
1.2 kristaps 485: return(mlg_beginblk((struct md_mlg *)arg,
486: MD_NS_HEAD, tok, argc, argv));
487: }
1.1 kristaps 488:
489:
1.2 kristaps 490: static int
491: mlg_roffblkheadout(void *arg, int tok)
492: {
1.1 kristaps 493:
1.2 kristaps 494: return(mlg_endblk((struct md_mlg *)arg, MD_NS_HEAD, tok));
1.1 kristaps 495: }
496:
497:
498: static int
499: mlg_roffin(void *arg, int tok, int *argc, char **argv)
500: {
501: struct md_mlg *p;
502:
503: assert(arg);
504: p = (struct md_mlg *)arg;
505:
506: /* FIXME: this part. */
507:
508: if ( ! (ML_OVERRIDE_ONE & p->flags) &&
509: ! (ML_OVERRIDE_ALL & p->flags) &&
510: p->pos + 11 > COLUMNS)
511: if ( ! mlg_newline(p))
512: return(0);
513:
514: if (0 != p->pos && (MD_TEXT == p->last ||
515: MD_INLINE_OUT == p->last)
516: && ! (ML_OVERRIDE_ONE & p->flags)
517: && ! (ML_OVERRIDE_ALL & p->flags))
518: if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
519: return(0);
520:
521: if (0 == p->pos && ! mlg_indent(p))
522: return(0);
523:
524: mlg_mode(p, MD_INLINE_IN);
525: return(mlg_begintag(p, MD_NS_INLINE, tok, argc, argv));
526: }
527:
528:
529: static int
530: mlg_roffout(void *arg, int tok)
531: {
532: struct md_mlg *p;
533:
534: assert(arg);
535: p = (struct md_mlg *)arg;
536:
537: if (0 == p->pos && ! mlg_indent(p))
538: return(0);
539:
540: mlg_mode(p, MD_INLINE_OUT);
541: return(mlg_endtag(p, MD_NS_INLINE, tok));
542: }
543:
544:
545: static void
546: mlg_roffmsg(void *arg, enum roffmsg lvl,
547: const char *buf, const char *pos, char *msg)
548: {
1.5 kristaps 549:
550: mlg_msg((struct md_mlg *)arg, lvl, buf, pos, msg);
551: }
552:
553:
554: static int
555: mlg_rofftoken(void *arg, int space, int value)
556: {
557: struct md_mlg *p;
558: const char *seq;
559: size_t sz, res;
560:
561: assert(arg);
562: p = (struct md_mlg *)arg;
563:
564: switch (value) {
565: case (ROFFTok_Sp_A):
566: seq = "\\a";
567: sz = 2;
568: break;
569: case (ROFFTok_Sp_B):
570: seq = "\\b";
571: sz = 2;
572: break;
573: case (ROFFTok_Sp_F):
574: seq = "\\f";
575: sz = 2;
576: break;
577: case (ROFFTok_Sp_N):
578: seq = "\\n";
579: sz = 2;
580: break;
581: case (ROFFTok_Sp_R):
582: seq = "\\r";
583: sz = 2;
584: break;
585: case (ROFFTok_Sp_T):
586: seq = "\\t";
587: sz = 2;
588: break;
589: case (ROFFTok_Sp_V):
590: seq = "\\v";
591: sz = 2;
592: break;
593: case (ROFFTok_Space):
594: seq = " ";
595: sz = 6;
596: break;
597: case (ROFFTok_Hyphen):
598: seq = "‐";
599: sz = 7;
600: break;
601: case (ROFFTok_Em):
602: seq = "—";
603: sz = 7;
604: break;
605: case (ROFFTok_En):
606: seq = "–";
607: sz = 7;
608: break;
609: case (ROFFTok_Ge):
610: seq = "≥";
611: sz = 7;
612: break;
613: case (ROFFTok_Le):
614: seq = "≤";
615: sz = 7;
616: break;
617: case (ROFFTok_Rquote):
618: seq = "”";
619: sz = 7;
620: break;
621: case (ROFFTok_Lquote):
622: seq = "“";
623: sz = 7;
624: break;
625: case (ROFFTok_Uparrow):
626: seq = "↑";
627: sz = 7;
628: break;
629: case (ROFFTok_Acute):
630: seq = "´";
631: sz = 6;
632: break;
633: case (ROFFTok_Grave):
634: seq = "`";
635: sz = 5;
636: break;
637: case (ROFFTok_Pi):
638: seq = "π";
639: sz = 6;
640: break;
641: case (ROFFTok_Ne):
642: seq = "≠";
643: sz = 7;
644: break;
645: case (ROFFTok_Lt):
646: seq = "<";
647: sz = 4;
648: break;
649: case (ROFFTok_Gt):
650: seq = ">";
651: sz = 4;
652: break;
653: case (ROFFTok_Plusmin):
654: seq = "±";
655: sz = 6;
656: break;
657: case (ROFFTok_Infty):
658: seq = "∞";
659: sz = 7;
660: break;
661: case (ROFFTok_Bar):
662: seq = "|";
663: sz = 6;
664: break;
665: case (ROFFTok_Nan):
666: seq = "Nan";
667: sz = 3;
668: break;
1.6 ! kristaps 669: case (ROFFTok_Quote):
! 670: seq = """;
! 671: sz = 6;
! 672: break;
! 673: default:
! 674: /* TODO: print error. */
! 675: return(0);
1.5 kristaps 676: }
677:
678: if (space && ! ml_nputs(p->mbuf, " ", 1, &res))
679: return(0);
680: p->pos += res;
681:
1.6 ! kristaps 682: if ( ! ml_nputs(p->mbuf, seq, sz, &res))
1.5 kristaps 683: return(0);
684: p->pos += res;
685:
686: return(1);
687: }
688:
689:
690: static int
691: mlg_roffdata(void *arg, int space, const char *start, char *buf)
692: {
1.1 kristaps 693: struct md_mlg *p;
694:
695: assert(arg);
696: p = (struct md_mlg *)arg;
697:
1.5 kristaps 698: if ( ! mlg_data(p, space, start, buf))
699: return(0);
700:
701: mlg_mode(p, MD_TEXT);
702: return(1);
703: }
704:
705:
706: static void
707: mlg_err(struct md_mlg *p, const char *buf, const char *pos, char *msg)
708: {
709:
710: mlg_msg(p, ROFF_ERROR, buf, pos, msg);
711: }
712:
713:
714: static void
715: mlg_warn(struct md_mlg *p, const char *buf, const char *pos, char *msg)
716: {
717:
718: mlg_msg(p, ROFF_WARN, buf, pos, msg);
719: }
720:
721:
722: static void
723: mlg_msg(struct md_mlg *p, enum roffmsg lvl,
724: const char *buf, const char *pos, char *msg)
725: {
726: char *level;
727:
1.1 kristaps 728: switch (lvl) {
729: case (ROFF_WARN):
730: if ( ! (MD_WARN_ALL & p->args->warnings))
731: return;
732: level = "warning";
733: break;
734: case (ROFF_ERROR):
735: level = "error";
736: break;
737: default:
738: abort();
739: }
740:
741: if (pos)
742: (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
743: p->rbuf->name, p->rbuf->line, level,
744: msg, pos - buf);
745: else
746: (void)fprintf(stderr, "%s: %s: %s\n",
747: p->rbuf->name, level, msg);
748:
749: }
CVSweb