version 1.9, 2008/12/02 18:26:57 |
version 1.13, 2008/12/04 11:25:29 |
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
* PERFORMANCE OF THIS SOFTWARE. |
* PERFORMANCE OF THIS SOFTWARE. |
*/ |
*/ |
#include <sys/param.h> |
|
|
|
#include <assert.h> |
|
#include <ctype.h> |
|
#include <err.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
|
#include "libmdocml.h" |
#include "libmdocml.h" |
#include "private.h" |
#include "private.h" |
|
#include "ml.h" |
|
|
#define COLUMNS 72 |
|
|
|
enum md_ns { |
static ssize_t xml_endtag(struct md_mbuf *, |
MD_NS_BLOCK, |
const struct md_args *, |
MD_NS_INLINE, |
enum md_ns, int); |
MD_NS_DEFAULT |
static ssize_t xml_begintag(struct md_mbuf *, |
}; |
const struct md_args *, |
|
enum md_ns, int, |
|
const int *, const char **); |
|
static int xml_begin(struct md_mbuf *, |
|
const struct md_args *, |
|
const struct tm *, |
|
const char *, const char *, |
|
const char *, const char *); |
|
static int xml_end(struct md_mbuf *, |
|
const struct md_args *); |
|
|
enum md_tok { |
|
MD_BLKIN, /* Controls spacing. */ |
|
MD_BLKOUT, |
|
MD_IN, |
|
MD_OUT, |
|
MD_TEXT |
|
}; |
|
|
|
struct md_xml { |
/* ARGSUSED */ |
const struct md_args *args; |
static int |
const struct md_rbuf *rbuf; |
xml_begin(struct md_mbuf *mbuf, const struct md_args *args, |
|
const struct tm *tm, const char *os, |
|
const char *title, const char *section, |
|
const char *vol) |
|
{ |
|
size_t res; |
|
|
struct md_mbuf *mbuf; |
if ( ! ml_puts(mbuf, "<?xml version=\"1.0\" " |
struct rofftree *tree; |
"encoding=\"UTF-8\"?>\n", &res)) |
size_t indent; |
return(0); |
size_t pos; |
if ( ! ml_puts(mbuf, "<mdoc xmlns:block=\"block\" " |
enum md_tok last; |
"xmlns:special=\"special\" " |
int flags; |
"xmlns:inline=\"inline\">", &res)) |
#define MD_LITERAL (1 << 0) /* TODO */ |
return(0); |
#define MD_OVERRIDE_ONE (1 << 1) |
|
#define MD_OVERRIDE_ALL (1 << 2) |
|
}; |
|
|
|
static void roffmsg(void *arg, enum roffmsg, |
return(1); |
const char *, const char *, char *); |
} |
static int roffhead(void *); |
|
static int rofftail(void *); |
|
static int roffin(void *, int, int *, char **); |
|
static int roffdata(void *, int, char *); |
|
static int roffout(void *, int); |
|
static int roffblkin(void *, int, int *, char **); |
|
static int roffblkout(void *, int); |
|
static int roffspecial(void *, int, int *, char **, char **); |
|
|
|
static void mbuf_mode(struct md_xml *, enum md_ns); |
|
static int mbuf_newline(struct md_xml *); |
|
static int xml_indent(struct md_xml *); |
|
static int mbuf_data(struct md_xml *, int, char *); |
|
static int xml_nputstring(struct md_xml *, |
|
const char *, size_t); |
|
static int xml_puts(struct md_xml *, const char *); |
|
static int xml_nputs(struct md_xml *, |
|
const char *, size_t); |
|
static int xml_begintag(struct md_xml *, const char *, |
|
enum md_ns, int *, char **); |
|
static int xml_endtag(struct md_xml *, |
|
const char *, enum md_ns); |
|
|
|
#ifdef __linux__ /* FIXME: remove */ |
/* ARGSUSED */ |
static size_t strlcat(char *, const char *, size_t); |
static int |
static size_t strlcpy(char *, const char *, size_t); |
xml_end(struct md_mbuf *mbuf, const struct md_args *args) |
#endif |
{ |
|
size_t res; |
|
|
|
res = 0; |
|
if ( ! ml_puts(mbuf, "</mdoc>", &res)) |
|
return(0); |
|
|
static void |
return(1); |
mbuf_mode(struct md_xml *p, enum md_ns ns) |
|
{ |
|
p->flags &= ~MD_OVERRIDE_ONE; |
|
p->last = ns; |
|
} |
} |
|
|
|
|
static int |
/* ARGSUSED */ |
xml_begintag(struct md_xml *p, const char *name, enum md_ns ns, |
static ssize_t |
int *argc, char **argv) |
xml_begintag(struct md_mbuf *mbuf, const struct md_args *args, |
|
enum md_ns ns, int tok, |
|
const int *argc, const char **argv) |
{ |
{ |
char buf[64]; |
|
ssize_t sz; |
|
size_t res; |
size_t res; |
|
|
|
res = 0; |
|
|
switch (ns) { |
switch (ns) { |
case (MD_NS_BLOCK): |
case (MD_NS_BLOCK): |
res = strlcpy(buf, "block:", sizeof(buf)); |
if ( ! ml_nputs(mbuf, "block:", 6, &res)) |
assert(res < sizeof(buf)); |
return(-1); |
break; |
break; |
|
case (MD_NS_BODY): |
|
if ( ! ml_nputs(mbuf, "body:", 5, &res)) |
|
return(-1); |
|
break; |
|
case (MD_NS_HEAD): |
|
if ( ! ml_nputs(mbuf, "head:", 5, &res)) |
|
return(-1); |
|
break; |
case (MD_NS_INLINE): |
case (MD_NS_INLINE): |
res = strlcpy(buf, "inline:", sizeof(buf)); |
if ( ! ml_nputs(mbuf, "inline:", 7, &res)) |
assert(res < sizeof(buf)); |
return(-1); |
break; |
break; |
default: |
default: |
*buf = 0; |
|
break; |
break; |
} |
} |
|
|
res = strlcat(buf, name, sizeof(buf)); |
if ( ! ml_puts(mbuf, toknames[tok], &res)) |
assert(res < sizeof(buf)); |
return(-1); |
|
|
if (-1 == (sz = ml_begintag(p->mbuf, buf, argc, argv))) |
return((ssize_t)res); |
return(0); |
|
|
|
p->pos += sz; |
|
return(1); |
|
} |
} |
|
|
|
|
static int |
/* ARGSUSED */ |
xml_endtag(struct md_xml *p, const char *name, enum md_ns ns) |
static ssize_t |
|
xml_endtag(struct md_mbuf *mbuf, const struct md_args *args, |
|
enum md_ns ns, int tok) |
{ |
{ |
char buf[64]; |
|
ssize_t sz; |
|
size_t res; |
size_t res; |
|
|
|
res = 0; |
|
|
switch (ns) { |
switch (ns) { |
case (MD_NS_BLOCK): |
case (MD_NS_BLOCK): |
res = strlcpy(buf, "block:", sizeof(buf)); |
if ( ! ml_nputs(mbuf, "block:", 6, &res)) |
assert(res < sizeof(buf)); |
return(-1); |
break; |
break; |
case (MD_NS_INLINE): |
case (MD_NS_INLINE): |
res = strlcpy(buf, "inline:", sizeof(buf)); |
if ( ! ml_nputs(mbuf, "inline:", 7, &res)) |
assert(res < sizeof(buf)); |
return(-1); |
break; |
break; |
|
case (MD_NS_BODY): |
|
if ( ! ml_nputs(mbuf, "body:", 5, &res)) |
|
return(-1); |
|
break; |
|
case (MD_NS_HEAD): |
|
if ( ! ml_nputs(mbuf, "head:", 5, &res)) |
|
return(-1); |
|
break; |
default: |
default: |
*buf = 0; |
|
break; |
break; |
} |
} |
|
|
res = strlcat(buf, name, sizeof(buf)); |
if ( ! ml_puts(mbuf, toknames[tok], &res)) |
assert(res < sizeof(buf)); |
return(-1); |
|
|
if (-1 == (sz = ml_endtag(p->mbuf, buf))) |
return((ssize_t)res); |
return(0); |
|
|
|
p->pos += sz; |
|
return(1); |
|
} |
} |
|
|
|
|
static int |
|
xml_nputstring(struct md_xml *p, const char *buf, size_t sz) |
|
{ |
|
ssize_t res; |
|
|
|
if (-1 == (res = ml_nputstring(p->mbuf, buf, sz))) |
|
return(0); |
|
p->pos += res; |
|
return(1); |
|
} |
|
|
|
|
|
static int |
|
xml_nputs(struct md_xml *p, const char *buf, size_t sz) |
|
{ |
|
ssize_t res; |
|
|
|
if (-1 == (res = ml_nputs(p->mbuf, buf, sz))) |
|
return(0); |
|
p->pos += res; |
|
return(1); |
|
} |
|
|
|
|
|
static int |
|
xml_puts(struct md_xml *p, const char *buf) |
|
{ |
|
|
|
return(xml_nputs(p, buf, strlen(buf))); |
|
} |
|
|
|
|
|
static int |
|
xml_indent(struct md_xml *p) |
|
{ |
|
ssize_t res; |
|
|
|
if (-1 == (res = ml_indent(p->mbuf, p->indent))) |
|
return(0); |
|
p->pos += res; |
|
return(1); |
|
} |
|
|
|
|
|
static int |
|
mbuf_newline(struct md_xml *p) |
|
{ |
|
|
|
if ( ! md_buf_putchar(p->mbuf, '\n')) |
|
return(0); |
|
|
|
p->pos = 0; |
|
return(1); |
|
} |
|
|
|
|
|
static int |
|
mbuf_data(struct md_xml *p, int space, char *buf) |
|
{ |
|
size_t sz; |
|
char *bufp; |
|
|
|
assert(p->mbuf); |
|
assert(0 != p->indent); |
|
|
|
if (MD_OVERRIDE_ONE & p->flags || MD_OVERRIDE_ALL & p->flags) |
|
space = 0; |
|
|
|
if (MD_LITERAL & p->flags) |
|
return(xml_nputstring(p, buf, sizeof(buf))); |
|
|
|
while (*buf) { |
|
while (*buf && isspace(*buf)) |
|
buf++; |
|
|
|
if (0 == *buf) |
|
break; |
|
|
|
bufp = buf; |
|
while (*buf && ! isspace(*buf)) |
|
buf++; |
|
|
|
if (0 != *buf) |
|
*buf++ = 0; |
|
|
|
sz = strlen(bufp); |
|
|
|
if (0 == p->pos) { |
|
if ( ! xml_indent(p)) |
|
return(0); |
|
if ( ! xml_nputstring(p, bufp, sz)) |
|
return(0); |
|
if (p->indent * MAXINDENT + sz >= COLUMNS) |
|
if ( ! mbuf_newline(p)) |
|
return(0); |
|
if ( ! (MD_OVERRIDE_ALL & p->flags)) |
|
space = 1; |
|
continue; |
|
} |
|
|
|
if (space && sz + p->pos >= COLUMNS) { |
|
if ( ! mbuf_newline(p)) |
|
return(0); |
|
if ( ! xml_indent(p)) |
|
return(0); |
|
} else if (space) { |
|
if ( ! xml_nputs(p, " ", 1)) |
|
return(0); |
|
} |
|
|
|
if ( ! xml_nputstring(p, bufp, sz)) |
|
return(0); |
|
|
|
if ( ! (MD_OVERRIDE_ALL & p->flags)) |
|
space = 1; |
|
} |
|
|
|
return(1); |
|
} |
|
|
|
|
|
int |
int |
md_line_xml(void *arg, char *buf) |
md_line_xml(void *data, char *buf) |
{ |
{ |
struct md_xml *p; |
|
|
|
p = (struct md_xml *)arg; |
return(mlg_line((struct md_mlg *)data, buf)); |
return(roff_engine(p->tree, buf)); |
|
} |
} |
|
|
|
|
int |
int |
md_exit_xml(void *data, int flush) |
md_exit_xml(void *data, int flush) |
{ |
{ |
int c; |
|
struct md_xml *p; |
|
|
|
p = (struct md_xml *)data; |
return(mlg_exit((struct md_mlg *)data, flush)); |
c = roff_free(p->tree, flush); |
|
free(p); |
|
|
|
return(c); |
|
} |
} |
|
|
|
|
|
|
md_init_xml(const struct md_args *args, |
md_init_xml(const struct md_args *args, |
struct md_mbuf *mbuf, const struct md_rbuf *rbuf) |
struct md_mbuf *mbuf, const struct md_rbuf *rbuf) |
{ |
{ |
struct roffcb cb; |
|
struct md_xml *p; |
|
|
|
cb.roffhead = roffhead; |
return(mlg_alloc(args, rbuf, mbuf, xml_begintag, |
cb.rofftail = rofftail; |
xml_endtag, xml_begin, xml_end)); |
cb.roffin = roffin; |
|
cb.roffout = roffout; |
|
cb.roffblkin = roffblkin; |
|
cb.roffblkout = roffblkout; |
|
cb.roffspecial = roffspecial; |
|
cb.roffmsg = roffmsg; |
|
cb.roffdata = roffdata; |
|
|
|
if (NULL == (p = calloc(1, sizeof(struct md_xml)))) |
|
err(1, "malloc"); |
|
|
|
p->args = args; |
|
p->mbuf = mbuf; |
|
p->rbuf = rbuf; |
|
|
|
assert(mbuf); |
|
|
|
if (NULL == (p->tree = roff_alloc(&cb, p))) { |
|
free(p); |
|
return(NULL); |
|
} |
|
|
|
return(p); |
|
} |
} |
|
|
|
|
/* ARGSUSED */ |
|
static int |
|
roffhead(void *arg) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
if (-1 == xml_puts(p, "<?xml version=\"1.0\" " |
|
"encoding=\"UTF-8\"?>\n")) |
|
return(0); |
|
if (-1 == xml_puts(p, "<mdoc xmlns:block=\"block\" " |
|
"xmlns:special=\"special\" " |
|
"xmlns:inline=\"inline\">")) |
|
return(0); |
|
|
|
p->indent++; |
|
mbuf_mode(p, MD_BLKIN); |
|
return(mbuf_newline(p)); |
|
} |
|
|
|
|
|
static int |
|
rofftail(void *arg) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
if (0 != p->pos && ! mbuf_newline(p)) |
|
return(0); |
|
|
|
mbuf_mode(p, MD_BLKOUT); |
|
if ( ! xml_endtag(p, "mdoc", MD_NS_DEFAULT)) |
|
return(0); |
|
return(mbuf_newline(p)); |
|
} |
|
|
|
|
|
/* ARGSUSED */ |
|
static int |
|
roffspecial(void *arg, int tok, int *argc, char **argv, char **more) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
/* FIXME: this is completely ad hoc. */ |
|
|
|
switch (tok) { |
|
case (ROFF_Ns): |
|
p->flags |= MD_OVERRIDE_ONE; |
|
break; |
|
case (ROFF_Sm): |
|
assert(*more); |
|
if (0 == strcmp(*more, "on")) |
|
p->flags |= MD_OVERRIDE_ALL; |
|
else |
|
p->flags &= ~MD_OVERRIDE_ALL; |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return(1); |
|
} |
|
|
|
|
|
static int |
|
roffblkin(void *arg, int tok, int *argc, char **argv) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
if (0 != p->pos) { |
|
if ( ! mbuf_newline(p)) |
|
return(0); |
|
if ( ! xml_indent(p)) |
|
return(0); |
|
} else if ( ! xml_indent(p)) |
|
return(0); |
|
|
|
/* FIXME: xml won't like standards args (e.g., p1003.1-90). */ |
|
|
|
p->indent++; |
|
mbuf_mode(p, MD_BLKIN); |
|
|
|
if ( ! xml_begintag(p, toknames[tok], MD_NS_BLOCK, |
|
argc, argv)) |
|
return(0); |
|
return(mbuf_newline(p)); |
|
} |
|
|
|
|
|
static int |
|
roffblkout(void *arg, int tok) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
p->indent--; |
|
|
|
if (0 != p->pos) { |
|
if ( ! mbuf_newline(p)) |
|
return(0); |
|
if ( ! xml_indent(p)) |
|
return(0); |
|
} else if ( ! xml_indent(p)) |
|
return(0); |
|
|
|
mbuf_mode(p, MD_BLKOUT); |
|
if ( ! xml_endtag(p, toknames[tok], MD_NS_BLOCK)) |
|
return(0); |
|
return(mbuf_newline(p)); |
|
} |
|
|
|
|
|
static int |
|
roffin(void *arg, int tok, int *argc, char **argv) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
if ( ! (MD_OVERRIDE_ONE & p->flags) && |
|
! (MD_OVERRIDE_ALL & p->flags) && |
|
p->pos + 11 > COLUMNS) |
|
if ( ! mbuf_newline(p)) |
|
return(0); |
|
|
|
if (0 != p->pos && (MD_TEXT == p->last || MD_OUT == p->last) |
|
&& ! (MD_OVERRIDE_ONE & p->flags) |
|
&& ! (MD_OVERRIDE_ALL & p->flags)) |
|
if ( ! xml_nputs(p, " ", 1)) |
|
return(0); |
|
|
|
if (0 == p->pos && ! xml_indent(p)) |
|
return(0); |
|
|
|
mbuf_mode(p, MD_IN); |
|
return(xml_begintag(p, toknames[tok], |
|
MD_NS_INLINE, argc, argv)); |
|
} |
|
|
|
|
|
static int |
|
roffout(void *arg, int tok) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
if (0 == p->pos && ! xml_indent(p)) |
|
return(0); |
|
|
|
mbuf_mode(p, MD_OUT); |
|
return(xml_endtag(p, toknames[tok], MD_NS_INLINE)); |
|
} |
|
|
|
|
|
static void |
|
roffmsg(void *arg, enum roffmsg lvl, |
|
const char *buf, const char *pos, char *msg) |
|
{ |
|
char *level; |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
|
|
switch (lvl) { |
|
case (ROFF_WARN): |
|
if ( ! (MD_WARN_ALL & p->args->warnings)) |
|
return; |
|
level = "warning"; |
|
break; |
|
case (ROFF_ERROR): |
|
level = "error"; |
|
break; |
|
default: |
|
abort(); |
|
} |
|
|
|
if (pos) |
|
(void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n", |
|
p->rbuf->name, p->rbuf->line, level, |
|
msg, pos - buf); |
|
else |
|
(void)fprintf(stderr, "%s: %s: %s\n", |
|
p->rbuf->name, level, msg); |
|
|
|
} |
|
|
|
|
|
static int |
|
roffdata(void *arg, int space, char *buf) |
|
{ |
|
struct md_xml *p; |
|
|
|
assert(arg); |
|
p = (struct md_xml *)arg; |
|
if ( ! mbuf_data(p, space, buf)) |
|
return(0); |
|
|
|
mbuf_mode(p, MD_TEXT); |
|
return(1); |
|
} |
|
|
|
|
|
#ifdef __linux /* FIXME: remove. */ |
|
/* $OpenBSD$ */ |
|
|
|
/* |
|
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> |
|
* |
|
* Permission to use, copy, modify, and distribute this software for any |
|
* purpose with or without fee is hereby granted, provided that the |
|
* above copyright notice and this permission notice appear in all |
|
* copies. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
|
* PERFORMANCE OF THIS SOFTWARE. |
|
*/ |
|
static size_t |
|
strlcat(char *dst, const char *src, size_t siz) |
|
{ |
|
char *d = dst; |
|
const char *s = src; |
|
size_t n = siz; |
|
size_t dlen; |
|
|
|
/* Find the end of dst and adjust bytes left but don't go past |
|
* end */ |
|
while (n-- != 0 && *d != '\0') |
|
d++; |
|
dlen = d - dst; |
|
n = siz - dlen; |
|
|
|
if (n == 0) |
|
return(dlen + strlen(s)); |
|
while (*s != '\0') { |
|
if (n != 1) { |
|
*d++ = *s; |
|
n--; |
|
} |
|
s++; |
|
} |
|
*d = '\0'; |
|
|
|
return(dlen + (s - src)); /* count does not include NUL */ |
|
} |
|
|
|
|
|
static size_t |
|
strlcpy(char *dst, const char *src, size_t siz) |
|
{ |
|
char *d = dst; |
|
const char *s = src; |
|
size_t n = siz; |
|
|
|
/* Copy as many bytes as will fit */ |
|
if (n != 0) { |
|
while (--n != 0) { |
|
if ((*d++ = *s++) == '\0') |
|
break; |
|
} |
|
} |
|
|
|
/* Not enough room in dst, add NUL and traverse rest of src */ |
|
if (n == 0) { |
|
if (siz != 0) |
|
*d = '\0'; /* NUL-terminate dst */ |
|
while (*s++) |
|
; |
|
} |
|
|
|
return(s - src - 1); /* count does not include NUL */ |
|
} |
|
#endif /*__linux__*/ |
|