QUOTE: Love yourself first, then others.

main.c - ctl - Static template generator for C

ctl

Static template generator for C
git clone git@soophie.de:/srv/git/ctl
log | files | refs | readme

main.c (4252B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 
      5 #define LIB_STR_IMPL
      6 #include <libstr.h>
      7 
      8 typedef enum {
      9   CTL_ERR_OK,
     10   CTL_ERR_NO_ARGS,
     11   CTL_ERR_NO_FILE,
     12   CTL_ERR_UNCLOSED_CODE,
     13 } ctl_error_t;
     14 
     15 void ctl_error_str(ctl_error_t err) {
     16   switch (err) {
     17     case CTL_ERR_OK:
     18       break;
     19     case CTL_ERR_NO_ARGS:
     20       printf("Usage: ctl <filename>\n");
     21       break;
     22     case CTL_ERR_NO_FILE:
     23       printf("Error: No file found!\n");
     24       break;
     25     case CTL_ERR_UNCLOSED_CODE:
     26       printf("Error: Unclosed code block!\n");
     27       break;
     28   }
     29 }
     30 
     31 char *str_replace_all_len(char *buffer, char *str, char *replacement, int len) {
     32   char *ret = NULL;
     33   int str_len = strlen(str);
     34   for (int i = 0; i < len; i++) {
     35     char *ptr = &buffer[i];
     36     if (strncmp(ptr, str, str_len) == 0) {
     37       str_append(&ret, replacement);
     38       i += str_len - 1;
     39     }
     40     else {
     41       str_append_len(&ret, &buffer[i], 1);
     42     }
     43   }
     44   return ret;
     45 }
     46 
     47 char *str_hex_len(char *str, int len) {
     48   const int HEX = 4;
     49   char *ret = calloc(HEX * len + 1, sizeof(char));
     50   for (int i = 0; i < len; i++) {
     51     unsigned char hex = str[i] & 0xff;
     52     snprintf(ret + HEX * i, HEX * len, "\\x%02x", hex);
     53   }
     54   ret[HEX * len] = '\0';
     55   return ret;
     56 }
     57 
     58 ctl_error_t ctl_render(char **template, char *content) {
     59   int len = strlen(content);
     60   int set = 0;
     61   int is_code = 0;
     62   str_append(template, "{\n");
     63   for (int i = 0; i < len; i++) {
     64     if (content[i] == '%') {
     65       // ignore escaped %
     66       if (i + 1 < len && content[i + 1] == '%') {
     67         i += 1;
     68         continue;
     69       }
     70       if (is_code) {
     71         str_append_len(template, content + set, i - set);
     72         str_append(template, "\n");
     73       }
     74       else {
     75         const int STRING_LIMIT = 4095;
     76         int str_len = i - set;
     77         int rem_len = str_len;
     78         int hex_len = 0;
     79         int idx = 0;
     80         while (rem_len > 0) {
     81           if (rem_len > STRING_LIMIT) {
     82             hex_len = STRING_LIMIT;
     83           }
     84           else {
     85             hex_len = str_len - idx;
     86           }
     87           rem_len -= hex_len;
     88           char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);
     89           char *hex = str_hex_len(str, strlen(str));
     90           str_append(template, "CTL_OUT(ctl_out, \"");
     91           str_append(template, hex);
     92           str_append(template, "\");\n");
     93           free(str);
     94           free(hex);
     95           idx += hex_len;
     96         }
     97       }
     98       is_code = !is_code;
     99       set = i + 1;
    100     }
    101   }
    102   if (is_code) {
    103     return CTL_ERR_UNCLOSED_CODE;
    104   }
    105   else {
    106     const int STRING_LIMIT = 4095;
    107     int str_len = len - set;
    108     int rem_len = str_len;
    109     int hex_len = 0;
    110     int idx = 0;
    111     while (rem_len > 0) {
    112       if (rem_len > STRING_LIMIT) {
    113         hex_len = STRING_LIMIT;
    114       }
    115       else {
    116         hex_len = str_len - idx;
    117       }
    118       rem_len -= hex_len;
    119       char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);
    120       char *hex = str_hex_len(str, strlen(str));
    121       str_append(template, "CTL_OUT(ctl_out, \"");
    122       str_append(template, hex);
    123       str_append(template, "\");\n");
    124       free(str);
    125       free(hex);
    126       idx += hex_len;
    127     }
    128   }
    129   str_append(template, "}\n");
    130   *template = str_replace_all(*template, "%%", "%");
    131   return CTL_ERR_OK;
    132 }
    133 
    134 ctl_error_t ctl_file_read(char **content, const char *filename) {
    135   FILE *fp = fopen(filename, "r");
    136   if (fp == NULL) {
    137     return CTL_ERR_NO_FILE;
    138   }
    139   fseek(fp, 0, SEEK_END);
    140   int len = ftell(fp);
    141   fseek(fp, 0, SEEK_SET);
    142   *content = calloc(len + 1, sizeof(char));
    143   fread(*content, sizeof(char), len, fp);
    144   (*content)[len] = '\0';
    145   fclose(fp);
    146   return CTL_ERR_OK;
    147 }
    148 
    149 int main(int argc, char **argv) {
    150   ctl_error_t err;
    151   if (argc < 2) {
    152     err = CTL_ERR_NO_ARGS;
    153     ctl_error_str(err);
    154     return err;
    155   }
    156   char *filename = argv[1];
    157   char *content = NULL;
    158   err = ctl_file_read(&content, filename);
    159   if (err != CTL_ERR_OK) {
    160     ctl_error_str(err);
    161     return err;
    162   }
    163   char *template = NULL;
    164   err = ctl_render(&template, content);
    165   if (err != CTL_ERR_OK) {
    166     ctl_error_str(err);
    167     free(content);
    168     if (template != NULL) {
    169       free(template);
    170     }
    171     return err;
    172   }
    173   printf("%s\n", template);
    174   free(template);
    175   free(content);
    176   return CTL_ERR_OK;
    177 }