Index: BSDmakefile ================================================================== --- BSDmakefile +++ BSDmakefile @@ -16,11 +16,11 @@ DEPDIR=depends ALLDEPS=$(DEPDIR)/all CFLAGS=-c -g -O3 -Wall -Werror -fPIC LDFLAGS=-g -O3 -Wall -Werror -CC=gcc +CC=cc all: libsoldout.so mkd2html mkd2latex mkd2man .PHONY: all clean Index: mkd2man.c ================================================================== --- mkd2man.c +++ mkd2man.c @@ -16,12 +16,17 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "markdown.h" -#include +#include + +#include #include +#include +#include +#include #include #define READ_UNIT 1024 #define OUTPUT_UNIT 64 @@ -28,11 +33,32 @@ /**************************** * MARKDOWN TO MAN RENDERER * ****************************/ +/* usage • print the option list */ + void +usage(FILE *out, const char *name) { + fprintf(out, "Usage: %s [-h] [-d ] [-s
] [ -t ] [input-file]\n\n", + name); + fprintf(out, "\t-d, --date\n" + "\t\tSet the date of the manpage (default: now),\n" + "\t-h, --help\n" + "\t\tDisplay this help text and exit without further processing\n" + "\t-s, --section\n" + "\t\tSet the section of the manpage (default: 1)\n" + "\t-t, --title\n" + "\t\tSet the title of the manpage (default: filename)\n"); } + +static struct metadata { + char *title; + char *date; + int section; +} man_metadata; + +static void man_text_escape(struct buf *ob, char *src, size_t size) { size_t i = 0, org; while (i < size) { /* copying directly unescaped characters */ org = i; @@ -43,37 +69,61 @@ /* escaping */ if (i >= size) break; else if (src[i] == '-') BUFPUTSL(ob, "\\-"); i += 1; } } +static void +man_prolog(struct buf *ob, void *opaque) { + struct metadata *m = (struct metadata *)opaque; + bufprintf(ob, + ".\\\" Generated by mkd2man\n" + ".Dd %s\n" + ".Dt %s %d\n" + ".Os", + m->date, + m->title, + m->section + ); } + +static void +man_epilog(struct buf *ob, void *opaque) { + BUFPUTSL(ob, "\n"); } + static void man_blockcode(struct buf *ob, struct buf *text, void *opaque) { if (ob->size) bufputc(ob, '\n'); - BUFPUTSL(ob, ".nf\n"); + BUFPUTSL(ob, ".Bd -literal\n"); + if (text) man_text_escape(ob, text->data, text->size); + BUFPUTSL(ob, ".Ed"); } + +static void +man_blockquote(struct buf *ob, struct buf *text, void *opaque) { + if (ob->size) bufputc(ob, '\n'); + BUFPUTSL(ob, ".Eo\n"); if (text) man_text_escape(ob, text->data, text->size); - BUFPUTSL(ob, ".fi\n"); } + BUFPUTSL(ob, "\n.Ec"); } static int man_codespan(struct buf *ob, struct buf *text, void *opaque) { if (ob->size) bufputc(ob, '\n'); - BUFPUTSL(ob, ".nf\n"); + BUFPUTSL(ob, ".Bd -literal\n"); if (text) man_text_escape(ob, text->data, text->size); - BUFPUTSL(ob, ".fi\n"); + BUFPUTSL(ob, ".Ed"); return 1; } static void man_header(struct buf *ob, struct buf *text, int level, void *opaque) { if (ob->size) bufputc(ob, '\n'); switch(level) { case 1: - bufprintf(ob,".TH "); + BUFPUTSL(ob,".Sh "); break; case 2: - bufprintf(ob, ".SH "); + BUFPUTSL(ob, ".Ss "); break; case 3: - bufprintf(ob, ".SS "); + BUFPUTSL(ob, ".Pp\n.Em "); break; } if (text) bufput(ob, text->data, text->size); } @@ -93,37 +143,32 @@ BUFPUTSL(ob, "\\fP"); return 1; } static int man_linebreak(struct buf *ob, void *opaque) { - BUFPUTSL(ob, "\n.LP\n"); + BUFPUTSL(ob, ".br"); return 1; } static void man_paragraph(struct buf *ob, struct buf *text, void *opaque) { if (ob->size) bufputc(ob, '\n'); - BUFPUTSL(ob, ".TP\n"); - if (text) bufput(ob, text->data, text->size); - BUFPUTSL(ob, "\n"); } + BUFPUTSL(ob, ".Pp\n"); + if (text) bufput(ob, text->data, text->size); } static void man_list(struct buf *ob, struct buf *text, int flags, void *opaque) { if (ob->size) bufputc(ob, '\n'); if (flags & MKD_LIST_ORDERED) - BUFPUTSL(ob,"\n.nr step 0 1\n"); + BUFPUTSL(ob,".Bl -enum\n"); else - BUFPUTSL(ob,".\n"); + BUFPUTSL(ob,".Bl -bullet\n"); if (text) bufput(ob, text->data, text->size); - BUFPUTSL(ob, "\n"); } + BUFPUTSL(ob, ".El"); } static void man_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) { - if (flags & MKD_LIST_ORDERED) - BUFPUTSL(ob, ".IP \\n+[step]\n"); - else - BUFPUTSL(ob, ".IP \\[bu] 2 \n"); - + BUFPUTSL(ob, ".It\n"); if (text) { while (text->size && text->data[text->size - 1] == '\n') text->size -= 1; bufput(ob, text->data, text->size); } BUFPUTSL(ob, "\n"); } @@ -134,16 +179,16 @@ /* renderer structure */ struct mkd_renderer to_man = { /* document-level callbacks */ - NULL, - NULL, + man_prolog, + man_epilog, /* block-level callbacks */ man_blockcode, - NULL, + man_blockquote, NULL, man_header, NULL, man_list, man_listitem, @@ -182,28 +227,107 @@ int main(int argc, char **argv) { struct buf *ib, *ob; size_t ret; FILE *in = stdin; + int ch, argerr, help, i; + char *tmp; + char datebuf[64]; + time_t ttm; + struct tm *tm; + struct stat st; + + struct option longopts[] = { + { "date", no_argument, 0, 'd' }, + { "help", required_argument, 0, 'h' }, + { "section", required_argument, 0, 's' }, + { "title", required_argument, 0, 't' }, + { 0, 0, 0, 0} + }; + man_metadata.section = 1; + man_metadata.title = NULL; + man_metadata.date = NULL; /* opening the file if given from the command line */ - if (argc > 1) { - in = fopen(argv[1], "r"); + argerr = help = 0; + while (!argerr && + (ch = getopt_long(argc, argv, "d:hs:t:", longopts, 0)) != -1) + switch (ch) { + case 'd': + man_metadata.date = optarg; + break; + case 'h': + argerr = help = 1; + break; + case 's': + if (strlen(optarg) != 1 && + strspn(optarg, "123456789") != 1) { + argerr = 1; + break; } + man_metadata.section = (int)strtol(optarg, (char **)NULL, 10); + break; + case 't': + man_metadata.title = optarg; + break; + default: + argerr = 1; } + if (argerr) { + usage(help ? stdout : stderr, argv[0]); + return help ? EXIT_SUCCESS : EXIT_FAILURE; + } + + argc -= optind; + argv += optind; + + if (argc > 0) { + in = fopen(argv[0], "r"); if (!in) { fprintf(stderr,"Unable to open input file \"%s\": %s\n", argv[1], strerror(errno)); return 1; } } + if (!man_metadata.date) { + if (in == stdin || stat(argv[0], &st) == -1) { + ttm = time(NULL); + tm = localtime(&ttm); } + else + tm = localtime(&st.st_mtime); + strftime(datebuf, sizeof(datebuf), "%B %d, %Y", tm); + man_metadata.date = datebuf; + } + + if (in == stdin && !man_metadata.title) { + fprintf(stderr, "When reading from stdin the title should be " + "specified is expected\n"); + return 1; } + + if (!man_metadata.title) { + tmp = strrchr(argv[0], '/'); + man_metadata.title = strrchr(argv[0], '/'); + if (!tmp) + tmp = argv[0]; + else + tmp++; + man_metadata.title = tmp; + tmp = strrchr(man_metadata.title, '.'); + if (tmp) + *tmp = '\0'; } + + /* Ensure the title is uppercase */ + for (i = 0; i < strlen(man_metadata.title); i++) + man_metadata.title[i] = toupper(man_metadata.title[i]); + /* reading everything */ ib = bufnew(READ_UNIT); bufgrow(ib, READ_UNIT); while ((ret = fread(ib->data + ib->size, 1, ib->asize - ib->size, in)) > 0) { ib->size += ret; bufgrow(ib, ib->size + READ_UNIT); } if (in != stdin) fclose(in); + to_man.opaque = &man_metadata; /* performing markdown to man */ ob = bufnew(OUTPUT_UNIT); markdown(ob, ib, &to_man); /* writing the result to stdout */