aboutsummaryrefslogtreecommitdiffstats
path: root/src/blogc/toctree.c
blob: 307c62c45066484a5ae7c997cfa7a3e1cd88afea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
 * blogc: A blog compiler.
 * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
 *
 * This program can be distributed under the terms of the BSD License.
 * See the file LICENSE.
 */

#include <stdlib.h>
#include "../common/utils.h"
#include "toctree.h"

bc_slist_t*
blogc_toctree_append(bc_slist_t *headers, size_t level, const char *slug, const char *text)
{
    if (level == 0)
        return headers;

    blogc_toctree_header_t *t = bc_malloc(sizeof(blogc_toctree_header_t));
    t->level = level;
    t->slug = bc_strdup(slug);
    t->text = bc_strdup(text);
    return bc_slist_append(headers, t);
}


char*
blogc_toctree_render(bc_slist_t *headers, int maxdepth, const char *endl)
{
    if (headers == NULL || maxdepth == 0)
        return NULL;

    // find lower level
    size_t lower_level = 0;
    for (bc_slist_t *l = headers; l != NULL; l = l->next) {
        size_t lv = ((blogc_toctree_header_t*) l->data)->level;
        if (lower_level == 0 || lower_level > lv) {
            lower_level = lv;
        }
    }

    if (lower_level == 0)
        return NULL;

    // render
    bc_string_t *rv = bc_string_new();
    bc_string_append_printf(rv, "<ul>%s", endl == NULL ? "\n" : endl);
    size_t spacing = 4;
    size_t current_level = lower_level;
    for (bc_slist_t *l = headers; l != NULL; l = l->next) {
        blogc_toctree_header_t *t = l->data;
        if (t->level - lower_level >= maxdepth) {
            continue;
        }
        while (current_level > t->level) {
            spacing -= 4;
            bc_string_append_printf(rv, "%*s</ul>%s", spacing, "",
                endl == NULL ? "\n" : endl);
            current_level--;
        }
        while (current_level < t->level) {
            bc_string_append_printf(rv, "%*s<ul>%s", spacing, "",
                endl == NULL ? "\n" : endl);
            current_level++;
            spacing += 4;
        }
        bc_string_append_printf(rv, "%*s<li>", spacing, "");
        if (t->slug != NULL) {
            bc_string_append_printf(rv, "<a href=\"#%s\">%s</a>", t->slug,
                t->text != NULL ? t->text : "");
        }
        else {
            bc_string_append(rv, t->text);
        }
        bc_string_append_printf(rv, "</li>%s", endl == NULL ? "\n" : endl);
    }

    // close leftovers
    while (current_level >= lower_level) {
        spacing -= 4;
        bc_string_append_printf(rv, "%*s</ul>%s", spacing, "",
            endl == NULL ? "\n" : endl);
        current_level--;
    }

    return bc_string_free(rv, false);
}


static void
free_header(blogc_toctree_header_t *h)
{
    free(h->slug);
    free(h->text);
    free(h);
}


void
blogc_toctree_free(bc_slist_t *l)
{
    bc_slist_free_full(l, (bc_free_func_t) free_header);
}