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