From 185e2940ee1afc3b196089c92a803144fcff8a82 Mon Sep 17 00:00:00 2001 From: NishiOwO Date: Tue, 10 Mar 2026 03:28:25 +0900 Subject: [PATCH] markdown converter --- objs.mk | 3 + src/action/markdown.c | 463 ++++++++++++++++++++++++++++++++++++++++++ src/default.c | 33 +-- src/main.c | 4 +- src/process.c | 3 +- src/skin/classic.c | 12 +- src/skin/simple.c | 12 +- src/taiga.h | 5 +- src/util.c | 2 +- 9 files changed, 507 insertions(+), 30 deletions(-) create mode 100644 src/action/markdown.c diff --git a/objs.mk b/objs.mk index 548ca21..ae2fe44 100644 --- a/objs.mk +++ b/objs.mk @@ -26,6 +26,9 @@ src/external_xemil_src_xpointer.o: external/xemil/src/xpointer.c OBJS += src/src_action_help.o src/src_action_help.o: src/action/help.c $(CC) $(CFLAGS) -c -o $@ src/action/help.c +OBJS += src/src_action_markdown.o +src/src_action_markdown.o: src/action/markdown.c + $(CC) $(CFLAGS) -c -o $@ src/action/markdown.c OBJS += src/src_action_seed.o src/src_action_seed.o: src/action/seed.c $(CC) $(CFLAGS) -c -o $@ src/action/seed.c diff --git a/src/action/markdown.c b/src/action/markdown.c new file mode 100644 index 0000000..57f1b15 --- /dev/null +++ b/src/action/markdown.c @@ -0,0 +1,463 @@ +#include + +static FILE* out; +static int indent = 0; +static int mode = 0; +static char* title = NULL; +static int in_title = 0; +static int in_section = 0; +static int in_pre = 0; +static char** mapping; +static int mapping_len; + +static void print_indent(void) { + int i; + + for(i = 0; i < indent; i++) fprintf(out, "\t"); +} + +static char* encode(const char* str, int len) { + int actlen = (len == -1 ? strlen(str) : len); + char* r = malloc(actlen * 5 + 1); + int n = 0; + int i; + + for(i = 0; i < actlen; i++) { + if(str[i] == '<') { + r[n++] = '&'; + r[n++] = 'l'; + r[n++] = 't'; + r[n++] = ';'; + } else if(str[i] == '>') { + r[n++] = '&'; + r[n++] = 'g'; + r[n++] = 't'; + r[n++] = ';'; + } else if(str[i] == '&') { + r[n++] = '&'; + r[n++] = 'a'; + r[n++] = 'm'; + r[n++] = 'p'; + r[n++] = ';'; + } else { + r[n++] = str[i]; + } + } + r[n] = 0; + + return r; +} + +static int enter_block(MD_BLOCKTYPE type, void* detail, void* user) { + if(mode == 0 && type == MD_BLOCK_H && ((MD_BLOCK_H_DETAIL*)detail)->level == 1 && title == NULL) { + in_title = 1; + } + + if(!mode) return 0; + + if(type == MD_BLOCK_H) { + MD_BLOCK_H_DETAIL* det = detail; + + if(det->level <= 2) { + if(title != NULL) free(title); + title = NULL; + + in_title = 1; + } else { + print_indent(); + fprintf(out, "\n", det->level); + indent++; + } + } else if(type == MD_BLOCK_P) { + print_indent(); + fprintf(out, "

