cnfparse.c (5394B)
1 #ifdef __cplusplus 2 extern "C" { 3 #endif 4 5 // includes/system {{{ 6 #include <stdbool.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 // }}} 11 // includes/deps {{{ 12 #include "finwo/buf.h" 13 // }}} 14 // includes/local {{{ 15 #include "cnfparse.h" 16 // }}} 17 18 #define STATE_BLANK 0 19 #define STATE_COMMENT 1 20 #define STATE_DQUOTE 2 21 #define STATE_SQUOTE 3 22 #define STATE_WORD 4 23 24 // cnf_unescape(c) {{{ 25 // Unknown escapes keep the character verbatim 26 static int cnf_unescape(char c) { 27 switch (c) { 28 case 'n': return '\n'; 29 case 't': return '\t'; 30 case 'r': return '\r'; 31 case '\\': return '\\'; 32 case '"': return '"'; 33 case '\'': return '\''; 34 case '0': 35 fprintf(stderr, "cnfparse: warning: \\0 becomes '0', not a null byte\n"); 36 return '0'; 37 default: return c; 38 } 39 } 40 // }}} 41 42 // cnf_directive_append(subject,value,len) {{{ 43 // caller frees 44 void cnf_directive_append(struct cnf_directive *subject, char *value, size_t len) { 45 if (!value) value = ""; // len is leading, not value 46 char *val = calloc(1, len+1); 47 memcpy(val, value, len); 48 if (!subject->name) { 49 subject->name = val; 50 return; 51 } 52 subject->argv = realloc(subject->argv, (subject->argc + 1) * sizeof(char*)); 53 subject->argv[subject->argc++] = val; 54 } 55 // }}} 56 // cnf_directive_free(subject) {{{ 57 void cnf_directive_free(struct cnf_directive *subject) { 58 if (!subject) return; 59 if (subject->name) { 60 free(subject->name); 61 } 62 for(size_t i = 0 ; i < subject->argc ; i++) { 63 free(subject->argv[i]); 64 } 65 if (subject->argv) { 66 free(subject->argv); 67 } 68 free(subject); 69 } 70 // }}} 71 // cnf_directive_read(fd) {{{ 72 struct cnf_directive * cnf_directive_read(FILE *fd) { 73 if (feof(fd)) return NULL; 74 75 struct buf *acc = calloc(1, sizeof(struct buf)); 76 struct cnf_directive *output = calloc(1, sizeof(struct cnf_directive)); 77 78 int state = STATE_BLANK; 79 char c; 80 bool escape; 81 82 while(!feof(fd)) { 83 if (!fread(&c, sizeof(char), 1, fd)) break; 84 85 switch(state) { 86 case STATE_BLANK: 87 switch(c) { 88 case ' ': 89 case '\t': 90 case '\r': 91 // Skip leading whitespace 92 // Consume without further action 93 continue; 94 case '\n': 95 // Output directive if we have something 96 if (output->name) goto cnf_directive_read_finalize; 97 continue; 98 case '#': 99 state = STATE_COMMENT; 100 continue; 101 case '"': 102 state = STATE_DQUOTE; 103 escape = false; 104 continue; 105 case '\'': 106 state = STATE_SQUOTE; 107 escape = false; 108 continue; 109 default: 110 buf_append_byte(acc, c); 111 state = STATE_WORD; 112 continue; 113 } 114 break; 115 case STATE_COMMENT: 116 switch(c) { 117 case '\n': // newline breaks comment 118 if (output->name) goto cnf_directive_read_finalize; // Operator = directive complete 119 state = STATE_BLANK; // No operator = read one more line 120 // acc->len = 0; 121 continue; 122 default: 123 // Consume without further action 124 continue; 125 } 126 break; 127 case STATE_DQUOTE: 128 if (escape) { 129 buf_append_byte(acc, cnf_unescape(c)); 130 escape = false; 131 continue; 132 } 133 if (c == '\\') { 134 escape = true; 135 continue; 136 } 137 if (c == '"') { 138 cnf_directive_append(output, acc->dat, acc->len); 139 state = STATE_BLANK; 140 memset(acc->dat, 0, acc->cap); 141 acc->len = 0; 142 continue; 143 } 144 buf_append_byte(acc, c); 145 continue; 146 case STATE_SQUOTE: 147 if (escape) { 148 // only \' is an escape inside single quotes; any other backslash stays literal 149 if (c == '\'') { 150 buf_append_byte(acc, '\''); 151 } else { 152 buf_append_byte(acc, '\\'); 153 buf_append_byte(acc, c); 154 } 155 escape = false; 156 continue; 157 } 158 if (c == '\\') { 159 escape = true; 160 continue; 161 } 162 if (c == '\'') { 163 cnf_directive_append(output, acc->dat, acc->len); 164 state = STATE_BLANK; 165 memset(acc->dat, 0, acc->cap); 166 acc->len = 0; 167 continue; 168 } 169 buf_append_byte(acc, c); 170 continue; 171 case STATE_WORD: 172 switch(c) { 173 case ' ': 174 case '\t': 175 case '\r': 176 case '\n': 177 // whitespace breaks word 178 cnf_directive_append(output, acc->dat, acc->len); 179 state = STATE_BLANK; 180 memset(acc->dat, 0, acc->cap); 181 acc->len = 0; 182 if ('\n' == c) { 183 goto cnf_directive_read_finalize; 184 } 185 continue; 186 default: 187 // append char to word 188 buf_append_byte(acc, c); 189 continue; 190 } 191 break; 192 default: 193 fprintf(stderr, "Invalid state: %d\n", state); 194 exit(1); 195 } 196 197 } 198 199 cnf_directive_read_finalize: 200 if (acc->len) { 201 cnf_directive_append(output, acc->dat, acc->len); 202 } 203 buf_clear(acc); 204 free(acc); 205 if (!output->name) { 206 free(output); 207 return NULL; 208 } 209 return output; 210 } 211 // }}} 212 213 #ifdef __cplusplus 214 } // extern "C" 215 #endif 216 217 // vim:fdm=marker:fdl=0