diff options
author | Mavridis Philippe <[email protected]> | 2022-02-05 17:44:26 +0000 |
---|---|---|
committer | Slávek Banko <[email protected]> | 2022-04-17 18:35:01 +0200 |
commit | 95279fbf6dfeb43d80590740a9259d7caa614177 (patch) | |
tree | 8c3ff4de77102f1e55357dd81d650e9f22be69a0 /tdemarkdown/md4c/md2html | |
parent | a291f3a0a3fab073c009f77a36745c5c7bd48e9b (diff) | |
download | tdelibs-95279fbf6dfeb43d80590740a9259d7caa614177.tar.gz tdelibs-95279fbf6dfeb43d80590740a9259d7caa614177.zip |
Add tdemarkdown part - embeddable lightweight markdown viewing component.
TDEMarkdown is based on the md4c library and using TDEHTML for rendering
its output. For enhanced safety, on HTML widget is turned off everything
we don't need for viewing. It integrates nicely into Konqueror and
supports both Commonmark and GitHub markdown syntaxes.
Signed-off-by: Mavridis Philippe <[email protected]>
Prepare to merge tdemarkdown into tdelibs.
Signed-off-by: Slávek Banko <[email protected]>
Diffstat (limited to 'tdemarkdown/md4c/md2html')
-rw-r--r-- | tdemarkdown/md4c/md2html/CMakeLists.txt | 22 | ||||
-rw-r--r-- | tdemarkdown/md4c/md2html/cmdline.c | 205 | ||||
-rw-r--r-- | tdemarkdown/md4c/md2html/cmdline.h | 153 | ||||
-rw-r--r-- | tdemarkdown/md4c/md2html/md2html.1 | 113 | ||||
-rw-r--r-- | tdemarkdown/md4c/md2html/md2html.c | 383 |
5 files changed, 876 insertions, 0 deletions
diff --git a/tdemarkdown/md4c/md2html/CMakeLists.txt b/tdemarkdown/md4c/md2html/CMakeLists.txt new file mode 100644 index 000000000..14de6712e --- /dev/null +++ b/tdemarkdown/md4c/md2html/CMakeLists.txt @@ -0,0 +1,22 @@ + +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG") + + +# Build rules for md2html command line utility + +include_directories("${PROJECT_SOURCE_DIR}/src") +add_executable(md2html cmdline.c cmdline.h md2html.c) +target_link_libraries(md2html md4c-html) + + +# Install rules + +install( + TARGETS md2html + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) +install(FILES "md2html.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") + diff --git a/tdemarkdown/md4c/md2html/cmdline.c b/tdemarkdown/md4c/md2html/cmdline.c new file mode 100644 index 000000000..c3fddfaa4 --- /dev/null +++ b/tdemarkdown/md4c/md2html/cmdline.c @@ -0,0 +1,205 @@ +/* + * C Reusables + * <http://github.com/mity/c-reusables> + * + * Copyright (c) 2017-2020 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "cmdline.h" + +#include <stdio.h> +#include <string.h> + + +#ifdef _WIN32 + #define snprintf _snprintf +#endif + + +#define CMDLINE_AUXBUF_SIZE 32 + + + +static int +cmdline_handle_short_opt_group(const CMDLINE_OPTION* options, const char* arggroup, + int (*callback)(int /*optval*/, const char* /*arg*/, void* /*userdata*/), + void* userdata) +{ + const CMDLINE_OPTION* opt; + int i; + int ret = 0; + + for(i = 0; arggroup[i] != '\0'; i++) { + for(opt = options; opt->id != 0; opt++) { + if(arggroup[i] == opt->shortname) + break; + } + + if(opt->id != 0 && !(opt->flags & CMDLINE_OPTFLAG_REQUIREDARG)) { + ret = callback(opt->id, NULL, userdata); + } else { + /* Unknown option. */ + char badoptname[3]; + badoptname[0] = '-'; + badoptname[1] = arggroup[i]; + badoptname[2] = '\0'; + ret = callback((opt->id != 0 ? CMDLINE_OPTID_MISSINGARG : CMDLINE_OPTID_UNKNOWN), + badoptname, userdata); + } + + if(ret != 0) + break; + } + + return ret; +} + +int +cmdline_read(const CMDLINE_OPTION* options, int argc, char** argv, + int (*callback)(int /*optval*/, const char* /*arg*/, void* /*userdata*/), + void* userdata) +{ + const CMDLINE_OPTION* opt; + char auxbuf[CMDLINE_AUXBUF_SIZE+1]; + int fast_optarg_decision = 1; + int after_doubledash = 0; + int i = 1; + int ret = 0; + + auxbuf[CMDLINE_AUXBUF_SIZE] = '\0'; + + /* Check whether there is any CMDLINE_OPTFLAG_COMPILERLIKE option with + * a name not starting with '-'. That would imply we can to check for + * non-option arguments only after refusing all such options. */ + for(opt = options; opt->id != 0; opt++) { + if((opt->flags & CMDLINE_OPTFLAG_COMPILERLIKE) && opt->longname[0] != '-') + fast_optarg_decision = 0; + } + + while(i < argc) { + if(after_doubledash || strcmp(argv[i], "-") == 0) { + /* Non-option argument. + * Standalone "-" usually means "read from stdin" or "write to + * stdout" so treat it always as a non-option. */ + ret = callback(CMDLINE_OPTID_NONE, argv[i], userdata); + } else if(strcmp(argv[i], "--") == 0) { + /* End of options. All the remaining tokens are non-options + * even if they start with a dash. */ + after_doubledash = 1; + } else if(fast_optarg_decision && argv[i][0] != '-') { + /* Non-option argument. */ + ret = callback(CMDLINE_OPTID_NONE, argv[i], userdata); + } else { + for(opt = options; opt->id != 0; opt++) { + if(opt->flags & CMDLINE_OPTFLAG_COMPILERLIKE) { + size_t len = strlen(opt->longname); + if(strncmp(argv[i], opt->longname, len) == 0) { + /* Compiler-like option. */ + if(argv[i][len] != '\0') + ret = callback(opt->id, argv[i] + len, userdata); + else if(i+1 < argc) + ret = callback(opt->id, argv[++i], userdata); + else + ret = callback(CMDLINE_OPTID_MISSINGARG, opt->longname, userdata); + break; + } + } else if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { + size_t len = strlen(opt->longname); + if(strncmp(argv[i]+2, opt->longname, len) == 0) { + /* Regular long option. */ + if(argv[i][2+len] == '\0') { + /* with no argument provided. */ + if(!(opt->flags & CMDLINE_OPTFLAG_REQUIREDARG)) + ret = callback(opt->id, NULL, userdata); + else + ret = callback(CMDLINE_OPTID_MISSINGARG, argv[i], userdata); + break; + } else if(argv[i][2+len] == '=') { + /* with an argument provided. */ + if(opt->flags & (CMDLINE_OPTFLAG_OPTIONALARG | CMDLINE_OPTFLAG_REQUIREDARG)) { + ret = callback(opt->id, argv[i]+2+len+1, userdata); + } else { + snprintf(auxbuf, CMDLINE_AUXBUF_SIZE, "--%s", opt->longname); + ret = callback(CMDLINE_OPTID_BOGUSARG, auxbuf, userdata); + } + break; + } else { + continue; + } + } + } else if(opt->shortname != '\0' && argv[i][0] == '-') { + if(argv[i][1] == opt->shortname) { + /* Regular short option. */ + if(opt->flags & CMDLINE_OPTFLAG_REQUIREDARG) { + if(argv[i][2] != '\0') + ret = callback(opt->id, argv[i]+2, userdata); + else if(i+1 < argc) + ret = callback(opt->id, argv[++i], userdata); + else + ret = callback(CMDLINE_OPTID_MISSINGARG, argv[i], userdata); + break; + } else { + ret = callback(opt->id, NULL, userdata); + + /* There might be more (argument-less) short options + * grouped together. */ + if(ret == 0 && argv[i][2] != '\0') + ret = cmdline_handle_short_opt_group(options, argv[i]+2, callback, userdata); + break; + } + } + } + } + + if(opt->id == 0) { /* still not handled? */ + if(argv[i][0] != '-') { + /* Non-option argument. */ + ret = callback(CMDLINE_OPTID_NONE, argv[i], userdata); + } else { + /* Unknown option. */ + char* badoptname = argv[i]; + + if(strncmp(badoptname, "--", 2) == 0) { + /* Strip any argument from the long option. */ + char* assignment = strchr(badoptname, '='); + if(assignment != NULL) { + size_t len = assignment - badoptname; + if(len > CMDLINE_AUXBUF_SIZE) + len = CMDLINE_AUXBUF_SIZE; + strncpy(auxbuf, badoptname, len); + auxbuf[len] = '\0'; + badoptname = auxbuf; + } + } + + ret = callback(CMDLINE_OPTID_UNKNOWN, badoptname, userdata); + } + } + } + + if(ret != 0) + return ret; + i++; + } + + return ret; +} + diff --git a/tdemarkdown/md4c/md2html/cmdline.h b/tdemarkdown/md4c/md2html/cmdline.h new file mode 100644 index 000000000..5bbde47eb --- /dev/null +++ b/tdemarkdown/md4c/md2html/cmdline.h @@ -0,0 +1,153 @@ +/* + * C Reusables + * <http://github.com/mity/c-reusables> + * + * Copyright (c) 2017 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef CRE_CMDLINE_H +#define CRE_CMDLINE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* The option may have an argument. (Affects only long option.) */ +#define CMDLINE_OPTFLAG_OPTIONALARG 0x0001 + +/* The option must have an argument. + * Such short option cannot be grouped within single '-abc'. */ +#define CMDLINE_OPTFLAG_REQUIREDARG 0x0002 + +/* Enable special compiler-like mode for the long option. + * + * Note ::shortname is not supported with this flag. CMDLINE_OPTION::shortname + * is silently ignored if the flag is used. + * + * With this flag, CMDLINE_OPTION::longname is treated differently as follows: + * + * 1. The option matches if the CMDLINE_OPTION::longname is the exact prefix + * of the argv[i] from commandline. + * + * 2. Double dash ("--") is not automatically prepended to + * CMDLINE_OPTION::longname. (If you desire any leading dash, include it + * explicitly in CMDLINE_OPTION initialization.) + * + * 3. An argument (optionally after a whitespace) is required (the flag + * CMDLINE_OPTFLAG_COMPILERLIKE implicitly implies also the flag + * CMDLINE_OPTFLAG_REQUIREDARG). + * + * But there is no delimiter expected (no "=" between the option and its + * argument). Whitespace is optional between the option and its argument. + * + * Intended use is for options similar to what many compilers accept. + * For example: + * -DDEBUG=0 (-D is the option, DEBUG=0 is the argument). + * -Isrc/include (-I is the option, src/include is the argument). + * -isystem /usr/include (-isystem is the option, /usr/include is the argument). + * -lmath (-l is the option, math is the argument). + */ +#define CMDLINE_OPTFLAG_COMPILERLIKE 0x0004 + + +/* Special (reserved) option IDs. Do not use these for any CMDLINE_OPTION::id. + * See documentation of cmdline_read() to get info about their meaning. + */ +#define CMDLINE_OPTID_NONE 0 +#define CMDLINE_OPTID_UNKNOWN (-0x7fffffff + 0) +#define CMDLINE_OPTID_MISSINGARG (-0x7fffffff + 1) +#define CMDLINE_OPTID_BOGUSARG (-0x7fffffff + 2) + + +typedef struct CMDLINE_OPTION { + char shortname; /* Short (single char) option or 0. */ + const char* longname; /* Long name (after "--") or NULL. */ + int id; /* Non-zero ID to identify the option in the callback; or zero to denote end of options list. */ + unsigned flags; /* Bitmask of CMDLINE_OPTFLAG_xxxx flags. */ +} CMDLINE_OPTION; + + +/* Parses all options and their arguments as specified by argc, argv accordingly + * with the given options (except argv[0] which is ignored). + * + * The caller must specify the list of supported options in the 1st parameter + * of the function. The array must end with a record whose CMDLINE_OPTION::id + * is zero to zero. + * + * The provided callback function is called for each option on the command + * line so that: + * + * -- the "id" refers to the id of the option as specified in options[]. + * + * -- the "arg" specifies an argument of the option or NULL if none is + * provided. + * + * -- the "userdata" just allows to pass in some caller's context into + * the callback. + * + * Special cases (recognized via special "id" value) are reported to the + * callback as follows: + * + * -- If id is CMDLINE_OPTID_NONE, the callback informs about a non-option + * also known as a positional argument. + * + * All argv[] tokens which are not interpreted as an options or an argument + * of any option fall into this category. + * + * Usually, programs interpret these as paths to file to process. + * + * -- If id is CMDLINE_OPTID_UNKNOWN, the corresponding argv[] looks like an + * option but it is not found in the options[] passed to cmdline_read(). + * + * The callback's parameter arg specifies the guilty command line token. + * Usually, program writes down an error message and exits. + * + * -- If id is CMDLINE_OPTID_MISSINGARG, the given option is valid but its + * flag in options[] requires an argument; yet there is none on the + * command line. + * + * The callback's parameter arg specifies the guilty option name. + * Usually, program writes down an error message and exits. + * + * -- If id is CMDLINE_OPTID_BOGUSARG, the given option is valid but its + * flag in options[] does not expect an argument; yet the command line + * does provide one. + * + * The callback's parameter arg specifies the guilty option name. + * Usually, program writes down an error message and exits. + * + * On success, zero is returned. + * + * If the callback returns a non-zero, cmdline_read() aborts immediately and + * cmdline_read() propagates the same return value to the caller. + */ + +int cmdline_read(const CMDLINE_OPTION* options, int argc, char** argv, + int (*callback)(int /*id*/, const char* /*arg*/, void* /*userdata*/), + void* userdata); + + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* CRE_CMDLINE_H */ diff --git a/tdemarkdown/md4c/md2html/md2html.1 b/tdemarkdown/md4c/md2html/md2html.1 new file mode 100644 index 000000000..cffaee8b8 --- /dev/null +++ b/tdemarkdown/md4c/md2html/md2html.1 @@ -0,0 +1,113 @@ +.TH MD2HTML 1 "June 2019" "" "General Commands Manual" +.nh +.ad l +. +.SH NAME +. +md2html \- convert Markdown to HTML +. +.SH SYNOPSIS +. +.B md2html +.RI [ OPTION ]...\& +.RI [ FILE ] +. +.SH OPTIONS +. +.SS General options: +. +.TP +.BR -o ", " --output= \fIOUTFILE\fR +Write output to \fIOUTFILE\fR instead of \fBstdout\fR(3) +. +.TP +.BR -f ", " --full-html +Generate full HTML document, including header +. +.TP +.BR -s ", " --stat +Measure time of input parsing +. +.TP +.BR -h ", " --help +Display help and exit +. +.TP +.BR -v ", " --version +Display version and exit +. +.SS Markdown dialect options: +. +.TP +.B --commonmark +CommonMark (the default) +. +.TP +.B --github +Github Flavored Markdown +. +.PP +Note: dialect options are equivalent to some combination of flags below. +. +.SS Markdown extension options: +. +.TP +.B --fcollapse-whitespace +Collapse non-trivial whitespace +. +.TP +.B --fverbatim-entities +Do not translate entities +. +.TP +.B --fpermissive-atx-headers +Allow ATX headers without delimiting space +. +.TP +.B --fpermissive-url-autolinks +Allow URL autolinks without "<" and ">" delimiters +. +.TP +.B --fpermissive-www-autolinks +Allow WWW autolinks without any scheme (e.g. "www.example.com") +. +.TP +.B --fpermissive-email-autolinks +Allow e-mail autolinks without "<", ">" and "mailto:" +. +.TP +.B --fpermissive-autolinks +Enable all 3 of the above permissive autolinks options +. +.TP +.B --fno-indented-code +Disable indented code blocks +. +.TP +.B --fno-html-blocks +Disable raw HTML blocks +. +.TP +.B --fno-html-spans +Disable raw HTML spans +. +.TP +.B --fno-html +Same as \fB--fno-html-blocks --fno-html-spans\fR +. +.TP +.B --ftables +Enable tables +. +.TP +.B --fstrikethrough +Enable strikethrough spans +. +.TP +.B --ftasklists +Enable task lists +. +.SH SEE ALSO +. +https://github.com/mity/md4c +. diff --git a/tdemarkdown/md4c/md2html/md2html.c b/tdemarkdown/md4c/md2html/md2html.c new file mode 100644 index 000000000..06b2b74b1 --- /dev/null +++ b/tdemarkdown/md4c/md2html/md2html.c @@ -0,0 +1,383 @@ +/* + * MD4C: Markdown parser for C + * (http://github.com/mity/md4c) + * + * Copyright (c) 2016-2020 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "md4c-html.h" +#include "cmdline.h" + + + +/* Global options. */ +static unsigned parser_flags = 0; +#ifndef MD4C_USE_ASCII + static unsigned renderer_flags = MD_HTML_FLAG_DEBUG | MD_HTML_FLAG_SKIP_UTF8_BOM; +#else + static unsigned renderer_flags = MD_HTML_FLAG_DEBUG; +#endif +static int want_fullhtml = 0; +static int want_xhtml = 0; +static int want_stat = 0; + + +/********************************* + *** Simple grow-able buffer *** + *********************************/ + +/* We render to a memory buffer instead of directly outputting the rendered + * documents, as this allows using this utility for evaluating performance + * of MD4C (--stat option). This allows us to measure just time of the parser, + * without the I/O. + */ + +struct membuffer { + char* data; + size_t asize; + size_t size; +}; + +static void +membuf_init(struct membuffer* buf, MD_SIZE new_asize) +{ + buf->size = 0; + buf->asize = new_asize; + buf->data = malloc(buf->asize); + if(buf->data == NULL) { + fprintf(stderr, "membuf_init: malloc() failed.\n"); + exit(1); + } +} + +static void +membuf_fini(struct membuffer* buf) +{ + if(buf->data) + free(buf->data); +} + +static void +membuf_grow(struct membuffer* buf, size_t new_asize) +{ + buf->data = realloc(buf->data, new_asize); + if(buf->data == NULL) { + fprintf(stderr, "membuf_grow: realloc() failed.\n"); + exit(1); + } + buf->asize = new_asize; +} + +static void +membuf_append(struct membuffer* buf, const char* data, MD_SIZE size) +{ + if(buf->asize < buf->size + size) + membuf_grow(buf, buf->size + buf->size / 2 + size); + memcpy(buf->data + buf->size, data, size); + buf->size += size; +} + + +/********************** + *** Main program *** + **********************/ + +static void +process_output(const MD_CHAR* text, MD_SIZE size, void* userdata) +{ + membuf_append((struct membuffer*) userdata, text, size); +} + +static int +process_file(FILE* in, FILE* out) +{ + size_t n; + struct membuffer buf_in = {0}; + struct membuffer buf_out = {0}; + int ret = -1; + clock_t t0, t1; + + membuf_init(&buf_in, 32 * 1024); + + /* Read the input file into a buffer. */ + while(1) { + if(buf_in.size >= buf_in.asize) + membuf_grow(&buf_in, buf_in.asize + buf_in.asize / 2); + + n = fread(buf_in.data + buf_in.size, 1, buf_in.asize - buf_in.size, in); + if(n == 0) + break; + buf_in.size += n; + } + + /* Input size is good estimation of output size. Add some more reserve to + * deal with the HTML header/footer and tags. */ + membuf_init(&buf_out, (MD_SIZE)(buf_in.size + buf_in.size/8 + 64)); + + /* Parse the document. This shall call our callbacks provided via the + * md_renderer_t structure. */ + t0 = clock(); + + ret = md_html(buf_in.data, (MD_SIZE)buf_in.size, process_output, (void*) &buf_out, + parser_flags, renderer_flags); + + t1 = clock(); + if(ret != 0) { + fprintf(stderr, "Parsing failed.\n"); + goto out; + } + + /* Write down the document in the HTML format. */ + if(want_fullhtml) { + if(want_xhtml) { + fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + fprintf(out, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" " + "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"); + fprintf(out, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"); + } else { + fprintf(out, "<!DOCTYPE html>\n"); + fprintf(out, "<html>\n"); + } + fprintf(out, "<head>\n"); + fprintf(out, "<title></title>\n"); + fprintf(out, "<meta name=\"generator\" content=\"md2html\"%s>\n", want_xhtml ? " /" : ""); + fprintf(out, "</head>\n"); + fprintf(out, "<body>\n"); + } + + fwrite(buf_out.data, 1, buf_out.size, out); + + if(want_fullhtml) { + fprintf(out, "</body>\n"); + fprintf(out, "</html>\n"); + } + + if(want_stat) { + if(t0 != (clock_t)-1 && t1 != (clock_t)-1) { + double elapsed = (double)(t1 - t0) / CLOCKS_PER_SEC; + if (elapsed < 1) + fprintf(stderr, "Time spent on parsing: %7.2f ms.\n", elapsed*1e3); + else + fprintf(stderr, "Time spent on parsing: %6.3f s.\n", elapsed); + } + } + + /* Success if we have reached here. */ + ret = 0; + +out: + membuf_fini(&buf_in); + membuf_fini(&buf_out); + + return ret; +} + + +static const CMDLINE_OPTION cmdline_options[] = { + { 'o', "output", 'o', CMDLINE_OPTFLAG_REQUIREDARG }, + { 'f', "full-html", 'f', 0 }, + { 'x', "xhtml", 'x', 0 }, + { 's', "stat", 's', 0 }, + { 'h', "help", 'h', 0 }, + { 'v', "version", 'v', 0 }, + + { 0, "commonmark", 'c', 0 }, + { 0, "github", 'g', 0 }, + + { 0, "fcollapse-whitespace", 'W', 0 }, + { 0, "flatex-math", 'L', 0 }, + { 0, "fpermissive-atx-headers", 'A', 0 }, + { 0, "fpermissive-autolinks", 'V', 0 }, + { 0, "fpermissive-email-autolinks", '@', 0 }, + { 0, "fpermissive-url-autolinks", 'U', 0 }, + { 0, "fpermissive-www-autolinks", '.', 0 }, + { 0, "fstrikethrough", 'S', 0 }, + { 0, "ftables", 'T', 0 }, + { 0, "ftasklists", 'X', 0 }, + { 0, "funderline", '_', 0 }, + { 0, "fverbatim-entities", 'E', 0 }, + { 0, "fwiki-links", 'K', 0 }, + + { 0, "fno-html-blocks", 'F', 0 }, + { 0, "fno-html-spans", 'G', 0 }, + { 0, "fno-html", 'H', 0 }, + { 0, "fno-indented-code", 'I', 0 }, + + { 0, NULL, 0, 0 } +}; + +static void +usage(void) +{ + printf( + "Usage: md2html [OPTION]... [FILE]\n" + "Convert input FILE (or standard input) in Markdown format to HTML.\n" + "\n" + "General options:\n" + " -o --output=FILE Output file (default is standard output)\n" + " -f, --full-html Generate full HTML document, including header\n" + " -x, --xhtml Generate XHTML instead of HTML\n" + " -s, --stat Measure time of input parsing\n" + " -h, --help Display this help and exit\n" + " -v, --version Display version and exit\n" + "\n" + "Markdown dialect options:\n" + "(note these are equivalent to some combinations of the flags below)\n" + " --commonmark CommonMark (this is default)\n" + " --github Github Flavored Markdown\n" + "\n" + "Markdown extension options:\n" + " --fcollapse-whitespace\n" + " Collapse non-trivial whitespace\n" + " --flatex-math Enable LaTeX style mathematics spans\n" + " --fpermissive-atx-headers\n" + " Allow ATX headers without delimiting space\n" + " --fpermissive-url-autolinks\n" + " Allow URL autolinks without '<', '>'\n" + " --fpermissive-www-autolinks\n" + " Allow WWW autolinks without any scheme (e.g. 'www.example.com')\n" + " --fpermissive-email-autolinks \n" + " Allow e-mail autolinks without '<', '>' and 'mailto:'\n" + " --fpermissive-autolinks\n" + " Same as --fpermissive-url-autolinks --fpermissive-www-autolinks\n" + " --fpermissive-email-autolinks\n" + " --fstrikethrough Enable strike-through spans\n" + " --ftables Enable tables\n" + " --ftasklists Enable task lists\n" + " --funderline Enable underline spans\n" + " --fwiki-links Enable wiki links\n" + "\n" + "Markdown suppression options:\n" + " --fno-html-blocks\n" + " Disable raw HTML blocks\n" + " --fno-html-spans\n" + " Disable raw HTML spans\n" + " --fno-html Same as --fno-html-blocks --fno-html-spans\n" + " --fno-indented-code\n" + " Disable indented code blocks\n" + "\n" + "HTML generator options:\n" + " --fverbatim-entities\n" + " Do not translate entities\n" + "\n" + ); +} + +static void +version(void) +{ + printf("%d.%d.%d\n", MD_VERSION_MAJOR, MD_VERSION_MINOR, MD_VERSION_RELEASE); +} + +static const char* input_path = NULL; +static const char* output_path = NULL; + +static int +cmdline_callback(int opt, char const* value, void* data) +{ + switch(opt) { + case 0: + if(input_path) { + fprintf(stderr, "Too many arguments. Only one input file can be specified.\n"); + fprintf(stderr, "Use --help for more info.\n"); + exit(1); + } + input_path = value; + break; + + case 'o': output_path = value; break; + case 'f': want_fullhtml = 1; break; + case 'x': want_xhtml = 1; renderer_flags |= MD_HTML_FLAG_XHTML; break; + case 's': want_stat = 1; break; + case 'h': usage(); exit(0); break; + case 'v': version(); exit(0); break; + + case 'c': parser_flags |= MD_DIALECT_COMMONMARK; break; + case 'g': parser_flags |= MD_DIALECT_GITHUB; break; + + case 'E': renderer_flags |= MD_HTML_FLAG_VERBATIM_ENTITIES; break; + case 'A': parser_flags |= MD_FLAG_PERMISSIVEATXHEADERS; break; + case 'I': parser_flags |= MD_FLAG_NOINDENTEDCODEBLOCKS; break; + case 'F': parser_flags |= MD_FLAG_NOHTMLBLOCKS; break; + case 'G': parser_flags |= MD_FLAG_NOHTMLSPANS; break; + case 'H': parser_flags |= MD_FLAG_NOHTML; break; + case 'W': parser_flags |= MD_FLAG_COLLAPSEWHITESPACE; break; + case 'U': parser_flags |= MD_FLAG_PERMISSIVEURLAUTOLINKS; break; + case '.': parser_flags |= MD_FLAG_PERMISSIVEWWWAUTOLINKS; break; + case '@': parser_flags |= MD_FLAG_PERMISSIVEEMAILAUTOLINKS; break; + case 'V': parser_flags |= MD_FLAG_PERMISSIVEAUTOLINKS; break; + case 'T': parser_flags |= MD_FLAG_TABLES; break; + case 'S': parser_flags |= MD_FLAG_STRIKETHROUGH; break; + case 'L': parser_flags |= MD_FLAG_LATEXMATHSPANS; break; + case 'K': parser_flags |= MD_FLAG_WIKILINKS; break; + case 'X': parser_flags |= MD_FLAG_TASKLISTS; break; + case '_': parser_flags |= MD_FLAG_UNDERLINE; break; + + default: + fprintf(stderr, "Illegal option: %s\n", value); + fprintf(stderr, "Use --help for more info.\n"); + exit(1); + break; + } + + return 0; +} + +int +main(int argc, char** argv) +{ + FILE* in = stdin; + FILE* out = stdout; + int ret = 0; + + if(cmdline_read(cmdline_options, argc, argv, cmdline_callback, NULL) != 0) { + usage(); + exit(1); + } + + if(input_path != NULL && strcmp(input_path, "-") != 0) { + in = fopen(input_path, "rb"); + if(in == NULL) { + fprintf(stderr, "Cannot open %s.\n", input_path); + exit(1); + } + } + if(output_path != NULL && strcmp(output_path, "-") != 0) { + out = fopen(output_path, "wt"); + if(out == NULL) { + fprintf(stderr, "Cannot open %s.\n", output_path); + exit(1); + } + } + + ret = process_file(in, out); + if(in != stdin) + fclose(in); + if(out != stdout) + fclose(out); + + return ret; +} |