]> git.cameronkatri.com Git - mandoc.git/blob - mlg.c
*** empty log message ***
[mandoc.git] / mlg.c
1 /* $Id: mlg.c,v 1.15 2008/12/06 19:41:41 kristaps Exp $ */
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 <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "libmdocml.h"
28 #include "private.h"
29 #include "ml.h"
30
31 /* TODO: literal tokens. */
32
33 #define COLUMNS 72
34 #define INDENT 4
35 #define MAXINDENT 10
36
37 enum md_tok {
38 MD_TEXT,
39 MD_INLINE_IN,
40 MD_INLINE_OUT,
41 MD_BLK_IN,
42 MD_BLK_OUT,
43 };
44
45 struct md_mlg {
46 const struct md_args *args;
47 const struct md_rbuf *rbuf;
48
49 struct md_mbuf *mbuf;
50 struct rofftree *tree;
51 size_t indent;
52 size_t pos;
53 enum md_tok last;
54 void *arg;
55 struct ml_cbs cbs;
56 int flags;
57 #define ML_OVERRIDE_ONE (1 << 0)
58 #define ML_OVERRIDE_ALL (1 << 1)
59 void *data;
60 };
61
62
63 static char *mlg_literal(int);
64 static char *mlg_At_literal(const char *);
65 static void mlg_roffmsg(void *arg, enum roffmsg,
66 const char *, const char *, char *);
67 static int mlg_roffhead(void *, const struct tm *,
68 const char *, const char *,
69 const char *, const char *);
70 static int mlg_rofftail(void *);
71 static int mlg_roffin(void *, int, int *, char **);
72 static int mlg_roffdata(void *, int,
73 const char *, char *);
74 static int mlg_roffout(void *, int);
75 static int mlg_roffblkin(void *, int, int *, char **);
76 static int mlg_roffblkout(void *, int);
77 static int mlg_roffspecial(void *, int,
78 const char *, char **);
79 static int mlg_roffblkheadin(void *, int,
80 int *, char **);
81 static int mlg_roffblkheadout(void *, int);
82 static int mlg_roffblkbodyin(void *, int,
83 int *, char **);
84 static int mlg_roffblkbodyout(void *, int);
85
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);
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 *, const char *, ...);
96 static void mlg_warn(struct md_mlg *, const char *,
97 const char *, const char *, ...);
98 static void mlg_msg(struct md_mlg *, enum roffmsg,
99 const char *, const char *, char *);
100 static void mlg_vmsg(struct md_mlg *, enum roffmsg,
101 const char *, const char *,
102 const char *, va_list);
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 char *
111 mlg_At_literal(const char *p)
112 {
113 if (NULL == p)
114 return("AT&amp;T UNIX");
115 if (0 == strcmp(p, "v6"))
116 return("Version 6 AT&amp;T UNIX");
117 else if (0 == strcmp(p, "v7"))
118 return("Version 7 AT&amp;T UNIX");
119 else if (0 == strcmp(p, "32v"))
120 return("Version 32v AT&amp;T UNIX");
121 else if (0 == strcmp(p, "V.1"))
122 return("AT&amp;T System V.1 UNIX");
123 else if (0 == strcmp(p, "V.4"))
124 return("AT&amp;T System V.4 UNIX");
125
126 abort();
127 /* NOTREACHED */
128 }
129
130
131 static char *
132 mlg_literal(int tok)
133 {
134 switch (tok) {
135 case (ROFF_Bt):
136 return("is currently in beta test.");
137 case (ROFF_Ud):
138 return("currently under development.");
139 case (ROFF_Fx):
140 return("FreeBSD");
141 case (ROFF_Nx):
142 return("NetBSD");
143 case (ROFF_Ox):
144 return("OpenBSD");
145 case (ROFF_Ux):
146 return("UNIX");
147 case (ROFF_Bx):
148 return("BSD");
149 case (ROFF_Bsx):
150 return("BSDI BSD/OS");
151 default:
152 break;
153 }
154 abort();
155 /* NOTREACHED */
156 }
157
158
159 static int
160 mlg_begintag(struct md_mlg *p, enum md_ns ns, int tok,
161 int *argc, char **argv)
162 {
163 ssize_t res;
164
165 assert(MD_NS_DEFAULT != ns);
166
167 switch (ns) {
168 case (MD_NS_INLINE):
169 if ( ! (ML_OVERRIDE_ONE & p->flags) &&
170 ! (ML_OVERRIDE_ALL & p->flags) &&
171 p->pos + 11 >= COLUMNS)
172 if ( ! mlg_newline(p))
173 return(0);
174 if (0 != p->pos && (MD_TEXT == p->last ||
175 MD_INLINE_OUT == p->last)
176 && ! (ML_OVERRIDE_ONE & p->flags)
177 && ! (ML_OVERRIDE_ALL & p->flags))
178 if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
179 return(0);
180 if (0 == p->pos && ! mlg_indent(p))
181 return(0);
182 mlg_mode(p, MD_INLINE_IN);
183 break;
184 default:
185 if (0 != p->pos) {
186 if ( ! mlg_newline(p))
187 return(0);
188 if ( ! mlg_indent(p))
189 return(0);
190 } else if ( ! mlg_indent(p))
191 return(0);
192 p->indent++;
193 mlg_mode(p, MD_BLK_IN);
194 break;
195 }
196
197 if ( ! ml_nputs(p->mbuf, "<", 1, &p->pos))
198 return(0);
199
200 res = (*p->cbs.ml_begintag)(p->mbuf, p->data, p->args, ns, tok,
201 argc, (const char **)argv);
202 if (-1 == res)
203 return(0);
204
205 assert(res >= 0);
206 p->pos += (size_t)res;
207
208 if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
209 return(0);
210
211 switch (ns) {
212 case (MD_NS_INLINE):
213 break;
214 default:
215 if ( ! mlg_newline(p))
216 return(0);
217 break;
218 }
219
220 return(1);
221 }
222
223
224 static int
225 mlg_endtag(struct md_mlg *p, enum md_ns ns, int tok)
226 {
227 ssize_t res;
228
229 assert(MD_NS_DEFAULT != ns);
230
231 switch (ns) {
232 case (MD_NS_INLINE):
233 break;
234 default:
235 p->indent--;
236 if (0 != p->pos) {
237 if ( ! mlg_newline(p))
238 return(0);
239 if ( ! mlg_indent(p))
240 return(0);
241 } else if ( ! mlg_indent(p))
242 return(0);
243 break;
244 }
245
246 if ( ! ml_nputs(p->mbuf, "</", 2, &p->pos))
247 return(0);
248
249 res = (*p->cbs.ml_endtag)(p->mbuf, p->data, p->args, ns, tok);
250 if (-1 == res)
251 return(0);
252
253 assert(res >= 0);
254 p->pos += (size_t)res;
255
256 if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
257 return(0);
258
259 switch (ns) {
260 case (MD_NS_INLINE):
261 mlg_mode(p, MD_INLINE_OUT);
262 break;
263 default:
264 mlg_mode(p, MD_BLK_OUT);
265 break;
266 }
267
268 return(1);
269 }
270
271
272 static int
273 mlg_indent(struct md_mlg *p)
274 {
275 size_t count;
276
277 count = p->indent > MAXINDENT ?
278 (size_t)MAXINDENT : p->indent;
279 count *= INDENT;
280
281 assert(0 == p->pos);
282 return(ml_putchars(p->mbuf, ' ', count, &p->pos));
283 }
284
285
286 static int
287 mlg_newline(struct md_mlg *p)
288 {
289
290 p->pos = 0;
291 return(ml_nputs(p->mbuf, "\n", 1, NULL));
292 }
293
294
295 static void
296 mlg_mode(struct md_mlg *p, enum md_tok ns)
297 {
298
299 p->flags &= ~ML_OVERRIDE_ONE;
300 p->last = ns;
301 }
302
303
304 static int
305 mlg_data(struct md_mlg *p, int space, const char *start, char *buf)
306 {
307 size_t sz;
308 int c;
309
310 assert(p->mbuf);
311 assert(0 != p->indent);
312
313 if (ML_OVERRIDE_ONE & p->flags ||
314 ML_OVERRIDE_ALL & p->flags)
315 space = 0;
316
317 sz = strlen(buf);
318
319 if (0 == p->pos) {
320 if ( ! mlg_indent(p))
321 return(0);
322
323 c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
324
325 if (0 == c) {
326 mlg_err(p, start, buf, "bad char sequence");
327 return(0);
328 } else if (c > 1) {
329 mlg_warn(p, start, buf, "bogus char sequence");
330 return(0);
331 } else if (-1 == c)
332 return(0);
333
334 if (p->indent * INDENT + sz >= COLUMNS)
335 if ( ! mlg_newline(p))
336 return(0);
337
338 return(1);
339 }
340
341 if (space && sz + p->pos >= COLUMNS) {
342 if ( ! mlg_newline(p))
343 return(0);
344 if ( ! mlg_indent(p))
345 return(0);
346 } else if (space) {
347 if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
348 return(0);
349 }
350
351 c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
352
353 if (0 == c) {
354 mlg_err(p, start, buf, "bad char sequence");
355 return(0);
356 } else if (c > 1) {
357 mlg_warn(p, start, buf, "bogus char sequence");
358 return(0);
359 } else if (-1 == c)
360 return(0);
361
362 return(1);
363 }
364
365
366 int
367 mlg_line(struct md_mlg *p, char *buf)
368 {
369
370 return(roff_engine(p->tree, buf));
371 }
372
373
374 int
375 mlg_exit(struct md_mlg *p, int flush)
376 {
377 int c;
378
379 c = roff_free(p->tree, flush);
380 free(p);
381
382 (*p->cbs.ml_free)(p->data);
383
384 return(c);
385 }
386
387
388 struct md_mlg *
389 mlg_alloc(const struct md_args *args,
390 const struct md_rbuf *rbuf,
391 struct md_mbuf *mbuf,
392 const struct ml_cbs *cbs)
393 {
394 struct roffcb cb;
395 struct md_mlg *p;
396
397 cb.roffhead = mlg_roffhead;
398 cb.rofftail = mlg_rofftail;
399 cb.roffin = mlg_roffin;
400 cb.roffout = mlg_roffout;
401 cb.roffblkin = mlg_roffblkin;
402 cb.roffblkheadin = mlg_roffblkheadin;
403 cb.roffblkheadout = mlg_roffblkheadout;
404 cb.roffblkbodyin = mlg_roffblkbodyin;
405 cb.roffblkbodyout = mlg_roffblkbodyout;
406 cb.roffblkout = mlg_roffblkout;
407 cb.roffspecial = mlg_roffspecial;
408 cb.roffmsg = mlg_roffmsg;
409 cb.roffdata = mlg_roffdata;
410
411 if (NULL == (p = calloc(1, sizeof(struct md_mlg))))
412 err(1, "calloc");
413
414 p->args = args;
415 p->mbuf = mbuf;
416 p->rbuf = rbuf;
417
418 (void)memcpy(&p->cbs, cbs, sizeof(struct ml_cbs));
419
420 if (NULL == (p->tree = roff_alloc(&cb, p)))
421 free(p);
422 else if ( ! (*p->cbs.ml_alloc)(&p->data))
423 free(p);
424 else
425 return(p);
426
427 return(NULL);
428 }
429
430
431 static int
432 mlg_roffhead(void *arg, const struct tm *tm, const char *os,
433 const char *title, const char *sec, const char *vol)
434 {
435 struct md_mlg *p;
436
437 assert(arg);
438 p = (struct md_mlg *)arg;
439
440 mlg_mode(p, MD_BLK_IN);
441
442 if ( ! (*p->cbs.ml_begin)(p->mbuf, p->args, tm, os, title, sec, vol))
443 return(0);
444
445 p->indent++;
446 return(mlg_newline(p));
447 }
448
449
450 static int
451 mlg_rofftail(void *arg)
452 {
453 struct md_mlg *p;
454
455 assert(arg);
456 p = (struct md_mlg *)arg;
457
458 if (0 != p->pos)
459 if ( ! mlg_newline(p))
460 return(0);
461
462 if ( ! (*p->cbs.ml_end)(p->mbuf, p->args))
463 return(0);
464
465 mlg_mode(p, MD_BLK_OUT);
466
467 return(mlg_newline(p));
468 }
469
470
471 static int
472 mlg_roffspecial(void *arg, int tok, const char *start, char **more)
473 {
474 struct md_mlg *p;
475
476 assert(arg);
477 p = (struct md_mlg *)arg;
478
479 /*
480 * First handle macros without content.
481 */
482
483 switch (tok) {
484 case (ROFF_Ns):
485 p->flags |= ML_OVERRIDE_ONE;
486 return(1);
487 case (ROFF_Sm):
488 assert(*more);
489 if (0 == strcmp(*more, "on"))
490 p->flags |= ML_OVERRIDE_ALL;
491 else
492 p->flags &= ~ML_OVERRIDE_ALL;
493 return(1);
494 default:
495 break;
496 }
497
498 if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, more))
499 return(0);
500
501 switch (tok) {
502 case (ROFF_Xr):
503 if ( ! *more) {
504 mlg_err(p, start, start, "missing argument");
505 return(0);
506 }
507 if ( ! ml_puts(p->mbuf, *more++, &p->pos))
508 return(0);
509 if (*more) {
510 if ( ! ml_nputs(p->mbuf, "(", 1, &p->pos))
511 return(0);
512 if ( ! ml_puts(p->mbuf, *more++, &p->pos))
513 return(0);
514 if ( ! ml_nputs(p->mbuf, ")", 1, &p->pos))
515 return(0);
516 }
517 if (*more) {
518 mlg_err(p, start, start, "too many arguments");
519 return(0);
520 }
521 break;
522 case (ROFF_Sx):
523 /* FALLTHROUGH */
524 case (ROFF_Nm):
525 assert(*more);
526 if ( ! ml_puts(p->mbuf, *more++, &p->pos))
527 return(0);
528 assert(NULL == *more);
529 break;
530 case (ROFF_Ex):
531 assert(*more);
532 if ( ! ml_puts(p->mbuf, "The ", &p->pos))
533 return(0);
534 if ( ! mlg_begintag(p, MD_NS_INLINE, ROFF_Xr, NULL, NULL))
535 return(0);
536 if ( ! ml_puts(p->mbuf, *more++, &p->pos))
537 return(0);
538 if ( ! mlg_endtag(p, MD_NS_INLINE, ROFF_Xr))
539 return(0);
540 if ( ! ml_puts(p->mbuf, " utility exits 0 on success, "
541 "and &gt;0 if an error "
542 "occurs.", &p->pos))
543 return(0);
544 assert(NULL == *more);
545 break;
546 case (ROFF_At):
547 if ( ! ml_puts(p->mbuf, mlg_At_literal(*more), &p->pos))
548 return(0);
549 break;
550 case (ROFF_Bx):
551 /* FALLTHROUGH */
552 case (ROFF_Bsx):
553 /* FALLTHROUGH */
554 case (ROFF_Fx):
555 /* FALLTHROUGH */
556 case (ROFF_Nx):
557 /* FALLTHROUGH */
558 case (ROFF_Ox):
559 if ( ! ml_puts(p->mbuf, mlg_literal(tok), &p->pos))
560 return(0);
561 while (*more) {
562 if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
563 return(0);
564 if ( ! ml_putstring(p->mbuf, *more++, &p->pos))
565 return(0);
566 }
567 break;
568 case (ROFF_Bt):
569 /* FALLTHROUGH */
570 case (ROFF_Ud):
571 /* FALLTHROUGH */
572 case (ROFF_Ux):
573 assert(NULL == *more);
574 if ( ! ml_puts(p->mbuf, mlg_literal(tok), &p->pos))
575 return(0);
576 break;
577 default:
578 mlg_err(p, start, start, "`%s' not yet supported",
579 toknames[tok]);
580 return(0);
581 }
582
583 return(mlg_endtag(p, MD_NS_INLINE, tok));
584 }
585
586
587 static int
588 mlg_roffblkin(void *arg, int tok, int *argc, char **argv)
589 {
590
591 return(mlg_begintag((struct md_mlg *)arg,
592 MD_NS_BLOCK, tok, argc, argv));
593 }
594
595
596 static int
597 mlg_roffblkout(void *arg, int tok)
598 {
599
600 return(mlg_endtag((struct md_mlg *)arg, MD_NS_BLOCK, tok));
601 }
602
603
604 static int
605 mlg_roffblkbodyin(void *arg, int tok, int *argc, char **argv)
606 {
607
608 return(mlg_begintag((struct md_mlg *)arg,
609 MD_NS_BODY, tok, argc, argv));
610 }
611
612
613 static int
614 mlg_roffblkbodyout(void *arg, int tok)
615 {
616
617 return(mlg_endtag((struct md_mlg *)arg, MD_NS_BODY, tok));
618 }
619
620
621 static int
622 mlg_roffblkheadin(void *arg, int tok, int *argc, char **argv)
623 {
624
625 return(mlg_begintag((struct md_mlg *)arg,
626 MD_NS_HEAD, tok, argc, argv));
627 }
628
629
630 static int
631 mlg_roffblkheadout(void *arg, int tok)
632 {
633
634 return(mlg_endtag((struct md_mlg *)arg, MD_NS_HEAD, tok));
635 }
636
637
638 static int
639 mlg_roffin(void *arg, int tok, int *argc, char **argv)
640 {
641
642 return(mlg_begintag((struct md_mlg *)arg,
643 MD_NS_INLINE, tok, argc, argv));
644 }
645
646
647 static int
648 mlg_roffout(void *arg, int tok)
649 {
650
651 return(mlg_endtag((struct md_mlg *)arg, MD_NS_INLINE, tok));
652 }
653
654
655 static void
656 mlg_roffmsg(void *arg, enum roffmsg lvl,
657 const char *buf, const char *pos, char *msg)
658 {
659
660 mlg_msg((struct md_mlg *)arg, lvl, buf, pos, msg);
661 }
662
663
664 static int
665 mlg_roffdata(void *arg, int space, const char *start, char *buf)
666 {
667 struct md_mlg *p;
668
669 assert(arg);
670 p = (struct md_mlg *)arg;
671
672 if ( ! mlg_data(p, space, start, buf))
673 return(0);
674
675 mlg_mode(p, MD_TEXT);
676
677 return(1);
678 }
679
680
681 static void
682 mlg_vmsg(struct md_mlg *p, enum roffmsg lvl, const char *start,
683 const char *pos, const char *fmt, va_list ap)
684 {
685 char buf[128];
686
687 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
688 mlg_msg(p, lvl, start, pos, buf);
689 }
690
691
692 static void
693 mlg_warn(struct md_mlg *p, const char *start,
694 const char *pos, const char *fmt, ...)
695 {
696 va_list ap;
697
698 va_start(ap, fmt);
699 mlg_vmsg(p, ROFF_WARN, start, pos, fmt, ap);
700 va_end(ap);
701 }
702
703
704 static void
705 mlg_err(struct md_mlg *p, const char *start,
706 const char *pos, const char *fmt, ...)
707 {
708 va_list ap;
709
710 va_start(ap, fmt);
711 mlg_vmsg(p, ROFF_ERROR, start, pos, fmt, ap);
712 va_end(ap);
713 }
714
715
716 static void
717 mlg_msg(struct md_mlg *p, enum roffmsg lvl,
718 const char *buf, const char *pos, char *msg)
719 {
720 char *level;
721
722 switch (lvl) {
723 case (ROFF_WARN):
724 if ( ! (MD_WARN_ALL & p->args->warnings))
725 return;
726 level = "warning";
727 break;
728 case (ROFF_ERROR):
729 level = "error";
730 break;
731 default:
732 abort();
733 }
734
735 if (pos)
736 (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
737 p->rbuf->name, p->rbuf->line, level,
738 msg, pos - buf);
739 else
740 (void)fprintf(stderr, "%s: %s: %s\n",
741 p->rbuf->name, level, msg);
742 }