aboutsummaryrefslogtreecommitdiff
path: root/src/template.c
blob: 646f5f993438833978ae15995f5de5562213954c (plain) (blame)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//
// Created by jhunk on 12/17/23.
//

#include "template.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>


struct tpl_item {
    char *key;
    char **ptr;
};
struct tpl_item *tpl_pool[1024] = {0};
unsigned tpl_pool_used = 0;

void tpl_register(char *key, char **ptr) {
    struct tpl_item *item = NULL;
    item = calloc(1, sizeof(*item));
    if (!item) {
        perror("unable to register tpl_item");
        exit(1);
    }
    item->key = strdup(key);
    item->ptr = ptr;
    tpl_pool[tpl_pool_used] = item;
    tpl_pool_used++;
}

void tpl_free() {
    for (unsigned i = 0; i < tpl_pool_used; i++) {
        struct tpl_item *item = tpl_pool[i];
        if (item) {
            if (item->key) {
                free(item->key);
            }
            free(item);
        }
    }
}

char *tpl_getval(char *key) {
    char *result = NULL;
    for (size_t i = 0; i < tpl_pool_used; i++) {
        if (tpl_pool[i]->key) {
            if (!strcmp(tpl_pool[i]->key, key)) {
                result = *tpl_pool[i]->ptr;
                break;
            }
        }
    }
    return result;
}

static int grow(size_t z, size_t *output_bytes, char **output) {
    if (z >= *output_bytes) {
        size_t new_size = *output_bytes + z + 1;
#ifdef DEBUG
        fprintf(stderr, "template output buffer new size: %zu\n", *output_bytes);
#endif
        char *tmp = realloc(*output, new_size);
        if (!tmp) {
            perror("realloc failed");
            return -1;
        }
        memset(tmp + strlen(tmp), 0, new_size - strlen(tmp));
        *output = tmp;
        *output_bytes = new_size;
    }
    return 0;
}

char *tpl_render(char *str) {
    if (!str) {
        return NULL;
    }
    size_t output_bytes = strlen(str) * 2;
    char *output = NULL;
    char *b_close = NULL;
    char *pos = NULL;
    pos = str;

    output = calloc(output_bytes, sizeof(*output));
    if (!output) {
        perror("unable to allocate output buffer");
        return NULL;
    }

    size_t z = 0;
    while (*pos != 0) {
        char key[255] = {0};
        char *value = NULL;

        memset(key, 0, sizeof(key));
        grow(z, &output_bytes, &output);
        // At opening brace
        if (!strncmp(pos, "{{", 2)) {
            // Scan until key is reached
            while (!isalnum(*pos)) {
                pos++;
            }

            // Read key name
            size_t key_len = 0;
            while (isalnum(*pos) || *pos != '}') {
                if (isspace(*pos) || isblank(*pos)) {
                    break;
                }
                key[key_len] = *pos;
                key_len++;
                pos++;
            }

            char *type_stop = NULL;
            type_stop = strchr(key, ':');

            int do_env = 0;
            if (type_stop) {
                if (!strncmp(key, "env", type_stop - key)) {
                    do_env = 1;
                }
            }

            // Find closing brace
            b_close = strstr(pos, "}}");
            if (!b_close) {
                grow(z + strlen(pos), &output_bytes, &output);
                strcpy(output, pos);
                z += strlen(pos);
                continue;
            }
            // Jump past closing brace
            pos += (b_close + 2 - pos);

            if (do_env) {
                char *k = type_stop + 1;
                size_t klen = strlen(k);
                memmove(key, k, klen);
                key[klen] = 0;
                char *env_val = getenv(key);
                value = strdup(env_val ? env_val : "");
            } else {
                // Read replacement value
                value = tpl_getval(key);
            }
        }

        if (value) {
            // Set output iterator to end of replacement value
            z += strlen(value);
            // Append replacement value
            grow(z, &output_bytes, &output);
            strcat(output, value);
        }

#ifdef DEBUG
        fprintf(stderr, "z=%zu, output_bytes=%zu\n", z, output_bytes);
#endif
        output[z] = *pos;
        pos++;
        z++;
    }
#ifdef DEBUG
    fprintf(stderr, "template output length: %zu\n", strlen(output));
    fprintf(stderr, "template output bytes: %zu\n", output_bytes);
#endif
    return output;
}

int tpl_render_to_file(char *str, const char *filename) {
    char *result;
    FILE *fp;

    // Render the input string
    result = tpl_render(str);
    if (!result) {
        return -1;
    }

    // Open the destination file for writing
    fp = fopen(filename, "w+");
    if (!fp) {
        return -1;
    }

    // Write rendered string to file
    fprintf(fp, "%s", result);
    fclose(fp);

    guard_free(result);
    return 0;
}