47 memset(prop, 0, sizeof(CxProperties)); |
45 memset(prop, 0, sizeof(CxProperties)); |
48 prop->config = config; |
46 prop->config = config; |
49 } |
47 } |
50 |
48 |
51 void cxPropertiesDestroy(CxProperties *prop) { |
49 void cxPropertiesDestroy(CxProperties *prop) { |
52 if (0 == (prop->flags & CX_PROPERTIES_FLAG_USE_STACK)) { |
50 cxBufferDestroy(&prop->input); |
53 free(prop->buf); |
51 cxBufferDestroy(&prop->buffer); |
54 } |
|
55 prop->buf = NULL; |
|
56 prop->buf_capacity = prop->buf_size = 0; |
|
57 } |
|
58 |
|
59 static int cx_properties_ensure_buf_capacity(CxProperties *prop, size_t cap) { |
|
60 if (prop->buf_capacity >= cap) { |
|
61 return 0; |
|
62 } |
|
63 |
|
64 // not enough capacity - are we on the stack right now? |
|
65 if ((prop->flags & CX_PROPERTIES_FLAG_USE_STACK) != 0) { |
|
66 // move to the heap |
|
67 char *newbuf = malloc(cap); |
|
68 if (newbuf == NULL) return 1; |
|
69 memcpy(newbuf, prop->buf, prop->buf_size); |
|
70 prop->buf = newbuf; |
|
71 prop->flags &= CX_PROPERTIES_FLAG_USE_STACK; |
|
72 } else { |
|
73 // we are on the heap already, reallocate |
|
74 // this is legit, because realloc() behaves like malloc() when the |
|
75 // current pointer is NULL |
|
76 char *newbuf = realloc(prop->buf, cap); |
|
77 if (newbuf == NULL) return 1; |
|
78 prop->buf = newbuf; |
|
79 } |
|
80 |
|
81 // store new capacity and return |
|
82 prop->buf_capacity = cap; |
|
83 return 0; |
|
84 } |
|
85 |
|
86 static int cx_properties_rescuen_input(CxProperties *prop, size_t len) { |
|
87 if (cx_properties_ensure_buf_capacity(prop, prop->buf_size + len)) { |
|
88 return 1; |
|
89 } |
|
90 const char *src = prop->text + prop->text_pos; |
|
91 char *dest = prop->buf + prop->buf_size; |
|
92 memcpy(dest, src, len); |
|
93 prop->buf_size += len; |
|
94 prop->text_pos += len; |
|
95 return 0; |
|
96 } |
|
97 |
|
98 static int cx_properties_rescue_input(CxProperties *prop) { |
|
99 // someone fucked around with our integers, exit immediately |
|
100 if (prop->text_pos > prop->text_size) return 0; |
|
101 |
|
102 // determine the bytes needed |
|
103 size_t len = prop->text_size - prop->text_pos; |
|
104 |
|
105 return cx_properties_rescuen_input(prop, len); |
|
106 } |
52 } |
107 |
53 |
108 int cxPropertiesFilln( |
54 int cxPropertiesFilln( |
109 CxProperties *prop, |
55 CxProperties *prop, |
110 const char *buf, |
56 const char *buf, |
111 size_t len |
57 size_t len |
112 ) { |
58 ) { |
113 if (cx_properties_rescue_input(prop)) return 1; |
59 if (cxBufferEof(&prop->input)) { |
114 prop->text = buf; |
60 // destroy a possible previously initialized buffer |
115 prop->text_size = len; |
61 cxBufferDestroy(&prop->input); |
116 prop->text_pos = 0; |
62 cxBufferInit(&prop->input, (void*) buf, len, |
|
63 NULL, CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_AUTO_EXTEND); |
|
64 prop->input.size = len; |
|
65 } else { |
|
66 if (cxBufferAppend(buf, 1, len, &prop->input) < len) return -1; |
|
67 } |
117 return 0; |
68 return 0; |
118 } |
69 } |
119 |
70 |
120 void cxPropertiesUseStack( |
71 void cxPropertiesUseStack( |
121 CxProperties *prop, |
72 CxProperties *prop, |
122 char *buf, |
73 char *buf, |
123 size_t capacity |
74 size_t capacity |
124 ) { |
75 ) { |
125 assert(prop->buf == NULL); |
76 cxBufferInit(&prop->buffer, buf, capacity, NULL, CX_BUFFER_COPY_ON_EXTEND); |
126 prop->buf = buf; |
|
127 prop->buf_capacity = capacity; |
|
128 prop->buf_size = 0; |
|
129 prop->flags |= CX_PROPERTIES_FLAG_USE_STACK; |
|
130 } |
77 } |
131 |
78 |
132 CxPropertiesStatus cxPropertiesNext( |
79 CxPropertiesStatus cxPropertiesNext( |
133 CxProperties *prop, |
80 CxProperties *prop, |
134 cxstring *key, |
81 cxstring *key, |
135 cxstring *value |
82 cxstring *value |
136 ) { |
83 ) { |
137 // check if we have a text buffer |
84 // check if we have a text buffer |
138 if (prop->text == NULL) { |
85 if (prop->input.space == NULL) { |
139 return CX_PROPERTIES_NULL_INPUT; |
86 return CX_PROPERTIES_NULL_INPUT; |
140 } |
87 } |
|
88 |
|
89 // a pointer to the buffer we want to read from |
|
90 CxBuffer *current_buffer = &prop->input; |
|
91 |
141 // check if we have rescued data |
92 // check if we have rescued data |
142 if (prop->buf_size > 0) { |
93 if (!cxBufferEof(&prop->buffer)) { |
143 // check if we can now get a complete line |
94 // check if we can now get a complete line |
144 const char *buf = prop->text + prop->text_pos; |
95 cxstring input = cx_strn(prop->input.space + prop->input.pos, |
145 size_t len = prop->text_size - prop->text_pos; |
96 prop->input.size - prop->input.pos); |
146 cxstring str = cx_strn(buf, len); |
97 cxstring nl = cx_strchr(input, '\n'); |
147 cxstring nl = cx_strchr(str, '\n'); |
98 if (nl.length > 0) { |
148 if(nl.length > 0) { |
|
149 // we add as much data to the rescue buffer as we need |
99 // we add as much data to the rescue buffer as we need |
150 // to complete the line |
100 // to complete the line |
151 size_t len_until_nl = (size_t)(nl.ptr - buf) + 1; |
101 size_t len_until_nl = (size_t)(nl.ptr - input.ptr) + 1; |
152 |
102 |
153 if (cx_properties_rescuen_input(prop, len_until_nl)) { |
103 if (cxBufferAppend(input.ptr, 1, |
|
104 len_until_nl, &prop->buffer) < len_until_nl) { |
154 return CX_PROPERTIES_BUFFER_ALLOC_FAILED; |
105 return CX_PROPERTIES_BUFFER_ALLOC_FAILED; |
155 } |
106 } |
156 |
107 |
157 // the tmp buffer contains exactly one line now |
108 // advance the position in the input buffer |
158 // we use a trick here: we swap the buffers and recurse |
109 prop->input.pos += len_until_nl; |
159 const char *orig_text = prop->text; |
110 |
160 size_t orig_size = prop->text_size; |
111 // we now want to read from the rescue buffer |
161 prop->text = prop->buf; |
112 current_buffer = &prop->buffer; |
162 prop->text_size = prop->buf_size; |
|
163 prop->text_pos = 0; |
|
164 prop->buf_size = 0; |
|
165 |
|
166 CxPropertiesStatus result; |
|
167 result = cxPropertiesNext(prop, key, value); |
|
168 |
|
169 // restore original buffer |
|
170 prop->text = orig_text; |
|
171 prop->text_size = orig_size; |
|
172 |
|
173 // set the position to after the newline |
|
174 prop->text_pos = len_until_nl; |
|
175 |
|
176 // check the result |
|
177 if (result == CX_PROPERTIES_NO_ERROR) { |
|
178 // reset the rescue buffer and return with the result |
|
179 prop->buf_size = 0; |
|
180 return result; |
|
181 } else if (result == CX_PROPERTIES_NO_DATA) { |
|
182 // rescue buffer contained only blanks or comments |
|
183 // reset the rescue buffer and retry with text buffer |
|
184 prop->buf_size = 0; |
|
185 return cxPropertiesNext(prop, key, value); |
|
186 } else { |
|
187 // CX_PROPERTIES_INCOMPLETE_DATA is not possible |
|
188 // so it must have been another error |
|
189 // do not reset the rescue buffer and return the error |
|
190 return result; |
|
191 } |
|
192 } else { |
113 } else { |
193 // still not enough data |
114 // still not enough data, copy input buffer to internal buffer |
194 if (cx_properties_rescue_input(prop)) { |
115 if (cxBufferAppend(input.ptr, 1, |
|
116 input.length, &prop->buffer) < input.length) { |
195 return CX_PROPERTIES_BUFFER_ALLOC_FAILED; |
117 return CX_PROPERTIES_BUFFER_ALLOC_FAILED; |
196 } |
118 } |
|
119 // reset the input buffer (make way for a re-fill) |
|
120 cxBufferReset(&prop->input); |
197 return CX_PROPERTIES_INCOMPLETE_DATA; |
121 return CX_PROPERTIES_INCOMPLETE_DATA; |
198 } |
122 } |
199 } |
123 } |
200 |
124 |
201 char comment1 = prop->config.comment1; |
125 char comment1 = prop->config.comment1; |
202 char comment2 = prop->config.comment2; |
126 char comment2 = prop->config.comment2; |
203 char comment3 = prop->config.comment3; |
127 char comment3 = prop->config.comment3; |
204 char delimiter = prop->config.delimiter; |
128 char delimiter = prop->config.delimiter; |
205 |
129 |
206 // get one line and parse it |
130 // get one line and parse it |
207 while (prop->text_pos < prop->text_size) { |
131 while (!cxBufferEof(current_buffer)) { |
208 const char *buf = prop->text + prop->text_pos; |
132 const char *buf = current_buffer->space + current_buffer->pos; |
209 size_t len = prop->text_size - prop->text_pos; |
133 size_t len = current_buffer->size - current_buffer->pos; |
210 |
134 |
211 /* |
135 /* |
212 * First we check if we have at least one line. We also get indices of |
136 * First we check if we have at least one line. We also get indices of |
213 * delimiter and comment chars |
137 * delimiter and comment chars |
214 */ |
138 */ |
349 |
303 |
350 CxPropertiesSource cxPropertiesStringSource(cxstring str) { |
304 CxPropertiesSource cxPropertiesStringSource(cxstring str) { |
351 CxPropertiesSource src; |
305 CxPropertiesSource src; |
352 src.src = (void*) str.ptr; |
306 src.src = (void*) str.ptr; |
353 src.data_size = str.length; |
307 src.data_size = str.length; |
|
308 src.data_ptr = NULL; |
354 src.read_func = cx_properties_read_string; |
309 src.read_func = cx_properties_read_string; |
355 src.read_init_func = NULL; |
310 src.read_init_func = NULL; |
356 src.read_clean_func = NULL; |
311 src.read_clean_func = NULL; |
357 return src; |
312 return src; |
358 } |
313 } |
359 |
314 |
360 CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len) { |
315 CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len) { |
361 CxPropertiesSource src; |
316 CxPropertiesSource src; |
362 src.src = (void*) str; |
317 src.src = (void*) str; |
363 src.data_size = len; |
318 src.data_size = len; |
|
319 src.data_ptr = NULL; |
364 src.read_func = cx_properties_read_string; |
320 src.read_func = cx_properties_read_string; |
365 src.read_init_func = NULL; |
321 src.read_init_func = NULL; |
366 src.read_clean_func = NULL; |
322 src.read_clean_func = NULL; |
367 return src; |
323 return src; |
368 } |
324 } |
369 |
325 |
370 CxPropertiesSource cxPropertiesCstrSource(const char *str) { |
326 CxPropertiesSource cxPropertiesCstrSource(const char *str) { |
371 CxPropertiesSource src; |
327 CxPropertiesSource src; |
372 src.src = (void*) str; |
328 src.src = (void*) str; |
373 src.data_size = strlen(str); |
329 src.data_size = strlen(str); |
|
330 src.data_ptr = NULL; |
374 src.read_func = cx_properties_read_string; |
331 src.read_func = cx_properties_read_string; |
375 src.read_init_func = NULL; |
332 src.read_init_func = NULL; |
376 src.read_clean_func = NULL; |
333 src.read_clean_func = NULL; |
377 return src; |
334 return src; |
378 } |
335 } |
379 |
336 |
380 CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size) { |
337 CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size) { |
381 CxPropertiesSource src; |
338 CxPropertiesSource src; |
382 src.src = file; |
339 src.src = file; |
383 src.data_size = chunk_size; |
340 src.data_size = chunk_size; |
|
341 src.data_ptr = NULL; |
384 src.read_func = cx_properties_read_file; |
342 src.read_func = cx_properties_read_file; |
385 src.read_init_func = cx_properties_read_init_file; |
343 src.read_init_func = cx_properties_read_init_file; |
386 src.read_clean_func = cx_properties_read_clean_file; |
344 src.read_clean_func = cx_properties_read_clean_file; |
387 return src; |
345 return src; |
388 } |
346 } |