diff options
| author | 2023-08-08 13:04:55 +0200 | |
|---|---|---|
| committer | 2023-08-08 13:04:55 +0200 | |
| commit | 7c8a6b71d9344ff9023620d48ecc81507f5e14ce (patch) | |
| tree | 4303403486aabec16b1606de7a60e8d1336b5272 /xs_json.h | |
| parent | Limited users can announce our own posts. (diff) | |
| download | penes-snac2-7c8a6b71d9344ff9023620d48ecc81507f5e14ce.tar.gz penes-snac2-7c8a6b71d9344ff9023620d48ecc81507f5e14ce.tar.xz penes-snac2-7c8a6b71d9344ff9023620d48ecc81507f5e14ce.zip | |
Backport from xs.
Diffstat (limited to '')
| -rw-r--r-- | xs_json.h | 213 |
1 files changed, 86 insertions, 127 deletions
| @@ -6,8 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | int xs_json_dump_pp(const xs_val *data, int indent, FILE *f); | 7 | int xs_json_dump_pp(const xs_val *data, int indent, FILE *f); |
| 8 | xs_str *xs_json_dumps_pp(const xs_val *data, int indent); | 8 | xs_str *xs_json_dumps_pp(const xs_val *data, int indent); |
| 9 | #define xs_json_dumps(data) xs_json_dumps_pp(data, 0) | ||
| 10 | #define xs_json_dump(data, f) xs_json_dumps_pp(data, 0, f) | ||
| 11 | xs_val *xs_json_loads(const xs_str *json); | 9 | xs_val *xs_json_loads(const xs_str *json); |
| 12 | xs_val *xs_json_load(FILE *f); | 10 | xs_val *xs_json_load(FILE *f); |
| 13 | 11 | ||
| @@ -201,17 +199,15 @@ typedef enum { | |||
| 201 | } js_type; | 199 | } js_type; |
| 202 | 200 | ||
| 203 | 201 | ||
| 204 | static xs_val *_xs_json_loads_lexer(const char **json, js_type *t) | 202 | static xs_val *_xs_json_load_lexer(FILE *f, js_type *t) |
| 205 | { | 203 | { |
| 206 | char c; | 204 | int c; |
| 207 | const char *s = *json; | ||
| 208 | xs_val *v = NULL; | 205 | xs_val *v = NULL; |
| 209 | 206 | ||
| 210 | /* skip blanks */ | 207 | *t = JS_ERROR; |
| 211 | while (*s == L' ' || *s == L'\t' || *s == L'\n' || *s == L'\r') | ||
| 212 | s++; | ||
| 213 | 208 | ||
| 214 | c = *s++; | 209 | /* skip blanks */ |
| 210 | while ((c = fgetc(f)) == L' ' || c == L'\t' || c == L'\n' || c == L'\r'); | ||
| 215 | 211 | ||
| 216 | if (c == '{') | 212 | if (c == '{') |
| 217 | *t = JS_OCURLY; | 213 | *t = JS_OCURLY; |
| @@ -236,141 +232,103 @@ static xs_val *_xs_json_loads_lexer(const char **json, js_type *t) | |||
| 236 | 232 | ||
| 237 | v = xs_str_new(NULL); | 233 | v = xs_str_new(NULL); |
| 238 | 234 | ||
| 239 | while ((c = *s) != '"' && c != '\0') { | 235 | while ((c = fgetc(f)) != '"' && c != EOF && *t != JS_ERROR) { |
| 240 | char tmp[5]; | ||
| 241 | int cp, i; | ||
| 242 | |||
| 243 | if (c == '\\') { | 236 | if (c == '\\') { |
| 244 | s++; | 237 | unsigned int cp = fgetc(f); |
| 245 | c = *s; | ||
| 246 | switch (c) { | ||
| 247 | case 'n': c = '\n'; break; | ||
| 248 | case 'r': c = '\r'; break; | ||
| 249 | case 't': c = '\t'; break; | ||
| 250 | case 'u': /* Unicode codepoint as an hex char */ | ||
| 251 | s++; | ||
| 252 | strncpy(tmp, s, 4); | ||
| 253 | tmp[4] = '\0'; | ||
| 254 | 238 | ||
| 255 | if (strlen(tmp) != 4) { | 239 | switch (cp) { |
| 240 | case 'n': cp = '\n'; break; | ||
| 241 | case 'r': cp = '\r'; break; | ||
| 242 | case 't': cp = '\t'; break; | ||
| 243 | case 'u': /* Unicode codepoint as an hex char */ | ||
| 244 | if (fscanf(f, "%04x", &cp) != 1) { | ||
| 256 | *t = JS_ERROR; | 245 | *t = JS_ERROR; |
| 257 | break; | 246 | break; |
| 258 | } | 247 | } |
| 259 | 248 | ||
| 260 | s += 3; /* skip as it was one byte */ | 249 | if (cp >= 0xd800 && cp <= 0xdfff) { |
| 261 | |||
| 262 | sscanf(tmp, "%04x", &i); | ||
| 263 | |||
| 264 | if (i >= 0xd800 && i <= 0xdfff) { | ||
| 265 | /* it's a surrogate pair */ | 250 | /* it's a surrogate pair */ |
| 266 | cp = (i & 0x3ff) << 10; | 251 | cp = (cp & 0x3ff) << 10; |
| 267 | 252 | ||
| 268 | /* skip to the next value (last char + \ + u) */ | 253 | /* \u must follow */ |
| 269 | s++; | 254 | if (fgetc(f) != '\\' || fgetc(f) != 'u') { |
| 270 | if (memcmp(s, "\\u", 2) != 0) { | ||
| 271 | *t = JS_ERROR; | 255 | *t = JS_ERROR; |
| 272 | break; | 256 | break; |
| 273 | } | 257 | } |
| 274 | s += 2; | ||
| 275 | 258 | ||
| 276 | strncpy(tmp, s, 4); | 259 | unsigned int i; |
| 277 | tmp[4] = '\0'; | 260 | if (fscanf(f, "%04x", &i) != 1) { |
| 278 | |||
| 279 | if (strlen(tmp) != 4) { | ||
| 280 | *t = JS_ERROR; | 261 | *t = JS_ERROR; |
| 281 | break; | 262 | break; |
| 282 | } | 263 | } |
| 283 | 264 | ||
| 284 | s += 3; /* skip as it was one byte */ | ||
| 285 | |||
| 286 | sscanf(tmp, "%04x", &i); | ||
| 287 | cp |= (i & 0x3ff); | 265 | cp |= (i & 0x3ff); |
| 288 | cp += 0x10000; | 266 | cp += 0x10000; |
| 289 | } | 267 | } |
| 290 | else | ||
| 291 | cp = i; | ||
| 292 | 268 | ||
| 293 | /* replace dangerous control codes with their visual representations */ | 269 | /* replace dangerous control codes with their visual representations */ |
| 294 | if (cp >= '\0' && cp < ' ' && !strchr("\r\n\t", cp)) | 270 | if (cp < ' ' && !strchr("\r\n\t", cp)) |
| 295 | cp += 0x2400; | 271 | cp += 0x2400; |
| 296 | 272 | ||
| 297 | v = xs_utf8_enc(v, cp); | ||
| 298 | c = '\0'; | ||
| 299 | |||
| 300 | break; | 273 | break; |
| 301 | } | 274 | } |
| 302 | } | ||
| 303 | |||
| 304 | if (c) | ||
| 305 | v = xs_append_m(v, &c, 1); | ||
| 306 | 275 | ||
| 307 | s++; | 276 | v = xs_utf8_enc(v, cp); |
| 277 | } | ||
| 278 | else { | ||
| 279 | char cc = c; | ||
| 280 | v = xs_append_m(v, &cc, 1); | ||
| 281 | } | ||
| 308 | } | 282 | } |
| 309 | |||
| 310 | if (c != '\0') | ||
| 311 | s++; | ||
| 312 | } | 283 | } |
| 313 | else | 284 | else |
| 314 | if (c == '-' || (c >= '0' && c <= '9') || c == '.') { | 285 | if (c == '-' || (c >= '0' && c <= '9') || c == '.') { |
| 315 | xs *vn = NULL; | 286 | double d; |
| 316 | |||
| 317 | *t = JS_INTEGER; | ||
| 318 | |||
| 319 | vn = xs_str_new(NULL); | ||
| 320 | vn = xs_append_m(vn, &c, 1); | ||
| 321 | |||
| 322 | while (((c = *s) >= '0' && c <= '9') || c == '.') { | ||
| 323 | if (c == '.') | ||
| 324 | *t = JS_REAL; | ||
| 325 | 287 | ||
| 326 | vn = xs_append_m(vn, &c, 1); | 288 | ungetc(c, f); |
| 327 | s++; | 289 | if (fscanf(f, "%lf", &d) == 1) { |
| 290 | *t = JS_REAL; | ||
| 291 | v = xs_number_new(d); | ||
| 328 | } | 292 | } |
| 329 | |||
| 330 | /* convert to XSTYPE_NUMBER */ | ||
| 331 | v = xs_number_new(atof(vn)); | ||
| 332 | } | 293 | } |
| 333 | else | 294 | else |
| 334 | if (c == 't' && strncmp(s, "rue", 3) == 0) { | 295 | if (c == 't') { |
| 335 | s += 3; | 296 | if (fgetc(f) == 'r' && fgetc(f) == 'u' && fgetc(f) == 'e') { |
| 336 | *t = JS_TRUE; | 297 | *t = JS_TRUE; |
| 337 | 298 | v = xs_val_new(XSTYPE_TRUE); | |
| 338 | v = xs_val_new(XSTYPE_TRUE); | 299 | } |
| 339 | } | 300 | } |
| 340 | else | 301 | else |
| 341 | if (c == 'f' && strncmp(s, "alse", 4) == 0) { | 302 | if (c == 'f') { |
| 342 | s += 4; | 303 | if (fgetc(f) == 'a' && fgetc(f) == 'l' && |
| 343 | *t = JS_FALSE; | 304 | fgetc(f) == 's' && fgetc(f) == 'e') { |
| 344 | 305 | *t = JS_FALSE; | |
| 345 | v = xs_val_new(XSTYPE_FALSE); | 306 | v = xs_val_new(XSTYPE_FALSE); |
| 307 | } | ||
| 346 | } | 308 | } |
| 347 | else | 309 | else |
| 348 | if (c == 'n' && strncmp(s, "ull", 3) == 0) { | 310 | if (c == 'n') { |
| 349 | s += 3; | 311 | if (fgetc(f) == 'u' && fgetc(f) == 'l' && fgetc(f) == 'l') { |
| 350 | *t = JS_NULL; | 312 | *t = JS_NULL; |
| 351 | 313 | v = xs_val_new(XSTYPE_NULL); | |
| 352 | v = xs_val_new(XSTYPE_NULL); | 314 | } |
| 353 | } | 315 | } |
| 354 | else | ||
| 355 | *t = JS_ERROR; | ||
| 356 | |||
| 357 | *json = s; | ||
| 358 | 316 | ||
| 359 | return v; | 317 | return v; |
| 360 | } | 318 | } |
| 361 | 319 | ||
| 362 | 320 | ||
| 363 | static xs_list *_xs_json_loads_array(const char **json, js_type *t); | 321 | static xs_list *_xs_json_load_array(FILE *f, js_type *t); |
| 364 | static xs_dict *_xs_json_loads_object(const char **json, js_type *t); | 322 | static xs_dict *_xs_json_load_object(FILE *f, js_type *t); |
| 365 | 323 | ||
| 366 | static xs_val *_xs_json_loads_value(const char **json, js_type *t, xs_val *v) | 324 | static xs_val *_xs_json_load_value(FILE *f, js_type *t, xs_val *v) |
| 367 | /* parses a JSON value */ | 325 | /* parses a JSON value */ |
| 368 | { | 326 | { |
| 369 | if (*t == JS_OBRACK) | 327 | if (*t == JS_OBRACK) |
| 370 | v = _xs_json_loads_array(json, t); | 328 | v = _xs_json_load_array(f, t); |
| 371 | else | 329 | else |
| 372 | if (*t == JS_OCURLY) | 330 | if (*t == JS_OCURLY) |
| 373 | v = _xs_json_loads_object(json, t); | 331 | v = _xs_json_load_object(f, t); |
| 374 | 332 | ||
| 375 | if (*t >= JS_VALUE) | 333 | if (*t >= JS_VALUE) |
| 376 | *t = JS_VALUE; | 334 | *t = JS_VALUE; |
| @@ -381,10 +339,9 @@ static xs_val *_xs_json_loads_value(const char **json, js_type *t, xs_val *v) | |||
| 381 | } | 339 | } |
| 382 | 340 | ||
| 383 | 341 | ||
| 384 | static xs_list *_xs_json_loads_array(const char **json, js_type *t) | 342 | static xs_list *_xs_json_load_array(FILE *f, js_type *t) |
| 385 | /* parses a JSON array */ | 343 | /* parses a JSON array */ |
| 386 | { | 344 | { |
| 387 | const char *s = *json; | ||
| 388 | xs *v; | 345 | xs *v; |
| 389 | xs_list *l; | 346 | xs_list *l; |
| 390 | js_type tt; | 347 | js_type tt; |
| @@ -393,18 +350,18 @@ static xs_list *_xs_json_loads_array(const char **json, js_type *t) | |||
| 393 | 350 | ||
| 394 | *t = JS_INCOMPLETE; | 351 | *t = JS_INCOMPLETE; |
| 395 | 352 | ||
| 396 | v = _xs_json_loads_lexer(&s, &tt); | 353 | v = _xs_json_load_lexer(f, &tt); |
| 397 | 354 | ||
| 398 | if (tt == JS_CBRACK) | 355 | if (tt == JS_CBRACK) |
| 399 | *t = JS_ARRAY; | 356 | *t = JS_ARRAY; |
| 400 | else { | 357 | else { |
| 401 | v = _xs_json_loads_value(&s, &tt, v); | 358 | v = _xs_json_load_value(f, &tt, v); |
| 402 | 359 | ||
| 403 | if (tt == JS_VALUE) { | 360 | if (tt == JS_VALUE) { |
| 404 | l = xs_list_append(l, v); | 361 | l = xs_list_append(l, v); |
| 405 | 362 | ||
| 406 | while (*t == JS_INCOMPLETE) { | 363 | while (*t == JS_INCOMPLETE) { |
| 407 | xs_free(_xs_json_loads_lexer(&s, &tt)); | 364 | xs_free(_xs_json_load_lexer(f, &tt)); |
| 408 | 365 | ||
| 409 | if (tt == JS_CBRACK) | 366 | if (tt == JS_CBRACK) |
| 410 | *t = JS_ARRAY; | 367 | *t = JS_ARRAY; |
| @@ -412,8 +369,8 @@ static xs_list *_xs_json_loads_array(const char **json, js_type *t) | |||
| 412 | if (tt == JS_COMMA) { | 369 | if (tt == JS_COMMA) { |
| 413 | xs *v2; | 370 | xs *v2; |
| 414 | 371 | ||
| 415 | v2 = _xs_json_loads_lexer(&s, &tt); | 372 | v2 = _xs_json_load_lexer(f, &tt); |
| 416 | v2 = _xs_json_loads_value(&s, &tt, v2); | 373 | v2 = _xs_json_load_value(f, &tt, v2); |
| 417 | 374 | ||
| 418 | if (tt == JS_VALUE) | 375 | if (tt == JS_VALUE) |
| 419 | l = xs_list_append(l, v2); | 376 | l = xs_list_append(l, v2); |
| @@ -431,16 +388,13 @@ static xs_list *_xs_json_loads_array(const char **json, js_type *t) | |||
| 431 | if (*t == JS_ERROR) | 388 | if (*t == JS_ERROR) |
| 432 | l = xs_free(l); | 389 | l = xs_free(l); |
| 433 | 390 | ||
| 434 | *json = s; | ||
| 435 | |||
| 436 | return l; | 391 | return l; |
| 437 | } | 392 | } |
| 438 | 393 | ||
| 439 | 394 | ||
| 440 | static xs_dict *_xs_json_loads_object(const char **json, js_type *t) | 395 | static xs_dict *_xs_json_load_object(FILE *f, js_type *t) |
| 441 | /* parses a JSON object */ | 396 | /* parses a JSON object */ |
| 442 | { | 397 | { |
| 443 | const char *s = *json; | ||
| 444 | xs *k1; | 398 | xs *k1; |
| 445 | xs_dict *d; | 399 | xs_dict *d; |
| 446 | js_type tt; | 400 | js_type tt; |
| @@ -449,40 +403,40 @@ static xs_dict *_xs_json_loads_object(const char **json, js_type *t) | |||
| 449 | 403 | ||
| 450 | *t = JS_INCOMPLETE; | 404 | *t = JS_INCOMPLETE; |
| 451 | 405 | ||
| 452 | k1 = _xs_json_loads_lexer(&s, &tt); | 406 | k1 = _xs_json_load_lexer(f, &tt); |
| 453 | 407 | ||
| 454 | if (tt == JS_CCURLY) | 408 | if (tt == JS_CCURLY) |
| 455 | *t = JS_OBJECT; | 409 | *t = JS_OBJECT; |
| 456 | else | 410 | else |
| 457 | if (tt == JS_STRING) { | 411 | if (tt == JS_STRING) { |
| 458 | xs_free(_xs_json_loads_lexer(&s, &tt)); | 412 | xs_free(_xs_json_load_lexer(f, &tt)); |
| 459 | 413 | ||
| 460 | if (tt == JS_COLON) { | 414 | if (tt == JS_COLON) { |
| 461 | xs *v1; | 415 | xs *v1; |
| 462 | 416 | ||
| 463 | v1 = _xs_json_loads_lexer(&s, &tt); | 417 | v1 = _xs_json_load_lexer(f, &tt); |
| 464 | v1 = _xs_json_loads_value(&s, &tt, v1); | 418 | v1 = _xs_json_load_value(f, &tt, v1); |
| 465 | 419 | ||
| 466 | if (tt == JS_VALUE) { | 420 | if (tt == JS_VALUE) { |
| 467 | d = xs_dict_append(d, k1, v1); | 421 | d = xs_dict_append(d, k1, v1); |
| 468 | 422 | ||
| 469 | while (*t == JS_INCOMPLETE) { | 423 | while (*t == JS_INCOMPLETE) { |
| 470 | xs_free(_xs_json_loads_lexer(&s, &tt)); | 424 | xs_free(_xs_json_load_lexer(f, &tt)); |
| 471 | 425 | ||
| 472 | if (tt == JS_CCURLY) | 426 | if (tt == JS_CCURLY) |
| 473 | *t = JS_OBJECT; | 427 | *t = JS_OBJECT; |
| 474 | else | 428 | else |
| 475 | if (tt == JS_COMMA) { | 429 | if (tt == JS_COMMA) { |
| 476 | xs *k = _xs_json_loads_lexer(&s, &tt); | 430 | xs *k = _xs_json_load_lexer(f, &tt); |
| 477 | 431 | ||
| 478 | if (tt == JS_STRING) { | 432 | if (tt == JS_STRING) { |
| 479 | xs_free(_xs_json_loads_lexer(&s, &tt)); | 433 | xs_free(_xs_json_load_lexer(f, &tt)); |
| 480 | 434 | ||
| 481 | if (tt == JS_COLON) { | 435 | if (tt == JS_COLON) { |
| 482 | xs *v; | 436 | xs *v; |
| 483 | 437 | ||
| 484 | v = _xs_json_loads_lexer(&s, &tt); | 438 | v = _xs_json_load_lexer(f, &tt); |
| 485 | v = _xs_json_loads_value(&s, &tt, v); | 439 | v = _xs_json_load_value(f, &tt, v); |
| 486 | 440 | ||
| 487 | if (tt == JS_VALUE) | 441 | if (tt == JS_VALUE) |
| 488 | d = xs_dict_append(d, k, v); | 442 | d = xs_dict_append(d, k, v); |
| @@ -511,8 +465,6 @@ static xs_dict *_xs_json_loads_object(const char **json, js_type *t) | |||
| 511 | if (*t == JS_ERROR) | 465 | if (*t == JS_ERROR) |
| 512 | d = xs_free(d); | 466 | d = xs_free(d); |
| 513 | 467 | ||
| 514 | *json = s; | ||
| 515 | |||
| 516 | return d; | 468 | return d; |
| 517 | } | 469 | } |
| 518 | 470 | ||
| @@ -520,16 +472,31 @@ static xs_dict *_xs_json_loads_object(const char **json, js_type *t) | |||
| 520 | xs_val *xs_json_loads(const xs_str *json) | 472 | xs_val *xs_json_loads(const xs_str *json) |
| 521 | /* loads a string in JSON format and converts to a multiple data */ | 473 | /* loads a string in JSON format and converts to a multiple data */ |
| 522 | { | 474 | { |
| 475 | FILE *f; | ||
| 476 | xs_val *v = NULL; | ||
| 477 | |||
| 478 | if ((f = fmemopen((char *)json, strlen(json), "r")) != NULL) { | ||
| 479 | v = xs_json_load(f); | ||
| 480 | fclose(f); | ||
| 481 | } | ||
| 482 | |||
| 483 | return v; | ||
| 484 | } | ||
| 485 | |||
| 486 | |||
| 487 | xs_val *xs_json_load(FILE *f) | ||
| 488 | /* loads a JSON file */ | ||
| 489 | { | ||
| 523 | xs_val *v = NULL; | 490 | xs_val *v = NULL; |
| 524 | js_type t; | 491 | js_type t; |
| 525 | 492 | ||
| 526 | xs_free(_xs_json_loads_lexer(&json, &t)); | 493 | xs_free(_xs_json_load_lexer(f, &t)); |
| 527 | 494 | ||
| 528 | if (t == JS_OBRACK) | 495 | if (t == JS_OBRACK) |
| 529 | v = _xs_json_loads_array(&json, &t); | 496 | v = _xs_json_load_array(f, &t); |
| 530 | else | 497 | else |
| 531 | if (t == JS_OCURLY) | 498 | if (t == JS_OCURLY) |
| 532 | v = _xs_json_loads_object(&json, &t); | 499 | v = _xs_json_load_object(f, &t); |
| 533 | else | 500 | else |
| 534 | t = JS_ERROR; | 501 | t = JS_ERROR; |
| 535 | 502 | ||
| @@ -537,14 +504,6 @@ xs_val *xs_json_loads(const xs_str *json) | |||
| 537 | } | 504 | } |
| 538 | 505 | ||
| 539 | 506 | ||
| 540 | xs_val *xs_json_load(FILE *f) | ||
| 541 | /* loads a JSON file */ | ||
| 542 | { | ||
| 543 | xs *o = xs_readall(f); | ||
| 544 | return o ? xs_json_loads(o) : NULL; | ||
| 545 | } | ||
| 546 | |||
| 547 | |||
| 548 | #endif /* XS_IMPLEMENTATION */ | 507 | #endif /* XS_IMPLEMENTATION */ |
| 549 | 508 | ||
| 550 | #endif /* _XS_JSON_H */ | 509 | #endif /* _XS_JSON_H */ |