Skip to content

Commit

Permalink
rg_utils: Added rg_json_fixup to fix json errors
Browse files Browse the repository at this point in the history
In order to provide a better user experience, rg_json_fixup strips trailing commas. In the future it will also strip comments.

NOTE: The way it finds trailing commas is certainly naive and could break things, so we should always try to parse the raw string before falling back to rg_json_fixup!
  • Loading branch information
ducalex committed Oct 30, 2024
1 parent 88f2c0a commit 28b83c6
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 7 deletions.
8 changes: 5 additions & 3 deletions components/retro-go/rg_gui.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,13 @@ bool rg_gui_set_theme(const char *theme_name)
if (theme_name && theme_name[0])
{
snprintf(pathbuf, RG_PATH_MAX, "%s/%s/theme.json", RG_BASE_PATH_THEMES, theme_name);
void *data;
char *data;
size_t data_len;
if (rg_storage_read_file(pathbuf, &data, &data_len, 0))
if (rg_storage_read_file(pathbuf, (void **)&data, &data_len, 0))
{
new_theme = cJSON_Parse((char *)data);
new_theme = cJSON_Parse(data);
if (!new_theme) // Parse failure, clean the markup and try again
new_theme = cJSON_Parse(rg_json_fixup(data));
free(data);
}
if (!new_theme)
Expand Down
8 changes: 5 additions & 3 deletions components/retro-go/rg_settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ static cJSON *json_root(const char *name, bool set_dirty)
cJSON_AddStringToObject(branch, "namespace", name);
cJSON_AddNumberToObject(branch, "changed", 0);

void *data; size_t data_len;
char *data; size_t data_len;
char pathbuf[RG_PATH_MAX];
snprintf(pathbuf, RG_PATH_MAX, "%s/%s.json", RG_BASE_PATH_CONFIG, name);
if (rg_storage_read_file(pathbuf, &data, &data_len, 0))
if (rg_storage_read_file(pathbuf, (void **)&data, &data_len, 0))
{
cJSON *values = cJSON_Parse((char *)data);
cJSON *values = cJSON_Parse(data);
if (!values) // Parse failure, clean the markup and try again
values = cJSON_Parse(rg_json_fixup(data));
if (values)
{
RG_LOGI("Config file loaded: '%s'", pathbuf);
Expand Down
19 changes: 19 additions & 0 deletions components/retro-go/rg_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ char *rg_strtoupper(char *str)
return str;
}

char *rg_json_fixup(char *json)
{
// Strip trailing commas, eg [1,2,3,] {"a":1,}
for (char *ptr = json, *prev = ptr; ptr && *ptr; ++ptr)
{
if ((*ptr == '}' || *ptr == ']' || *ptr == ',') && *prev == ',')
{
RG_LOGW("Found trailing comma at pos %d", (int)(ptr - json));
*prev = ' ';
}
if (*ptr != '\t' && *ptr != '\n' && *ptr != ' ')
prev = ptr;
}

// TODO: We should also strip C-style comments!

return json;
}

const char *rg_dirname(const char *path)
{
static char buffer[100];
Expand Down
10 changes: 9 additions & 1 deletion components/retro-go/rg_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,31 @@
#define PRINTF_BINARY_32 PRINTF_BINARY_16 " " PRINTF_BINARY_16
#define PRINTF_BINVAL_32(i) PRINTF_BINVAL_16((i) >> 16), PRINTF_BINVAL_16(i)

/* String functions */

/**
* both functions give you an allocation of strlen(str) + 1 valid for the lifetime of the application
* they cannot be freed. unique avoids keeping multiple copies of an identical string (eg a path)
* Things like unique_string("abc") == unique_string("abc") are guaranteed to be true
*/
const char *rg_const_string(const char *str);
const char *rg_unique_string(const char *str);

char *rg_strtolower(char *str);
char *rg_strtoupper(char *str);
char *rg_json_fixup(char *json);

/* Paths functions */
const char *rg_dirname(const char *path);
const char *rg_basename(const char *path);
const char *rg_extension(const char *filename);
bool rg_extension_match(const char *filename, const char *extensions);
const char *rg_relpath(const char *path);

/* Hashing */
uint32_t rg_crc32(uint32_t crc, const uint8_t *buf, size_t len);
uint32_t rg_hash(const char *buf, size_t len);

/* Misc */
void *rg_alloc(size_t size, uint32_t caps);
// rg_usleep behaves like usleep in libc: it will sleep for *at least* `us` microseconds, but possibly more
// due to scheduling. You should use rg_task_delay() if you don't need more than 10-15ms granularity.
Expand Down

0 comments on commit 28b83c6

Please sign in to comment.