move control socket handling to separate file
[mizunara.git] / ucx / properties.c
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "ucx/properties.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 UcxProperties *ucx_properties_new() {
36     UcxProperties *parser = (UcxProperties*)malloc(
37             sizeof(UcxProperties));
38     if(!parser) {
39         return NULL;
40     }
41     
42     parser->buffer = NULL;
43     parser->buflen = 0;
44     parser->pos = 0;
45     parser->tmp = NULL;
46     parser->tmplen = 0;
47     parser->tmpcap = 0;
48     parser->error = 0;
49     parser->delimiter = '=';
50     parser->comment1 = '#';
51     parser->comment2 = 0;
52     parser->comment3 = 0;   
53     
54     return parser;
55 }
56
57 void ucx_properties_free(UcxProperties *parser) {
58     if(parser->tmp) {
59         free(parser->tmp);
60     }
61     free(parser);
62 }
63
64 void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) {
65     parser->buffer = buf;
66     parser->buflen = len;
67     parser->pos = 0;
68 }
69
70 static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) {
71     if(parser->tmpcap - parser->tmplen < len) {
72         size_t newcap = parser->tmpcap + len + 64;
73         parser->tmp = (char*)realloc(parser->tmp, newcap);
74         parser->tmpcap = newcap;
75     }
76     memcpy(parser->tmp + parser->tmplen, buf, len);
77     parser->tmplen += len;
78 }
79
80 int ucx_properties_next(UcxProperties *parser, sstr_t *name, sstr_t *value)  {   
81     if(parser->tmplen > 0) {
82         char *buf = parser->buffer + parser->pos;
83         size_t len = parser->buflen - parser->pos;
84         sstr_t str = sstrn(buf, len);
85         sstr_t nl = sstrchr(str, '\n');
86         if(nl.ptr) {
87             size_t newlen = (size_t)(nl.ptr - buf) + 1;
88             parser_tmp_append(parser, buf, newlen);
89             // the tmp buffer contains exactly one line now
90             
91             char *orig_buf = parser->buffer;
92             size_t orig_len = parser->buflen;
93             
94             parser->buffer = parser->tmp;
95             parser->buflen = parser->tmplen;
96             parser->pos = 0;    
97             parser->tmp = NULL;
98             parser->tmpcap = 0;
99             parser->tmplen = 0;
100             // run ucx_properties_next with the tmp buffer as main buffer
101             int ret = ucx_properties_next(parser, name, value);
102             
103             // restore original buffer
104             parser->tmp = parser->buffer;
105             parser->buffer = orig_buf;
106             parser->buflen = orig_len;
107             parser->pos = newlen;
108             
109             /*
110              * if ret == 0 the tmp buffer contained just space or a comment
111              * we parse again with the original buffer to get a name/value
112              * or a new tmp buffer
113              */
114             return ret ? ret : ucx_properties_next(parser, name, value);
115         } else {
116             parser_tmp_append(parser, buf, len);
117             return 0;
118         }
119     } else if(parser->tmp) {
120         free(parser->tmp);
121         parser->tmp = NULL;
122     }
123     
124     char comment1 = parser->comment1;
125     char comment2 = parser->comment2;
126     char comment3 = parser->comment3;
127     char delimiter = parser->delimiter;
128     
129     // get one line and parse it
130     while(parser->pos < parser->buflen) {
131         char *buf = parser->buffer + parser->pos;
132         size_t len = parser->buflen - parser->pos;
133         
134         /*
135          * First we check if we have at least one line. We also get indices of
136          * delimiter and comment chars
137          */
138         size_t delimiter_index = 0;
139         size_t comment_index = 0;
140         int has_comment = 0;
141
142         size_t i = 0;
143         char c = 0;
144         for(;i<len;i++) {
145             c = buf[i];
146             if(c == comment1 || c == comment2 || c == comment3) {
147                 if(comment_index == 0) {
148                     comment_index = i;
149                     has_comment = 1;
150                 }
151             } else if(c == delimiter) {
152                 if(delimiter_index == 0 && !has_comment) {
153                     delimiter_index = i;
154                 }
155             } else if(c == '\n') {
156                 break;
157             }
158         }
159
160         if(c != '\n') {
161             // we don't have enough data for a line
162             // store remaining bytes in temporary buffer for next round
163             parser->tmpcap = len + 128;
164             parser->tmp = (char*)malloc(parser->tmpcap);
165             parser->tmplen = len;
166             memcpy(parser->tmp, buf, len);
167             return 0;
168         }
169         
170         sstr_t line = has_comment ? sstrn(buf, comment_index) : sstrn(buf, i);
171         // check line
172         if(delimiter_index == 0) {
173             line = sstrtrim(line);
174             if(line.length != 0) {
175                 parser->error = 1;
176             }
177         } else {
178             sstr_t n = sstrn(buf, delimiter_index);
179             sstr_t v = sstrn(
180                     buf + delimiter_index + 1,
181                     line.length - delimiter_index - 1); 
182             n = sstrtrim(n);
183             v = sstrtrim(v);
184             if(n.length != 0 || v.length != 0) {
185                 *name = n;
186                 *value = v;
187                 parser->pos += i + 1;
188                 return 1;
189             } else {
190                 parser->error = 1;
191             }
192         }
193         
194         parser->pos += i + 1;
195     }
196     
197     return 0;
198 }
199
200 int ucx_properties2map(UcxProperties *parser, UcxMap *map) {
201     sstr_t name;
202     sstr_t value;
203     while(ucx_properties_next(parser, &name, &value)) {
204         value = sstrdup_a(map->allocator, value);
205         if(!value.ptr) {
206             return 1;
207         }
208         if(ucx_map_sstr_put(map, name, value.ptr)) {
209             alfree(map->allocator, value.ptr);
210             return 1;
211         }
212     }
213     if (parser->error) {
214         return parser->error;
215     } else {
216         return 0;
217     }
218 }
219
220 // buffer size is documented - change doc, when you change bufsize!
221 #define UCX_PROPLOAD_BUFSIZE  1024
222 int ucx_properties_load(UcxMap *map, FILE *file) {
223     UcxProperties *parser = ucx_properties_new();
224     if(!(parser && map && file)) {
225         return 1;
226     }
227     
228     int error = 0;
229     size_t r;
230     char buf[UCX_PROPLOAD_BUFSIZE];
231     while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) {
232         ucx_properties_fill(parser, buf, r);
233         error = ucx_properties2map(parser, map);
234         if (error) {
235             break;
236         }
237     }
238     ucx_properties_free(parser);
239     return error;
240 }
241
242 int ucx_properties_store(UcxMap *map, FILE *file) {
243     UcxMapIterator iter = ucx_map_iterator(map);
244     void *v;
245     sstr_t value;
246     size_t written;
247
248     UCX_MAP_FOREACH(k, v, iter) {
249         value = sstr((char*)v);
250
251         written = 0;
252         written += fwrite(k.data, 1, k.len, file);
253         written += fwrite(" = ", 1, 3, file);
254         written += fwrite(value.ptr, 1, value.length, file);
255         written += fwrite("\n", 1, 1, file);
256
257         if (written != k.len + value.length + 4) {
258             return 1;
259         }
260     }
261
262     return 0;
263 }
264