// retoor <retoor@molodetz.nl>
#include "markdown.h"
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// --- ANSI Escape Codes ---
#define RESET "\033[0m"
#define BOLD "\033[1m"
#define ITALIC "\033[3m"
#define STRIKETHROUGH "\033[9m"
#define FG_YELLOW "\033[33m"
#define FG_BLUE "\033[34m"
#define FG_CYAN "\033[36m"
#define FG_MAGENTA "\033[35m"
#define BG_YELLOW_FG_BLACK "\033[43;30m"
/**
* @brief Checks if a given word is a programming language keyword.
*/
static int is_keyword(const char *word) {
const char *keywords[] = {
"int", "float", "double", "char", "void", "if", "else", "while", "for",
"return", "struct", "printf", "let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub",
"const", "static", "def", "class", "import", "from", "as", "with", "try", "except",
"finally", "lambda", "async", "await", "public", "private", "protected", "interface", "extends",
"implements", "new", "synchronized", "var", "switch", "case", "break", "continue",
"namespace", "template", "typename", "virtual", "override", "friend", "package", "func", "type", "go", "defer", "select",
"then", "elif", "fi", "esac", "do", "done", "using"};
for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
if (strcmp(word, keywords[i]) == 0) return 1;
}
return 0;
}
void highlight_code(const char *code) {
const char *ptr = code;
char buffer[4096];
size_t index = 0;
while (*ptr) {
if (isalpha((unsigned char)*ptr) || *ptr == '_') {
while (isalnum((unsigned char)*ptr) || *ptr == '_') {
if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++;
else ptr++;
}
buffer[index] = '\0';
if (is_keyword(buffer)) printf(FG_BLUE "%s" RESET FG_YELLOW, buffer);
else printf("%s", buffer);
index = 0;
} else if (isdigit((unsigned char)*ptr)) {
while (isdigit((unsigned char)*ptr)) {
if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++;
else ptr++;
}
buffer[index] = '\0';
printf(FG_CYAN "%s" RESET FG_YELLOW, buffer);
index = 0;
} else {
putchar(*ptr);
ptr++;
}
}
}
void parse_markdown_to_ansi(const char *markdown) {
const char *ptr = markdown;
bool is_start_of_line = true;
while (*ptr) {
if (is_start_of_line && strncmp(ptr, "```", 3) == 0) {
ptr += 3;
while (*ptr && *ptr != '\n') ptr++;
if (*ptr) ptr++;
const char *code_start = ptr;
const char *code_end = strstr(code_start, "```");
if (code_end) {
char *block_buffer = strndup(code_start, (size_t)(code_end - code_start));
printf(FG_YELLOW);
highlight_code(block_buffer);
printf(RESET);
free(block_buffer);
ptr = code_end + 3;
if (*ptr == '\n') ptr++;
is_start_of_line = true;
continue;
} else {
printf(FG_YELLOW);
highlight_code(code_start);
printf(RESET);
break;
}
}
if (is_start_of_line) {
const char *line_start_ptr = ptr;
int indent_level = 0;
while (*ptr == ' ') { indent_level++; ptr++; }
bool block_processed = true;
if (strncmp(ptr, "###### ", 7) == 0) { printf(BOLD FG_YELLOW); ptr += 7; }
else if (strncmp(ptr, "##### ", 6) == 0) { printf(BOLD FG_YELLOW); ptr += 6; }
else if (strncmp(ptr, "#### ", 5) == 0) { printf(BOLD FG_YELLOW); ptr += 5; }
else if (strncmp(ptr, "### ", 4) == 0) { printf(BOLD FG_YELLOW); ptr += 4; }
else if (strncmp(ptr, "## ", 3) == 0) { printf(BOLD FG_YELLOW); ptr += 3; }
else if (strncmp(ptr, "# ", 2) == 0) { printf(BOLD FG_YELLOW); ptr += 2; }
else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) && (*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) {
printf(FG_CYAN "─────────────────────────────────────────────────────────────────────────" RESET "\n");
ptr += 3; if (*ptr == '\n') ptr++; is_start_of_line = true; continue;
} else if (strncmp(ptr, "> ", 2) == 0) {
for (int i = 0; i < indent_level; i++) putchar(' ');
printf(ITALIC FG_CYAN "" RESET); ptr += 2; is_start_of_line = false; continue;
} else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') && *(ptr + 1) == ' ') {
for (int i = 0; i < indent_level; i++) putchar(' ');
printf(FG_MAGENTA "" RESET); ptr += 2; is_start_of_line = false; continue;
} else {
const char *temp_ptr = ptr;
while (isdigit((unsigned char)*temp_ptr)) temp_ptr++;
if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') {
for (int i = 0; i < indent_level; i++) putchar(' ');
printf(FG_MAGENTA); fwrite(ptr, 1, (size_t)(temp_ptr - ptr) + 1, stdout); printf(" " RESET);
ptr = temp_ptr + 2; is_start_of_line = false; continue;
} else { block_processed = false; ptr = line_start_ptr; }
}
if (block_processed) {
while (*ptr && *ptr != '\n') putchar(*ptr++);
printf(RESET "\n"); if (*ptr == '\n') ptr++;
is_start_of_line = true; continue;
}
}
if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) {
const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___";
printf(BOLD ITALIC); ptr += 3;
const char *end = strstr(ptr, marker);
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 3; }
else { fputs(ptr, stdout); ptr += strlen(ptr); }
printf(RESET); continue;
}
if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) {
const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__";
printf(BOLD); ptr += 2;
const char *end = strstr(ptr, marker);
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; }
else { fputs(ptr, stdout); ptr += strlen(ptr); }
printf(RESET); continue;
}
if (strncmp(ptr, "~~", 2) == 0) {
printf(STRIKETHROUGH); ptr += 2;
const char *end = strstr(ptr, "~~");
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; }
else { fputs(ptr, stdout); ptr += strlen(ptr); }
printf(RESET); continue;
}
if (strncmp(ptr, "==", 2) == 0) {
printf(BG_YELLOW_FG_BLACK); ptr += 2;
const char *end = strstr(ptr, "==");
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; }
else { fputs(ptr, stdout); ptr += strlen(ptr); }
printf(RESET); continue;
}
if (*ptr == '`' && *(ptr + 1) != '`') {
printf(FG_YELLOW); ptr++; const char *start = ptr;
while (*ptr && *ptr != '`') ptr++;
fwrite(start, 1, (size_t)(ptr - start), stdout); if (*ptr == '`') ptr++;
printf(RESET); continue;
}
if (*ptr == '[') {
const char *text_start = ptr + 1;
const char *text_end = strchr(text_start, ']');
if (text_end && *(text_end + 1) == '(') {
const char *url_start = text_end + 2;
const char *url_end = strchr(url_start, ')');
if (url_end) {
printf(FG_BLUE); fwrite(text_start, 1, (size_t)(text_end - text_start), stdout);
printf(RESET " ("); printf(ITALIC FG_CYAN);
fwrite(url_start, 1, (size_t)(url_end - url_start), stdout);
printf(RESET ")"); ptr = url_end + 1; continue;
}
}
}
if (*ptr == '\n') is_start_of_line = true;
else if (!isspace((unsigned char)*ptr)) is_start_of_line = false;
putchar(*ptr);
ptr++;
}
}