| 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]; |
| 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 } |