]> git.cameronkatri.com Git - mandoc.git/blob - mlg.c
*** empty log message ***
[mandoc.git] / mlg.c
1 /* $Id: mlg.c,v 1.18 2008/12/07 16:41:04 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 char *mlg_fmt(int);
66 static char *mlg_St_literal(int);
67 static void mlg_roffmsg(void *arg, enum roffmsg,
68 const char *, const char *, char *);
69 static int mlg_roffhead(void *, const struct tm *,
70 const char *, const char *,
71 const char *, const char *);
72 static int mlg_rofftail(void *);
73 static int mlg_roffin(void *, int, int *, char **);
74 static int mlg_roffdata(void *, int,
75 const char *, char *);
76 static int mlg_roffout(void *, int);
77 static int mlg_roffblkin(void *, int, int *, char **);
78 static int mlg_roffblkout(void *, int);
79 static int mlg_roffspecial(void *, int,
80 const char *, const int *,
81 const char **, char **);
82 static int mlg_roffblkheadin(void *, int,
83 int *, char **);
84 static int mlg_roffblkheadout(void *, int);
85 static int mlg_roffblkbodyin(void *, int,
86 int *, char **);
87 static int mlg_roffblkbodyout(void *, int);
88
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);
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 *, const char *, ...);
99 static void mlg_warn(struct md_mlg *, const char *,
100 const char *, const char *, ...);
101 static void mlg_msg(struct md_mlg *, enum roffmsg,
102 const char *, const char *, char *);
103 static void mlg_vmsg(struct md_mlg *, enum roffmsg,
104 const char *, const char *,
105 const char *, va_list);
106
107 #ifdef __linux__
108 extern size_t strlcat(char *, const char *, size_t);
109 extern size_t strlcpy(char *, const char *, size_t);
110 #endif
111
112
113 static char *
114 mlg_St_literal(int argc)
115 {
116
117 switch (argc) {
118 case(ROFF_p1003_1_88):
119 return("IEEE Std 1003.1-1988 (&#8220;POSIX&#8221;)");
120 case(ROFF_p1003_1_90):
121 return("IEEE Std 1003.1-1990 (&#8220;POSIX&#8221;)");
122 case(ROFF_p1003_1_96):
123 return("ISO/IEC 9945-1:1996 (&#8220;POSIX&#8221;)");
124 case(ROFF_p1003_1_2001):
125 return("IEEE Std 1003.1-2001 (&#8220;POSIX&#8221;)");
126 case(ROFF_p1003_1_2004):
127 return("IEEE Std 1003.1-2004 (&#8220;POSIX&#8221;)");
128 case(ROFF_p1003_1):
129 return("IEEE Std 1003.1 (&#8220;POSIX&#8221;)");
130 case(ROFF_p1003_1b):
131 return("IEEE Std 1003.1b (&#8220;POSIX&#8221;)");
132 case(ROFF_p1003_1b_93):
133 return("IEEE Std 1003.1b-1993 (&#8220;POSIX&#8221;)");
134 case(ROFF_p1003_1c_95):
135 return("IEEE Std 1003.1c-1995 (&#8220;POSIX&#8221;)");
136 case(ROFF_p1003_1g_2000):
137 return("IEEE Std 1003.1g-2000 (&#8220;POSIX&#8221;)");
138 case(ROFF_p1003_2_92):
139 return("IEEE Std 1003.2-1992 (&#8220;POSIX.2&#8221;)");
140 case(ROFF_p1387_2_95):
141 return("IEEE Std 1387.2-1995 (&#8220;POSIX.7.2&#8221;)");
142 case(ROFF_p1003_2):
143 return("IEEE Std 1003.2 (&#8220;POSIX.2&#8221;)");
144 case(ROFF_p1387_2):
145 return("IEEE Std 1387.2 (&#8220;POSIX.7.2&#8221;)");
146 case(ROFF_isoC_90):
147 return("ISO/IEC 9899:1990 (&#8220;ISO C90&#8221;)");
148 case(ROFF_isoC_amd1):
149 return("ISO/IEC 9899/AMD1:1995 (&#8220;ISO C90&#8221;)");
150 case(ROFF_isoC_tcor1):
151 return("ISO/IEC 9899/TCOR1:1994 (&#8220;ISO C90&#8221;)");
152 case(ROFF_isoC_tcor2):
153 return("ISO/IEC 9899/TCOR2:1995 (&#8220;ISO C90&#8221;)");
154 case(ROFF_isoC_99):
155 return("ISO/IEC 9899:1999 (&#8220;ISO C99&#8221;)");
156 case(ROFF_ansiC):
157 return("ANSI X3.159-1989 (&#8220;ANSI C&#8221;)");
158 case(ROFF_ansiC_89):
159 return("ANSI X3.159-1989 (&#8220;ANSI C&#8221;)");
160 case(ROFF_ansiC_99):
161 return("ANSI/ISO/IEC 9899-1999 (&#8220;ANSI C99&#8221;)");
162 case(ROFF_ieee754):
163 return("IEEE Std 754-1985");
164 case(ROFF_iso8802_3):
165 return("ISO 8802-3: 1989");
166 case(ROFF_xpg3):
167 return("X/Open Portability Guide Issue 3 (&#8220;XPG3&#8221;)");
168 case(ROFF_xpg4):
169 return("X/Open Portability Guide Issue 4 (&#8220;XPG4&#8221;)");
170 case(ROFF_xpg4_2):
171 return("X/Open Portability Guide Issue 4.2 (&#8220;XPG4.2&#8221;)");
172 case(ROFF_xpg4_3):
173 return("X/Open Portability Guide Issue 4.3 (&#8220;XPG4.3&#8221;)");
174 case(ROFF_xbd5):
175 return("X/Open System Interface Definitions Issue 5 (&#8220;XBD5&#8221;)");
176 case(ROFF_xcu5):
177 return("X/Open Commands and Utilities Issue 5 (&#8220;XCU5&#8221;)");
178 case(ROFF_xsh5):
179 return("X/Open System Interfaces and Headers Issue 5 (&#8220;XSH5&#8221;)");
180 case(ROFF_xns5):
181 return("X/Open Networking Services Issue 5 (&#8220;XNS5&#8221;)");
182 case(ROFF_xns5_2d2_0):
183 return("X/Open Networking Services Issue 5.2 Draft 2.0 (&#8220;XNS5.2D2.0&#8221;)");
184 case(ROFF_xcurses4_2):
185 return("X/Open Curses Issue 4 Version 2 (&#8220;XCURSES4.2&#8221;)");
186 case(ROFF_susv2):
187 return("Version 2 of the Single UNIX Specification");
188 case(ROFF_susv3):
189 return("Version 3 of the Single UNIX Specification");
190 case(ROFF_svid4):
191 return("System V Interface Definition, Fourth Edition (&#8220;SVID4&#8221;)");
192 default:
193 break;
194 }
195
196 abort();
197 /* NOTREACHED */
198 }
199
200
201 static char *
202 mlg_At_literal(const char *p)
203 {
204
205 if (NULL == p)
206 return("AT&amp;T UNIX");
207 if (0 == strcmp(p, "v6"))
208 return("Version 6 AT&amp;T UNIX");
209 else if (0 == strcmp(p, "v7"))
210 return("Version 7 AT&amp;T UNIX");
211 else if (0 == strcmp(p, "32v"))
212 return("Version 32v AT&amp;T UNIX");
213 else if (0 == strcmp(p, "V.1"))
214 return("AT&amp;T System V.1 UNIX");
215 else if (0 == strcmp(p, "V.4"))
216 return("AT&amp;T System V.4 UNIX");
217
218 abort();
219 /* NOTREACHED */
220 }
221
222
223 static char *
224 mlg_fmt(int tok)
225 {
226
227 switch (tok) {
228 case (ROFF_Ex):
229 return ("The %s utility exits 0 on success, and "
230 "&gt;0 if an error occurs.");
231 case (ROFF_Rv):
232 return ("The %s() function returns the value 0 if "
233 "successful; otherwise the value -1 "
234 "is returned and the global variable "
235 "<span class=\"inline-Va\">errno</span> "
236 "is set to indicate the error.");
237 case (ROFF_In):
238 return("#include &lt;%s&gt;");
239 default:
240 break;
241 }
242
243 abort();
244 /* NOTREACHED */
245 }
246
247
248 static char *
249 mlg_literal(int tok)
250 {
251
252 switch (tok) {
253 case (ROFF_Bt):
254 return("is currently in beta test.");
255 case (ROFF_Ud):
256 return("currently under development.");
257 case (ROFF_Fx):
258 return("FreeBSD");
259 case (ROFF_Nx):
260 return("NetBSD");
261 case (ROFF_Ox):
262 return("OpenBSD");
263 case (ROFF_Ux):
264 return("UNIX");
265 case (ROFF_Bx):
266 return("BSD");
267 case (ROFF_Bsx):
268 return("BSDI BSD/OS");
269 default:
270 break;
271 }
272 abort();
273 /* NOTREACHED */
274 }
275
276
277 static int
278 mlg_begintag(struct md_mlg *p, enum md_ns ns, int tok,
279 int *argc, char **argv)
280 {
281 ssize_t res;
282
283 assert(MD_NS_DEFAULT != ns);
284
285 switch (ns) {
286 case (MD_NS_INLINE):
287 if ( ! (ML_OVERRIDE_ONE & p->flags) &&
288 ! (ML_OVERRIDE_ALL & p->flags) &&
289 p->pos + 11 >= COLUMNS)
290 if ( ! mlg_newline(p))
291 return(0);
292 if (0 != p->pos && (MD_TEXT == p->last ||
293 MD_INLINE_OUT == p->last)
294 && ! (ML_OVERRIDE_ONE & p->flags)
295 && ! (ML_OVERRIDE_ALL & p->flags))
296 if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
297 return(0);
298 if (0 == p->pos && ! mlg_indent(p))
299 return(0);
300 mlg_mode(p, MD_INLINE_IN);
301 break;
302 default:
303 if (0 != p->pos) {
304 if ( ! mlg_newline(p))
305 return(0);
306 if ( ! mlg_indent(p))
307 return(0);
308 } else if ( ! mlg_indent(p))
309 return(0);
310 p->indent++;
311 mlg_mode(p, MD_BLK_IN);
312 break;
313 }
314
315 if ( ! ml_nputs(p->mbuf, "<", 1, &p->pos))
316 return(0);
317
318 res = (*p->cbs.ml_begintag)(p->mbuf, p->data, p->args, ns, tok,
319 argc, (const char **)argv);
320 if (-1 == res)
321 return(0);
322
323 assert(res >= 0);
324 p->pos += (size_t)res;
325
326 if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
327 return(0);
328
329 switch (ns) {
330 case (MD_NS_INLINE):
331 break;
332 default:
333 if ( ! mlg_newline(p))
334 return(0);
335 break;
336 }
337
338 return(1);
339 }
340
341
342 static int
343 mlg_endtag(struct md_mlg *p, enum md_ns ns, int tok)
344 {
345 ssize_t res;
346
347 assert(MD_NS_DEFAULT != ns);
348
349 switch (ns) {
350 case (MD_NS_INLINE):
351 break;
352 default:
353 p->indent--;
354 if (0 != p->pos) {
355 if ( ! mlg_newline(p))
356 return(0);
357 if ( ! mlg_indent(p))
358 return(0);
359 } else if ( ! mlg_indent(p))
360 return(0);
361 break;
362 }
363
364 if ( ! ml_nputs(p->mbuf, "</", 2, &p->pos))
365 return(0);
366
367 res = (*p->cbs.ml_endtag)(p->mbuf, p->data, p->args, ns, tok);
368 if (-1 == res)
369 return(0);
370
371 assert(res >= 0);
372 p->pos += (size_t)res;
373
374 if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
375 return(0);
376
377 switch (ns) {
378 case (MD_NS_INLINE):
379 mlg_mode(p, MD_INLINE_OUT);
380 break;
381 default:
382 mlg_mode(p, MD_BLK_OUT);
383 break;
384 }
385
386 return(1);
387 }
388
389
390 static int
391 mlg_indent(struct md_mlg *p)
392 {
393 size_t count;
394
395 count = p->indent > MAXINDENT ?
396 (size_t)MAXINDENT : p->indent;
397 count *= INDENT;
398
399 assert(0 == p->pos);
400 return(ml_putchars(p->mbuf, ' ', count, &p->pos));
401 }
402
403
404 static int
405 mlg_newline(struct md_mlg *p)
406 {
407
408 p->pos = 0;
409 return(ml_nputs(p->mbuf, "\n", 1, NULL));
410 }
411
412
413 static void
414 mlg_mode(struct md_mlg *p, enum md_tok ns)
415 {
416
417 p->flags &= ~ML_OVERRIDE_ONE;
418 p->last = ns;
419 }
420
421
422 static int
423 mlg_data(struct md_mlg *p, int space, const char *start, char *buf)
424 {
425 size_t sz;
426 int c;
427
428 assert(p->mbuf);
429 assert(0 != p->indent);
430
431 if (ML_OVERRIDE_ONE & p->flags ||
432 ML_OVERRIDE_ALL & p->flags)
433 space = 0;
434
435 sz = strlen(buf);
436
437 if (0 == p->pos) {
438 if ( ! mlg_indent(p))
439 return(0);
440
441 c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
442
443 if (0 == c) {
444 mlg_err(p, start, buf, "bad char sequence");
445 return(0);
446 } else if (c > 1) {
447 mlg_warn(p, start, buf, "bogus char sequence");
448 return(0);
449 } else if (-1 == c)
450 return(0);
451
452 if (p->indent * INDENT + sz >= COLUMNS)
453 if ( ! mlg_newline(p))
454 return(0);
455
456 return(1);
457 }
458
459 if (space && sz + p->pos >= COLUMNS) {
460 if ( ! mlg_newline(p))
461 return(0);
462 if ( ! mlg_indent(p))
463 return(0);
464 } else if (space) {
465 if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
466 return(0);
467 }
468
469 c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
470
471 if (0 == c) {
472 mlg_err(p, start, buf, "bad char sequence");
473 return(0);
474 } else if (c > 1) {
475 mlg_warn(p, start, buf, "bogus char sequence");
476 return(0);
477 } else if (-1 == c)
478 return(0);
479
480 return(1);
481 }
482
483
484 int
485 mlg_line(struct md_mlg *p, char *buf)
486 {
487
488 return(roff_engine(p->tree, buf));
489 }
490
491
492 int
493 mlg_exit(struct md_mlg *p, int flush)
494 {
495 int c;
496
497 c = roff_free(p->tree, flush);
498 free(p);
499
500 (*p->cbs.ml_free)(p->data);
501
502 return(c);
503 }
504
505
506 struct md_mlg *
507 mlg_alloc(const struct md_args *args,
508 const struct md_rbuf *rbuf,
509 struct md_mbuf *mbuf,
510 const struct ml_cbs *cbs)
511 {
512 struct roffcb cb;
513 struct md_mlg *p;
514
515 cb.roffhead = mlg_roffhead;
516 cb.rofftail = mlg_rofftail;
517 cb.roffin = mlg_roffin;
518 cb.roffout = mlg_roffout;
519 cb.roffblkin = mlg_roffblkin;
520 cb.roffblkheadin = mlg_roffblkheadin;
521 cb.roffblkheadout = mlg_roffblkheadout;
522 cb.roffblkbodyin = mlg_roffblkbodyin;
523 cb.roffblkbodyout = mlg_roffblkbodyout;
524 cb.roffblkout = mlg_roffblkout;
525 cb.roffspecial = mlg_roffspecial;
526 cb.roffmsg = mlg_roffmsg;
527 cb.roffdata = mlg_roffdata;
528
529 if (NULL == (p = calloc(1, sizeof(struct md_mlg))))
530 err(1, "calloc");
531
532 p->args = args;
533 p->mbuf = mbuf;
534 p->rbuf = rbuf;
535
536 (void)memcpy(&p->cbs, cbs, sizeof(struct ml_cbs));
537
538 if (NULL == (p->tree = roff_alloc(&cb, p)))
539 free(p);
540 else if ( ! (*p->cbs.ml_alloc)(&p->data))
541 free(p);
542 else
543 return(p);
544
545 return(NULL);
546 }
547
548
549 static int
550 mlg_roffhead(void *arg, const struct tm *tm, const char *os,
551 const char *title, const char *sec, const char *vol)
552 {
553 struct md_mlg *p;
554
555 assert(arg);
556 p = (struct md_mlg *)arg;
557
558 mlg_mode(p, MD_BLK_IN);
559
560 if ( ! (*p->cbs.ml_begin)(p->mbuf, p->args, tm, os, title, sec, vol))
561 return(0);
562
563 p->indent++;
564 return(mlg_newline(p));
565 }
566
567
568 static int
569 mlg_rofftail(void *arg)
570 {
571 struct md_mlg *p;
572
573 assert(arg);
574 p = (struct md_mlg *)arg;
575
576 if (0 != p->pos)
577 if ( ! mlg_newline(p))
578 return(0);
579
580 if ( ! (*p->cbs.ml_end)(p->mbuf, p->args))
581 return(0);
582
583 mlg_mode(p, MD_BLK_OUT);
584
585 return(mlg_newline(p));
586 }
587
588
589 /* ARGSUSED */
590 static int
591 mlg_roffspecial(void *arg, int tok, const char *start,
592 const int *argc, const char **argv, char **more)
593 {
594 struct md_mlg *p;
595 char buf[256];
596
597 assert(arg);
598 p = (struct md_mlg *)arg;
599
600 /*
601 * First handle macros without content.
602 */
603
604 switch (tok) {
605 case (ROFF_Ns):
606 p->flags |= ML_OVERRIDE_ONE;
607 return(1);
608 case (ROFF_Sm):
609 assert(*more);
610 if (0 == strcmp(*more, "on"))
611 p->flags |= ML_OVERRIDE_ALL;
612 else
613 p->flags &= ~ML_OVERRIDE_ALL;
614 return(1);
615 default:
616 break;
617 }
618
619 /*
620 * Handle macros put into different-token tags.
621 */
622
623 switch (tok) {
624 case (ROFF_Fn):
625 assert(*more);
626 if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, more))
627 return(0);
628 if ( ! ml_putstring(p->mbuf, *more++, &p->pos))
629 return(0);
630 if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
631 return(0);
632 if (*more) {
633 if ( ! ml_nputs(p->mbuf, "(", 1, &p->pos))
634 return(0);
635 p->flags |= ML_OVERRIDE_ONE;
636 if ( ! mlg_begintag(p, MD_NS_INLINE,
637 ROFF_Fa, NULL, more))
638 return(0);
639 if ( ! ml_putstring(p->mbuf, *more++, &p->pos))
640 return(0);
641 if ( ! mlg_endtag(p, MD_NS_INLINE, ROFF_Fa))
642 return(0);
643 while (*more) {
644 if ( ! ml_nputs(p->mbuf, ", ", 2, &p->pos))
645 return(0);
646 if ( ! mlg_begintag(p, MD_NS_INLINE, ROFF_Fa, NULL, more))
647 return(0);
648 if ( ! ml_putstring(p->mbuf, *more++, &p->pos))
649 return(0);
650 if ( ! mlg_endtag(p, MD_NS_INLINE, ROFF_Fa))
651 return(0);
652 }
653 if ( ! ml_nputs(p->mbuf, ")", 1, &p->pos))
654 return(0);
655 }
656 return(1);
657 default:
658 break;
659 }
660
661 /*
662 * Now handle macros in their environments.
663 */
664
665 if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, more))
666 return(0);
667
668 switch (tok) {
669 case (ROFF_St):
670 assert(NULL == *argv);
671 assert(ROFF_ARGMAX != *argc);
672 if ( ! ml_puts(p->mbuf, mlg_St_literal(*argc),
673 &p->pos))
674 return(0);
675 while (*more) {
676 if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
677 return(0);
678 if ( ! ml_putstring(p->mbuf, *more++, &p->pos))
679 return(0);
680 }
681 break;
682
683 case (ROFF_Xr):
684 if ( ! *more) {
685 mlg_err(p, start, start, "missing argument");
686 return(0);
687 }
688 if ( ! ml_puts(p->mbuf, *more++, &p->pos))
689 return(0);
690 if (*more) {
691 if ( ! ml_nputs(p->mbuf, "(", 1, &p->pos))
692 return(0);
693 if ( ! ml_puts(p->mbuf, *more++, &p->pos))
694 return(0);
695 if ( ! ml_nputs(p->mbuf, ")", 1, &p->pos))
696 return(0);
697 }
698 if (*more) {
699 mlg_err(p, start, start, "too many arguments");
700 return(0);
701 }
702 break;
703 case (ROFF_Sx):
704 /* FALLTHROUGH */
705 case (ROFF_Nm):
706 assert(*more);
707 if ( ! ml_putstring(p->mbuf, *more++, &p->pos))
708 return(0);
709 assert(NULL == *more);
710 break;
711
712 case (ROFF_In):
713 /* NOTREACHED */
714 case (ROFF_Ex):
715 /* NOTREACHED */
716 case (ROFF_Rv):
717 assert(*more);
718 /* FIXME: *more must be ml-filtered. */
719 (void)snprintf(buf, sizeof(buf),
720 mlg_fmt(tok), *more++);
721 if ( ! ml_puts(p->mbuf, buf, &p->pos))
722 return(0);
723 assert(NULL == *more);
724 break;
725 case (ROFF_At):
726 if ( ! ml_puts(p->mbuf, mlg_At_literal(*more), &p->pos))
727 return(0);
728 break;
729 case (ROFF_Bx):
730 /* FALLTHROUGH */
731 case (ROFF_Bsx):
732 /* FALLTHROUGH */
733 case (ROFF_Fx):
734 /* FALLTHROUGH */
735 case (ROFF_Nx):
736 /* FALLTHROUGH */
737 case (ROFF_Ox):
738 if ( ! ml_puts(p->mbuf, mlg_literal(tok), &p->pos))
739 return(0);
740 while (*more) {
741 if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
742 return(0);
743 if ( ! ml_putstring(p->mbuf, *more++, &p->pos))
744 return(0);
745 }
746 break;
747 case (ROFF_Bt):
748 /* FALLTHROUGH */
749 case (ROFF_Ud):
750 /* FALLTHROUGH */
751 case (ROFF_Ux):
752 assert(NULL == *more);
753 if ( ! ml_puts(p->mbuf, mlg_literal(tok), &p->pos))
754 return(0);
755 break;
756 default:
757 mlg_err(p, start, start, "`%s' not yet supported",
758 toknames[tok]);
759 return(0);
760 }
761
762 return(mlg_endtag(p, MD_NS_INLINE, tok));
763 }
764
765
766 static int
767 mlg_roffblkin(void *arg, int tok, int *argc, char **argv)
768 {
769
770 return(mlg_begintag((struct md_mlg *)arg,
771 MD_NS_BLOCK, tok, argc, argv));
772 }
773
774
775 static int
776 mlg_roffblkout(void *arg, int tok)
777 {
778
779 return(mlg_endtag((struct md_mlg *)arg, MD_NS_BLOCK, tok));
780 }
781
782
783 static int
784 mlg_roffblkbodyin(void *arg, int tok, int *argc, char **argv)
785 {
786
787 return(mlg_begintag((struct md_mlg *)arg,
788 MD_NS_BODY, tok, argc, argv));
789 }
790
791
792 static int
793 mlg_roffblkbodyout(void *arg, int tok)
794 {
795
796 return(mlg_endtag((struct md_mlg *)arg, MD_NS_BODY, tok));
797 }
798
799
800 static int
801 mlg_roffblkheadin(void *arg, int tok, int *argc, char **argv)
802 {
803
804 return(mlg_begintag((struct md_mlg *)arg,
805 MD_NS_HEAD, tok, argc, argv));
806 }
807
808
809 static int
810 mlg_roffblkheadout(void *arg, int tok)
811 {
812
813 return(mlg_endtag((struct md_mlg *)arg, MD_NS_HEAD, tok));
814 }
815
816
817 static int
818 mlg_roffin(void *arg, int tok, int *argc, char **argv)
819 {
820
821 return(mlg_begintag((struct md_mlg *)arg,
822 MD_NS_INLINE, tok, argc, argv));
823 }
824
825
826 static int
827 mlg_roffout(void *arg, int tok)
828 {
829
830 return(mlg_endtag((struct md_mlg *)arg, MD_NS_INLINE, tok));
831 }
832
833
834 static void
835 mlg_roffmsg(void *arg, enum roffmsg lvl,
836 const char *buf, const char *pos, char *msg)
837 {
838
839 mlg_msg((struct md_mlg *)arg, lvl, buf, pos, msg);
840 }
841
842
843 static int
844 mlg_roffdata(void *arg, int space, const char *start, char *buf)
845 {
846 struct md_mlg *p;
847
848 assert(arg);
849 p = (struct md_mlg *)arg;
850
851 if ( ! mlg_data(p, space, start, buf))
852 return(0);
853
854 mlg_mode(p, MD_TEXT);
855
856 return(1);
857 }
858
859
860 static void
861 mlg_vmsg(struct md_mlg *p, enum roffmsg lvl, const char *start,
862 const char *pos, const char *fmt, va_list ap)
863 {
864 char buf[128];
865
866 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
867 mlg_msg(p, lvl, start, pos, buf);
868 }
869
870
871 static void
872 mlg_warn(struct md_mlg *p, const char *start,
873 const char *pos, const char *fmt, ...)
874 {
875 va_list ap;
876
877 va_start(ap, fmt);
878 mlg_vmsg(p, ROFF_WARN, start, pos, fmt, ap);
879 va_end(ap);
880 }
881
882
883 static void
884 mlg_err(struct md_mlg *p, const char *start,
885 const char *pos, const char *fmt, ...)
886 {
887 va_list ap;
888
889 va_start(ap, fmt);
890 mlg_vmsg(p, ROFF_ERROR, start, pos, fmt, ap);
891 va_end(ap);
892 }
893
894
895 static void
896 mlg_msg(struct md_mlg *p, enum roffmsg lvl,
897 const char *buf, const char *pos, char *msg)
898 {
899 char *level;
900
901 switch (lvl) {
902 case (ROFF_WARN):
903 if ( ! (MD_WARN_ALL & p->args->warnings))
904 return;
905 level = "warning";
906 break;
907 case (ROFF_ERROR):
908 level = "error";
909 break;
910 default:
911 abort();
912 }
913
914 if (pos)
915 (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
916 p->rbuf->name, p->rbuf->line, level,
917 msg, pos - buf);
918 else
919 (void)fprintf(stderr, "%s: %s: %s\n",
920 p->rbuf->name, level, msg);
921 }