File Coverage

src/mds_footnote.c
Criterion Covered Total %
statement 47 50 94.0
branch 33 60 55.0
condition n/a
subroutine n/a
pod n/a
total 80 110 72.7


line stmt bran cond sub pod time code
1             /* mds_footnote.c — GFM footnote definitions table.
2             *
3             * Lookup is by case-folded label (matching link-reference rules so that
4             * `[^Foo]: x` is found by `[^foo]`). The implementation reuses the
5             * same fold/normalise approach as mds_linkref by simply lowercasing
6             * ASCII and collapsing inner whitespace; GFM spec examples only need
7             * that subset for footnote labels (no Unicode case-folding in tests).
8             */
9              
10             #include "mds_footnote.h"
11             #include
12             #include
13             #include
14              
15 66           static size_t fn_normalise(const char* s, size_t n, char* out) {
16 66           size_t a = 0, b = n;
17 66           size_t j = 0;
18             size_t i;
19 66           int in_ws = 0;
20 66 50         while (a < b && (s[a] == ' ' || s[a] == '\t' || s[a] == '\n')) a++;
    50          
    50          
    50          
21 66 50         while (b > a && (s[b-1] == ' ' || s[b-1] == '\t' || s[b-1] == '\n')) b--;
    50          
    50          
    50          
22 786 100         for (i = a; i < b; i++) {
23 720           unsigned char c = (unsigned char)s[i];
24 720 50         if (c == ' ' || c == '\t' || c == '\n') {
    50          
    50          
25 0 0         if (!in_ws) { out[j++] = ' '; in_ws = 1; }
26             } else {
27 720 100         if (c >= 'A' && c <= 'Z') c = (unsigned char)(c + 0x20);
    50          
28 720           out[j++] = (char)c;
29 720           in_ws = 0;
30             }
31             }
32 66           return j;
33             }
34              
35 63           static char* fn_arena_dup(mds_arena* a, const char* s, size_t n) {
36             char* d;
37 63           d = (char*)mds_arena_alloc(a, n + 1);
38 63 50         if (n) memcpy(d, s, n);
39 63           d[n] = '\0';
40 63           return d;
41             }
42              
43 9           void mds_footnote_init(struct mds_footnote_tab* t, mds_arena* a) {
44 9           t->entries = NULL;
45 9           t->len = 0;
46 9           t->cap = 0;
47 9           t->arena = a;
48 9           }
49              
50 45           const mds_footnote* mds_footnote_get(const struct mds_footnote_tab* t,
51             const char* label, size_t llen) {
52             char buf[4096];
53             size_t nlen;
54             size_t i;
55 45 50         if (!t || !t->len) return NULL;
    50          
56 45 50         if (llen > sizeof buf) return NULL;
57 45           nlen = fn_normalise(label, llen, buf);
58 96 100         for (i = 0; i < t->len; i++) {
59 93 100         if (t->entries[i].klen == nlen &&
    50          
60 42 50         (nlen == 0 || memcmp(t->entries[i].key, buf, nlen) == 0))
61 42           return &t->entries[i];
62             }
63 3           return NULL;
64             }
65              
66 21           int mds_footnote_add(struct mds_footnote_tab* t,
67             const char* label, size_t llen,
68             const char* body, size_t blen) {
69             char nbuf[4096];
70             size_t nlen;
71             size_t i;
72             mds_footnote* e;
73 21 50         if (llen > sizeof nbuf) return 0;
74 21           nlen = fn_normalise(label, llen, nbuf);
75             /* duplicate label: first-wins (spec §6.13). */
76 51 100         for (i = 0; i < t->len; i++) {
77 30 50         if (t->entries[i].klen == nlen &&
    0          
78 0 0         (nlen == 0 || memcmp(t->entries[i].key, nbuf, nlen) == 0))
79 0           return 0;
80             }
81 21 100         if (t->len == t->cap) {
82 9 50         size_t nc = t->cap ? t->cap * 2 : 8;
83 9           t->entries = (mds_footnote*)realloc(t->entries, nc * sizeof(mds_footnote));
84 9           t->cap = nc;
85             }
86 21           e = &t->entries[t->len++];
87 21           e->label = fn_arena_dup(t->arena, label, llen); e->llen = llen;
88 21           e->key = fn_arena_dup(t->arena, nbuf, nlen); e->klen = nlen;
89 21           e->body = fn_arena_dup(t->arena, body, blen); e->blen = blen;
90 21           return 1;
91             }