cnfparse.c (4108B)
1 #ifdef __cplusplus 2 extern "C" { 3 #endif 4 5 // includes/system {{{ 6 #include <stdbool.h> 7 #include <stdlib.h> 8 #include <string.h> 9 // }}} 10 // includes/deps {{{ 11 #include "finwo/buf.h" 12 // }}} 13 // includes/local {{{ 14 #include "cnfparse.h" 15 // }}} 16 17 #define STATE_BLANK 0 18 #define STATE_COMMENT 1 19 #define STATE_QUOTE 2 20 #define STATE_WORD 3 21 22 // cnf_directive_append(subject,value,len) {{{ 23 // caller frees 24 void cnf_directive_append(struct cnf_directive *subject, char *value, size_t len) { 25 if (!value) return; 26 char *val = calloc(1, len+1); 27 memcpy(val, value, len); 28 if (!subject->name) { 29 subject->name = val; 30 return; 31 } 32 subject->argv = realloc(subject->argv, (subject->argc + 1) * sizeof(char*)); 33 subject->argv[subject->argc++] = val; 34 } 35 // }}} 36 // cnf_directive_free(subject) {{{ 37 void cnf_directive_free(struct cnf_directive *subject) { 38 if (!subject) return; 39 if (subject->name) { 40 free(subject->name); 41 } 42 for(int i = 0 ; i < subject->argc ; i++) { 43 free(subject->argv[i]); 44 } 45 if (subject->argv) { 46 free(subject->argv); 47 } 48 free(subject); 49 } 50 // }}} 51 // cnf_directive_read(fd) {{{ 52 struct cnf_directive * cnf_directive_read(FILE *fd) { 53 if (feof(fd)) return NULL; 54 55 struct buf *acc = calloc(1, sizeof(struct buf)); 56 struct cnf_directive *output = calloc(1, sizeof(struct cnf_directive)); 57 58 int state = STATE_BLANK; 59 char c; 60 char quote; 61 bool escape; 62 63 while(!feof(fd)) { 64 if (!fread(&c, sizeof(char), 1, fd)) break; 65 66 switch(state) { 67 case STATE_BLANK: 68 switch(c) { 69 case ' ': 70 case '\t': 71 case '\r': 72 // Skip leading whitespace 73 // Consume without further action 74 continue; 75 case '\n': 76 // Output directive if we have something 77 if (output->name) goto cnf_directive_read_finalize; 78 continue; 79 case '#': 80 state = STATE_COMMENT; 81 continue; 82 case '"': 83 case '\'': 84 state = STATE_QUOTE; 85 quote = c; 86 escape = false; 87 continue; 88 default: 89 buf_append_byte(acc, c); 90 state = STATE_WORD; 91 continue; 92 } 93 break; 94 case STATE_COMMENT: 95 switch(c) { 96 case '\n': // newline breaks comment 97 if (output->name) goto cnf_directive_read_finalize; // Operator = directive complete 98 state = STATE_BLANK; // No operator = read one more line 99 // acc->len = 0; 100 continue; 101 default: 102 // Consume without further action 103 continue; 104 } 105 break; 106 case STATE_QUOTE: 107 if (escape) { 108 buf_append_byte(acc, c); 109 escape = false; 110 continue; 111 } 112 if (c == '\\') { 113 escape = true; 114 continue; 115 } 116 if (c == quote) { 117 cnf_directive_append(output, acc->dat, acc->len); 118 state = STATE_BLANK; 119 memset(acc->dat, 0, acc->cap); 120 acc->len = 0; 121 continue; 122 } 123 buf_append_byte(acc, c); 124 continue;; 125 case STATE_WORD: 126 switch(c) { 127 case ' ': 128 case '\t': 129 case '\r': 130 case '\n': 131 // whitespace breaks word 132 cnf_directive_append(output, acc->dat, acc->len); 133 state = STATE_BLANK; 134 memset(acc->dat, 0, acc->cap); 135 acc->len = 0; 136 if ('\n' == c) { 137 goto cnf_directive_read_finalize; 138 } 139 continue; 140 default: 141 // append char to word 142 buf_append_byte(acc, c); 143 continue; 144 } 145 break; 146 default: 147 fprintf(stderr, "Invalid state: %d\n", state); 148 exit(1); 149 } 150 151 } 152 153 cnf_directive_read_finalize: 154 if (acc->len) { 155 cnf_directive_append(output, acc->dat, acc->len); 156 } 157 buf_clear(acc); 158 free(acc); 159 if (!output->name) { 160 free(output); 161 return NULL; 162 } 163 return output; 164 } 165 // }}} 166 167 #ifdef __cplusplus 168 } // extern "C" 169 #endif 170 171 // vim:fdm=marker:fdl=0