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 }