nisse.h (15680B)
1 #ifndef NISSE_H_ 2 #define NISSE_H_ 3 4 /* 5 ** nisse, the not intricate serialised s expressions. 6 ** 7 ** do #define NISSE_NO_ERROR to disable errors on stdout 8 ** 9 ** NOTE: does not support escaped characters in strings! 10 ** However strings will include whatever binary junk you put into them except ` 11 ** Basically you only have "raw" strings 12 */ 13 14 /* 15 ** TODO: 16 ** Perhaps some better error messages with line indication and so on. 17 ** Better programatic error handling. 18 */ 19 20 #include <stdarg.h> 21 #include <stdlib.h> 22 #include <stdio.h> 23 #include <stdint.h> 24 #include <string.h> 25 #include <assert.h> 26 #include <ctype.h> 27 28 enum nisse_types { 29 NISSE_TYPE_NONE, 30 NISSE_TYPE_INT, // 10 31 NISSE_TYPE_FLOAT, // 1.000000 32 NISSE_TYPE_STRING, // string or `string with whitespace` 33 NISSE_TYPE_ARRAY, // (....) or (name ......) for 'tagged' variables 34 }; 35 36 typedef struct nisse_data_entry_s { 37 enum nisse_types type; 38 union { 39 int i; 40 float f; 41 char* str; 42 struct nisse_data_entry_s* nde; 43 }; 44 45 int is_str_allocated; 46 47 ////////////////// 48 // NISSE_TYPE_ARRAY stuff 49 int nde_len; 50 int is_nde_allocated; 51 int len_is_fixed; 52 53 // for pretty serialisation 54 int new_line_at_start; 55 int new_line_at_end; 56 int new_line_at_end_of_subsequent_elements; 57 } nde_t; 58 59 // anonymous nde macros 60 #define nisse_andei(__i) {.type = NISSE_TYPE_INT, .i = __i} 61 #define nisse_andef(__f) {.type = NISSE_TYPE_FLOAT, .f = __f} 62 #define nisse_andes(__s) {.type = NISSE_TYPE_STRING, .str = __s} 63 #define nisse_andea(__count, ...) {.type = NISSE_TYPE_ARRAY, .nde_len = __count, .nde = (nde_t[]){__VA_ARGS__}} 64 #define nisse_andeanl(__count, ...) {.new_line_at_end_of_subsequent_elements = 1, .type = NISSE_TYPE_ARRAY, .nde_len = __count, .nde = (nde_t[]){__VA_ARGS__}} 65 66 // tagged nde macros 67 #define nisse_tndei(__name, __i) {.type = NISSE_TYPE_ARRAY, .nde_len = 2, .nde = (nde_t[]){nisse_andes(__name), nisse_andei(__i)}} 68 #define nisse_tndef(__name, __f) {.type = NISSE_TYPE_ARRAY, .nde_len = 2, .nde = (nde_t[]){nisse_andes(__name), nisse_andef(__f)}} 69 #define nisse_tndes(__name, __s) {.type = NISSE_TYPE_ARRAY, .nde_len = 2, .nde = (nde_t[]){nisse_andes(__name), nisse_andes(__s)}} 70 #define nisse_tndea(__name, __count, ...) nisse_andea(__count + 1, nisse_andes(__name), __VA_ARGS__) 71 #define nisse_tndeanl(__name, __count, ...) nisse_andeanl(__count + 1, nisse_andes(__name), __VA_ARGS__) 72 73 extern int nisse_nde_fits_format(nde_t* nde, nde_t* fmt); 74 // shallow comapre, does not support anonymous string arrays 75 // if nde is array: 76 // > will check if named structs are present in fmt 77 // > will check if anonymous variables have the right type for their given index in nde 78 // > (direct index in nde, not like in c literals) 79 // > if it is anonymous and fmt has tagged array at that index with a single variable, 80 // > it will check them instead 81 // else: 82 // > check if type is the same 83 84 extern nde_t* nisse_nde_get_value(nde_t* nde, int* len); // returns nde->nde + 1 if nde->nde is string 85 extern nde_t* nisse_nde_get_tagged(nde_t* nde, const char* tag); 86 extern nde_t* nisse_nde_get_index(nde_t* nde, int index); // has bounds checking 87 88 extern int nisse_write_to_file(char* filename, const nde_t nde); 89 90 extern nde_t nisse_parse_file(char* filename); 91 extern nde_t nisse_parse_memory(char* mem, int sz); 92 93 extern void nisse_free_nde(nde_t* nde); 94 extern nde_t nisse_dup_nde(nde_t* nde, int free_old_nde); 95 96 97 98 99 100 101 102 #ifdef NISSE_IMPL 103 104 105 #ifndef NISSE_NO_ERROR 106 static void 107 nisse_eprintf(const char* fmt, ...) 108 { 109 va_list args; 110 va_start(args, fmt); 111 vfprintf(stderr, fmt, args); 112 va_end(args); 113 } 114 #else // NISSE_NO_ERROR 115 #define nisse_eprintf(...) 116 #endif // NISSE_NO_ERROR 117 118 int 119 nisse_nde_fits_format(nde_t* nde, nde_t* fmt) 120 { 121 if (!nde) return 0; 122 123 if (nde->type == NISSE_TYPE_ARRAY) { 124 for (int i = nde->nde_len && nde->nde->type == NISSE_TYPE_STRING; i < nde->nde_len; i++) { 125 if (nde->nde[i].type == NISSE_TYPE_ARRAY && nde->nde[i].nde_len && nde->nde[i].nde->type == NISSE_TYPE_STRING) { 126 if (!nisse_nde_get_tagged(fmt, nde->nde[i].nde->str)) return 0; 127 } else { 128 nde_t* res = nisse_nde_get_index(fmt, i); 129 if (!res) return 0; 130 if (nde->nde[i].type != NISSE_TYPE_ARRAY && res->type == NISSE_TYPE_ARRAY && res->nde_len == 2) { 131 if (res->nde[1].type != nde->nde[i].type) 132 return 0; 133 } else if (nde->nde[i].type != res->type) { 134 return 0; 135 } 136 } 137 } 138 return 1; 139 } 140 return nde->type == fmt->type; 141 } 142 143 nde_t* 144 nisse_nde_get_value(nde_t* nde, int* len) 145 { 146 if (!nde) return NULL; 147 if (len) *len = 1; 148 if (nde->type != NISSE_TYPE_ARRAY) return nde; 149 150 if (!nde->nde || nde->nde_len < 2 || nde->nde->type != NISSE_TYPE_STRING) return nde->nde; 151 if (len) *len = nde->nde_len - 1; 152 return nde->nde + 1; 153 } 154 155 nde_t* 156 nisse_nde_get_tagged(nde_t* nde, const char* tag) 157 { 158 if (!nde || nde->type != NISSE_TYPE_ARRAY || !nde->nde) return NULL; 159 160 for (int i = 0; i < nde->nde_len; i++) 161 if (nde->nde[i].type == NISSE_TYPE_ARRAY && nde->nde[i].nde_len && nde->nde[i].nde->type == NISSE_TYPE_STRING) { 162 if (strcmp(tag, nde->nde[i].nde->str) == 0) 163 return nde->nde + i; 164 } 165 return NULL; 166 } 167 168 nde_t* 169 nisse_nde_get_index(nde_t* nde, int index) 170 { 171 if (!nde || nde->type != NISSE_TYPE_ARRAY || !nde->nde || index < 0 || index >= nde->nde_len) 172 return NULL; 173 return nde->nde + index; 174 } 175 176 static 177 void nisse_write_nde(FILE* fd, const nde_t* nde, int root, int indents, int new_line_start, int first) 178 { 179 if (!nde) { 180 nisse_eprintf("NISSE ERROR: nde was NULL\n"); 181 return; 182 } 183 184 if ((new_line_start && (!first || root)) || nde->new_line_at_start) { 185 fprintf(fd, "\n"); 186 for (int i = 0; i < indents; i++) 187 fprintf(fd, " "); 188 } else if (!root && !first) { 189 fprintf(fd, " "); 190 } 191 192 if (nde->type == NISSE_TYPE_ARRAY) { 193 fprintf(fd, "("); 194 } else if (root) { 195 nisse_eprintf("NISSE ERROR: a root nde was not an array\n"); 196 return; 197 } 198 199 if (nde->type == NISSE_TYPE_NONE) { 200 nisse_eprintf("NISSE ERROR: skipping writing an uninitialized member\n"); 201 } else if (nde->type == NISSE_TYPE_INT) { 202 fprintf(fd, "%d", nde->i); 203 } else if (nde->type == NISSE_TYPE_FLOAT) { 204 fprintf(fd, "%f", nde->f); 205 } else if (nde->type == NISSE_TYPE_STRING) { 206 int res = 0; 207 for (int i = 0; i < strlen(nde->str); i++) { 208 if (i == 0 && memchr("1234567890.-", nde->str[i], sizeof("1234567890.-"))) { 209 res = 1; 210 break; 211 } 212 if (memchr("\n '\t\v()\r", nde->str[i], sizeof("\n '\t\v()\r"))) { 213 res = 1; 214 break; 215 } 216 } 217 if (strchr(nde->str, '`')) { 218 nisse_eprintf("NISSE ERROR: string '%s' contains a `, nisse does not support this\n", nde->str); 219 } else { 220 if (res) fprintf(fd, "`%s`", nde->str); 221 else fprintf(fd, "%s", nde->str); 222 } 223 } else if (nde->type == NISSE_TYPE_ARRAY) { 224 for (int i = 0; i < nde->nde_len; i++) 225 nisse_write_nde(fd, nde->nde + i, 0, indents + 1, nde->new_line_at_end_of_subsequent_elements, i == 0); 226 } 227 228 if (nde->new_line_at_end_of_subsequent_elements) { 229 fprintf(fd, "\n"); 230 for (int i = 0; i < indents; i++) 231 fprintf(fd, " "); 232 } 233 if (nde->type == NISSE_TYPE_ARRAY) 234 fprintf(fd, ")"); 235 } 236 237 int 238 nisse_write_to_file(char* filename, const nde_t nde) 239 { 240 if (nde.type != NISSE_TYPE_ARRAY) return 0; 241 242 FILE* fd = fopen(filename, "wb"); 243 if (!fd) { 244 nisse_eprintf("NISSE ERROR: unable to open file\n"); 245 return 0; 246 } 247 248 for (int i = 0; i < nde.nde_len; i++) 249 nisse_write_nde(fd, nde.nde + i, 1, 0, i > 0, 1); 250 251 fclose(fd); 252 return 1; 253 } 254 255 nde_t 256 nisse_parse_file(char* filename) 257 { 258 FILE* file = fopen(filename, "rb"); 259 if (!file) { 260 nisse_eprintf("NISSE ERROR: unable to open file\n"); 261 return (nde_t){0}; 262 } 263 264 fseek(file, 0L, SEEK_END); 265 int readsize = ftell(file); 266 rewind(file); 267 268 char* buffer = malloc(readsize); 269 if (!buffer) return (nde_t){0}; 270 271 fread(buffer, 1, readsize, file); 272 fclose(file); 273 274 return nisse_parse_memory(buffer, readsize); 275 } 276 277 static int 278 nisse_seek_whitespace(char* mem, int index, int sz) 279 { 280 while (index < sz && !memchr("\n '\t()\v\r", mem[index], sizeof("\n '\t()\v\r"))) 281 index++; 282 return index; 283 } 284 285 static int 286 nisse_parse_memory_array(char* mem, int sz, nde_t* nde) { 287 int index = 0; 288 if (index >= sz) return index; 289 if (mem[index] == '(') index++; 290 if (index >= sz) return index; 291 292 nde->type = NISSE_TYPE_ARRAY; 293 nde->is_nde_allocated = 1; 294 int new_line_on_all_elements = 1; 295 296 while (index < sz) { 297 int last_index = index; 298 299 while (index < sz && memchr("\n \t\v\r", mem[index], sizeof("\n \t\v\r"))) 300 index++; 301 302 if (mem[index] == ')') { 303 if (nde->nde_len <= 1) new_line_on_all_elements = 0; 304 break; 305 } 306 307 int i = ++nde->nde_len - 1; 308 nde->nde = realloc(nde->nde, sizeof(*nde->nde) * nde->nde_len); 309 nde->nde[i] = (nde_t){0}; 310 311 if (index != last_index && memchr(mem + last_index, '\n', index - last_index)) 312 nde->nde[i].new_line_at_start = 1; 313 314 if (memchr("1234567890.-", mem[index], sizeof("1234567890.-"))) { 315 int number_end = nisse_seek_whitespace(mem, index, sz); 316 317 char tmp = mem[number_end]; 318 mem[number_end] = 0; 319 if (memchr(mem + index, '.', number_end - index)) /* float */ { 320 nde->nde[i].f = atof(mem + index); 321 nde->nde[i].type = NISSE_TYPE_FLOAT; 322 } else /* int */ { 323 nde->nde[i].i = atoi(mem + index); 324 nde->nde[i].type = NISSE_TYPE_INT; 325 } 326 mem[number_end] = tmp; 327 index = number_end; 328 } else if (mem[index] == '(') /* array */ { 329 index += nisse_parse_memory_array(mem + index, sz - index, nde->nde + i); 330 } else if (mem[index] == '`') /* string */ { 331 char* end; 332 int times = 0; 333 do { 334 times++; 335 end = (sz - index - 1 < 0) ? NULL : memchr(mem + index + times, '`', sz - index - times); 336 } while (end && end[-1] == '\\'); 337 if (!end) { 338 nisse_eprintf("NISSE ERROR: unable to find closing quote\n"); 339 return sz; 340 } 341 size_t strsz = (end) - (mem + index + 1); 342 343 nde->nde[i].str = malloc(strsz + 1); 344 nde->nde[i].is_str_allocated = 1; 345 346 memcpy(nde->nde[i].str, mem + index + 1, strsz); 347 nde->nde[i].str[strsz] = 0; 348 349 index += strsz + 2; 350 nde->nde[i].type = NISSE_TYPE_STRING; 351 } else /* string without " " */ { 352 int end = nisse_seek_whitespace(mem, index, sz); 353 354 size_t strsz = (end - index); 355 356 nde->nde[i].str = malloc(strsz + 1); 357 nde->nde[i].is_str_allocated = 1; 358 359 memcpy(nde->nde[i].str, mem + index, strsz); 360 nde->nde[i].str[strsz] = 0; 361 362 index += strsz; 363 nde->nde[i].type = NISSE_TYPE_STRING; 364 } 365 366 if (mem[index] == '\n' && nde->nde[i].type == NISSE_TYPE_ARRAY && !nde->nde[i].new_line_at_start) 367 nde->nde[i].new_line_at_end = 1; 368 369 if ((!nde->nde[i].new_line_at_start || nde->nde[i].new_line_at_end) && i != 0) 370 new_line_on_all_elements = 0; 371 } 372 nde->new_line_at_end_of_subsequent_elements = new_line_on_all_elements; 373 374 if (index >= sz) { 375 nisse_eprintf("NISSE ERROR: unable to find closing parenthesis\n"); 376 return sz; 377 } 378 return index + 1; 379 } 380 381 nde_t 382 nisse_parse_memory(char* mem, int sz) 383 { 384 int index = 0; 385 nde_t nde = {.type = NISSE_TYPE_ARRAY}; 386 nde.nde_len = 0; 387 while (index < sz) { 388 char* new_pos = memchr(mem + index, '(', sz - index); 389 if (!new_pos) return nde; 390 391 index += (int)(new_pos - (mem + index)); 392 393 nde.nde_len += 1; 394 nde.nde = realloc(nde.nde, sizeof(*nde.nde) * nde.nde_len); 395 nde.nde[nde.nde_len - 1] = (nde_t){0}; 396 397 index += nisse_parse_memory_array(mem + index, sz - index, nde.nde + (nde.nde_len - 1)); 398 } 399 if (nde.nde_len) nde.is_nde_allocated = 1; 400 return nde; 401 } 402 403 void 404 nisse_free_nde(nde_t* nde) 405 { 406 407 if (nde->type == NISSE_TYPE_STRING && nde->is_str_allocated) { 408 free(nde->str); 409 } else if (nde->type == NISSE_TYPE_ARRAY) { 410 for (int i = 0; i < nde->nde_len; i++) nisse_free_nde(nde->nde + i); 411 if (nde->is_nde_allocated) free(nde->nde); 412 } 413 } 414 415 nde_t 416 nisse_dup_nde(nde_t* nde, int free_old_nde) 417 { 418 if (nde->type == NISSE_TYPE_STRING) { 419 char* str = strdup(nde->str); 420 if (free_old_nde && nde->is_str_allocated) free(nde->str); 421 nde->is_str_allocated = 1; 422 nde->str = str; 423 } else if (nde->type == NISSE_TYPE_ARRAY) { 424 nde_t* n = malloc(nde->nde_len * sizeof(*n)); 425 memcpy(n, nde->nde, nde->nde_len * sizeof(*n)); 426 if (free_old_nde && nde->is_nde_allocated) free(nde->nde); 427 nde->nde = n; 428 nde->is_nde_allocated = 1; 429 for (int i = 0; i < nde->nde_len; i++) nisse_dup_nde(nde->nde + i, free_old_nde); 430 } 431 return *nde; 432 } 433 434 #endif // NISSE_IMPL 435 436 #endif // NISSE_H_