343 return CX_JSON_INCOMPLETE_DATA; |
343 return CX_JSON_INCOMPLETE_DATA; |
344 } |
344 } |
345 |
345 |
346 static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) { |
346 static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) { |
347 // TODO: support more escape sequences |
347 // TODO: support more escape sequences |
348 // we know that the unescaped string will be shorter by at least 2 chars |
348 // TODO: to be consistent with escape_string() we might want to expect that the enclosing quotes were already removed |
349 cxmutstr result; |
349 cxmutstr result; |
350 result.length = 0; |
350 result.length = 0; |
351 result.ptr = cxMalloc(a, str.length - 1); |
351 result.ptr = cxMalloc(a, str.length - 1); |
352 if (result.ptr == NULL) return result; // LCOV_EXCL_LINE |
352 if (result.ptr == NULL) return result; // LCOV_EXCL_LINE |
353 |
353 |
371 } |
371 } |
372 } |
372 } |
373 result.ptr[result.length] = 0; |
373 result.ptr[result.length] = 0; |
374 |
374 |
375 return result; |
375 return result; |
|
376 } |
|
377 |
|
378 static cxmutstr escape_string(cxmutstr str) { |
|
379 CxBuffer buf = {0}; |
|
380 |
|
381 bool all_printable = true; |
|
382 for (size_t i = 0; i < str.length; i++) { |
|
383 bool escape = !isprint(str.ptr[i]) |
|
384 || str.ptr[i] == '\\' |
|
385 || str.ptr[i] == '"' |
|
386 // TODO: make escaping slash optional |
|
387 || str.ptr[i] == '/'; |
|
388 |
|
389 if (all_printable && escape) { |
|
390 size_t capa = str.length + 32; |
|
391 char *space = malloc(capa); |
|
392 if (space == NULL) return cx_mutstrn(NULL, 0); |
|
393 cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); |
|
394 cxBufferWrite(str.ptr, 1, i, &buf); |
|
395 all_printable = false; |
|
396 } |
|
397 if (escape) { |
|
398 cxBufferPut(&buf, '\\'); |
|
399 if (str.ptr[i] == '\"') { |
|
400 cxBufferPut(&buf, '\"'); |
|
401 } else if (str.ptr[i] == '\n') { |
|
402 cxBufferPut(&buf, 'n'); |
|
403 } else if (str.ptr[i] == '\t') { |
|
404 cxBufferPut(&buf, 't'); |
|
405 } else if (str.ptr[i] == '\r') { |
|
406 cxBufferPut(&buf, 'r'); |
|
407 } else if (str.ptr[i] == '\\') { |
|
408 cxBufferPut(&buf, '\\'); |
|
409 } else if (str.ptr[i] == '/') { |
|
410 cxBufferPut(&buf, '/'); |
|
411 } else if (str.ptr[i] == '\f') { |
|
412 cxBufferPut(&buf, 'f'); |
|
413 } else if (str.ptr[i] == '\b') { |
|
414 cxBufferPut(&buf, 'b'); |
|
415 } else { |
|
416 char code[6]; |
|
417 snprintf(code, sizeof(code), "u%04x", |
|
418 (unsigned int)(0xff & str.ptr[i])); |
|
419 cxBufferPutString(&buf, code); |
|
420 } |
|
421 } else if (!all_printable) { |
|
422 cxBufferPut(&buf, str.ptr[i]); |
|
423 } |
|
424 } |
|
425 if (!all_printable) { |
|
426 str = cx_mutstrn(buf.space, buf.size); |
|
427 } |
|
428 cxBufferDestroy(&buf); |
|
429 return str; |
376 } |
430 } |
377 |
431 |
378 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
432 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
379 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); |
433 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); |
380 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
434 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
1082 } |
1136 } |
1083 } |
1137 } |
1084 |
1138 |
1085 // the name |
1139 // the name |
1086 actual += wfunc("\"", 1, 1, target); |
1140 actual += wfunc("\"", 1, 1, target); |
1087 // TODO: escape the string |
1141 cxmutstr name = escape_string(member->name); |
1088 actual += wfunc(member->name.ptr, 1, |
1142 actual += wfunc(name.ptr, 1, name.length, target); |
1089 member->name.length, target); |
1143 if (name.ptr != member->name.ptr) { |
|
1144 cx_strfree(&name); |
|
1145 } |
1090 actual += wfunc("\"", 1, 1, target); |
1146 actual += wfunc("\"", 1, 1, target); |
1091 const char *obj_name_sep = ": "; |
1147 const char *obj_name_sep = ": "; |
1092 if (settings->pretty) { |
1148 if (settings->pretty) { |
1093 actual += wfunc(obj_name_sep, 1, 2, target); |
1149 actual += wfunc(obj_name_sep, 1, 2, target); |
1094 expected += 4 + member->name.length; |
1150 expected += 4 + member->name.length; |
1150 expected++; |
1206 expected++; |
1151 break; |
1207 break; |
1152 } |
1208 } |
1153 case CX_JSON_STRING: { |
1209 case CX_JSON_STRING: { |
1154 actual += wfunc("\"", 1, 1, target); |
1210 actual += wfunc("\"", 1, 1, target); |
1155 // TODO: escape the string |
1211 cxmutstr str = escape_string(value->value.string); |
1156 actual += wfunc(value->value.string.ptr, 1, |
1212 actual += wfunc(str.ptr, 1, str.length, target); |
1157 value->value.string.length, target); |
1213 if (str.ptr != value->value.string.ptr) { |
|
1214 cx_strfree(&str); |
|
1215 } |
1158 actual += wfunc("\"", 1, 1, target); |
1216 actual += wfunc("\"", 1, 1, target); |
1159 expected += 2 + value->value.string.length; |
1217 expected += 2 + value->value.string.length; |
1160 break; |
1218 break; |
1161 } |
1219 } |
1162 case CX_JSON_NUMBER: { |
1220 case CX_JSON_NUMBER: { |