mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
spa: Improve JSON error reporting
Add struct spa_error_location that holds information about some parsing
context such as the line and column number, error and line fragment
with the error.
Make spa_json_get_error() fill in the spa_error_location instead. Add
some error codes to the error state and use this to add a parsing reason
to the location.
Add a debug function to log the error location in a nice way. Also
add a FILE based debug context to log to any FILE.
Replace pw_properties_check_string() with
pw_properties_update_string_checked() and add
pw_properties_new_string_checked(). The check string behaviour can still
be done by setting props to NULL but the main purpose is to be able to
avoid parsing the json file twice in the future.
When using the old pw_properties_update_string(), log a warning to the
log when we fail to parse the complete string.
Use the new checked functions and the debug functions to report about
parsing errors in the tools and conf parsing.
This gives errors like:
```
> pw-loopback --playback-props '{ foo = [ f : g ] }'
error: syntax error in --playback-props: Invalid array separator
line: 1 | { foo = [ f : g ] }
col: 14 | ^
```
This commit is contained in:
parent
96fb63dfa1
commit
d4581755e6
17 changed files with 328 additions and 116 deletions
|
|
@ -11,6 +11,7 @@ extern "C" {
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <spa/utils/defs.h>
|
#include <spa/utils/defs.h>
|
||||||
/**
|
/**
|
||||||
|
|
@ -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__)
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \}
|
* \}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
62
spa/include/spa/debug/file.h
Normal file
62
spa/include/spa/debug/file.h
Normal file
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <spa/utils/defs.h>
|
||||||
|
#include <spa/support/log.h>
|
||||||
|
#include <spa/debug/context.h>
|
||||||
|
#include <spa/debug/dict.h>
|
||||||
|
#include <spa/debug/format.h>
|
||||||
|
#include <spa/debug/mem.h>
|
||||||
|
#include <spa/debug/pod.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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 */
|
||||||
|
|
@ -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); \
|
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); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \}
|
* \}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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_1(...) #__VA_ARGS__
|
||||||
#define SPA_STRINGIFY(...) 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) \
|
#define spa_return_if_fail(expr) \
|
||||||
do { \
|
do { \
|
||||||
if (SPA_UNLIKELY(!(expr))) { \
|
if (SPA_UNLIKELY(!(expr))) { \
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ struct spa_json {
|
||||||
const char *cur;
|
const char *cur;
|
||||||
const char *end;
|
const char *end;
|
||||||
struct spa_json *parent;
|
struct spa_json *parent;
|
||||||
|
#define SPA_JSON_ERROR_FLAG 0x100
|
||||||
uint32_t state;
|
uint32_t state;
|
||||||
uint32_t depth;
|
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. */
|
* 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)
|
static inline int spa_json_next(struct spa_json * iter, const char **value)
|
||||||
{
|
{
|
||||||
int utf8_remain = 0;
|
int utf8_remain = 0, err = 0;
|
||||||
enum {
|
enum {
|
||||||
__NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT,
|
__NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT,
|
||||||
__ARRAY_FLAG = 0x10, /* in array context */
|
__ARRAY_FLAG = 0x10, /* in array context */
|
||||||
__PREV_ARRAY_FLAG = 0x20, /* depth=0 array context flag */
|
__PREV_ARRAY_FLAG = 0x20, /* depth=0 array context flag */
|
||||||
__ERROR_FLAG = 0x40,
|
__KEY_FLAG = 0x40, /* inside object key */
|
||||||
__KEY_FLAG = 0x80, /* inside object key */
|
__SUB_FLAG = 0x80, /* not at top-level */
|
||||||
__SUB_FLAG = 0x100, /* not at top-level */
|
|
||||||
__FLAGS = 0xff0,
|
__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 */
|
uint64_t array_stack[8] = {0}; /* array context flags of depths 1...512 */
|
||||||
|
|
||||||
*value = iter->cur;
|
*value = iter->cur;
|
||||||
|
|
||||||
if (iter->state & __ERROR_FLAG)
|
if (iter->state & SPA_JSON_ERROR_FLAG)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (; iter->cur < iter->end; iter->cur++) {
|
for (; iter->cur < iter->end; iter->cur++) {
|
||||||
unsigned char cur = (unsigned char)*iter->cur;
|
unsigned char cur = (unsigned char)*iter->cur;
|
||||||
uint32_t flag;
|
uint32_t flag;
|
||||||
|
|
||||||
|
#define _SPA_ERROR(reason) { err = __ERROR_ ## reason; goto error; }
|
||||||
again:
|
again:
|
||||||
flag = iter->state & __FLAGS;
|
flag = iter->state & __FLAGS;
|
||||||
switch (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;
|
continue;
|
||||||
case ':': case '=':
|
case ':': case '=':
|
||||||
if (flag & __ARRAY_FLAG)
|
if (flag & __ARRAY_FLAG)
|
||||||
goto error;
|
_SPA_ERROR(INVALID_ARRAY_SEPARATOR);
|
||||||
if (!(flag & __KEY_FLAG))
|
if (!(flag & __KEY_FLAG))
|
||||||
goto error;
|
_SPA_ERROR(EXPECTED_OBJECT_KEY);
|
||||||
iter->state |= __SUB_FLAG;
|
iter->state |= __SUB_FLAG;
|
||||||
continue;
|
continue;
|
||||||
case '#':
|
case '#':
|
||||||
|
|
@ -115,7 +129,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value)
|
||||||
* accept array/object here.
|
* accept array/object here.
|
||||||
*/
|
*/
|
||||||
if ((iter->state & __SUB_FLAG) && !(flag & __KEY_FLAG))
|
if ((iter->state & __SUB_FLAG) && !(flag & __KEY_FLAG))
|
||||||
goto error;
|
_SPA_ERROR(EXPECTED_OBJECT_KEY);
|
||||||
SPA_FLAG_CLEAR(flag, __KEY_FLAG);
|
SPA_FLAG_CLEAR(flag, __KEY_FLAG);
|
||||||
}
|
}
|
||||||
iter->state = __STRUCT | __SUB_FLAG | 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);
|
SPA_FLAG_UPDATE(array_stack[(iter->depth-1) >> 6], mask, flag & __ARRAY_FLAG);
|
||||||
} else {
|
} else {
|
||||||
/* too deep */
|
/* too deep */
|
||||||
goto error;
|
_SPA_ERROR(TOO_DEEP_NESTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
*value = iter->cur;
|
*value = iter->cur;
|
||||||
|
|
@ -142,19 +156,19 @@ static inline int spa_json_next(struct spa_json * iter, const char **value)
|
||||||
return 1;
|
return 1;
|
||||||
case '}': case ']':
|
case '}': case ']':
|
||||||
if ((flag & __ARRAY_FLAG) && cur != ']')
|
if ((flag & __ARRAY_FLAG) && cur != ']')
|
||||||
goto error;
|
_SPA_ERROR(EXPECTED_ARRAY_CLOSE);
|
||||||
if (!(flag & __ARRAY_FLAG) && cur != '}')
|
if (!(flag & __ARRAY_FLAG) && cur != '}')
|
||||||
goto error;
|
_SPA_ERROR(EXPECTED_OBJECT_CLOSE);
|
||||||
if (flag & __KEY_FLAG) {
|
if (flag & __KEY_FLAG) {
|
||||||
/* incomplete key-value pair */
|
/* incomplete key-value pair */
|
||||||
goto error;
|
_SPA_ERROR(EXPECTED_OBJECT_VALUE);
|
||||||
}
|
}
|
||||||
iter->state = __STRUCT | __SUB_FLAG | flag;
|
iter->state = __STRUCT | __SUB_FLAG | flag;
|
||||||
if (iter->depth == 0) {
|
if (iter->depth == 0) {
|
||||||
if (iter->parent)
|
if (iter->parent)
|
||||||
iter->parent->cur = iter->cur;
|
iter->parent->cur = iter->cur;
|
||||||
else
|
else
|
||||||
goto error;
|
_SPA_ERROR(MISMATCHED_BRACKET);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
--iter->depth;
|
--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));
|
SPA_FLAG_IS_SET(array_stack[(iter->depth-1) >> 6], mask));
|
||||||
} else {
|
} else {
|
||||||
/* too deep */
|
/* too deep */
|
||||||
goto error;
|
_SPA_ERROR(TOO_DEEP_NESTING);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case '\\':
|
case '\\':
|
||||||
/* disallow bare escape */
|
/* disallow bare escape */
|
||||||
goto error;
|
_SPA_ERROR(ESCAPE_NOT_ALLOWED);
|
||||||
default:
|
default:
|
||||||
/* allow bare ascii */
|
/* allow bare ascii */
|
||||||
if (!(cur >= 32 && cur <= 126))
|
if (!(cur >= 32 && cur <= 126))
|
||||||
goto error;
|
_SPA_ERROR(CHARACTERS_NOT_ALLOWED);
|
||||||
if (flag & __KEY_FLAG)
|
if (flag & __KEY_FLAG)
|
||||||
flag |= __SUB_FLAG;
|
flag |= __SUB_FLAG;
|
||||||
if (!(flag & __ARRAY_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;
|
return iter->cur - *value;
|
||||||
case '\\':
|
case '\\':
|
||||||
/* disallow bare escape */
|
/* disallow bare escape */
|
||||||
goto error;
|
_SPA_ERROR(ESCAPE_NOT_ALLOWED);
|
||||||
default:
|
default:
|
||||||
/* allow bare ascii */
|
/* allow bare ascii */
|
||||||
if (cur >= 32 && cur <= 126)
|
if (cur >= 32 && cur <= 126)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
goto error;
|
_SPA_ERROR(CHARACTERS_NOT_ALLOWED);
|
||||||
case __STRING:
|
case __STRING:
|
||||||
switch (cur) {
|
switch (cur) {
|
||||||
case '\\':
|
case '\\':
|
||||||
|
|
@ -227,7 +241,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value)
|
||||||
if (cur >= 32 && cur <= 127)
|
if (cur >= 32 && cur <= 127)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
goto error;
|
_SPA_ERROR(CHARACTERS_NOT_ALLOWED);
|
||||||
case __UTF8:
|
case __UTF8:
|
||||||
switch (cur) {
|
switch (cur) {
|
||||||
case 128 ... 191:
|
case 128 ... 191:
|
||||||
|
|
@ -235,7 +249,7 @@ static inline int spa_json_next(struct spa_json * iter, const char **value)
|
||||||
iter->state = __STRING | flag;
|
iter->state = __STRING | flag;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
goto error;
|
_SPA_ERROR(CHARACTERS_NOT_ALLOWED);
|
||||||
case __ESC:
|
case __ESC:
|
||||||
switch (cur) {
|
switch (cur) {
|
||||||
case '"': case '\\': case '/': case 'b': case 'f':
|
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;
|
iter->state = __STRING | flag;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
goto error;
|
_SPA_ERROR(INVALID_ESCAPE);
|
||||||
case __COMMENT:
|
case __COMMENT:
|
||||||
switch (cur) {
|
switch (cur) {
|
||||||
case '\n': case '\r':
|
case '\n': case '\r':
|
||||||
|
|
@ -251,17 +265,17 @@ static inline int spa_json_next(struct spa_json * iter, const char **value)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
goto error;
|
_SPA_ERROR(INVALID_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (iter->depth != 0 || iter->parent)
|
if (iter->depth != 0 || iter->parent)
|
||||||
goto error;
|
_SPA_ERROR(MISMATCHED_BRACKET);
|
||||||
|
|
||||||
switch (iter->state & ~__FLAGS) {
|
switch (iter->state & ~__FLAGS) {
|
||||||
case __STRING: case __UTF8: case __ESC:
|
case __STRING: case __UTF8: case __ESC:
|
||||||
/* string/escape not closed */
|
/* string/escape not closed */
|
||||||
goto error;
|
_SPA_ERROR(UNFINISHED_STRING);
|
||||||
case __COMMENT:
|
case __COMMENT:
|
||||||
/* trailing comment */
|
/* trailing comment */
|
||||||
return 0;
|
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)) {
|
if ((iter->state & __SUB_FLAG) && (iter->state & __KEY_FLAG)) {
|
||||||
/* incomplete key-value pair */
|
/* incomplete key-value pair */
|
||||||
goto error;
|
_SPA_ERROR(EXPECTED_OBJECT_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((iter->state & ~__FLAGS) != __STRUCT) {
|
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 iter->cur - *value;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
#undef _SPA_ERROR
|
||||||
|
|
||||||
error:
|
error:
|
||||||
iter->state |= __ERROR_FLAG;
|
iter->state = err;
|
||||||
while (iter->parent) {
|
while (iter->parent) {
|
||||||
if (iter->parent->state & __ERROR_FLAG)
|
if (iter->parent->state & SPA_JSON_ERROR_FLAG)
|
||||||
break;
|
break;
|
||||||
iter->parent->state |= __ERROR_FLAG;
|
iter->parent->state = err;
|
||||||
iter->parent->cur = iter->cur;
|
iter->parent->cur = iter->cur;
|
||||||
iter = iter->parent;
|
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
|
* \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;
|
static const char *reasons[] = {
|
||||||
const char *p;
|
"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;
|
return false;
|
||||||
|
|
||||||
for (p = start; p && p != iter->cur; ++p) {
|
if (loc) {
|
||||||
if (*p == '\n') {
|
int linepos = 1, colpos = 1, code;
|
||||||
linepos++;
|
const char *p, *l;
|
||||||
colpos = 1;
|
|
||||||
} else {
|
for (l = p = start; p && p != iter->cur; ++p) {
|
||||||
colpos++;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
#include <spa/utils/result.h>
|
#include <spa/utils/result.h>
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
|
#include <spa/debug/log.h>
|
||||||
|
|
||||||
#include "defs.h"
|
#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 data = SPA_JSON_INIT(str, len);
|
||||||
struct spa_json rules;
|
struct spa_json rules;
|
||||||
char key[1024];
|
char key[1024];
|
||||||
int line, col;
|
struct spa_error_location loc;
|
||||||
|
|
||||||
if (spa_json_enter_object(&data, &rules) <= 0)
|
if (spa_json_enter_object(&data, &rules) <= 0)
|
||||||
spa_json_init(&rules, str, len);
|
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);
|
this->device_rules = strndup(value, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spa_json_get_error(&rules, str, &line, &col))
|
if (spa_json_get_error(&rules, str, &loc))
|
||||||
spa_log_error(this->log, "spa.bluez5 quirks syntax error, line:%d col:%d", line, col);
|
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)
|
static int load_conf(struct spa_bt_quirks *this, const char *path)
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ static int dump(FILE *file, int indent, struct spa_json *it, const char *value,
|
||||||
encode_string(file, value, len);
|
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 -EINVAL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -140,10 +140,11 @@ static int process_json(const char *filename, void *buf, size_t size)
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
int line, col;
|
struct spa_error_location loc;
|
||||||
|
|
||||||
if (spa_json_get_error(&it, buf, &line, &col))
|
if (spa_json_get_error(&it, buf, &loc))
|
||||||
fprintf(stderr, "syntax error in file '%s': at line:%d col:%d\n", filename, line, col);
|
fprintf(stderr, "syntax error in file '%s': at line:%d col:%d: %s\n",
|
||||||
|
filename, loc.line, loc.col, loc.reason);
|
||||||
else
|
else
|
||||||
fprintf(stderr, "error parsing file '%s': %s\n", filename, spa_strerror(res));
|
fprintf(stderr, "error parsing file '%s': %s\n", filename, spa_strerror(res));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <spa/utils/result.h>
|
#include <spa/utils/result.h>
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
|
#include <spa/debug/log.h>
|
||||||
|
|
||||||
#include <pipewire/cleanup.h>
|
#include <pipewire/cleanup.h>
|
||||||
#include <pipewire/impl.h>
|
#include <pipewire/impl.h>
|
||||||
|
|
@ -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)
|
static int conf_load(const char *path, struct pw_properties *conf)
|
||||||
{
|
{
|
||||||
char *data;
|
char *data = NULL;
|
||||||
struct stat sbuf;
|
struct stat sbuf;
|
||||||
int count;
|
int count;
|
||||||
int line = -1, col = -1;
|
struct spa_error_location loc = { 0 };
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
spa_autoclose int fd = open(path, O_CLOEXEC | O_RDONLY);
|
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)
|
if ((data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
|
||||||
goto error;
|
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;
|
errno = EINVAL;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = pw_properties_update_string(conf, data, sbuf.st_size);
|
|
||||||
munmap(data, sbuf.st_size);
|
munmap(data, sbuf.st_size);
|
||||||
} else {
|
} else {
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
@ -418,8 +418,9 @@ static int conf_load(const char *path, struct pw_properties *conf)
|
||||||
|
|
||||||
error:
|
error:
|
||||||
res = -errno;
|
res = -errno;
|
||||||
if (line != -1)
|
if (loc.line != 0)
|
||||||
pw_log_warn("%p: syntax error in config '%s': line:%d col:%d", conf, path, line, col);
|
spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_WARN, &loc,
|
||||||
|
"%p: error in config '%s': %s", conf, path, loc.reason);
|
||||||
else
|
else
|
||||||
pw_log_warn("%p: error loading config '%s': %m", conf, path);
|
pw_log_warn("%p: error loading config '%s': %m", conf, path);
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
#include <spa/utils/cleanup.h>
|
#include <spa/utils/cleanup.h>
|
||||||
|
#include <spa/debug/log.h>
|
||||||
|
|
||||||
#include "pipewire/array.h"
|
#include "pipewire/array.h"
|
||||||
#include "pipewire/log.h"
|
#include "pipewire/log.h"
|
||||||
|
|
@ -129,11 +130,14 @@ struct pw_properties *pw_properties_new_dict(const struct spa_dict *dict)
|
||||||
return &impl->this;
|
return &impl->this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool update_string(struct pw_properties *props, const char *str, size_t size,
|
static int update_string(struct pw_properties *props, const char *str, size_t size,
|
||||||
int *count, int *err_line, int *err_col)
|
int *count, struct spa_error_location *loc)
|
||||||
{
|
{
|
||||||
struct spa_json it[2];
|
struct spa_json it[2];
|
||||||
char key[1024];
|
char key[1024];
|
||||||
|
struct spa_error_location el;
|
||||||
|
bool err;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
spa_json_init(&it[0], str, size);
|
spa_json_init(&it[0], str, size);
|
||||||
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
|
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)
|
if (len <= 0)
|
||||||
break;
|
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);
|
spa_json_parse_stringn(value, len, val, len+1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (props)
|
if (props)
|
||||||
*count += pw_properties_set(props, key, val);
|
cnt += pw_properties_set(props, key, val);
|
||||||
}
|
}
|
||||||
|
if ((err = spa_json_get_error(&it[1], str, &el))) {
|
||||||
return !spa_json_get_error(&it[1], str, err_line, err_col);
|
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
|
/** 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 pw_properties_update_string(struct pw_properties *props, const char *str, size_t size)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
update_string(props, str, size, &count, NULL);
|
||||||
update_string(props, str, size, &count, NULL, NULL);
|
|
||||||
return count;
|
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
|
* \a str should be a whitespace separated list of key=value
|
||||||
* \param col Return value for parse error column position
|
* strings or a json object, see pw_properties_new_string().
|
||||||
* \return true if string is valid
|
*
|
||||||
|
* 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
|
* \since 1.1.0
|
||||||
*/
|
*/
|
||||||
SPA_EXPORT
|
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
|
/** Make a new properties object from the given str
|
||||||
|
|
@ -224,6 +259,27 @@ error:
|
||||||
return NULL;
|
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
|
/** Copy a properties object
|
||||||
*
|
*
|
||||||
* \param properties properties to copy
|
* \param properties properties to copy
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ pw_properties_new_dict(const struct spa_dict *dict);
|
||||||
struct pw_properties *
|
struct pw_properties *
|
||||||
pw_properties_new_string(const char *args);
|
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 *
|
struct pw_properties *
|
||||||
pw_properties_copy(const struct pw_properties *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 */
|
/* Update props with all key/value pairs from dict */
|
||||||
int pw_properties_update(struct pw_properties *props,
|
int pw_properties_update(struct pw_properties *props,
|
||||||
const struct spa_dict *dict);
|
const struct spa_dict *dict);
|
||||||
|
|
||||||
/* Update props with all key/value pairs from str */
|
/* Update props with all key/value pairs from str */
|
||||||
int pw_properties_update_string(struct pw_properties *props,
|
int pw_properties_update_string(struct pw_properties *props,
|
||||||
const char *str, size_t size);
|
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,
|
int pw_properties_add(struct pw_properties *oldprops,
|
||||||
const struct spa_dict *dict);
|
const struct spa_dict *dict);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/debug/types.h>
|
#include <spa/debug/types.h>
|
||||||
|
#include <spa/debug/file.h>
|
||||||
|
|
||||||
#include <pipewire/cleanup.h>
|
#include <pipewire/cleanup.h>
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
|
|
@ -1606,8 +1607,9 @@ int main(int argc, char *argv[])
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
const char *prog;
|
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;
|
enum pw_stream_flags flags = 0;
|
||||||
|
struct spa_error_location loc;
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
pw_init(&argc, &argv);
|
pw_init(&argc, &argv);
|
||||||
|
|
@ -1729,11 +1731,12 @@ int main(int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
|
if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
|
||||||
fprintf(stderr, "error: syntax error in --properties at line:%d col:%d\n", line, col);
|
spa_debug_file_error_location(stderr, &loc,
|
||||||
|
"error: syntax error in --properties: %s",
|
||||||
|
loc.reason);
|
||||||
goto error_usage;
|
goto error_usage;
|
||||||
}
|
}
|
||||||
pw_properties_update_string(data.props, optarg, strlen(optarg));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPT_TARGET:
|
case OPT_TARGET:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
static struct pw_properties *properties_new_checked(const char *str, char **error)
|
||||||
{
|
{
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
int line, col;
|
struct spa_error_location loc;
|
||||||
|
|
||||||
if (!pw_properties_check_string(str, strlen(str), &line, &col)) {
|
props = pw_properties_new_string_checked(str, strlen(str), &loc);
|
||||||
*error = spa_aprintf("syntax error in properties, line:%d col:%d", line, col);
|
if (!props) {
|
||||||
return NULL;
|
*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;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include <spa/debug/pod.h>
|
#include <spa/debug/pod.h>
|
||||||
#include <spa/debug/format.h>
|
#include <spa/debug/format.h>
|
||||||
#include <spa/debug/types.h>
|
#include <spa/debug/types.h>
|
||||||
|
#include <spa/debug/file.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <pipewire/extensions/security-context.h>
|
#include <pipewire/extensions/security-context.h>
|
||||||
|
|
@ -147,7 +148,8 @@ int main(int argc, char *argv[])
|
||||||
{ "properties", required_argument, NULL, 'P' },
|
{ "properties", required_argument, NULL, 'P' },
|
||||||
{ NULL, 0, NULL, 0}
|
{ 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";
|
char temp[PATH_MAX] = "/tmp/pipewire-XXXXXX";
|
||||||
struct sockaddr_un sockaddr = {0};
|
struct sockaddr_un sockaddr = {0};
|
||||||
|
|
||||||
|
|
@ -176,11 +178,12 @@ int main(int argc, char *argv[])
|
||||||
opt_remote = optarg;
|
opt_remote = optarg;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
|
if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
|
||||||
fprintf(stderr, "error: syntax error in --properties at line:%d col:%d\n", line, col);
|
spa_debug_file_error_location(stderr, &loc,
|
||||||
|
"error: syntax error in --properties: %s",
|
||||||
|
loc.reason);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pw_properties_update_string(data.props, optarg, strlen(optarg));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
show_help(&data, argv[0], true);
|
show_help(&data, argv[0], true);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/debug/types.h>
|
#include <spa/debug/types.h>
|
||||||
|
#include <spa/debug/file.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
|
@ -1277,7 +1278,7 @@ static int get_data_from_json(struct data *data, const char *json_path)
|
||||||
struct stat sbuf;
|
struct stat sbuf;
|
||||||
struct spa_json it[2];
|
struct spa_json it[2];
|
||||||
const char *value;
|
const char *value;
|
||||||
int line, col;
|
struct spa_error_location loc;
|
||||||
|
|
||||||
if ((fd = open(json_path, O_CLOEXEC | O_RDONLY)) < 0) {
|
if ((fd = open(json_path, O_CLOEXEC | O_RDONLY)) < 0) {
|
||||||
fprintf(stderr, "error opening file '%s': %m\n", json_path);
|
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);
|
munmap(json, sbuf.st_size);
|
||||||
|
|
||||||
if (spa_json_get_error(&it[0], json, &line, &col)) {
|
if (spa_json_get_error(&it[0], json, &loc)) {
|
||||||
fprintf(stderr, "JSON syntax error on line:%d col:%d\n", line, col);
|
spa_debug_file_error_location(stderr, &loc,
|
||||||
|
"JSON syntax error: %s\n",
|
||||||
|
loc.reason);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include <spa/utils/result.h>
|
#include <spa/utils/result.h>
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
#include <spa/utils/defs.h>
|
#include <spa/utils/defs.h>
|
||||||
|
#include <spa/debug/file.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <pipewire/filter.h>
|
#include <pipewire/filter.h>
|
||||||
|
|
@ -873,7 +874,8 @@ static int run(int argc, char *argv[])
|
||||||
.objects = SPA_LIST_INIT(&data.objects),
|
.objects = SPA_LIST_INIT(&data.objects),
|
||||||
.target_links = SPA_LIST_INIT(&data.target_links),
|
.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[] = {
|
static const struct option long_options[] = {
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "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");
|
pw_properties_set(data.props, PW_KEY_LINK_PASSIVE, "true");
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
|
if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
|
||||||
fprintf(stderr, "error: syntax error in --props at line:%d col:%d\n", line, col);
|
spa_debug_file_error_location(stderr, &loc,
|
||||||
|
"error: syntax error in --props: %s", loc.reason);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pw_properties_update_string(data.props, optarg, strlen(optarg));
|
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
data.opt_mode = MODE_DISCONNECT;
|
data.opt_mode = MODE_DISCONNECT;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <spa/param/audio/format-utils.h>
|
#include <spa/param/audio/format-utils.h>
|
||||||
#include <spa/param/audio/raw.h>
|
#include <spa/param/audio/raw.h>
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
|
#include <spa/debug/file.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <pipewire/impl.h>
|
#include <pipewire/impl.h>
|
||||||
|
|
@ -110,7 +111,8 @@ int main(int argc, char *argv[])
|
||||||
{ "playback-props", required_argument, NULL, 'o' },
|
{ "playback-props", required_argument, NULL, 'o' },
|
||||||
{ NULL, 0, NULL, 0}
|
{ NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
int c, res = -1, line, col;
|
int c, res = -1;
|
||||||
|
struct spa_error_location loc;
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
pw_init(&argc, &argv);
|
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);
|
pw_properties_set(data.playback_props, PW_KEY_TARGET_OBJECT, optarg);
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
|
if (pw_properties_update_string_checked(data.capture_props,
|
||||||
fprintf(stderr, "error: syntax error in --capture-props at line:%d col:%d\n", line, col);
|
optarg, strlen(optarg), &loc) < 0) {
|
||||||
|
spa_debug_file_error_location(stderr, &loc,
|
||||||
|
"error: syntax error in --capture-props: %s",
|
||||||
|
loc.reason);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pw_properties_update_string(data.capture_props, optarg, strlen(optarg));
|
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
|
if (pw_properties_update_string_checked(data.playback_props,
|
||||||
fprintf(stderr, "error: syntax error in --playback-props at line:%d col:%d\n", line, col);
|
optarg, strlen(optarg), &loc) < 0) {
|
||||||
|
spa_debug_file_error_location(stderr, &loc,
|
||||||
|
"error: syntax error in --playback-props: %s",
|
||||||
|
loc.reason);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pw_properties_update_string(data.playback_props, optarg, strlen(optarg));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
show_help(&data, argv[0], true);
|
show_help(&data, argv[0], true);
|
||||||
|
|
|
||||||
|
|
@ -87,12 +87,12 @@ static void expect_parse_error(struct spa_json *it, const char *str, int line, i
|
||||||
{
|
{
|
||||||
const char *value;
|
const char *value;
|
||||||
struct spa_json it2;
|
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_int_eq(spa_json_next(it, &value), -1);
|
||||||
pwtest_bool_true(spa_json_get_error(it, str, &linepos, &colpos));
|
pwtest_bool_true(spa_json_get_error(it, str, &loc));
|
||||||
pwtest_int_eq(linepos, line);
|
pwtest_int_eq(loc.line, line);
|
||||||
pwtest_int_eq(colpos, col);
|
pwtest_int_eq(loc.col, col);
|
||||||
|
|
||||||
/* parse error is idempotent also for parents */
|
/* parse error is idempotent also for parents */
|
||||||
while (it) {
|
while (it) {
|
||||||
|
|
@ -245,10 +245,10 @@ PWTEST(json_parse)
|
||||||
expect_end(&it[3]);
|
expect_end(&it[3]);
|
||||||
expect_end(&it[2]);
|
expect_end(&it[2]);
|
||||||
|
|
||||||
pwtest_bool_false(spa_json_get_error(&it[0], 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, NULL));
|
pwtest_bool_false(spa_json_get_error(&it[1], NULL, NULL));
|
||||||
pwtest_bool_false(spa_json_get_error(&it[2], NULL, NULL, NULL));
|
pwtest_bool_false(spa_json_get_error(&it[2], NULL, NULL));
|
||||||
pwtest_bool_false(spa_json_get_error(&it[3], NULL, NULL, NULL));
|
pwtest_bool_false(spa_json_get_error(&it[3], NULL, NULL));
|
||||||
|
|
||||||
json = "section={\"key\":value}, section2=[item1,item2]";
|
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:
|
done:
|
||||||
if (spa_json_get_error(it, NULL, NULL, NULL))
|
if (spa_json_get_error(it, NULL, NULL))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
|
@ -1052,9 +1052,9 @@ PWTEST(json_data)
|
||||||
|
|
||||||
fprintf(stdout, "%s (expect %s)\n", name, expect ? "fail" : "ok");
|
fprintf(stdout, "%s (expect %s)\n", name, expect ? "fail" : "ok");
|
||||||
fflush(stdout);
|
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)
|
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) {
|
if (result) {
|
||||||
while (strlen(result) > 0 && result[strlen(result) - 1] == '\n')
|
while (strlen(result) > 0 && result[strlen(result) - 1] == '\n')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue