/*
* Source: https://github.com/kukrimate/md2html
*
* Copyright (c) 2020, Máté Kukri
*
* Permission to use, copy, modify, and/or 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.
*/
#include "stdio/stdio.h"
int
peak(FILE *fp)
{
int ch;
ungetc((ch = fgetc(fp)), fp);
return ch;
}
int
next(FILE *fp, int want)
{
int ch;
ch = fgetc(fp);
if (ch != want)
ungetc(ch, fp);
return ch;
}
char *
nextstr(FILE *fp, char *want)
{
char *cur;
int ch;
for (cur = want; *cur; ++cur)
if ((ch = fgetc(fp)) != *cur)
goto fail;
return want;
fail:
ungetc(ch, fp);
while (--cur >= want)
ungetc(*cur, fp);
return NULL;
}
void
skip_white(FILE *fp)
{
int ch;
while ((ch = fgetc(fp)) != EOF)
switch (ch) {
case ' ':
case '\t':
case '\r':
case '\n':
case '\v':
case '\f':
break;
default:
ungetc(ch, fp);
return;
}
}
void
code(FILE *fp)
{
int ch;
printf("");
while ((ch = fgetc(fp)) != EOF)
switch (ch) {
case '`':
goto end;
default:
putchar(ch);
}
end:
printf("
");
}
void
esccode(FILE *fp)
{
int ch;
printf("");
while ((ch = fgetc(fp)) != EOF)
switch (ch) {
case '`':
if (next(fp, '`') == '`')
goto end;
default:
putchar(ch);
}
end:
printf("
");
}
void
codeblock(FILE *fp)
{
int ch;
skip_white(fp);
printf("
");
while ((ch = fgetc(fp)) != EOF)
switch (ch) {
case '`':
if (nextstr(fp, "``"))
goto end;
default:
putchar(ch);
}
end:
printf("
\n");
}
void
heading(FILE *fp, int lvl)
{
int ch;
skip_white(fp);
printf(""); while ((ch = fgetc(fp)) != EOF) switch (ch) { case '\n': /* End of blockquote */ if (next(fp, '\n') == '\n') goto end; /* Next top level element */ switch (peak(fp)) { case '#': /* Heading */ case '*': /* List */ goto end; case '`': /* Code block */ if (nextstr(fp, "```")) { ungetc('`', fp); ungetc('`', fp); ungetc('`', fp); goto end; } } /* Ignore > on subsequent lines */ if (next(fp, '>') == '>') break; default: putchar(ch); } end: printf("\n"); } void list(FILE *fp) { int ch; printf("
"); while ((ch = fgetc(fp)) != EOF) switch (ch) { case '`': if (next(fp, '`') == '`') /* Escaped code */ esccode(fp); else /* Normal code */ code(fp); break; case '\n': /* End of paragraph */ if (next(fp, '\n') == '\n') goto end; /* Next top level element */ switch (peak(fp)) { case '#': /* Heading */ case '>': /* Blockquote */ case '*': /* List */ goto end; case '`': /* Code block */ if (nextstr(fp, "```")) { ungetc('`', fp); ungetc('`', fp); ungetc('`', fp); goto end; } } default: putchar(ch); } end: printf("
\n"); } void md2html(FILE *fp) { int ch, cnt; for (;;) switch ((ch = fgetc(fp))) { case EOF: return; /* Consume newlines at the start of top level blocks */ case '\n': break; case '#': cnt = 1; while (next(fp, '#') == '#') ++cnt; heading(fp, cnt); break; case '>': blockquote(fp); break; case '*': list(fp); break; case '`': if (nextstr(fp, "``")) { codeblock(fp); break; } default: ungetc(ch, fp); paragraph(fp); } } int main(int argc, char *argv[]) { FILE *fp; if (argc < 2) { fp = stdin; } else { if (!(fp = fopen(argv[1], "r"))) { perror(argv[1]); return 1; } } md2html(fp); fclose(fp); return 0; }