1
0
mirror of https://github.com/systemd/systemd synced 2026-04-26 17:04:50 +02:00

Compare commits

...

22 Commits

Author SHA1 Message Date
Yu Watanabe
6bb8928205 meson: sort files 2022-05-11 02:13:15 +09:00
Yu Watanabe
97cda6e10f
Merge pull request #23335 from keszybz/fuzz-json-more-coverage
More coverage in fuzz-json
2022-05-11 02:12:57 +09:00
Zbigniew Jędrzejewski-Szmek
82544241ba fuzz-bootspec: limit input size
https://oss-fuzz.com/testcase-detail/5680508182331392 has the
first timeout with 811kb of input. As in the other cases, the code
is known to be slow with lots of repeated entries and we're fine with
that.
2022-05-11 02:12:35 +09:00
Yu Watanabe
01c99b29e9
Merge pull request #23336 from keszybz/fuzz-calendarspec-more-coverage
More coverage in fuzz-calendarspec
2022-05-11 02:12:11 +09:00
Zbigniew Jędrzejewski-Szmek
99b1145aae shared/json: fix memleak in sort 2022-05-10 17:08:37 +02:00
Zbigniew Jędrzejewski-Szmek
3b6ce05537 shared/json: fix another memleak in normalization 2022-05-10 17:08:37 +02:00
Zbigniew Jędrzejewski-Szmek
dbd27c6d28 fuzz-json: also try self-merge operations
This might even work ;)
2022-05-10 17:08:37 +02:00
Zbigniew Jędrzejewski-Szmek
96d651a22b basic/alloc-util: remove unnecessary parens
Those symbols are not macros anymore, so we can drop parens.
2022-05-10 17:08:37 +02:00
Zbigniew Jędrzejewski-Szmek
ce913e0ec4 shared/json: add helper to ref first, unref second
This normally wouldn't happen, but if some of those places were called
with lhs and rhs being the same object, we could unref the last ref first,
and then try to take the ref again. It's easier to be safe, and with the
helper we save some lines too.
2022-05-10 17:08:34 +02:00
Zbigniew Jędrzejewski-Szmek
8e1e59b9ad shared/calendarspec: fix formatting of entries which collapse to a star
We canonicalize repeats that cover the whole range: "0:0:0/1" → "0:0:*".  But
we'd also do "0:0:0/1,0" → "0:0:*,0", which we then refuse to parse.  Thus,
first go throug the whole chain, and print a '*' and nothing else if any of the
components covers the whole range.
2022-05-10 14:35:57 +02:00
Zbigniew Jędrzejewski-Szmek
3aff2ae9d5 shared/calendarspec: fix printing of second ranges which start with 0
0..3 is not the same as 0..infinity, we need to check both ends of the range.
This logic was added in 3215e35c405278491f55fb486d349f132e93f516, and back then
the field was called .value. .stop was added later and apparently wasn't taken
into account here.
2022-05-10 13:06:32 +02:00
Zbigniew Jędrzejewski-Szmek
4f23345592 test-calendarspec: tighten tests and add more logging 2022-05-10 13:06:32 +02:00
Zbigniew Jędrzejewski-Szmek
8b105ec831 shared/calendarspec: wrap long comments and reduce scope of one var 2022-05-10 13:06:32 +02:00
Zbigniew Jędrzejewski-Szmek
8e6e3ac7d1 fuzz-calendarspec: increase coverage by calculating occurences
Coverage data shows that we didn't test calendar_spec_next_usec() and
associated functions at all.

The input samples so far were only used until the first NUL. We take advantage
of that by using the part until the second NUL as the starting timestamp,
retaining backwards compatibility for how the first part is used.
2022-05-10 13:06:32 +02:00
Zbigniew Jędrzejewski-Szmek
262037f0b1 fuzz-calendarspec: add input sample with a list of weekdays and all syntax characters
This should make the fuzzer searches start off a bit better.
2022-05-10 13:06:32 +02:00
Zbigniew Jędrzejewski-Szmek
b456b09b25 shared/calendarspec: make function static void
calendar_spec_from_string() already calls calendar_spec_normalize(), so
there is no point in calling it from the fuzzer. Once that's removed, there's
just one internal caller and it can be made static.
2022-05-10 13:06:32 +02:00
Zbigniew Jędrzejewski-Szmek
7e4be6a584 shared/json: fix memory leak on failed normalization
We need to increase the counter immediately after taking the ref,
otherwise we may not unref it properly if we fail before incrementing.
2022-05-10 12:58:49 +02:00
Zbigniew Jędrzejewski-Szmek
bac06497fe shared/json: wrap long comments 2022-05-10 12:58:48 +02:00
Zbigniew Jędrzejewski-Szmek
a2c5735dd8 fuzz-json: also do sorting and normalizing and other easy calls 2022-05-10 12:58:48 +02:00
Zbigniew Jędrzejewski-Szmek
a4669764f7 shared/json: reduce scope of variables 2022-05-10 12:58:48 +02:00
Zbigniew Jędrzejewski-Szmek
9ad955ce40 fuzz-json: optionally allow logging and output
Similarly to other fuzzers… this makes development easier.
2022-05-10 12:58:48 +02:00
Zbigniew Jędrzejewski-Szmek
9674b089cf json: align table 2022-05-10 12:58:48 +02:00
20 changed files with 334 additions and 217 deletions

View File

@ -55,8 +55,8 @@ typedef void* (*mfree_func_t)(void *p);
typeof(a)* _a = &(a); \
typeof(b)* _b = &(b); \
free(*_a); \
(*_a) = (*_b); \
(*_b) = NULL; \
*_a = *_b; \
*_b = NULL; \
0; \
})

View File

