diff --git a/spa/include/spa/debug/context.h b/spa/include/spa/debug/context.h index a5eff13da..74266a90a 100644 --- a/spa/include/spa/debug/context.h +++ b/spa/include/spa/debug/context.h @@ -11,6 +11,7 @@ extern "C" { #include #include +#include #include /** @@ -31,6 +32,25 @@ struct spa_debug_context { #define spa_debugc(_c,_fmt,...) (_c)?((_c)->log((_c),_fmt, ## __VA_ARGS__)):(void)spa_debug(_fmt, ## __VA_ARGS__) +static inline void spa_debugc_error_location(struct spa_debug_context *c, + struct spa_error_location *loc) +{ + int i, skip = loc->col > 80 ? loc->col - 40 : 0; + char buf[80]; + + for (i = 0; (size_t)i < sizeof(buf)-1; i++) { + char ch = loc->location[i + skip]; + if (ch == '\n' || ch == '\0') + break; + buf[i] = isspace(ch) ? ' ' : ch; + } + buf[i] = '\0'; + spa_debugc(c, "line: %6d | %s%s", loc->line, skip ? "..." : "", buf); + for (i = 0; buf[i]; i++) + buf[i] = i+skip+1 == loc->col ? '^' : ' '; + spa_debugc(c, "col: %6d | %s%s", loc->col, skip ? " " : "", buf); +} + /** * \} */ diff --git a/spa/include/spa/debug/file.h b/spa/include/spa/debug/file.h new file mode 100644 index 000000000..115264f92 --- /dev/null +++ b/spa/include/spa/debug/file.h @@ -0,0 +1,62 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_DEBUG_FILE_H +#define SPA_DEBUG_FILE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * \addtogroup spa_debug + * \{ + */ + +struct spa_debug_file_ctx { + struct spa_debug_context ctx; + FILE *f; +}; + +SPA_PRINTF_FUNC(2,3) +static inline void spa_debug_file_log(struct spa_debug_context *ctx, const char *fmt, ...) +{ + struct spa_debug_file_ctx *c = SPA_CONTAINER_OF(ctx, struct spa_debug_file_ctx, ctx); + va_list args; + va_start(args, fmt); + vfprintf(c->f, fmt, args); fputc('\n', c->f); + va_end(args); +} + +#define SPA_DEBUG_FILE_INIT(_f) \ + (struct spa_debug_file_ctx){ { spa_debug_file_log }, _f, } + +#define spa_debug_file_error_location(f,loc,fmt,...) \ +({ \ + struct spa_debug_file_ctx c = SPA_DEBUG_FILE_INIT(f); \ + if (fmt) spa_debugc(&c.ctx, fmt, __VA_ARGS__); \ + spa_debugc_error_location(&c.ctx, loc); \ +}) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_DEBUG_FILE_H */ diff --git a/spa/include/spa/debug/log.h b/spa/include/spa/debug/log.h index aa16c5e9e..89eaee102 100644 --- a/spa/include/spa/debug/log.h +++ b/spa/include/spa/debug/log.h @@ -83,6 +83,15 @@ static inline void spa_debug_log_log(struct spa_debug_context *ctx, const char * spa_debugc_dict(&c.ctx, indent, dict); \ }) +#define spa_debug_log_error_location(l,lev,loc,fmt,...) \ +({ \ + struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ + if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) { \ + if (fmt) spa_debugc(&c.ctx, fmt, __VA_ARGS__); \ + spa_debugc_error_location(&c.ctx, loc); \ + } \ +}) + /** * \} */ diff --git a/spa/include/spa/utils/defs.h b/spa/include/spa/utils/defs.h index af7ac0ab5..8e0c8ed1d 100644 --- a/spa/include/spa/utils/defs.h +++ b/spa/include/spa/utils/defs.h @@ -327,6 +327,13 @@ static inline bool spa_ptr_inside_and_aligned(const void *p1, size_t s1, #define SPA_STRINGIFY_1(...) #__VA_ARGS__ #define SPA_STRINGIFY(...) SPA_STRINGIFY_1(__VA_ARGS__) +struct spa_error_location { + int line; + int col; + const char *location; + const char *reason; +}; + #define spa_return_if_fail(expr) \ do { \ if (SPA_UNLIKELY(!(expr))) { \ diff --git a/spa/include/spa/utils/json.h b/spa/include/spa/utils/json.h index 64334da45..06a4c3c08 100644 --- a/spa/include/spa/utils/json.h +++ b/spa/include/spa/utils/json.h @@ -34,6 +34,7 @@ struct spa_json { const char *cur; const char *end; struct spa_json *parent; +#define SPA_JSON_ERROR_FLAG 0x100 uint32_t state; uint32_t depth; }; @@ -57,27 +58,40 @@ static inline void spa_json_enter(struct spa_json * iter, struct spa_json * sub) * is the length. Returns -1 on parse error, 0 on end of input. */ static inline int spa_json_next(struct spa_json * iter, const char **value) { - int utf8_remain = 0; + int utf8_remain = 0, err = 0; enum { __NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT, __ARRAY_FLAG = 0x10, /* in array context */ __PREV_ARRAY_FLAG = 0x20, /* depth=0 array context flag */ - __ERROR_FLAG = 0x40, - __KEY_FLAG = 0x80, /* inside object key */ - __SUB_FLAG = 0x100, /* not at top-level */ + __KEY_FLAG = 0x40, /* inside object key */ + __SUB_FLAG = 0x80, /* not at top-level */ __FLAGS = 0xff0, + __ERROR_SYSTEM = SPA_JSON_ERROR_FLAG, + __ERROR_INVALID_ARRAY_SEPARATOR, + __ERROR_EXPECTED_OBJECT_KEY, + __ERROR_EXPECTED_OBJECT_VALUE, + __ERROR_TOO_DEEP_NESTING, + __ERROR_EXPECTED_ARRAY_CLOSE, + __ERROR_EXPECTED_OBJECT_CLOSE, + __ERROR_MISMATCHED_BRACKET, + __ERROR_ESCAPE_NOT_ALLOWED, + __ERROR_CHARACTERS_NOT_ALLOWED, + __ERROR_INVALID_ESCAPE, + __ERROR_INVALID_STATE, + __ERROR_UNFINISHED_STRING, }; uint64_t array_stack[8] = {0}; /* array context flags of depths 1...512 */ *value = iter->cur; - if (iter->state & __ERROR_FLAG) + if (iter->state & SPA_JSON_ERROR_FLAG) return -1; for (; iter->cur < iter->end; iter->cur++) { unsigned char cur = (unsigned char)*iter->cur; uint32_t flag; +#define _SPA_ERROR(reason) { err = __ERROR_ ## reason; goto error; } again: flag = iter->state & __FLAGS; switch (iter->state & ~__FLAGS) { @@ -92,9 +106,9 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) continue; case ':': case '=': if (flag & __ARRAY_FLAG) - goto error; + _SPA_ERROR(INVALID_ARRAY_SEPARATOR); if (!(flag & __KEY_FLAG)) - goto error; + _SPA_ERROR(EXPECTED_OBJECT_KEY); iter->state |= __SUB_FLAG; continue; case '#': @@ -115,7 +129,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) * accept array/object here. */ if ((iter->state & __SUB_FLAG) && !(flag & __KEY_FLAG)) - goto error; + _SPA_ERROR(EXPECTED_OBJECT_KEY); SPA_FLAG_CLEAR(flag, __KEY_FLAG); } iter->state = __STRUCT | __SUB_FLAG | flag; @@ -132,7 +146,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) SPA_FLAG_UPDATE(array_stack[(iter->depth-1) >> 6], mask, flag & __ARRAY_FLAG); } else { /* too deep */ - goto error; + _SPA_ERROR(TOO_DEEP_NESTING); } *value = iter->cur; @@ -142,19 +156,19 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) return 1; case '}': case ']': if ((flag & __ARRAY_FLAG) && cur != ']') - goto error; + _SPA_ERROR(EXPECTED_ARRAY_CLOSE); if (!(flag & __ARRAY_FLAG) && cur != '}') - goto error; + _SPA_ERROR(EXPECTED_OBJECT_CLOSE); if (flag & __KEY_FLAG) { /* incomplete key-value pair */ - goto error; + _SPA_ERROR(EXPECTED_OBJECT_VALUE); } iter->state = __STRUCT | __SUB_FLAG | flag; if (iter->depth == 0) { if (iter->parent) iter->parent->cur = iter->cur; else - goto error; + _SPA_ERROR(MISMATCHED_BRACKET); return 0; } --iter->depth; @@ -166,16 +180,16 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) SPA_FLAG_IS_SET(array_stack[(iter->depth-1) >> 6], mask)); } else { /* too deep */ - goto error; + _SPA_ERROR(TOO_DEEP_NESTING); } continue; case '\\': /* disallow bare escape */ - goto error; + _SPA_ERROR(ESCAPE_NOT_ALLOWED); default: /* allow bare ascii */ if (!(cur >= 32 && cur <= 126)) - goto error; + _SPA_ERROR(CHARACTERS_NOT_ALLOWED); if (flag & __KEY_FLAG) flag |= __SUB_FLAG; if (!(flag & __ARRAY_FLAG)) @@ -196,13 +210,13 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) return iter->cur - *value; case '\\': /* disallow bare escape */ - goto error; + _SPA_ERROR(ESCAPE_NOT_ALLOWED); default: /* allow bare ascii */ if (cur >= 32 && cur <= 126) continue; } - goto error; + _SPA_ERROR(CHARACTERS_NOT_ALLOWED); case __STRING: switch (cur) { case '\\': @@ -227,7 +241,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) if (cur >= 32 && cur <= 127) continue; } - goto error; + _SPA_ERROR(CHARACTERS_NOT_ALLOWED); case __UTF8: switch (cur) { case 128 ... 191: @@ -235,7 +249,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) iter->state = __STRING | flag; continue; } - goto error; + _SPA_ERROR(CHARACTERS_NOT_ALLOWED); case __ESC: switch (cur) { case '"': case '\\': case '/': case 'b': case 'f': @@ -243,7 +257,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) iter->state = __STRING | flag; continue; } - goto error; + _SPA_ERROR(INVALID_ESCAPE); case __COMMENT: switch (cur) { case '\n': case '\r': @@ -251,17 +265,17 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) } break; default: - goto error; + _SPA_ERROR(INVALID_STATE); } } if (iter->depth != 0 || iter->parent) - goto error; + _SPA_ERROR(MISMATCHED_BRACKET); switch (iter->state & ~__FLAGS) { case __STRING: case __UTF8: case __ESC: /* string/escape not closed */ - goto error; + _SPA_ERROR(UNFINISHED_STRING); case __COMMENT: /* trailing comment */ return 0; @@ -269,7 +283,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) if ((iter->state & __SUB_FLAG) && (iter->state & __KEY_FLAG)) { /* incomplete key-value pair */ - goto error; + _SPA_ERROR(EXPECTED_OBJECT_VALUE); } if ((iter->state & ~__FLAGS) != __STRUCT) { @@ -277,13 +291,14 @@ static inline int spa_json_next(struct spa_json * iter, const char **value) return iter->cur - *value; } return 0; +#undef _SPA_ERROR error: - iter->state |= __ERROR_FLAG; + iter->state = err; while (iter->parent) { - if (iter->parent->state & __ERROR_FLAG) + if (iter->parent->state & SPA_JSON_ERROR_FLAG) break; - iter->parent->state |= __ERROR_FLAG; + iter->parent->state = err; iter->parent->cur = iter->cur; iter = iter->parent; } @@ -291,32 +306,52 @@ error: } /** - * Return whether parse error occurred, and its possible location. + * Return it there was a parse error, and its possible location. * * \since 1.1.0 */ -static inline bool spa_json_get_error(struct spa_json *iter, const char *start, int *line, int *col) +static inline bool spa_json_get_error(struct spa_json *iter, const char *start, + struct spa_error_location *loc) { - int linepos = 1, colpos = 1; - const char *p; + static const char *reasons[] = { + "System error", + "Invalid array separator", + "Expected Object key", + "Expected Object value", + "Too deep nesting", + "Expected array close backet", + "Expected object close backet", + "Mismatched backet", + "Escape not allowed", + "Character not allowed", + "Invalid escape", + "Invalid state", + "Unfinished string", + "Expected key separtor", + }; - if (!(iter->state & 0x40)) + if (!(iter->state & SPA_JSON_ERROR_FLAG)) return false; - for (p = start; p && p != iter->cur; ++p) { - if (*p == '\n') { - linepos++; - colpos = 1; - } else { - colpos++; + if (loc) { + int linepos = 1, colpos = 1, code; + const char *p, *l; + + for (l = p = start; p && p != iter->cur; ++p) { + if (*p == '\n') { + linepos++; + colpos = 1; + l = p+1; + } else { + colpos++; + } } + code = SPA_CLAMP(iter->state & 0xff, 0u, SPA_N_ELEMENTS(reasons)-1); + loc->line = linepos; + loc->col = colpos; + loc->location = l; + loc->reason = code == 0 ? strerror(errno) : reasons[code]; } - - if (line) - *line = linepos; - if (col) - *col = colpos; - return true; } diff --git a/spa/plugins/bluez5/quirks.c b/spa/plugins/bluez5/quirks.c index 2ca4210a1..9af776186 100644 --- a/spa/plugins/bluez5/quirks.c +++ b/spa/plugins/bluez5/quirks.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "defs.h" @@ -159,7 +160,7 @@ static void load_quirks(struct spa_bt_quirks *this, const char *str, size_t len) struct spa_json data = SPA_JSON_INIT(str, len); struct spa_json rules; char key[1024]; - int line, col; + struct spa_error_location loc; if (spa_json_enter_object(&data, &rules) <= 0) spa_json_init(&rules, str, len); @@ -184,8 +185,9 @@ static void load_quirks(struct spa_bt_quirks *this, const char *str, size_t len) this->device_rules = strndup(value, sz); } - if (spa_json_get_error(&rules, str, &line, &col)) - spa_log_error(this->log, "spa.bluez5 quirks syntax error, line:%d col:%d", line, col); + if (spa_json_get_error(&rules, str, &loc)) + spa_debug_log_error_location(this->log, SPA_LOG_LEVEL_ERROR, &loc, + "spa.bluez5 quirks syntax error: %s", loc.reason); } static int load_conf(struct spa_bt_quirks *this, const char *path) diff --git a/spa/tools/spa-json-dump.c b/spa/tools/spa-json-dump.c index 63b918b5d..3d49df960 100644 --- a/spa/tools/spa-json-dump.c +++ b/spa/tools/spa-json-dump.c @@ -110,7 +110,7 @@ static int dump(FILE *file, int indent, struct spa_json *it, const char *value, encode_string(file, value, len); } - if (spa_json_get_error(it, NULL, NULL, NULL)) + if (spa_json_get_error(it, NULL, NULL)) return -EINVAL; return 0; @@ -140,10 +140,11 @@ static int process_json(const char *filename, void *buf, size_t size) fflush(stdout); if (res < 0) { - int line, col; + struct spa_error_location loc; - if (spa_json_get_error(&it, buf, &line, &col)) - fprintf(stderr, "syntax error in file '%s': at line:%d col:%d\n", filename, line, col); + if (spa_json_get_error(&it, buf, &loc)) + fprintf(stderr, "syntax error in file '%s': at line:%d col:%d: %s\n", + filename, loc.line, loc.col, loc.reason); else fprintf(stderr, "error parsing file '%s': %s\n", filename, spa_strerror(res)); diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index ab4e2e45d..5831b6687 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -384,10 +385,10 @@ int pw_conf_save_state(const char *prefix, const char *name, const struct pw_pro static int conf_load(const char *path, struct pw_properties *conf) { - char *data; + char *data = NULL; struct stat sbuf; int count; - int line = -1, col = -1; + struct spa_error_location loc = { 0 }; int res; spa_autoclose int fd = open(path, O_CLOEXEC | O_RDONLY); @@ -401,12 +402,11 @@ static int conf_load(const char *path, struct pw_properties *conf) if ((data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error; - if (!pw_properties_check_string(data, sbuf.st_size, &line, &col)) { + count = pw_properties_update_string_checked(conf, data, sbuf.st_size, &loc); + if (count < 0) { errno = EINVAL; goto error; } - - count = pw_properties_update_string(conf, data, sbuf.st_size); munmap(data, sbuf.st_size); } else { count = 0; @@ -418,8 +418,9 @@ static int conf_load(const char *path, struct pw_properties *conf) error: res = -errno; - if (line != -1) - pw_log_warn("%p: syntax error in config '%s': line:%d col:%d", conf, path, line, col); + if (loc.line != 0) + spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_WARN, &loc, + "%p: error in config '%s': %s", conf, path, loc.reason); else pw_log_warn("%p: error loading config '%s': %m", conf, path); return res; diff --git a/src/pipewire/properties.c b/src/pipewire/properties.c index 6c3be5893..fac2c961e 100644 --- a/src/pipewire/properties.c +++ b/src/pipewire/properties.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "pipewire/array.h" #include "pipewire/log.h" @@ -129,11 +130,14 @@ struct pw_properties *pw_properties_new_dict(const struct spa_dict *dict) return &impl->this; } -static bool update_string(struct pw_properties *props, const char *str, size_t size, - int *count, int *err_line, int *err_col) +static int update_string(struct pw_properties *props, const char *str, size_t size, + int *count, struct spa_error_location *loc) { struct spa_json it[2]; char key[1024]; + struct spa_error_location el; + bool err; + int cnt = 0; spa_json_init(&it[0], str, size); if (spa_json_enter_object(&it[0], &it[1]) <= 0) @@ -155,14 +159,29 @@ static bool update_string(struct pw_properties *props, const char *str, size_t s if (len <= 0) break; - if (props && (val = malloc(len+1)) != NULL) + if (props) { + if ((val = malloc(len+1)) == NULL) { + errno = ENOMEM; + it[1].state = SPA_JSON_ERROR_FLAG; + break; + } spa_json_parse_stringn(value, len, val, len+1); + } } if (props) - *count += pw_properties_set(props, key, val); + cnt += pw_properties_set(props, key, val); } - - return !spa_json_get_error(&it[1], str, err_line, err_col); + if ((err = spa_json_get_error(&it[1], str, &el))) { + if (loc == NULL) + spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_WARN, + &el, "error parsing more than %d properties: %s", + cnt, el.reason); + else + *loc = el; + } + if (count) + *count = cnt; + return !err; } /** Update the properties from the given string, overwriting any @@ -177,22 +196,38 @@ SPA_EXPORT int pw_properties_update_string(struct pw_properties *props, const char *str, size_t size) { int count = 0; - - update_string(props, str, size, &count, NULL, NULL); + update_string(props, str, size, &count, NULL); return count; } -/** Check \a str is a well-formed properties JSON string. +/** Check \a str is a well-formed properties JSON string and update + * the properties on success. * - * \param line Return value for parse error line position - * \param col Return value for parse error column position - * \return true if string is valid + * \a str should be a whitespace separated list of key=value + * strings or a json object, see pw_properties_new_string(). + * + * When the check fails, this function will not update \a props. + * + * \param props The properties to attempt to update, maybe be NULL + * to simply check the JSON string. + * \param str The JSON object with new values + * \param size The length of the JSON string. + * \param loc Return value for parse error location + * \return a negative value when string is not valid and \a loc contains + * the error location or the number of updated properties in + * \a props. * \since 1.1.0 */ SPA_EXPORT -bool pw_properties_check_string(const char *str, size_t size, int *line, int *col) +int pw_properties_update_string_checked(struct pw_properties *props, + const char *str, size_t size, struct spa_error_location *loc) { - return update_string(NULL, str, size, NULL, line, col); + int count = 0; + if (!update_string(NULL, str, size, NULL, loc)) + return -EINVAL; + if (props) + update_string(props, str, size, &count, NULL); + return count; } /** Make a new properties object from the given str @@ -224,6 +259,27 @@ error: return NULL; } +SPA_EXPORT +struct pw_properties * +pw_properties_new_string_checked(const char *object, size_t size, struct spa_error_location *loc) +{ + struct properties *impl; + int res; + + impl = properties_new(16); + if (impl == NULL) + return NULL; + + if ((res = pw_properties_update_string_checked(&impl->this, object, size, loc)) < 0) + goto error; + + return &impl->this; +error: + pw_properties_free(&impl->this); + errno = -res; + return NULL; +} + /** Copy a properties object * * \param properties properties to copy diff --git a/src/pipewire/properties.h b/src/pipewire/properties.h index a961bdf0a..86e7ae712 100644 --- a/src/pipewire/properties.h +++ b/src/pipewire/properties.h @@ -40,6 +40,10 @@ pw_properties_new_dict(const struct spa_dict *dict); struct pw_properties * pw_properties_new_string(const char *args); +struct pw_properties * +pw_properties_new_string_checked(const char *args, size_t size, + struct spa_error_location *loc); + struct pw_properties * pw_properties_copy(const struct pw_properties *properties); @@ -51,11 +55,13 @@ int pw_properties_update_ignore(struct pw_properties *props, /* Update props with all key/value pairs from dict */ int pw_properties_update(struct pw_properties *props, const struct spa_dict *dict); + /* Update props with all key/value pairs from str */ int pw_properties_update_string(struct pw_properties *props, const char *str, size_t size); -bool pw_properties_check_string(const char *str, size_t size, int *line, int *col); +int pw_properties_update_string_checked(struct pw_properties *props, + const char *str, size_t size, struct spa_error_location *loc); int pw_properties_add(struct pw_properties *oldprops, const struct spa_dict *dict); diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index 339d9fe1e..263857324 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1606,8 +1607,9 @@ int main(int argc, char *argv[]) uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); const char *prog; - int exit_code = EXIT_FAILURE, c, ret, line, col; + int exit_code = EXIT_FAILURE, c, ret; enum pw_stream_flags flags = 0; + struct spa_error_location loc; setlocale(LC_ALL, ""); pw_init(&argc, &argv); @@ -1729,11 +1731,12 @@ int main(int argc, char *argv[]) break; case 'P': - if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) { - fprintf(stderr, "error: syntax error in --properties at line:%d col:%d\n", line, col); + if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) { + spa_debug_file_error_location(stderr, &loc, + "error: syntax error in --properties: %s", + loc.reason); goto error_usage; } - pw_properties_update_string(data.props, optarg, strlen(optarg)); break; case OPT_TARGET: diff --git a/src/tools/pw-cli.c b/src/tools/pw-cli.c index 53f7188d9..39a4253bd 100644 --- a/src/tools/pw-cli.c +++ b/src/tools/pw-cli.c @@ -1405,17 +1405,13 @@ static bool do_info(struct data *data, const char *cmd, char *args, char **error static struct pw_properties *properties_new_checked(const char *str, char **error) { struct pw_properties *props; - int line, col; + struct spa_error_location loc; - if (!pw_properties_check_string(str, strlen(str), &line, &col)) { - *error = spa_aprintf("syntax error in properties, line:%d col:%d", line, col); - return NULL; + props = pw_properties_new_string_checked(str, strlen(str), &loc); + if (!props) { + *error = spa_aprintf("syntax error in properties, line:%d col:%d: %s", + loc.line, loc.col, loc.reason); } - - props = pw_properties_new_string(str); - if (!props) - *error = spa_aprintf("failed to allocate properties"); - return props; } diff --git a/src/tools/pw-container.c b/src/tools/pw-container.c index 0a1c9d01c..daa2c7b60 100644 --- a/src/tools/pw-container.c +++ b/src/tools/pw-container.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -147,7 +148,8 @@ int main(int argc, char *argv[]) { "properties", required_argument, NULL, 'P' }, { NULL, 0, NULL, 0} }; - int c, res, listen_fd, close_fd[2], line, col; + struct spa_error_location loc; + int c, res, listen_fd, close_fd[2]; char temp[PATH_MAX] = "/tmp/pipewire-XXXXXX"; struct sockaddr_un sockaddr = {0}; @@ -176,11 +178,12 @@ int main(int argc, char *argv[]) opt_remote = optarg; break; case 'P': - if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) { - fprintf(stderr, "error: syntax error in --properties at line:%d col:%d\n", line, col); + if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) { + spa_debug_file_error_location(stderr, &loc, + "error: syntax error in --properties: %s", + loc.reason); return -1; } - pw_properties_update_string(data.props, optarg, strlen(optarg)); break; default: show_help(&data, argv[0], true); diff --git a/src/tools/pw-dot.c b/src/tools/pw-dot.c index db9367640..3d5720786 100644 --- a/src/tools/pw-dot.c +++ b/src/tools/pw-dot.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -1277,7 +1278,7 @@ static int get_data_from_json(struct data *data, const char *json_path) struct stat sbuf; struct spa_json it[2]; const char *value; - int line, col; + struct spa_error_location loc; if ((fd = open(json_path, O_CLOEXEC | O_RDONLY)) < 0) { fprintf(stderr, "error opening file '%s': %m\n", json_path); @@ -1314,8 +1315,10 @@ static int get_data_from_json(struct data *data, const char *json_path) munmap(json, sbuf.st_size); - if (spa_json_get_error(&it[0], json, &line, &col)) { - fprintf(stderr, "JSON syntax error on line:%d col:%d\n", line, col); + if (spa_json_get_error(&it[0], json, &loc)) { + spa_debug_file_error_location(stderr, &loc, + "JSON syntax error: %s\n", + loc.reason); return -1; } diff --git a/src/tools/pw-link.c b/src/tools/pw-link.c index 13565683a..bb804ff49 100644 --- a/src/tools/pw-link.c +++ b/src/tools/pw-link.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -873,7 +874,8 @@ static int run(int argc, char *argv[]) .objects = SPA_LIST_INIT(&data.objects), .target_links = SPA_LIST_INIT(&data.target_links), }; - int res = 0, c, line, col; + int res = 0, c; + struct spa_error_location loc; static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -942,11 +944,11 @@ static int run(int argc, char *argv[]) pw_properties_set(data.props, PW_KEY_LINK_PASSIVE, "true"); break; case 'p': - if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) { - fprintf(stderr, "error: syntax error in --props at line:%d col:%d\n", line, col); + if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) { + spa_debug_file_error_location(stderr, &loc, + "error: syntax error in --props: %s", loc.reason); return -1; } - pw_properties_update_string(data.props, optarg, strlen(optarg)); break; case 'd': data.opt_mode = MODE_DISCONNECT; diff --git a/src/tools/pw-loopback.c b/src/tools/pw-loopback.c index fefb9706e..174617e4e 100644 --- a/src/tools/pw-loopback.c +++ b/src/tools/pw-loopback.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -110,7 +111,8 @@ int main(int argc, char *argv[]) { "playback-props", required_argument, NULL, 'o' }, { NULL, 0, NULL, 0} }; - int c, res = -1, line, col; + int c, res = -1; + struct spa_error_location loc; setlocale(LC_ALL, ""); pw_init(&argc, &argv); @@ -170,18 +172,22 @@ int main(int argc, char *argv[]) pw_properties_set(data.playback_props, PW_KEY_TARGET_OBJECT, optarg); break; case 'i': - if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) { - fprintf(stderr, "error: syntax error in --capture-props at line:%d col:%d\n", line, col); + if (pw_properties_update_string_checked(data.capture_props, + optarg, strlen(optarg), &loc) < 0) { + spa_debug_file_error_location(stderr, &loc, + "error: syntax error in --capture-props: %s", + loc.reason); return -1; } - pw_properties_update_string(data.capture_props, optarg, strlen(optarg)); break; case 'o': - if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) { - fprintf(stderr, "error: syntax error in --playback-props at line:%d col:%d\n", line, col); + if (pw_properties_update_string_checked(data.playback_props, + optarg, strlen(optarg), &loc) < 0) { + spa_debug_file_error_location(stderr, &loc, + "error: syntax error in --playback-props: %s", + loc.reason); return -1; } - pw_properties_update_string(data.playback_props, optarg, strlen(optarg)); break; default: show_help(&data, argv[0], true); diff --git a/test/test-spa-json.c b/test/test-spa-json.c index 41affe92b..545d6f30d 100644 --- a/test/test-spa-json.c +++ b/test/test-spa-json.c @@ -87,12 +87,12 @@ static void expect_parse_error(struct spa_json *it, const char *str, int line, i { const char *value; struct spa_json it2; - int linepos = 0, colpos = 0; + struct spa_error_location loc = { 0 }; pwtest_int_eq(spa_json_next(it, &value), -1); - pwtest_bool_true(spa_json_get_error(it, str, &linepos, &colpos)); - pwtest_int_eq(linepos, line); - pwtest_int_eq(colpos, col); + pwtest_bool_true(spa_json_get_error(it, str, &loc)); + pwtest_int_eq(loc.line, line); + pwtest_int_eq(loc.col, col); /* parse error is idempotent also for parents */ while (it) { @@ -245,10 +245,10 @@ PWTEST(json_parse) expect_end(&it[3]); expect_end(&it[2]); - pwtest_bool_false(spa_json_get_error(&it[0], NULL, NULL, NULL)); - pwtest_bool_false(spa_json_get_error(&it[1], NULL, NULL, NULL)); - pwtest_bool_false(spa_json_get_error(&it[2], NULL, NULL, NULL)); - pwtest_bool_false(spa_json_get_error(&it[3], NULL, NULL, NULL)); + pwtest_bool_false(spa_json_get_error(&it[0], NULL, NULL)); + pwtest_bool_false(spa_json_get_error(&it[1], NULL, NULL)); + pwtest_bool_false(spa_json_get_error(&it[2], NULL, NULL)); + pwtest_bool_false(spa_json_get_error(&it[3], NULL, NULL)); json = "section={\"key\":value}, section2=[item1,item2]"; @@ -891,7 +891,7 @@ static int validate_strict_json(struct spa_json *it, int depth, FILE *f) } done: - if (spa_json_get_error(it, NULL, NULL, NULL)) + if (spa_json_get_error(it, NULL, NULL)) return -1; return len; @@ -1052,9 +1052,9 @@ PWTEST(json_data) fprintf(stdout, "%s (expect %s)\n", name, expect ? "fail" : "ok"); fflush(stdout); - pwtest_bool_eq(res == -2 || spa_json_get_error(&it, NULL, NULL, NULL), expect); + pwtest_bool_eq(res == -2 || spa_json_get_error(&it, NULL, NULL), expect); if (res == -2) - pwtest_bool_false(spa_json_get_error(&it, NULL, NULL, NULL)); + pwtest_bool_false(spa_json_get_error(&it, NULL, NULL)); if (result) { while (strlen(result) > 0 && result[strlen(result) - 1] == '\n')