UNIXworkcode

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 265