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