#include "table_stats.h" #include "line_index.h" #include int table_is_separator_row(const char *line, size_t len) { int pipes = 0; int dashes = 0; for (size_t i = 0; i < len; i++) { char c = line[i]; if (c == '|') pipes++; else if (c == '-' || c == ':' || c == ' ') dashes++; else if (c != '\r') return 0; } return pipes >= 1 && dashes > 0; } /* GFM: leading/trailing '|' are delimiters, not empty cells. */ int table_count_cols(const char *line, size_t len, int *has_pipe) { if (has_pipe) *has_pipe = 0; while (len > 0 && line[len - 1] == '\r') len--; size_t start = 0; size_t end = len; while (start < end && (line[start] == ' ' || line[start] == '\t')) start++; if (start < end && line[start] == '|') { start++; if (has_pipe) *has_pipe = 1; } while (end > start && (line[end - 1] == ' ' || line[end - 1] == '\t')) end--; if (end > start && line[end - 1] == '|') { end--; if (has_pipe) *has_pipe = 1; } if (start >= end) return 1; int cols = 1; for (size_t i = start; i < end; i++) { if (line[i] == '|') { cols++; if (has_pipe) *has_pipe = 1; } } return cols > SASHIDOWN_MAX_COLS ? SASHIDOWN_MAX_COLS : cols; } static size_t te_line_end(sashidown_ctx_t *ctx, const char *input, size_t len, size_t pos) { if (ctx && ctx->line_ix) return line_end_ix((line_index_t *)ctx->line_ix, input, len, pos); const char *hit = memchr(input + pos, '\n', len - pos); return hit ? (size_t)(hit - input) : len; } static size_t te_skip_line(sashidown_ctx_t *ctx, const char *input, size_t len, size_t pos) { if (ctx && ctx->line_ix) return skip_line_ix((line_index_t *)ctx->line_ix, input, len, pos); pos = te_line_end(NULL, input, len, pos); if (pos < len) pos++; return pos; } static int line_has_pipe_raw(const char *line, size_t len) { return memchr(line, '|', len) != NULL; } static int try_parse_table(sashidown_ctx_t *ctx, const char *input, size_t len, size_t line_start, table_entry_t *e) { size_t le = te_line_end(ctx, input, len, line_start); int has_pipe = 0; int cols = table_count_cols(input + line_start, le - line_start, &has_pipe); if (!has_pipe) return 0; size_t sep = te_skip_line(ctx, input, len, line_start); if (sep >= len) { if (ctx && ctx->line_ix) line_index_seek((line_index_t *)ctx->line_ix, line_start); return 0; } size_t sep_le = te_line_end(ctx, input, len, sep); if (!table_is_separator_row(input + sep, sep_le - sep)) { if (ctx && ctx->line_ix) line_index_seek((line_index_t *)ctx->line_ix, line_start); return 0; } size_t pos = te_skip_line(ctx, input, len, sep); e->start = line_start; e->body_start = pos; e->cols = cols; /* GFM: header determines column count */ e->body_rows = 0; e->valid = 1; while (pos < len) { le = te_line_end(ctx, input, len, pos); size_t line_len = le - pos; if (!line_has_pipe_raw(input + pos, line_len)) break; if (table_is_separator_row(input + pos, line_len)) break; e->body_rows++; pos = te_skip_line(ctx, input, len, pos); } e->end = pos; return 1; } int table_try_parse_at(sashidown_ctx_t *ctx, const char *input, size_t len, size_t pos, table_entry_t *e) { e->start = 0; e->end = 0; e->body_start = 0; e->cols = 0; e->body_rows = 0; e->valid = 0; return try_parse_table(ctx, input, len, pos, e); }