@ -29,10 +29,10 @@
static clockid_t map_clock_id(clockid_t c) {
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
* fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
* when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
* those archs. */
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus,
* clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM
* pendants (their only difference is when timers are set on them), let's just map them
* accordingly. This way, we can get the correct time even on those archs. */
switch (c) {
@ -295,8 +295,8 @@ char *format_timestamp_style(
usec_t t,
TimestampStyle style) {
/* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
* generated timestamps may be parsed with parse_timestamp(), and always read the same. */
/* The weekdays in non-localized (English) form. We use this instead of the localized form, so that
* our generated timestamps may be parsed with parse_timestamp(), and always read the same. */
static const char * const weekdays[] = {
[0] = "Sun",
[1] = "Mon",
@ -383,8 +383,8 @@ char *format_timestamp_style(
/* Append the timezone */
n = strlen(buf);
if (utc) {
/* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
* obsolete "GMT" instead. */
/* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r()
* normally uses the obsolete "GMT" instead. */
if (n + 5 > l)
return NULL; /* "UTC" doesn't fit. */
@ -399,12 +399,14 @@ char *format_timestamp_style(
/* The full time zone does not fit in. Yuck. */
if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that
* case, complain that it doesn't fit. */
/* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
* minimum time zone length. In this case suppress the timezone entirely, in order not to dump
* an overly long, hard to read string on the user. This should be safe, because the user will
* assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
/* So the time zone doesn't fit in fully, but the caller passed enough space for the
* POSIX minimum time zone length. In this case suppress the timezone entirely, in
* order not to dump an overly long, hard to read string on the user. This should be
* safe, because the user will assume the local timezone anyway if none is shown. And
* so does parse_timestamp(). */
} else {
buf[n++] = ' ';
strcpy(buf + n, tm.tm_zone);
@ -700,10 +702,11 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
tzset();
/* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
* support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
* there are no nice APIs available to cover this. By accepting the local time zone strings, we make
* sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
/* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note
* that we only support the local timezones here, nothing else. Not because we
* wouldn't want to, but simply because there are no nice APIs available to cover
* this. By accepting the local time zone strings, we make sure that all timestamps
* written by format_timestamp() can be parsed correctly, even though we don't
* support arbitrary timezone specifications. */
for (j = 0; j <= 1; j++) {

View File

@ -84,6 +84,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
int r;
if (size > 65535)
return 0;
/* Disable most logging if not running standalone */
if (!getenv("SYSTEMD_LOG_LEVEL"))
log_set_max_level(LOG_CRIT);

View File

@ -0,0 +1,2 @@
[libfuzzer]
max_len = 65535

View File

@ -4,20 +4,54 @@
#include "calendarspec.h"
#include "fd-util.h"
#include "fuzz.h"
#include "string-util.h"
#include "time-util.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_(calendar_spec_freep) CalendarSpec *cspec = NULL;
_cleanup_free_ char *str = NULL, *p = NULL;
_cleanup_free_ char *str = NULL;
int r;
if (!getenv("SYSTEMD_LOG_LEVEL"))
log_set_max_level(LOG_CRIT);
str = memdup_suffix0(data, size);
if (calendar_spec_from_string(str, &cspec) >= 0) {
(void) calendar_spec_valid(cspec);
(void) calendar_spec_normalize(cspec);
(void) calendar_spec_to_string(cspec, &p);
size_t l1 = strlen(str);
const char* usecs = l1 < size ? str + l1 + 1 : "";
r = calendar_spec_from_string(str, &cspec);
if (r < 0) {
log_debug_errno(r, "Failed to parse \"%s\": %m", str);
return 0;
}
_cleanup_free_ char *p = NULL;
assert_se(calendar_spec_valid(cspec));
assert_se(calendar_spec_to_string(cspec, &p) == 0);
assert(p);
log_debug("spec: %s → %s", str, p);
_cleanup_(calendar_spec_freep) CalendarSpec *cspec2 = NULL;
assert_se(calendar_spec_from_string(p, &cspec2) >= 0);
assert_se(calendar_spec_valid(cspec2));
usec_t usec = 0;
(void) parse_time(usecs, &usec, 1);
/* If timezone is set, calendar_spec_next_usec() would fork, bleh :(
* Let's not try that. */
cspec->timezone = mfree(cspec->timezone);
log_debug("00: %s", strna(FORMAT_TIMESTAMP(usec)));
for (unsigned i = 1; i <= 20; i++) {
r = calendar_spec_next_usec(cspec, usec, &usec);
if (r < 0) {
log_debug_errno(r, "%02u: %m", i);
break;
}
log_debug("%02u: %s", i, FORMAT_TIMESTAMP(usec));
}
return 0;

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "env-util.h"
#include "fd-util.h"
#include "fuzz.h"
#include "json.h"
@ -10,18 +11,106 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
size_t out_size;
_cleanup_fclose_ FILE *f = NULL, *g = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
int r;
/* Disable most logging if not running standalone */
if (!getenv("SYSTEMD_LOG_LEVEL"))
log_set_max_level(LOG_CRIT);
f = data_to_file(data, size);
assert_se(f);
if (json_parse_file(f, NULL, 0, &v, NULL, NULL) < 0)
r = json_parse_file(f, NULL, 0, &v, NULL, NULL);
if (r < 0) {
log_debug_errno(r, "failed to parse input: %m");
return 0;
}
g = open_memstream_unlocked(&out, &out_size);
assert_se(g);
if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0)
assert_se(g = open_memstream_unlocked(&out, &out_size));
json_variant_dump(v, 0, g, NULL);
json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, g, NULL);
json_variant_dump(v, 0, g ?: stdout, NULL);
json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, g ?: stdout, NULL);
bool sorted = json_variant_is_sorted(v);
log_debug("json_variant_is_sorted: %s", yes_no(sorted));
r = json_variant_sort(&v);
log_debug_errno(r, "json_variant_sort: %d/%m", r);
sorted = json_variant_is_sorted(v);
log_debug("json_variant_is_sorted: %s", yes_no(sorted));
assert_se(r < 0 || sorted);
bool normalized = json_variant_is_normalized(v);
log_debug("json_variant_is_normalized: %s", yes_no(normalized));
r = json_variant_normalize(&v);
log_debug_errno(r, "json_variant_normalize: %d/%m", r);
normalized = json_variant_is_normalized(v);
log_debug("json_variant_is_normalized: %s", yes_no(normalized));
assert_se(r < 0 || normalized);
double real = json_variant_real(v);
log_debug("json_variant_real: %lf", real);
bool negative = json_variant_is_negative(v);
log_debug("json_variant_is_negative: %s", yes_no(negative));
bool blank = json_variant_is_blank_object(v);
log_debug("json_variant_is_blank_object: %s", yes_no(blank));
blank = json_variant_is_blank_array(v);
log_debug("json_variant_is_blank_array: %s", yes_no(blank));
size_t elements = json_variant_elements(v);
log_debug("json_variant_elements: %zu", elements);
for (size_t i = 0; i <= elements + 2; i++)
(void) json_variant_by_index(v, i);
assert_se(json_variant_equal(v, v));
assert_se(!json_variant_equal(v, NULL));
assert_se(!json_variant_equal(NULL, v));
bool sensitive = json_variant_is_sensitive(v);
log_debug("json_variant_is_sensitive: %s", yes_no(sensitive));
json_variant_sensitive(v);
sensitive = json_variant_is_sensitive(v);
log_debug("json_variant_is_sensitive: %s", yes_no(sensitive));
const char *source;
unsigned line, column;
assert_se(json_variant_get_source(v, &source, &line, &column) == 0);
log_debug("json_variant_get_source: %s:%u:%u", source ?: "-", line, column);
r = json_variant_set_field_string(&v, "a", "string-a");
log_debug_errno(r, "json_set_field_string: %d/%m", r);
r = json_variant_set_field_integer(&v, "b", -12345);
log_debug_errno(r, "json_set_field_integer: %d/%m", r);
r = json_variant_set_field_unsigned(&v, "c", 12345);
log_debug_errno(r, "json_set_field_unsigned: %d/%m", r);
r = json_variant_set_field_boolean(&v, "d", false);
log_debug_errno(r, "json_set_field_boolean: %d/%m", r);
r = json_variant_set_field_strv(&v, "e", STRV_MAKE("e-1", "e-2", "e-3"));
log_debug_errno(r, "json_set_field_strv: %d/%m", r);
r = json_variant_filter(&v, STRV_MAKE("a", "b", "c", "d", "e"));
log_debug_errno(r, "json_variant_filter: %d/%m", r);
/* I assume we can merge v with itself… */
r = json_variant_merge(&v, v);
log_debug_errno(r, "json_variant_merge: %d/%m", r);
r = json_variant_append_array(&v, v);
log_debug_errno(r, "json_variant_append_array: %d/%m", r);
return 0;
}

View File

@ -13,6 +13,8 @@ sources = files(
'netdev/dummy.h',
'netdev/fou-tunnel.c',
'netdev/fou-tunnel.h',
'netdev/geneve.c',
'netdev/geneve.h',
'netdev/ifb.c',
'netdev/ifb.h',
'netdev/ipoib.c',
@ -45,12 +47,10 @@ sources = files(
'netdev/vlan.h',
'netdev/vrf.c',
'netdev/vrf.h',
'netdev/vxlan.c',
'netdev/vxlan.h',
'netdev/geneve.c',
'netdev/geneve.h',
'netdev/vxcan.c',
'netdev/vxcan.h',
'netdev/vxlan.c',
'netdev/vxlan.h',
'netdev/wireguard.c',
'netdev/wireguard.h',
'netdev/wlan.c',
@ -115,8 +115,6 @@ sources = files(
'networkd-ndisc.h',
'networkd-neighbor.c',
'networkd-neighbor.h',
'networkd-radv.c',
'networkd-radv.h',
'networkd-network-bus.c',
'networkd-network-bus.h',
'networkd-network.c',
@ -125,6 +123,8 @@ sources = files(
'networkd-nexthop.h',
'networkd-queue.c',
'networkd-queue.h',
'networkd-radv.c',
'networkd-radv.h',
'networkd-route-util.c',
'networkd-route-util.h',
'networkd-route.c',
@ -157,12 +157,12 @@ sources = files(
'tc/ets.h',
'tc/fifo.c',
'tc/fifo.h',
'tc/fq.c',
'tc/fq.h',
'tc/fq-codel.c',
'tc/fq-codel.h',
'tc/fq-pie.c',
'tc/fq-pie.h',
'tc/fq.c',
'tc/fq.h',
'tc/gred.c',
'tc/gred.h',
'tc/hhf.c',

View File

@ -145,7 +145,7 @@ static void fix_year(CalendarComponent *c) {
}
}
int calendar_spec_normalize(CalendarSpec *c) {
static void calendar_spec_normalize(CalendarSpec *c) {
assert(c);
if (streq_ptr(c->timezone, "UTC")) {
@ -167,8 +167,6 @@ int calendar_spec_normalize(CalendarSpec *c) {
normalize_chain(&c->hour);
normalize_chain(&c->minute);
normalize_chain(&c->microsecond);
return 0;
}
static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) {
@ -290,17 +288,24 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) {
}
}
static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
static bool chain_is_star(const CalendarComponent *c, bool usec) {
/* Return true if the whole chain can be replaced by '*'.
* This happens when the chain is empty or one of the components covers all. */
if (!c)
return true;
if (usec)
for (; c; c = c->next)
if (c->start == 0 && c->stop < 0 && c->repeat == USEC_PER_SEC)
return true;
return false;
}
static void _format_chain(FILE *f, int space, const CalendarComponent *c, bool start, bool usec) {
int d = usec ? (int) USEC_PER_SEC : 1;
assert(f);
if (!c) {
fputc('*', f);
return;
}
if (usec && c->start == 0 && c->repeat == USEC_PER_SEC && !c->next) {
if (start && chain_is_star(c, usec)) {
fputc('*', f);
return;
}
@ -323,10 +328,14 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c, bool us
if (c->next) {
fputc(',', f);
format_chain(f, space, c->next, usec);
_format_chain(f, space, c->next, false, usec);
}
}
static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
_format_chain(f, space, c, /* start = */ true, usec);
}
int calendar_spec_to_string(const CalendarSpec *c, char **p) {
char *buf = NULL;
size_t sz = 0;
@ -431,12 +440,10 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
c->weekdays_bits |= 1 << day_nr[i].nr;
if (l >= 0) {
int j;
if (l > day_nr[i].nr)
return -EINVAL;
for (j = l + 1; j < day_nr[i].nr; j++)
for (int j = l + 1; j < day_nr[i].nr; j++)
c->weekdays_bits |= 1 << j;
}
@ -1086,9 +1093,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
return -EINVAL;
}
r = calendar_spec_normalize(c);
if (r < 0)
return r;
calendar_spec_normalize(c);
if (!calendar_spec_valid(c))
return -EINVAL;

View File

@ -35,7 +35,6 @@ typedef struct CalendarSpec {
CalendarSpec* calendar_spec_free(CalendarSpec *c);
int calendar_spec_normalize(CalendarSpec *spec);
bool calendar_spec_valid(CalendarSpec *spec);
int calendar_spec_to_string(const CalendarSpec *spec, char **p);

View File

@ -273,8 +273,8 @@ static JsonVariant *json_variant_formalize(JsonVariant *v) {
static JsonVariant *json_variant_conservative_formalize(JsonVariant *v) {
/* Much like json_variant_formalize(), but won't simplify if the variant has a source/line location attached to
* it, in order not to lose context */
/* Much like json_variant_formalize(), but won't simplify if the variant has a source/line location
* attached to it, in order not to lose context */
if (!v)
return NULL;
@ -546,7 +546,7 @@ int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) {
for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
JsonVariant *w = v + 1 + v->n_elements,
*c = array[v->n_elements];
*c = array[v->n_elements];
uint16_t d;
d = json_variant_depth(c);
@ -574,9 +574,6 @@ int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) {
}
int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) {
JsonVariant *v;
size_t i;
assert_return(ret, -EINVAL);
if (n == 0) {
*ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY;
@ -584,7 +581,7 @@ int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) {
}
assert_return(p, -EINVAL);
v = new(JsonVariant, n + 1);
JsonVariant *v = new(JsonVariant, n + 1);
if (!v)
return -ENOMEM;
@ -595,7 +592,7 @@ int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) {
.depth = 1,
};
for (i = 0; i < n; i++) {
for (size_t i = 0; i < n; i++) {
JsonVariant *w = v + 1 + i;
*w = (JsonVariant) {
@ -693,7 +690,7 @@ int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) {
for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
JsonVariant *w = v + 1 + v->n_elements,
*c = array[v->n_elements];
*c = array[v->n_elements];
uint16_t d;
if ((v->n_elements & 1) == 0) {
@ -734,7 +731,6 @@ int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) {
}
static size_t json_variant_size(JsonVariant* v) {
if (!json_variant_is_regular(v))
return 0;
@ -790,12 +786,9 @@ static void json_variant_free_inner(JsonVariant *v, bool force_sensitive) {
return;
}
if (IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) {
size_t i;
for (i = 0; i < v->n_elements; i++)
if (IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
for (size_t i = 0; i < v->n_elements; i++)
json_variant_free_inner(v + 1 + i, sensitive);
}
if (sensitive)
explicit_bzero_safe(v, json_variant_size(v));
@ -839,11 +832,9 @@ JsonVariant *json_variant_unref(JsonVariant *v) {
}
void json_variant_unref_many(JsonVariant **array, size_t n) {
size_t i;
assert(array || n == 0);
for (i = 0; i < n; i++)
for (size_t i = 0; i < n; i++)
json_variant_unref(array[i]);
}
@ -1218,8 +1209,6 @@ mismatch:
}
JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVariant **ret_key) {
size_t i;
if (!v)
goto not_found;
if (!key)
@ -1241,6 +1230,7 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria
while (b > a) {
JsonVariant *p;
const char *f;
size_t i;
int c;
i = (a + b) / 2;
@ -1264,7 +1254,7 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria
}
/* The variant is not sorted, hence search for the field linearly */
for (i = 0; i < v->n_elements; i += 2) {
for (size_t i = 0; i < v->n_elements; i += 2) {
JsonVariant *p;
p = json_variant_dereference(v + 1 + i);
@ -1335,34 +1325,28 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) {
return true;
case JSON_VARIANT_ARRAY: {
size_t i, n;
n = json_variant_elements(a);
size_t n = json_variant_elements(a);
if (n != json_variant_elements(b))
return false;
for (i = 0; i < n; i++) {
for (size_t i = 0; i < n; i++)
if (!json_variant_equal(json_variant_by_index(a, i), json_variant_by_index(b, i)))
return false;
}
return true;
}
case JSON_VARIANT_OBJECT: {
size_t i, n;
n = json_variant_elements(a);
size_t n = json_variant_elements(a);
if (n != json_variant_elements(b))
return false;
/* Iterate through all keys in 'a' */
for (i = 0; i < n; i += 2) {
for (size_t i = 0; i < n; i += 2) {
bool found = false;
size_t j;
/* Match them against all keys in 'b' */
for (j = 0; j < n; j += 2) {
for (size_t j = 0; j < n; j += 2) {
JsonVariant *key_b;
key_b = json_variant_by_index(b, j);
@ -1470,16 +1454,14 @@ static int print_source(FILE *f, JsonVariant *v, JsonFormatFlags flags, bool whi
DECIMAL_STR_MAX(unsigned) -1;
if (whitespace) {
size_t i, n;
size_t n = 1 + (v->source ? strlen(v->source->name) : 0) +
((v->source && (v->line > 0 || v->column > 0)) ? 1 : 0) +
(v->line > 0 ? w : 0) +
(((v->source || v->line > 0) && v->column > 0) ? 1 : 0) +
(v->column > 0 ? k : 0) +
2;
n = 1 + (v->source ? strlen(v->source->name) : 0) +
((v->source && (v->line > 0 || v->column > 0)) ? 1 : 0) +
(v->line > 0 ? w : 0) +
(((v->source || v->line > 0) && v->column > 0) ? 1 : 0) +
(v->column > 0 ? k : 0) +
2;
for (i = 0; i < n; i++)
for (size_t i = 0; i < n; i++)
fputc(' ', f);
} else {
fputc('[', f);
@ -1631,10 +1613,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
break;
case JSON_VARIANT_ARRAY: {
size_t i, n;
n = json_variant_elements(v);
size_t n = json_variant_elements(v);
if (n == 0)
fputs("[]", f);
else {
@ -1653,7 +1632,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
fputc('[', f);
}
for (i = 0; i < n; i++) {
for (size_t i = 0; i < n; i++) {
JsonVariant *e;
assert_se(e = json_variant_by_index(v, i));
@ -1687,10 +1666,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
}
case JSON_VARIANT_OBJECT: {
size_t i, n;
n = json_variant_elements(v);
size_t n = json_variant_elements(v);
if (n == 0)
fputs("{}", f);
else {
@ -1709,7 +1685,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
fputc('{', f);
}
for (i = 0; i < n; i += 2) {
for (size_t i = 0; i < n; i += 2) {
JsonVariant *e;
e = json_variant_by_index(v, i);
@ -1826,7 +1802,7 @@ void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const cha
int json_variant_filter(JsonVariant **v, char **to_remove) {
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
_cleanup_free_ JsonVariant **array = NULL;
size_t i, n = 0, k = 0;
size_t n = 0, k = 0;
int r;
assert(v);
@ -1839,7 +1815,7 @@ int json_variant_filter(JsonVariant **v, char **to_remove) {
if (strv_isempty(to_remove))
return 0;
for (i = 0; i < json_variant_elements(*v); i += 2) {
for (size_t i = 0; i < json_variant_elements(*v); i += 2) {
JsonVariant *p;
p = json_variant_by_index(*v, i);
@ -1871,9 +1847,7 @@ int json_variant_filter(JsonVariant **v, char **to_remove) {
return r;
json_variant_propagate_sensitive(*v, w);
json_variant_unref(*v);
*v = TAKE_PTR(w);
JSON_VARIANT_REPLACE(*v, TAKE_PTR(w));
return (int) n;
}
@ -1881,7 +1855,7 @@ int json_variant_filter(JsonVariant **v, char **to_remove) {
int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *value) {
_cleanup_(json_variant_unrefp) JsonVariant *field_variant = NULL, *w = NULL;
_cleanup_free_ JsonVariant **array = NULL;
size_t i, k = 0;
size_t k = 0;
int r;
assert(v);
@ -1896,7 +1870,7 @@ int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *valu
if (!json_variant_is_object(*v))
return -EINVAL;
for (i = 0; i < json_variant_elements(*v); i += 2) {
for (size_t i = 0; i < json_variant_elements(*v); i += 2) {
JsonVariant *p;
p = json_variant_by_index(*v, i);
@ -1942,9 +1916,7 @@ int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *valu
return r;
json_variant_propagate_sensitive(*v, w);
json_variant_unref(*v);
*v = TAKE_PTR(w);
JSON_VARIANT_REPLACE(*v, TAKE_PTR(w));
return 1;
}
@ -2007,7 +1979,7 @@ int json_variant_set_field_strv(JsonVariant **v, const char *field, char **l) {
int json_variant_merge(JsonVariant **v, JsonVariant *m) {
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
_cleanup_free_ JsonVariant **array = NULL;
size_t v_elements, m_elements, i, k;
size_t v_elements, m_elements, k;
bool v_blank, m_blank;
int r;
@ -2025,8 +1997,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) {
return 0; /* nothing to do */
if (v_blank) {
json_variant_unref(*v);
*v = json_variant_ref(m);
JSON_VARIANT_REPLACE(*v, json_variant_ref(m));
return 1;
}
@ -2040,7 +2011,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) {
return -ENOMEM;
k = 0;
for (i = 0; i < v_elements; i += 2) {
for (size_t i = 0; i < v_elements; i += 2) {
JsonVariant *u;
u = json_variant_by_index(*v, i);
@ -2054,7 +2025,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) {
array[k++] = json_variant_by_index(*v, i + 1);
}
for (i = 0; i < m_elements; i++)
for (size_t i = 0; i < m_elements; i++)
array[k++] = json_variant_by_index(m, i);
r = json_variant_new_object(&w, array, k);
@ -2063,9 +2034,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) {
json_variant_propagate_sensitive(*v, w);
json_variant_propagate_sensitive(m, w);
json_variant_unref(*v);
*v = TAKE_PTR(w);
JSON_VARIANT_REPLACE(*v, TAKE_PTR(w));
return 1;
}
@ -2089,34 +2058,29 @@ int json_variant_append_array(JsonVariant **v, JsonVariant *element) {
if (blank)
r = json_variant_new_array(&nv, (JsonVariant*[]) { element }, 1);
else {
_cleanup_free_ JsonVariant **array = NULL;
size_t i;
array = new(JsonVariant*, json_variant_elements(*v) + 1);
_cleanup_free_ JsonVariant **array = new(JsonVariant*, json_variant_elements(*v) + 1);
if (!array)
return -ENOMEM;
for (i = 0; i < json_variant_elements(*v); i++)
size_t size = json_variant_elements(*v);
for (size_t i = 0; i < size; i++)
array[i] = json_variant_by_index(*v, i);
array[i] = element;
array[size] = element;
r = json_variant_new_array(&nv, array, i + 1);
r = json_variant_new_array(&nv, array, size + 1);
}
if (r < 0)
return r;
json_variant_propagate_sensitive(*v, nv);
json_variant_unref(*v);
*v = TAKE_PTR(nv);
JSON_VARIANT_REPLACE(*v, TAKE_PTR(nv));
return 0;
}
int json_variant_strv(JsonVariant *v, char ***ret) {
char **l = NULL;
size_t n, i;
bool sensitive;
int r;
@ -2136,12 +2100,12 @@ int json_variant_strv(JsonVariant *v, char ***ret) {
sensitive = v->sensitive;
n = json_variant_elements(v);
size_t n = json_variant_elements(v);
l = new(char*, n+1);
if (!l)
return -ENOMEM;
for (i = 0; i < n; i++) {
for (size_t i = 0; i < n; i++) {
JsonVariant *e;
assert_se(e = json_variant_by_index(v, i));
@ -2160,7 +2124,7 @@ int json_variant_strv(JsonVariant *v, char ***ret) {
}
}
l[i] = NULL;
l[n] = NULL;
*ret = TAKE_PTR(l);
return 0;
@ -2279,8 +2243,9 @@ static int json_variant_set_source(JsonVariant **v, JsonSource *source, unsigned
assert(v);
/* Patch in source and line/column number. Tries to do this in-place if the caller is the sole referencer of
* the object. If not, allocates a new object, possibly a surrogate for the original one */
/* Patch in source and line/column number. Tries to do this in-place if the caller is the sole
* referencer of the object. If not, allocates a new object, possibly a surrogate for the original
* one */
if (!*v)
return 0;
@ -2323,8 +2288,7 @@ static int json_variant_set_source(JsonVariant **v, JsonSource *source, unsigned
w->line = line;
w->column = column;
json_variant_unref(*v);
*v = w;
JSON_VARIANT_REPLACE(*v, w);
return 1;
}
@ -2847,7 +2811,7 @@ static int json_parse_internal(
unsigned *column,
bool continue_end) {
size_t n_stack = 1, i;
size_t n_stack = 1;
unsigned line_buffer = 0, column_buffer = 0;
void *tokenizer_state = NULL;
JsonStack *stack = NULL;
@ -3186,7 +3150,7 @@ done:
r = 0;
finish:
for (i = 0; i < n_stack; i++)
for (size_t i = 0; i < n_stack; i++)
json_stack_release(stack + i);
free(stack);
@ -3229,7 +3193,7 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags fla
int json_buildv(JsonVariant **ret, va_list ap) {
JsonStack *stack = NULL;
size_t n_stack = 1, i;
size_t n_stack = 1;
int r;
assert_return(ret, -EINVAL);
@ -3757,10 +3721,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
stack[n_stack++] = (JsonStack) {
.expect = EXPECT_OBJECT_KEY,
.n_suppress = current->n_suppress != 0 ? SIZE_MAX : 0, /* if we shall suppress the
* new object, then we should
* also suppress all object
* members */
.n_suppress = current->n_suppress != 0 ? SIZE_MAX : 0, /* If we shall suppress the
* new object, then we should
* also suppress all object
* members. */
};
break;
@ -4128,9 +4092,9 @@ int json_buildv(JsonVariant **ret, va_list ap) {
current->elements[current->n_elements++] = TAKE_PTR(add_more);
}
/* If we are supposed to suppress items, let's subtract how many items where generated from that
* counter. Except if the counter is SIZE_MAX, i.e. we shall suppress an infinite number of elements
* on this stack level */
/* If we are supposed to suppress items, let's subtract how many items where generated from
* that counter. Except if the counter is SIZE_MAX, i.e. we shall suppress an infinite number
* of elements on this stack level */
if (current->n_suppress != SIZE_MAX) {
if (current->n_suppress <= n_subtract) /* Saturated */
current->n_suppress = 0;
@ -4147,7 +4111,7 @@ done:
r = 0;
finish:
for (i = 0; i < n_stack; i++)
for (size_t i = 0; i < n_stack; i++)
json_stack_release(stack + i);
free(stack);
@ -4231,8 +4195,7 @@ int json_log_internal(
}
int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata) {
const JsonDispatch *p;
size_t i, n, m;
size_t m;
int r, done = 0;
bool *found;
@ -4245,14 +4208,16 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba
return -EINVAL;
}
for (p = table, m = 0; p->name; p++)
m = 0;
for (const JsonDispatch *p = table; p->name; p++)
m++;
found = newa0(bool, m);
n = json_variant_elements(v);
for (i = 0; i < n; i += 2) {
size_t n = json_variant_elements(v);
for (size_t i = 0; i < n; i += 2) {
JsonVariant *key, *value;
const JsonDispatch *p;
assert_se(key = json_variant_by_index(v, i));
assert_se(value = json_variant_by_index(v, i+1));
@ -4326,7 +4291,7 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba
}
}
for (p = table; p->name; p++) {
for (const JsonDispatch *p = table; p->name; p++) {
JsonDispatchFlags merged_flags = p->flags | flags;
if ((merged_flags & JSON_MANDATORY) && !found[p-table]) {
@ -4524,14 +4489,10 @@ int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags
}
int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
JsonVariant **p = userdata;
JsonVariant **p = ASSERT_PTR(userdata);
assert(variant);
assert(p);
json_variant_unref(*p);
*p = json_variant_ref(variant);
JSON_VARIANT_REPLACE(*p, json_variant_ref(variant));
return 0;
}
@ -4620,8 +4581,8 @@ static int json_cmp_strings(const void *x, const void *y) {
int json_variant_sort(JsonVariant **v) {
_cleanup_free_ JsonVariant **a = NULL;
JsonVariant *n = NULL;
size_t i, m;
_cleanup_(json_variant_unrefp) JsonVariant *n = NULL;
size_t m;
int r;
assert(v);
@ -4639,7 +4600,7 @@ int json_variant_sort(JsonVariant **v) {
if (!a)
return -ENOMEM;
for (i = 0; i < m; i++)
for (size_t i = 0; i < m; i++)
a[i] = json_variant_by_index(*v, i);
qsort(a, m/2, sizeof(JsonVariant*)*2, json_cmp_strings);
@ -4653,16 +4614,15 @@ int json_variant_sort(JsonVariant **v) {
if (!n->sorted) /* Check if this worked. This will fail if there are multiple identical keys used. */
return -ENOTUNIQ;
json_variant_unref(*v);
*v = n;
JSON_VARIANT_REPLACE(*v, TAKE_PTR(n));
return 1;
}
int json_variant_normalize(JsonVariant **v) {
_cleanup_free_ JsonVariant **a = NULL;
JsonVariant *n = NULL;
size_t i, j, m;
_cleanup_(json_variant_unrefp) JsonVariant *n = NULL;
size_t i, m;
int r;
assert(v);
@ -4680,10 +4640,11 @@ int json_variant_normalize(JsonVariant **v) {
if (!a)
return -ENOMEM;
for (i = 0; i < m; i++) {
for (i = 0; i < m; ) {
a[i] = json_variant_ref(json_variant_by_index(*v, i));
i++;
r = json_variant_normalize(a + i);
r = json_variant_normalize(&a[i-1]);
if (r < 0)
goto finish;
}
@ -4708,23 +4669,21 @@ int json_variant_normalize(JsonVariant **v) {
goto finish;
}
json_variant_unref(*v);
*v = n;
JSON_VARIANT_REPLACE(*v, TAKE_PTR(n));
r = 1;
finish:
for (j = 0; j < i; j++)
for (size_t j = 0; j < i; j++)
json_variant_unref(a[j]);
return r;
}
bool json_variant_is_normalized(JsonVariant *v) {
/* For now, let's consider anything containing numbers not expressible as integers as
* non-normalized. That's because we cannot sensibly compare them due to accuracy issues, nor even
* store them if they are too large. */
/* For now, let's consider anything containing numbers not expressible as integers as non-normalized.
* That's because we cannot sensibly compare them due to accuracy issues, nor even store them if they
* are too large. */
if (json_variant_is_real(v) && !json_variant_is_integer(v) && !json_variant_is_unsigned(v))
return false;
@ -4754,7 +4713,6 @@ bool json_variant_is_sorted(JsonVariant *v) {
}
int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size) {
if (!json_variant_is_string(v))
return -EINVAL;
@ -4762,7 +4720,6 @@ int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size) {
}
int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size) {
if (!json_variant_is_string(v))
return -EINVAL;
@ -4770,15 +4727,15 @@ int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size) {
}
static const char* const json_variant_type_table[_JSON_VARIANT_TYPE_MAX] = {
[JSON_VARIANT_STRING] = "string",
[JSON_VARIANT_INTEGER] = "integer",
[JSON_VARIANT_STRING] = "string",
[JSON_VARIANT_INTEGER] = "integer",
[JSON_VARIANT_UNSIGNED] = "unsigned",
[JSON_VARIANT_REAL] = "real",
[JSON_VARIANT_NUMBER] = "number",
[JSON_VARIANT_BOOLEAN] = "boolean",
[JSON_VARIANT_ARRAY] = "array",
[JSON_VARIANT_OBJECT] = "object",
[JSON_VARIANT_NULL] = "null",
[JSON_VARIANT_REAL] = "real",
[JSON_VARIANT_NUMBER] = "number",
[JSON_VARIANT_BOOLEAN] = "boolean",
[JSON_VARIANT_ARRAY] = "array",
[JSON_VARIANT_OBJECT] = "object",
[JSON_VARIANT_NULL] = "null",
};
DEFINE_STRING_TABLE_LOOKUP(json_variant_type, JsonVariantType);

View File

@ -82,6 +82,14 @@ JsonVariant *json_variant_ref(JsonVariant *v);
JsonVariant *json_variant_unref(JsonVariant *v);
void json_variant_unref_many(JsonVariant **array, size_t n);
#define JSON_VARIANT_REPLACE(v, q) \
do { \
typeof(v)* _v = &(v); \
typeof(q) _q = (q); \
json_variant_unref(*_v); \
*_v = _q; \
} while(0)
DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
const char *json_variant_string(JsonVariant *v);

View File

@ -13,10 +13,15 @@ static void _test_one(int line, const char *input, const char *output) {
usec_t u;
int r;
assert_se(calendar_spec_from_string(input, &c) >= 0);
r = calendar_spec_from_string(input, &c);
if (r < 0)
log_error_errno(r, "Failed to parse \"%s\": %m", input);
assert_se(r >= 0);
assert_se(calendar_spec_to_string(c, &p) >= 0);
log_info("line %d: \"%s\"\"%s\"", line, input, p);
log_info("line %d: \"%s\"\"%s\"%s%s", line, input, p,
!streq(p, output) ? " expected:" : "",
!streq(p, output) ? output : "");
assert_se(streq(p, output));
@ -157,6 +162,9 @@ TEST(calendar_spec_one) {
test_one("00:00:1.0..3.8", "*-*-* 00:00:01..03");
test_one("00:00:01..03", "*-*-* 00:00:01..03");
test_one("00:00:01/2,02..03", "*-*-* 00:00:01/2,02..03");
test_one("*:4,30:0..3", "*-*-* *:04,30:00..03");
test_one("*:4,30:0/1", "*-*-* *:04,30:*");
test_one("*:4,30:0/1,3,5", "*-*-* *:04,30:*");
test_one("*-*~1 Utc", "*-*~01 00:00:00 UTC");
test_one("*-*~05,3 ", "*-*~03,05 00:00:00");
test_one("*-*~* 00:00:00", "*-*-* 00:00:00");
@ -222,31 +230,34 @@ TEST(calendar_spec_next) {
TEST(calendar_spec_from_string) {
CalendarSpec *c;
assert_se(calendar_spec_from_string("test", &c) < 0);
assert_se(calendar_spec_from_string(" utc", &c) < 0);
assert_se(calendar_spec_from_string(" ", &c) < 0);
assert_se(calendar_spec_from_string("", &c) < 0);
assert_se(calendar_spec_from_string("7", &c) < 0);
assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0);
assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0);
assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0);
assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) < 0);
assert_se(calendar_spec_from_string("2016~11-22", &c) < 0);
assert_se(calendar_spec_from_string("*-*~5/5", &c) < 0);
assert_se(calendar_spec_from_string("Monday.. 12:00", &c) < 0);
assert_se(calendar_spec_from_string("Monday..", &c) < 0);
assert_se(calendar_spec_from_string("-00:+00/-5", &c) < 0);
assert_se(calendar_spec_from_string("00:+00/-5", &c) < 0);
assert_se(calendar_spec_from_string("2016- 11- 24 12: 30: 00", &c) < 0);
assert_se(calendar_spec_from_string("*~29", &c) < 0);
assert_se(calendar_spec_from_string("*~16..31", &c) < 0);
assert_se(calendar_spec_from_string("12..1/2-*", &c) < 0);
assert_se(calendar_spec_from_string("20/4:00", &c) < 0);
assert_se(calendar_spec_from_string("00:00/60", &c) < 0);
assert_se(calendar_spec_from_string("00:00:2300", &c) < 0);
assert_se(calendar_spec_from_string("00:00:18446744073709551615", &c) < 0);
assert_se(calendar_spec_from_string("test", &c) == -EINVAL);
assert_se(calendar_spec_from_string(" utc", &c) == -EINVAL);
assert_se(calendar_spec_from_string(" ", &c) == -EINVAL);
assert_se(calendar_spec_from_string("", &c) == -EINVAL);
assert_se(calendar_spec_from_string("7", &c) == -EINVAL);
assert_se(calendar_spec_from_string("121212:1:2", &c) == -EINVAL);
assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) == -EINVAL);
assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) == -EINVAL);
assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) == -ERANGE);
assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) == -EINVAL);
assert_se(calendar_spec_from_string("2016~11-22", &c) == -EINVAL);
assert_se(calendar_spec_from_string("*-*~5/5", &c) == -EINVAL);
assert_se(calendar_spec_from_string("Monday.. 12:00", &c) == -EINVAL);
assert_se(calendar_spec_from_string("Monday..", &c) == -EINVAL);
assert_se(calendar_spec_from_string("-00:+00/-5", &c) == -EINVAL);
assert_se(calendar_spec_from_string("00:+00/-5", &c) == -EINVAL);
assert_se(calendar_spec_from_string("2016- 11- 24 12: 30: 00", &c) == -EINVAL);
assert_se(calendar_spec_from_string("*~29", &c) == -EINVAL);
assert_se(calendar_spec_from_string("*~16..31", &c) == -EINVAL);
assert_se(calendar_spec_from_string("12..1/2-*", &c) == -EINVAL);
assert_se(calendar_spec_from_string("20/4:00", &c) == -EINVAL);
assert_se(calendar_spec_from_string("00:00/60", &c) == -EINVAL);
assert_se(calendar_spec_from_string("00:00:2300", &c) == -ERANGE);
assert_se(calendar_spec_from_string("00:00:18446744073709551615", &c) == -ERANGE);
assert_se(calendar_spec_from_string("@88588582097858858", &c) == -ERANGE);
assert_se(calendar_spec_from_string("*:4,30:*,5", &c) == -EINVAL);
assert_se(calendar_spec_from_string("*:4,30:5,*", &c) == -EINVAL);
assert_se(calendar_spec_from_string("*:4,30:*\n", &c) == -EINVAL);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -0,0 +1 @@
*:4,30:0..5,0

Binary file not shown.

View File

@ -0,0 +1 @@
*:4,30:0/01,0

Binary file not shown.

View File

@ -0,0 +1 @@
*:4,30:0..3

View File

@ -0,0 +1 @@
[7E73]

View File

@ -0,0 +1 @@
[7,7,7,7,{"":7,"":7,"^t":7,"-":7},2777,7,7,7,3]

View File

@ -0,0 +1 @@
{"":2,"":6,"-":7}