\n"); + indent++; + } else if(type == MD_BLOCK_UL) { + print_indent(); + fprintf(out, "

    \n"); + indent++; + } else if(type == MD_BLOCK_OL) { + print_indent(); + fprintf(out, "
      \n"); + indent++; + } else if(type == MD_BLOCK_LI) { + print_indent(); + fprintf(out, "
    1. \n"); + indent++; + } else if(type == MD_BLOCK_HR) { + print_indent(); + fprintf(out, "
      \n"); + indent++; + } else if(type == MD_BLOCK_CODE) { + print_indent(); + fprintf(out, "
      ");
      +		indent++;
      +		print_indent();
      +		fprintf(out, "");
      +		indent++;
      +		in_pre = 1;
      +	} else if(type == MD_BLOCK_TABLE) {
      +		print_indent();
      +		fprintf(out, "\n");
      +		indent++;
      +	} else if(type == MD_BLOCK_TR) {
      +		print_indent();
      +		fprintf(out, "\n");
      +		indent++;
      +	} else if(type == MD_BLOCK_TH) {
      +		print_indent();
      +		fprintf(out, "
      \n"); + indent++; + } else if(type == MD_BLOCK_TD) { + print_indent(); + fprintf(out, "\n"); + indent++; + } else if(type == MD_BLOCK_QUOTE) { + print_indent(); + fprintf(out, "
      \n"); + indent++; + } + return 0; +} + +static int leave_block(MD_BLOCKTYPE type, void* detail, void* user) { + if(mode == 0 && type == MD_BLOCK_H && ((MD_BLOCK_H_DETAIL*)detail)->level == 1 && in_title) { + in_title = 0; + } + + if(!mode) return 0; + + if(type == MD_BLOCK_H) { + MD_BLOCK_H_DETAIL* det = detail; + + in_title = 0; + + if(det->level == 2) { + char* buf; + + if(in_section) { + indent--; + print_indent(); + fprintf(out, "\n"); + } + + buf = encode(title == NULL ? "" : title, -1); + + print_indent(); + fprintf(out, "
      \n", buf); + indent++; + + in_section = 1; + + free(buf); + } else if(det->level != 1) { + indent--; + print_indent(); + fprintf(out, "\n", det->level); + } + } else if(type == MD_BLOCK_P) { + indent--; + print_indent(); + fprintf(out, "

      \n"); + } else if(type == MD_BLOCK_UL) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_BLOCK_OL) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_BLOCK_LI) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_BLOCK_CODE) { + indent--; + print_indent(); + fprintf(out, "\n"); + indent--; + print_indent(); + fprintf(out, "\n"); + in_pre = 0; + } else if(type == MD_BLOCK_TABLE) { + indent--; + print_indent(); + fprintf(out, "
      \n"); + } else if(type == MD_BLOCK_TR) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_BLOCK_TH) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_BLOCK_TD) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_BLOCK_QUOTE) { + indent--; + print_indent(); + fprintf(out, "\n"); + } + + return 0; +} + +static int enter_span(MD_SPANTYPE type, void* detail, void* user) { + if(!mode) return 0; + + if(type == MD_SPAN_EM) { + print_indent(); + fprintf(out, "\n"); + indent++; + } else if(type == MD_SPAN_STRONG) { + print_indent(); + fprintf(out, "\n"); + indent++; + } else if(type == MD_SPAN_A) { + MD_SPAN_A_DETAIL* det = detail; + char* href = encode(det->href.text, det->href.size); + int i; + + for(i = 0; i < mapping_len; i++) { + char* key = u_strdup(mapping[i]); + char* value = strchr(key, '='); + + if(value != NULL) { + value[0] = 0; + value++; + } + + if(value != NULL && strcmp(key, href) == 0) { + free(href); + href = encode(value, -1); + } + + free(key); + } + + print_indent(); + fprintf(out, "\n", href); + indent++; + + free(href); + } else if(type == MD_SPAN_IMG) { + MD_SPAN_IMG_DETAIL* det = detail; + char* src = encode(det->src.text, det->src.size); + + print_indent(); + fprintf(out, "\"",\n"); + indent++; + } else if(type == MD_SPAN_DEL) { + print_indent(); + fprintf(out, "\n"); + indent++; + } else if(type == MD_SPAN_U) { + print_indent(); + fprintf(out, "\n"); + indent++; + } + + return 0; +} + +static int leave_span(MD_SPANTYPE type, void* detail, void* user) { + if(!mode) return 0; + + if(type == MD_SPAN_EM) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_SPAN_STRONG) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_SPAN_A) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_SPAN_IMG) { + char* buf = encode(title, -1); + + fprintf(out, "%s\" />\n", buf); + free(buf); + + in_title = 0; + } else if(type == MD_SPAN_CODE) { + indent--; + print_indent(); + fprintf(out, "
      \n"); + } else if(type == MD_SPAN_DEL) { + indent--; + print_indent(); + fprintf(out, "\n"); + } else if(type == MD_SPAN_U) { + indent--; + print_indent(); + fprintf(out, "\n"); + } + + return 0; +} + +static int text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* user) { + if(in_title && (type == MD_TEXT_NORMAL || type == MD_TEXT_ENTITY)) { + if(title == NULL) { + title = malloc(size + 1); + + memcpy(title, text, size); + title[size] = 0; + } else { + char* old = title; + char* buf = malloc(size + 1); + + memcpy(buf, text, size); + buf[size] = 0; + + title = u_strvacat(old, " ", buf, NULL); + free(old); + + free(buf); + } + } + + if(!mode) return 0; + + if(in_title) return 0; + + if(type == MD_TEXT_BR) { + print_indent(); + fprintf(out, "
      \n"); + } else if(type == MD_TEXT_NORMAL || type == MD_TEXT_ENTITY || type == MD_TEXT_CODE) { + char* buf; + + print_indent(); + + buf = encode(text, size); + fwrite(buf, 1, strlen(buf), out); + free(buf); + + fprintf(out, "\n"); + } + + return 0; +} + +int action_markdown(int argc, char** argv) { + FILE* f; + char* buffer; + int size; + MD_PARSER parser; + char* argin = NULL; + char* argout = NULL; + int i; + + memset(&parser, 0, sizeof(parser)); + parser.flags = MD_FLAG_COLLAPSEWHITESPACE | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_UNDERLINE; + parser.enter_block = enter_block; + parser.leave_block = leave_block; + parser.enter_span = enter_span; + parser.leave_span = leave_span; + parser.text = text; + + mapping = NULL; + mapping_len = 0; + for(i = 0; i < argc; i++) { + if(argin == NULL) { + argin = argv[i]; + } else if(argout == NULL) { + argout = argv[i]; + } else if(mapping == NULL) { + mapping = &argv[i]; + mapping_len = argc - i; + } + } + + if(argin == NULL || argout == NULL) { + fprintf(stderr, "Usage: markdown input output [linksrc=linkdest ...]\n"); + return 1; + } + + if((f = fopen(argin, "r")) == NULL) { + fprintf(stderr, "Failed to open input\n"); + return 1; + } + + if((out = fopen(argout, "w")) == NULL) { + fprintf(stderr, "Failed to open output\n"); + return 1; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + buffer = malloc(size); + fread(buffer, 1, size, f); + fclose(f); + + fprintf(out, "\n"); + indent++; + + in_title = 0; + in_section = 0; + in_pre = 0; + mode = 0; + if(md_parse(buffer, size, &parser, NULL)) { + free(buffer); + fclose(out); + remove(argout); + fprintf(stderr, "Failed to parse Markdown\n"); + return 1; + } + + fprintf(out, "
      \n"); + indent++; + if(title != NULL) fprintf(out, " %s\n", title); + fprintf(out, "
      \n"); + indent--; + + fprintf(out, " \n"); + indent++; + + in_title = 0; + in_section = 0; + in_pre = 0; + mode = 1; + if(md_parse(buffer, size, &parser, NULL)) { + free(buffer); + fclose(out); + remove(argout); + fprintf(stderr, "Failed to parse Markdown\n"); + return 1; + } + + if(in_section) { + indent--; + print_indent(); + fprintf(out, "\n"); + } + + fprintf(out, " \n"); + indent--; + fprintf(out, "
      \n"); + indent--; + + if(title != NULL) free(title); + + free(buffer); + + fclose(out); + + return 0; +} diff --git a/src/default.c b/src/default.c index 6cb13ae..ad811ff 100644 --- a/src/default.c +++ b/src/default.c @@ -1,17 +1,15 @@ #include -static void print(FILE* out, const char* txt, int indent) { +static void print(FILE* out, const char* txt, int pre, int indent) { int i; for(i = 0;; i++) { if(i == 0 || txt[i] == '\n' || txt[i] == 0) { int j; - if(i > 0) - fprintf(out, "\n"); - if(txt[i] == 0) - break; - for(j = 0; j < indent; j++) - fprintf(out, "\t"); + if(pre ? (txt[i] == '\n') : (i > 0)) fprintf(out, "\n"); + if(txt[i] == 0) break; + if(!pre) + for(j = 0; j < indent; j++) fprintf(out, "\t"); } if(txt[i] != '\n') fprintf(out, "%c", txt[i]); @@ -70,7 +68,7 @@ void default_head(FILE* out, const char* top, xl_node_t* element, int indent) { sprintf(tag, "<%s%s>", element->name, text); } - if(tag[0] != 0) print(out, tag, indent); + if(tag[0] != 0) print(out, tag, 0, indent); child = element->first_child; while(child != NULL) { @@ -86,11 +84,10 @@ void default_head(FILE* out, const char* top, xl_node_t* element, int indent) { child = child->next; } - if(end[0] != 0) - print(out, end, indent); + if(end[0] != 0) print(out, end, 0, indent); } -void default_body(FILE* out, const char* top, xl_node_t* element, int spec, int indent) { +void default_body(FILE* out, const char* top, xl_node_t* element, int spec, int pre, int indent) { xl_node_t* child; char tag[2048]; /* enough for most cases... :) */ char end[128]; @@ -149,11 +146,14 @@ void default_body(FILE* out, const char* top, xl_node_t* element, int spec, int add = 3; } else if(strcmp(element->name, "p") == 0 || /**/ strcmp(element->name, "code") == 0 || /**/ + strcmp(element->name, "pre") == 0 || /**/ strcmp(element->name, "blockquote") == 0 || /**/ strcmp(element->name, "li") == 0 || /**/ strcmp(element->name, "dt") == 0 || /**/ strcmp(element->name, "dd") == 0 || /**/ strcmp(element->name, "strong") == 0 || /**/ + strcmp(element->name, "del") == 0 || /**/ + strcmp(element->name, "u") == 0 || /**/ strcmp(element->name, "sub") == 0 || /**/ strcmp(element->name, "sup") == 0 || /**/ strcmp(element->name, "tr") == 0 || /**/ @@ -166,6 +166,8 @@ void default_body(FILE* out, const char* top, xl_node_t* element, int spec, int strcmp(element->name, "h6") == 0) { accept_attr(top, text, 0, element, "id", "class", NULL); + if(strcmp(element->name, "pre") == 0) pre = 1; + sprintf(tag, "<%s%s>", element->name, text); sprintf(end, "", element->name); @@ -231,20 +233,21 @@ void default_body(FILE* out, const char* top, xl_node_t* element, int spec, int sprintf(tag, "<%s%s>", element->name, text); } - if(tag[0] != 0) print(out, tag, indent); + if(tag[0] != 0) print(out, tag, pre, indent); child = element->first_child; while(child != NULL) { if(child->type == XL_NODE_NODE && child->name != NULL) { - default_body(out, top, child, spec, indent + 1 + add); + default_body(out, top, child, spec, pre, indent + 1 + add); } else if(child->type == XL_NODE_TEXT && child->text != NULL) { - print(out, child->text, indent + 1 + add); + print(out, child->text, pre, pre ? 0 : (indent + 1 + add)); } child = child->next; } - if(end[0] != 0) print(out, end, indent); + if(end[0] != 0) print(out, end, pre, indent); + if(strcmp(end, "
      ") == 0) fprintf(out, "\n"); } void default_nav(FILE* out, const char* top, xl_node_t* element, int indent) { diff --git a/src/main.c b/src/main.c index 8a21fb9..424912a 100644 --- a/src/main.c +++ b/src/main.c @@ -7,7 +7,7 @@ int main(int argc, char** argv) { char** target_argv = malloc(sizeof(*target_argv)); target_argv[0] = NULL; - printf("Taiga, static website generator - run `%s help' for help\n\n", argv[0]); + fprintf(stderr, "Taiga, static website generator - run `%s help' for help\n\n", argv[0]); for(i = 1; i < argc; i++) { if(argv[i][0] == '-') { @@ -30,6 +30,8 @@ int main(int argc, char** argv) { return action_help(target_argc, target_argv); } else if(strcmp(action, "seed") == 0) { return action_seed(target_argc, target_argv); + } else if(strcmp(action, "markdown") == 0) { + return action_markdown(target_argc, target_argv); } fprintf(stderr, "Unknown action\n"); diff --git a/src/process.c b/src/process.c index 22f6e95..5443e56 100644 --- a/src/process.c +++ b/src/process.c @@ -46,8 +46,7 @@ static int parse_file(const char* top, const char* full) { node = node->next; } - if(title == NULL) - title = u_strdup("Untitled"); + if(title == NULL) title = u_strdup("Untitled"); fprintf(f, "\n"); fprintf(f, "\n"); diff --git a/src/skin/classic.c b/src/skin/classic.c index bec8779..08220a6 100644 --- a/src/skin/classic.c +++ b/src/skin/classic.c @@ -323,11 +323,13 @@ void classic_body(FILE* out, const char* top, const char* title, xl_node_t* body fprintf(out, " \n"); fprintf(out, "
      \n"); - child = body->first_child; - while(child != NULL) { - default_body(out, top, child, 0, 6); + if(body != NULL) { + child = body->first_child; + while(child != NULL) { + default_body(out, top, child, 0, 0, 6); - child = child->next; + child = child->next; + } } fprintf(out, "
      \n"); @@ -336,7 +338,7 @@ void classic_body(FILE* out, const char* top, const char* title, xl_node_t* body if((nodes = xl_get_path(skinconf->root, "footer")) != NULL) { child = nodes[0]->first_child; while(child != NULL) { - default_body(out, top, child, 1, 6); + default_body(out, top, child, 1, 0, 6); child = child->next; } diff --git a/src/skin/simple.c b/src/skin/simple.c index 4f0338b..d9394f2 100644 --- a/src/skin/simple.c +++ b/src/skin/simple.c @@ -334,11 +334,13 @@ void simple_body(FILE* out, const char* top, const char* title, xl_node_t* body) fprintf(out, " \n"); fprintf(out, " \n"); - child = body->first_child; - while(child != NULL) { - default_body(out, top, child, 0, 5); + if(body != NULL) { + child = body->first_child; + while(child != NULL) { + default_body(out, top, child, 0, 0, 5); - child = child->next; + child = child->next; + } } fprintf(out, "
      \n"); @@ -346,7 +348,7 @@ void simple_body(FILE* out, const char* top, const char* title, xl_node_t* body) if((nodes = xl_get_path(skinconf->root, "footer")) != NULL) { child = nodes[0]->first_child; while(child != NULL) { - default_body(out, top, child, 1, 6); + default_body(out, top, child, 1, 0, 6); child = child->next; } diff --git a/src/taiga.h b/src/taiga.h index 8033e8e..1973a3a 100644 --- a/src/taiga.h +++ b/src/taiga.h @@ -62,6 +62,9 @@ int action_help(int argc, char** argv); /* action/seed.c */ int action_seed(int argc, char** argv); +/* action/markdown.c */ +int action_markdown(int argc, char** argv); + /* util.c */ char* u_strvacat(const char* str, ...); char* u_strdup(const char* str); @@ -78,7 +81,7 @@ int process(const char* top, const char* out, const char* full); /* default.c */ void default_head(FILE* out, const char* top, xl_node_t* element, int indent); -void default_body(FILE* out, const char* top, xl_node_t* element, int spec, int indent); /* pass non-zero to spec if you want links to be relative from top */ +void default_body(FILE* out, const char* top, xl_node_t* element, int spec, int pre, int indent); /* pass non-zero to spec if you want links to be relative from top */ void default_nav(FILE* out, const char* top, xl_node_t* element, int indent); /*** skins ***/ diff --git a/src/util.c b/src/util.c index 0072da2..7bb1532 100644 --- a/src/util.c +++ b/src/util.c @@ -131,7 +131,7 @@ char* u_section_id(xl_node_t* node) { id = malloc(64); - sprintf(id, "%d-%d", unique, crc32(name, strlen(name))); + sprintf(id, "section%d-%d", unique, crc32(name, strlen(name))); } return id;