src/properties.c

changeset 1562
f2b63cad2142
parent 1559
9e50f45f8736
equal deleted inserted replaced
1561:fcebf53de51c 1562:f2b63cad2142
29 #include "cx/properties.h" 29 #include "cx/properties.h"
30 30
31 #include <assert.h> 31 #include <assert.h>
32 #include <stdio.h> 32 #include <stdio.h>
33 #include <string.h> 33 #include <string.h>
34 #include <ctype.h>
34 35
35 const CxPropertiesConfig cx_properties_config_default = { 36 const CxPropertiesConfig cx_properties_config_default = {
36 '=', 37 '=',
37 '#', 38 '#',
38 '\0', 39 '\0',
39 '\0', 40 '\0',
40 '\\', 41 '\\',
41 }; 42 };
42 43
43 void cxPropertiesInit( 44 void cxPropertiesInit(
44 CxProperties *prop, 45 CxProperties *prop,
94 return CX_PROPERTIES_NULL_INPUT; 95 return CX_PROPERTIES_NULL_INPUT;
95 } 96 }
96 97
97 // a pointer to the buffer we want to read from 98 // a pointer to the buffer we want to read from
98 CxBuffer *current_buffer = &prop->input; 99 CxBuffer *current_buffer = &prop->input;
99 100
101 char comment1 = prop->config.comment1;
102 char comment2 = prop->config.comment2;
103 char comment3 = prop->config.comment3;
104 char delimiter = prop->config.delimiter;
105 char continuation = prop->config.continuation;
106
100 // check if we have rescued data 107 // check if we have rescued data
101 if (!cxBufferEof(&prop->buffer)) { 108 if (!cxBufferEof(&prop->buffer)) {
102 // check if we can now get a complete line 109 // check if we can now get a complete line
103 cxstring input = cx_strn(prop->input.space + prop->input.pos, 110 cxstring input = cx_strn(prop->input.space + prop->input.pos,
104 prop->input.size - prop->input.pos); 111 prop->input.size - prop->input.pos);
105 cxstring nl = cx_strchr(input, '\n'); 112 cxstring nl = cx_strchr(input, '\n');
113 while (nl.length > 0) {
114 // check for line continuation
115 char previous = nl.ptr > input.ptr ? nl.ptr[-1] : prop->buffer.space[prop->buffer.size-1];
116 if (previous == continuation) {
117 // this nl is a line continuation, check the next newline
118 nl = cx_strchr(cx_strsubs(nl, 1), '\n');
119 } else {
120 break;
121 }
122 }
123
106 if (nl.length > 0) { 124 if (nl.length > 0) {
107 // we add as much data to the rescue buffer as we need 125 // we add as much data to the rescue buffer as we need
108 // to complete the line 126 // to complete the line
109 size_t len_until_nl = (size_t)(nl.ptr - input.ptr) + 1; 127 size_t len_until_nl = (size_t)(nl.ptr - input.ptr) + 1;
110 128
127 // reset the input buffer (make way for a re-fill) 145 // reset the input buffer (make way for a re-fill)
128 cxBufferReset(&prop->input); 146 cxBufferReset(&prop->input);
129 return CX_PROPERTIES_INCOMPLETE_DATA; 147 return CX_PROPERTIES_INCOMPLETE_DATA;
130 } 148 }
131 } 149 }
132 150
133 char comment1 = prop->config.comment1;
134 char comment2 = prop->config.comment2;
135 char comment3 = prop->config.comment3;
136 char delimiter = prop->config.delimiter;
137
138 // get one line and parse it 151 // get one line and parse it
139 while (!cxBufferEof(current_buffer)) { 152 while (!cxBufferEof(current_buffer)) {
140 const char *buf = current_buffer->space + current_buffer->pos; 153 const char *buf = current_buffer->space + current_buffer->pos;
141 size_t len = current_buffer->size - current_buffer->pos; 154 size_t len = current_buffer->size - current_buffer->pos;
142 155
145 * delimiter and comment chars 158 * delimiter and comment chars
146 */ 159 */
147 size_t delimiter_index = 0; 160 size_t delimiter_index = 0;
148 size_t comment_index = 0; 161 size_t comment_index = 0;
149 bool has_comment = false; 162 bool has_comment = false;
163 bool has_continuation = false;
150 164
151 size_t i = 0; 165 size_t i = 0;
152 char c = 0; 166 char c = 0;
153 for (; i < len; i++) { 167 for (; i < len; i++) {
154 c = buf[i]; 168 c = buf[i];
159 } 173 }
160 } else if (c == delimiter) { 174 } else if (c == delimiter) {
161 if (delimiter_index == 0 && !has_comment) { 175 if (delimiter_index == 0 && !has_comment) {
162 delimiter_index = i; 176 delimiter_index = i;
163 } 177 }
178 } else if (delimiter_index > 0 && c == continuation && i+1 < len && buf[i+1] == '\n') {
179 has_continuation = true;
180 i++;
164 } else if (c == '\n') { 181 } else if (c == '\n') {
165 break; 182 break;
166 } 183 }
167 } 184 }
168 185
223 buf + delimiter_index + 1, 240 buf + delimiter_index + 1,
224 line.length - delimiter_index - 1); 241 line.length - delimiter_index - 1);
225 k = cx_strtrim(k); 242 k = cx_strtrim(k);
226 val = cx_strtrim(val); 243 val = cx_strtrim(val);
227 if (k.length > 0) { 244 if (k.length > 0) {
245 current_buffer->pos += i + 1;
246 assert(current_buffer->pos <= current_buffer->size);
247 assert(current_buffer != &prop->buffer || current_buffer->pos == current_buffer->size);
248
249 if (has_continuation) {
250 char *ptr = (char*)val.ptr;
251 if (current_buffer != &prop->buffer) {
252 // move value to the rescue buffer
253 if (prop->buffer.space == NULL) {
254 cxBufferInit(&prop->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND);
255 }
256 prop->buffer.size = 0;
257 prop->buffer.pos = 0;
258 if (cxBufferWrite(val.ptr, 1, val.length, &prop->buffer) != val.length) {
259 return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
260 }
261 val.ptr = prop->buffer.space;
262 ptr = prop->buffer.space;
263 }
264 // value.ptr is now inside the rescue buffer and we can
265 // remove the continuation character from the value
266 bool trim = false;
267 size_t x = 0;
268 for(size_t j=0;j<val.length;j++) {
269 c = ptr[j];
270 if (j+1 < val.length && c == '\\' && ptr[j+1] == '\n') {
271 // skip continuation and newline character
272 j++;
273 trim = true; // enable trim in the next line
274 continue;
275 }
276 if (j > x) {
277 if (trim) {
278 if (isspace((unsigned char)c)) {
279 continue;
280 }
281 trim = false;
282 }
283 ptr[x] = c;
284 }
285 x++;
286 }
287 val.length = x;
288 }
228 *key = k; 289 *key = k;
229 *value = val; 290 *value = val;
230 current_buffer->pos += i + 1; 291
231 assert(current_buffer->pos <= current_buffer->size);
232 return CX_PROPERTIES_NO_ERROR; 292 return CX_PROPERTIES_NO_ERROR;
233 } else { 293 } else {
234 return CX_PROPERTIES_INVALID_EMPTY_KEY; 294 return CX_PROPERTIES_INVALID_EMPTY_KEY;
235 } 295 }
236 } 296 }

mercurial