#include #include #include #include #include #include /* input/output streams */ FILE *IN; FILE *OUT; /* util */ void die(char *msg) { fprintf(stderr, "%s\n", msg); exit(1); } /* header/footer */ void write_file(char *input) { FILE *f = fopen(input, "r"); char c; while ((c = fgetc(f)) != EOF) fputc(c, OUT); fclose(f); } /* remove dangerous characters */ void emit_sanitized_char(char c) { switch (c) { case '<': fprintf(OUT, "<"); break; case '>': fprintf(OUT, ">"); break; case '&': fprintf(OUT, "&"); break; case '\'': fprintf(OUT, "'"); break; case '\"': fprintf(OUT, """); break; default: fputc(c, OUT); } } void emit_sanitized_string(char *str) { while (*str != '\0') { emit_sanitized_char(*str); str++; } } /* read n bytes from IN */ void read_n_bytes(size_t n, char *buf) { size_t i; for (i = 0; i < n; i++) buf[i] = fgetc(IN); buf[i] = '\0'; } /* remove occurrences of c from the right side of buf */ void rstrip(char *buf, char c) { size_t i = strlen(buf) - 1; for(;;) { if (buf[i] == c) buf[i] = '\0'; else if (i <= 0 || buf[i] != c) break; i--; } } /* headings */ int heading_level(void) { int heading_level = 1; while (fgetc(IN) == '*') heading_level++; return heading_level; } void emit_heading(void) { int level = heading_level(); fprintf(OUT, "", level); char c; while ((c = fgetc(IN)) != '\n') emit_sanitized_char(c); fprintf(OUT, "", level); /* We want the newline we used to break the loop to be picked up by the * formatter again. */ fseek(IN, -1, SEEK_CUR); } /* links */ bool external_link_p(void) { char buf[5] = {0}; fgets(buf, 5, IN); fseek(IN, -4, SEEK_CUR); return !strcmp(buf, "http"); } bool internal_implicit_link_p(void) { int count = 0; bool ret = true; while (1) { char c = fgetc(IN); if (c == EOF) { die("End of file encountered while parsing link."); } else if (c == '}') { break; } else if (c == ' ') { ret = false; break; } count--; } fseek(IN, count, SEEK_CUR); return ret; } void get_linkname(char *out) { char c; while ((c = fgetc(IN)) != '}') { if (c == EOF) die("End of file encountered while parsing link name."); else if (c == '\n') c = ' '; *(out++) = c; } *out = '\0'; } void get_link(char *out) { char c; /* some links add the initial { for some reason */ if (fgetc(IN) != '{') fseek(IN, -1L, SEEK_CUR); while (((c = fgetc(IN)) != ' ') && c != '\n' && c != '}') { if (c == EOF) die("End of file encountered while parsing link."); *(out)++ = c; } *out = '\0'; } void emit_external_link(void) { char link[1024]; get_link(link); char linkname[1024]; get_linkname(linkname); fprintf(OUT, "%s", link, linkname); } void emit_internal_implicit_link(void) { fseek(IN, -1, SEEK_CUR); char link[1024]; char page[1024]; get_link(link); memcpy(page, link, 1024); for (size_t i = 0; link[i] != '\0'; i++) if (link[i] >= 'A' && link[i] <= 'Z') link[i] = link[i] + 32; for (size_t i = 0; page[i] != '\0'; i++) if (page[i] == '-') page[i] = ' '; fprintf(OUT, "%s", link, page); } bool explicit_link_extension(char *link) { while (*link != '\0') { if (*link++ == '.') return true; } return false; } void emit_internal_explicit_link(void) { fseek(IN, -1, SEEK_CUR); char page[1024]; get_link(page); char linkname[1024]; get_linkname(linkname); if (explicit_link_extension(page)) fprintf(OUT, "%s", page, linkname); else fprintf(OUT, "%s", page, linkname); } void emit_link(void) { if (external_link_p()) emit_external_link(); else if (internal_implicit_link_p()) emit_internal_implicit_link(); else emit_internal_explicit_link(); } /* code blocks */ void emit_code_block(char firstc) { char nextc = fgetc(IN); fseek(IN, -1L, SEEK_CUR); bool pre = firstc == '\n' && nextc =='\n'; /* if this code block is on a separate line, create a
. otherwise, it
     * is inlined in a paragraph and we want a . */
    if (pre)
        fprintf(OUT, "
");
    else
        fprintf(OUT, "");

    for (;;) {
        char c = fgetc(IN);

        if (c == '`') {
            if (fgetc(IN) == '`')
                break;
            else
                fseek(IN, -1L, SEEK_CUR);
        }

        if (c == EOF)
            die("Unexpected EOF while converting code block.");
        emit_sanitized_char(c);
    }

    if (pre)
        fprintf(OUT, "
"); else fprintf(OUT, ""); } /* blockquotes */ void emit_blockquote(void) { fprintf(OUT, "
"); char c = fgetc(IN), lastc = 'a'; for(;;) { if (lastc == '\n' && c != '>') break; else if (lastc == '>' && c == ' ') ; else if (lastc == '\n' && c == '>') fputc(' ', OUT); else if (c != '\n') emit_sanitized_char(c); lastc = c; c = fgetc(IN); } fseek(IN, -1L, SEEK_CUR); fprintf(OUT, "
\n"); } void emit_image(void) { char buf[80]; char *bufptr = buf; for(;;) { char c = fgetc(IN); if (isspace(c)) break; else if (c == EOF) die("EOF encountered while parsing image filename."); else *(bufptr++) = c; } *bufptr = '\0'; fprintf(OUT, "", buf); fseek(IN, -1L, SEEK_CUR); } void emit_table(void) { char buf[256] = {0}; char *bufptr = buf; fprintf(OUT, "\n"); for(;;) { read_n_bytes(4, buf); if (!strcmp("===\n", buf)) { fseek(IN, -1L, SEEK_CUR); break; } else { fseek(IN, -4L, SEEK_CUR); memset(buf, '\0', 256); fprintf(OUT, "\n"); char c; while ((c = fgetc(IN)) != '\n') { if (c == '|') { rstrip(buf, ' '); fprintf(OUT, "\n"); memset(buf, '\0', 256); bufptr = buf; } else { *(bufptr++) = c; } } rstrip(buf, ' '); fprintf(OUT, "\n"); fprintf(OUT, "\n"); bufptr = buf; } } fprintf(OUT, "
"); emit_sanitized_string(buf); fprintf(OUT, ""); emit_sanitized_string(buf); fprintf(OUT, "
\n"); } /* main formatter */ void format(void) { char c, lastc = '\n'; char *em_tags[] = {"", ""}; int in_em = 0; /* boolean used as index, therefore it's an int */ bool last_was_paragraph = false; while ((c = fgetc(IN)) != EOF) { if (c == '\\') { /* escaped character */ emit_sanitized_char(fgetc(IN)); } else if (c == '*' && lastc == '\n') { /* heading */ emit_heading(); } else if (c == '{') { /* link */ if (!last_was_paragraph && lastc == '\n') fprintf(OUT, "\n

"); emit_link(); } else if (c == '$') { /* maybe image */ char buf[5]; read_n_bytes(4, buf); if (!strcmp(buf, "img ")) { emit_image(); } else { fseek(IN, -4L, SEEK_CUR); emit_sanitized_char(c); } } else if (c == '_') { fprintf(OUT, "%s", em_tags[in_em]); in_em = !in_em; } else if (c == '`') { /* code block */ if (fgetc(IN) == '`') emit_code_block(lastc); else fseek(IN, -1L, SEEK_CUR); } else if (c == '>' && lastc == '\n') { /* quote */ emit_blockquote(); } else if (c == '=' && lastc == '\n') { /* maybe table */ char buf[4]; read_n_bytes(3, buf); if (!strcmp(buf, "==\n")) { emit_table(); } else { fseek(IN, -3L, SEEK_CUR); emit_sanitized_char(c); } } else if (lastc == '\n' && c == '\n') { /* new paragraph */ fprintf(OUT, "\n

"); last_was_paragraph = true; lastc = c; continue; } else if (c == '\n') { /* just a new line, same paragraph */ fputc(' ', OUT); } else { emit_sanitized_char(c); } lastc = c; last_was_paragraph = false; } } void basename(char *in, char *out, char delim) { for (size_t i = 0; in[i] != delim; i++) out[i] = in[i]; } /* entrypoint */ int main(int argc, char *argv[]) { if (argc < 2) die("No filename provided, exiting."); IN = fopen(argv[1], "r"); if (argc == 2) { char bn[80] = {0}; basename(argv[1], bn, '.'); char out_name[85] = {0}; sprintf(out_name, "%s.html", bn); OUT = fopen(out_name, "w"); } else { OUT = fopen(argv[2], "w"); } write_file("resources/header.html"); format(); write_file("resources/footer.html"); fclose(IN); fclose(OUT); }