Compare commits

...

16 Commits

Author SHA1 Message Date
22a121383f Allow for overriding feature test macros 2022-09-11 00:44:14 +02:00
0e89bb9f46 Add some consts to function arguments 2022-09-01 12:44:58 +02:00
63aed8f0fd Fix up the PEG change from the last commit
This is not a regular expression.
2022-08-14 19:09:52 +02:00
f545be725d Extend string syntax in config
And actually test the results of string parsing.
2022-08-14 18:14:21 +02:00
7e8e085c97 Remove pointless, wrong constant
C99 allows trailing commas.
2021-12-18 00:25:13 +01:00
782a9a5977 Import libpulse poller integration, add tests 2021-11-07 15:37:21 +01:00
34f86651f6 Update .gitignore 2021-10-30 03:31:08 +02:00
5dec46df2c Add clang-format configuration, clean up 2021-10-30 03:10:17 +02:00
1b9d89cab3 Use kqueue on Darwin as well
Since poll() is implemented in terms of kqueue() there,
it doesn't seem like this could have improved anything.

Besides man 3 ev, libevent code, and [1],
I haven't managed to find much relevant information.

[1] https://daniel.haxx.se/blog/2016/10/11/poll-on-mac-10-12-is-broken/
2021-09-29 12:07:25 +02:00
a3ad5e7751 Ignore empty XDG_*_DIRS env. variables
As the specification says we should.  GLib does this as well.

It is still possible to achieve an empty set by using ":",
which are two non-absolute paths that should be ignored.
GLib doesn't implement this.  Thus, we're now better than GLib.
2021-09-26 08:49:51 +02:00
960420df3e Escape DEL character in config_item_write_string() 2020-10-31 21:28:29 +01:00
d71c47f8ce CMakeLists.txt: omit end{if,foreach} expressions
Their usefulness was almost negative.
2020-10-29 15:32:26 +01:00
425ea57b17 CMakeLists.txt: clean up OpenBSD support
A few things might have changed.
2020-10-29 15:31:05 +01:00
8822d06091 Don't suppress -Wimplicit-fallthrough
Might have already been resolved by: 9494e8e da75b6f
2020-10-26 18:25:32 +01:00
9639777814 Fix validation of overlong UTF-8
It was too strict and Egyptian dicks didn't want to pass,
so we'll do it half-arsedly for a subset.
2020-10-24 19:09:09 +02:00
929229a1d7 Fix config PEG grammar to match strtoll() 2020-10-24 08:05:17 +02:00
10 changed files with 609 additions and 47 deletions

32
.clang-format Normal file
View File

@@ -0,0 +1,32 @@
# clang-format is fairly limited, and these rules are approximate:
# - array initializers can get terribly mangled with clang-format 12.0,
# - sometimes it still aligns with space characters,
# - struct name NL { NL ... NL } NL name; is unachievable.
BasedOnStyle: GNU
ColumnLimit: 80
IndentWidth: 4
TabWidth: 4
UseTab: ForContinuationAndIndentation
BreakBeforeBraces: Allman
SpaceAfterCStyleCast: true
AlignAfterOpenBracket: DontAlign
AlignOperands: DontAlign
AlignConsecutiveMacros: Consecutive
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
IndentGotoLabels: false
# IncludeCategories has some potential, but it may also break the build.
# Note that the documentation says the value should be "Never".
SortIncludes: false
# This is a compromise, it generally works out aesthetically better.
BinPackArguments: false
# Unfortunately, this can't be told to align to column 40 or so.
SpacesBeforeTrailingComments: 2
# liberty-specific macro body wrappers.
MacroBlockBegin: "BLOCK_START"
MacroBlockEnd: "BLOCK_END"
ForEachMacros: ["LIST_FOR_EACH"]

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@
/liberty.files /liberty.files
/liberty.creator* /liberty.creator*
/liberty.includes /liberty.includes
/liberty.cflags
/liberty.cxxflags

View File

@@ -1,12 +1,12 @@
project (liberty C) project (liberty C)
cmake_minimum_required (VERSION 2.8.5) cmake_minimum_required (VERSION 2.8.12)
# Moar warnings # Moar warnings
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC) if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# -Wunused-function is pretty annoying here, as everything is static # -Wunused-function is pretty annoying here, as everything is static
set (wdisabled "-Wno-unused-function -Wno-implicit-fallthrough") set (wdisabled "-Wno-unused-function")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra ${wdisabled}") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra ${wdisabled}")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC) endif ()
# Dependencies # Dependencies
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
@@ -16,11 +16,9 @@ find_package (PkgConfig REQUIRED)
pkg_check_modules (libssl REQUIRED libssl libcrypto) pkg_check_modules (libssl REQUIRED libssl libcrypto)
if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD") if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
include_directories (/usr/local/include)
link_directories (/usr/local/lib)
# Our POSIX version macros make these undefined # Our POSIX version macros make these undefined
add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1) add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1)
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD") endif ()
set (common_libraries ${libssl_LIBRARIES}) set (common_libraries ${libssl_LIBRARIES})
include_directories (${libssl_INCLUDE_DIRS}) include_directories (${libssl_INCLUDE_DIRS})
@@ -32,15 +30,25 @@ foreach (extra iconv rt)
find_library (extra_lib_${extra} ${extra}) find_library (extra_lib_${extra} ${extra})
if (extra_lib_${extra}) if (extra_lib_${extra})
list (APPEND common_libraries ${extra}) list (APPEND common_libraries ${extra})
endif (extra_lib_${extra}) endif ()
endforeach (extra) endforeach ()
# Build some unit tests # Build some unit tests
include_directories (${PROJECT_SOURCE_DIR}) include_directories (${PROJECT_SOURCE_DIR})
enable_testing () enable_testing ()
foreach (name liberty proto) set (tests liberty proto)
pkg_check_modules (libpulse libpulse)
if (libpulse_FOUND)
list (APPEND tests pulse)
list (APPEND common_libraries ${libpulse_LIBRARIES})
include_directories (${libpulse_INCLUDE_DIRS})
link_directories (${libpulse_LIBRARY_DIRS})
endif ()
foreach (name ${tests})
add_executable (test-${name} tests/${name}.c ${common_sources}) add_executable (test-${name} tests/${name}.c ${common_sources})
add_threads (test-${name}) add_threads (test-${name})
target_link_libraries (test-${name} ${common_libraries}) target_link_libraries (test-${name} ${common_libraries})
add_test (NAME test-${name} COMMAND test-${name}) add_test (NAME test-${name} COMMAND test-${name})
endforeach (name) endforeach ()

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name> Copyright (c) 2014 - 2022, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.

View File

@@ -1321,7 +1321,6 @@ enum mpd_subsystem
#define XX(a, b, c) MPD_SUBSYSTEM_ ## a = (1 << b), #define XX(a, b, c) MPD_SUBSYSTEM_ ## a = (1 << b),
MPD_SUBSYSTEM_TABLE (XX) MPD_SUBSYSTEM_TABLE (XX)
#undef XX #undef XX
MPD_SUBSYSTEM_MAX
}; };
static const char *mpd_subsystem_names[] = static const char *mpd_subsystem_names[] =

348
liberty-pulse.c Normal file
View File

@@ -0,0 +1,348 @@
/*
* liberty-pulse.c: PulseAudio mainloop abstraction
*
* Copyright (c) 2016 - 2021, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <pulse/mainloop.h>
// --- PulseAudio mainloop abstraction -----------------------------------------
struct pa_io_event
{
LIST_HEADER (pa_io_event)
pa_mainloop_api *api; ///< Parent structure
struct poller_fd fd; ///< Underlying FD event
pa_io_event_cb_t dispatch; ///< Dispatcher
pa_io_event_destroy_cb_t free; ///< Destroyer
void *user_data; ///< User data
};
struct pa_time_event
{
LIST_HEADER (pa_time_event)
pa_mainloop_api *api; ///< Parent structure
struct poller_timer timer; ///< Underlying timer event
pa_time_event_cb_t dispatch; ///< Dispatcher
pa_time_event_destroy_cb_t free; ///< Destroyer
void *user_data; ///< User data
};
struct pa_defer_event
{
LIST_HEADER (pa_defer_event)
pa_mainloop_api *api; ///< Parent structure
struct poller_idle idle; ///< Underlying idle event
pa_defer_event_cb_t dispatch; ///< Dispatcher
pa_defer_event_destroy_cb_t free; ///< Destroyer
void *user_data; ///< User data
};
struct poller_pa
{
struct poller *poller; ///< The underlying event loop
pa_io_event *io_list; ///< I/O events
pa_time_event *time_list; ///< Timer events
pa_defer_event *defer_list; ///< Deferred events
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static short
poller_pa_flags_to_events (pa_io_event_flags_t flags)
{
short result = 0;
if (flags & PA_IO_EVENT_ERROR) result |= POLLERR;
if (flags & PA_IO_EVENT_HANGUP) result |= POLLHUP;
if (flags & PA_IO_EVENT_INPUT) result |= POLLIN;
if (flags & PA_IO_EVENT_OUTPUT) result |= POLLOUT;
return result;
}
static pa_io_event_flags_t
poller_pa_events_to_flags (short events)
{
pa_io_event_flags_t result = 0;
if (events & POLLERR) result |= PA_IO_EVENT_ERROR;
if (events & POLLHUP) result |= PA_IO_EVENT_HANGUP;
if (events & POLLIN) result |= PA_IO_EVENT_INPUT;
if (events & POLLOUT) result |= PA_IO_EVENT_OUTPUT;
return result;
}
static struct timeval
poller_pa_get_current_time (void)
{
struct timeval tv;
#ifdef _POSIX_TIMERS
struct timespec tp;
hard_assert (clock_gettime (CLOCK_REALTIME, &tp) != -1);
tv.tv_sec = tp.tv_sec;
tv.tv_usec = tp.tv_nsec / 1000;
#else
gettimeofday (&tv, NULL);
#endif
return tv;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
poller_pa_io_dispatcher (const struct pollfd *pfd, void *user_data)
{
pa_io_event *self = user_data;
self->dispatch (self->api, self,
pfd->fd, poller_pa_events_to_flags (pfd->revents), self->user_data);
}
static void
poller_pa_io_enable (pa_io_event *self, pa_io_event_flags_t events)
{
struct poller_fd *fd = &self->fd;
if (events)
poller_fd_set (fd, poller_pa_flags_to_events (events));
else
poller_fd_reset (fd);
}
static pa_io_event *
poller_pa_io_new (pa_mainloop_api *api, int fd_, pa_io_event_flags_t events,
pa_io_event_cb_t cb, void *userdata)
{
pa_io_event *self = xcalloc (1, sizeof *self);
self->api = api;
self->dispatch = cb;
self->user_data = userdata;
struct poller_pa *data = api->userdata;
self->fd = poller_fd_make (data->poller, fd_);
self->fd.user_data = self;
self->fd.dispatcher = poller_pa_io_dispatcher;
// FIXME: under x2go PA tries to register twice for the same FD,
// which fails with our curent poller implementation;
// we could maintain a list of { poller_fd, listeners } structures;
// or maybe we're doing something wrong, which is yet to be determined
poller_pa_io_enable (self, events);
LIST_PREPEND (data->io_list, self);
return self;
}
static void
poller_pa_io_free (pa_io_event *self)
{
if (self->free)
self->free (self->api, self, self->user_data);
struct poller_pa *data = self->api->userdata;
poller_fd_reset (&self->fd);
LIST_UNLINK (data->io_list, self);
free (self);
}
static void
poller_pa_io_set_destroy (pa_io_event *self, pa_io_event_destroy_cb_t cb)
{
self->free = cb;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
poller_pa_time_dispatcher (void *user_data)
{
pa_time_event *self = user_data;
// XXX: the meaning of the time argument is undocumented,
// so let's just put current Unix time in there
struct timeval now = poller_pa_get_current_time ();
self->dispatch (self->api, self, &now, self->user_data);
}
static void
poller_pa_time_restart (pa_time_event *self, const struct timeval *tv)
{
struct poller_timer *timer = &self->timer;
if (tv)
{
struct timeval now = poller_pa_get_current_time ();
poller_timer_set (timer,
(tv->tv_sec - now.tv_sec) * 1000 +
(tv->tv_usec - now.tv_usec) / 1000);
}
else
poller_timer_reset (timer);
}
static pa_time_event *
poller_pa_time_new (pa_mainloop_api *api, const struct timeval *tv,
pa_time_event_cb_t cb, void *userdata)
{
pa_time_event *self = xcalloc (1, sizeof *self);
self->api = api;
self->dispatch = cb;
self->user_data = userdata;
struct poller_pa *data = api->userdata;
self->timer = poller_timer_make (data->poller);
self->timer.user_data = self;
self->timer.dispatcher = poller_pa_time_dispatcher;
poller_pa_time_restart (self, tv);
LIST_PREPEND (data->time_list, self);
return self;
}
static void
poller_pa_time_free (pa_time_event *self)
{
if (self->free)
self->free (self->api, self, self->user_data);
struct poller_pa *data = self->api->userdata;
poller_timer_reset (&self->timer);
LIST_UNLINK (data->time_list, self);
free (self);
}
static void
poller_pa_time_set_destroy (pa_time_event *self, pa_time_event_destroy_cb_t cb)
{
self->free = cb;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
poller_pa_defer_dispatcher (void *user_data)
{
pa_defer_event *self = user_data;
self->dispatch (self->api, self, self->user_data);
}
static pa_defer_event *
poller_pa_defer_new (pa_mainloop_api *api,
pa_defer_event_cb_t cb, void *userdata)
{
pa_defer_event *self = xcalloc (1, sizeof *self);
self->api = api;
self->dispatch = cb;
self->user_data = userdata;
struct poller_pa *data = api->userdata;
self->idle = poller_idle_make (data->poller);
self->idle.user_data = self;
self->idle.dispatcher = poller_pa_defer_dispatcher;
poller_idle_set (&self->idle);
LIST_PREPEND (data->defer_list, self);
return self;
}
static void
poller_pa_defer_enable (pa_defer_event *self, int enable)
{
struct poller_idle *idle = &self->idle;
if (enable)
poller_idle_set (idle);
else
poller_idle_reset (idle);
}
static void
poller_pa_defer_free (pa_defer_event *self)
{
if (self->free)
self->free (self->api, self, self->user_data);
struct poller_pa *data = self->api->userdata;
poller_idle_reset (&self->idle);
LIST_UNLINK (data->defer_list, self);
free (self);
}
static void
poller_pa_defer_set_destroy (pa_defer_event *self,
pa_defer_event_destroy_cb_t cb)
{
self->free = cb;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
poller_pa_quit (pa_mainloop_api *api, int retval)
{
(void) api;
(void) retval;
// This is not called from within libpulse
hard_assert (!"quitting the libpulse event loop is unimplemented");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static struct pa_mainloop_api g_poller_pa_template =
{
.io_new = poller_pa_io_new,
.io_enable = poller_pa_io_enable,
.io_free = poller_pa_io_free,
.io_set_destroy = poller_pa_io_set_destroy,
.time_new = poller_pa_time_new,
.time_restart = poller_pa_time_restart,
.time_free = poller_pa_time_free,
.time_set_destroy = poller_pa_time_set_destroy,
.defer_new = poller_pa_defer_new,
.defer_enable = poller_pa_defer_enable,
.defer_free = poller_pa_defer_free,
.defer_set_destroy = poller_pa_defer_set_destroy,
.quit = poller_pa_quit,
};
static struct pa_mainloop_api *
poller_pa_new (struct poller *self)
{
struct poller_pa *data = xcalloc (1, sizeof *data);
data->poller = self;
struct pa_mainloop_api *api = xmalloc (sizeof *api);
*api = g_poller_pa_template;
api->userdata = data;
return api;
}
static void
poller_pa_destroy (struct pa_mainloop_api *api)
{
struct poller_pa *data = api->userdata;
LIST_FOR_EACH (pa_io_event, iter, data->io_list)
poller_pa_io_free (iter);
LIST_FOR_EACH (pa_time_event, iter, data->time_list)
poller_pa_time_free (iter);
LIST_FOR_EACH (pa_defer_event, iter, data->defer_list)
poller_pa_defer_free (iter);
free (data);
free (api);
}

View File

@@ -1,7 +1,7 @@
/* /*
* liberty.c: the ultimate C unlibrary * liberty.c: the ultimate C unlibrary
* *
* Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name> * Copyright (c) 2014 - 2022, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@@ -16,8 +16,13 @@
* *
*/ */
#define _POSIX_C_SOURCE 199309L #ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#endif
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
@@ -1749,10 +1754,9 @@ poller_run (struct poller *self)
self->revents_len = 0; self->revents_len = 0;
} }
#elif defined (BSD) // Sort of similar to the epoll version. Let's hope Darwin isn't broken,
// that'd mean reimplementing this in terms of select() just because of Crapple.
// Mac OS X's kqueue is fatally broken, or so I've been told; leaving it out. #elif defined (BSD) || defined (__APPLE__)
// Otherwise this is sort of similar to the epoll version.
#include <sys/types.h> #include <sys/types.h>
#include <sys/event.h> #include <sys/event.h>
@@ -2451,7 +2455,7 @@ write_queue_processed (struct write_queue *self, size_t len)
} }
static bool static bool
write_queue_is_empty (struct write_queue *self) write_queue_is_empty (const struct write_queue *self)
{ {
return self->head == NULL; return self->head == NULL;
} }
@@ -2546,7 +2550,7 @@ msg_unpacker_make (const void *data, size_t len)
} }
static size_t static size_t
msg_unpacker_get_available (struct msg_unpacker *self) msg_unpacker_get_available (const struct msg_unpacker *self)
{ {
return self->len - self->offset; return self->len - self->offset;
} }
@@ -2748,16 +2752,12 @@ utf8_decode (const char **s, size_t len)
} }
// In the middle of a character // In the middle of a character
if (sequence_len == 1) // or an overlong sequence (subset, possibly MUTF-8, not supported)
if (sequence_len == 1 || *p == 0xC0 || *p == 0xC1)
return -1; return -1;
// Check the rest of the sequence // Check the rest of the sequence
uint32_t cp = *p++ & ~mask; uint32_t cp = *p++ & ~mask;
// Overlong sequence (possibly MUTF-8, not supported)
if (!cp && sequence_len)
return -1;
while (sequence_len && --sequence_len) while (sequence_len && --sequence_len)
{ {
if (p == end) if (p == end)
@@ -3251,7 +3251,7 @@ get_xdg_config_dirs (struct strv *out)
str_free (&config_home); str_free (&config_home);
const char *xdg_config_dirs; const char *xdg_config_dirs;
if (!(xdg_config_dirs = getenv ("XDG_CONFIG_DIRS"))) if (!(xdg_config_dirs = getenv ("XDG_CONFIG_DIRS")) || !*xdg_config_dirs)
xdg_config_dirs = "/etc/xdg"; xdg_config_dirs = "/etc/xdg";
cstr_split (xdg_config_dirs, ":", true, out); cstr_split (xdg_config_dirs, ":", true, out);
} }
@@ -3276,7 +3276,7 @@ get_xdg_data_dirs (struct strv *out)
str_free (&data_home); str_free (&data_home);
const char *xdg_data_dirs; const char *xdg_data_dirs;
if (!(xdg_data_dirs = getenv ("XDG_DATA_DIRS"))) if (!(xdg_data_dirs = getenv ("XDG_DATA_DIRS")) || !*xdg_data_dirs)
xdg_data_dirs = "/usr/local/share/:/usr/share/"; xdg_data_dirs = "/usr/local/share/:/usr/share/";
cstr_split (xdg_data_dirs, ":", true, out); cstr_split (xdg_data_dirs, ":", true, out);
} }
@@ -4417,11 +4417,13 @@ socket_io_try_write (int socket_fd, struct str *wb)
// object = lws '{' entries endobj // object = lws '{' entries endobj
// endobj = lws '}' // endobj = lws '}'
// //
// string = lws '"' ('\\' escape / ![\\"] char)* '"' // quoted = lws '"' (!["\\] char / '\\' escape)* '"'
// / lws '`' (![`] char)* '`'
// string = (quoted)+
// char = [\0-\177] # or any Unicode codepoint in the UTF-8 encoding // char = [\0-\177] # or any Unicode codepoint in the UTF-8 encoding
// escape = [\\"abfnrtv] / [xX][0-9A-Fa-f][0-9A-Fa-f]? / [0-7][0-7]?[0-7]? // escape = [\\"abfnrtv] / [xX][0-9A-Fa-f][0-9A-Fa-f]? / [0-7][0-7]?[0-7]?
// //
// integer = lws '-'? [0-9]+ # whatever strtoll() accepts on your system // integer = lws [-+]? [0-9]+ # whatever strtoll() accepts on your system
// null = lws 'null' // null = lws 'null'
// boolean = lws 'yes' / lws 'YES' / lws 'no' / lws 'NO' // boolean = lws 'yes' / lws 'YES' / lws 'no' / lws 'NO'
// / lws 'on' / lws 'ON' / lws 'off' / lws 'OFF' // / lws 'on' / lws 'ON' / lws 'off' / lws 'OFF'
@@ -4717,8 +4719,10 @@ config_item_write_string (struct str *output, const struct str *s)
else if (c == '\t') str_append (output, "\\t"); else if (c == '\t') str_append (output, "\\t");
else if (c == '\\') str_append (output, "\\\\"); else if (c == '\\') str_append (output, "\\\\");
else if (c == '"') str_append (output, "\\\""); else if (c == '"') str_append (output, "\\\"");
else if (c < 32) str_append_printf (output, "\\x%02x", c); else if (iscntrl_ascii (c))
else str_append_c (output, c); str_append_printf (output, "\\x%02x", c);
else
str_append_c (output, c);
} }
str_append_c (output, '"'); str_append_c (output, '"');
} }
@@ -5042,10 +5046,10 @@ config_tokenizer_escape_sequence
} }
static bool static bool
config_tokenizer_string config_tokenizer_dq_string (struct config_tokenizer *self, struct str *output,
(struct config_tokenizer *self, struct str *output, struct error **e) struct error **e)
{ {
unsigned char c; unsigned char c = config_tokenizer_advance (self);
while (self->len) while (self->len)
{ {
if ((c = config_tokenizer_advance (self)) == '"') if ((c = config_tokenizer_advance (self)) == '"')
@@ -5059,6 +5063,44 @@ config_tokenizer_string
return false; return false;
} }
static bool
config_tokenizer_bt_string (struct config_tokenizer *self, struct str *output,
struct error **e)
{
unsigned char c = config_tokenizer_advance (self);
while (self->len)
{
if ((c = config_tokenizer_advance (self)) == '`')
return true;
str_append_c (output, c);
}
config_tokenizer_error (self, e, "premature end of string");
return false;
}
static bool
config_tokenizer_string (struct config_tokenizer *self, struct str *output,
struct error **e)
{
// Go-like strings, with C/AWK-like automatic concatenation
while (self->len)
{
bool ok = true;
if (isspace_ascii (*self->p) && *self->p != '\n')
config_tokenizer_advance (self);
else if (*self->p == '"')
ok = config_tokenizer_dq_string (self, output, e);
else if (*self->p == '`')
ok = config_tokenizer_bt_string (self, output, e);
else
break;
if (!ok)
return false;
}
return true;
}
static enum config_token static enum config_token
config_tokenizer_next (struct config_tokenizer *self, struct error **e) config_tokenizer_next (struct config_tokenizer *self, struct error **e)
{ {
@@ -5083,7 +5125,7 @@ config_tokenizer_next (struct config_tokenizer *self, struct error **e)
return CONFIG_T_ABORT; return CONFIG_T_ABORT;
case '"': case '"':
config_tokenizer_advance (self); case '`':
str_reset (&self->string); str_reset (&self->string);
if (!config_tokenizer_string (self, &self->string, e)) if (!config_tokenizer_string (self, &self->string, e))
return CONFIG_T_ABORT; return CONFIG_T_ABORT;

View File

@@ -82,4 +82,3 @@ siphash (const unsigned char key[16], const unsigned char *m, size_t len)
return v0 ^ v1 ^ v2 ^ v3; return v0 ^ v1 ^ v2 ^ v3;
} }

View File

@@ -1,7 +1,7 @@
/* /*
* tests/liberty.c * tests/liberty.c
* *
* Copyright (c) 2015 - 2016, Přemysl Eric Janouch <p@janouch.name> * Copyright (c) 2015 - 2022, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@@ -331,10 +331,12 @@ test_utf8 (void)
soft_assert (utf8_decode (&partial, 1) == -2); soft_assert (utf8_decode (&partial, 1) == -2);
soft_assert (utf8_decode (&empty, 0) == -1); soft_assert (utf8_decode (&empty, 0) == -1);
const char valid[] = "2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm"; const char valid_1[] = "2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm";
const char valid_2[] = "\xf0\x93\x82\xb9";
const char invalid_1[] = "\xf0\x90\x28\xbc"; const char invalid_1[] = "\xf0\x90\x28\xbc";
const char invalid_2[] = "\xc0\x80"; const char invalid_2[] = "\xc0\x80";
soft_assert ( utf8_validate (valid, sizeof valid)); soft_assert ( utf8_validate (valid_1, sizeof valid_1));
soft_assert ( utf8_validate (valid_2, sizeof valid_2));
soft_assert (!utf8_validate (invalid_1, sizeof invalid_1)); soft_assert (!utf8_validate (invalid_1, sizeof invalid_1));
soft_assert (!utf8_validate (invalid_2, sizeof invalid_2)); soft_assert (!utf8_validate (invalid_2, sizeof invalid_2));
@@ -647,7 +649,7 @@ static struct config_schema g_config_test[] =
.default_ = "1" }, .default_ = "1" },
{ .name = "foobar", { .name = "foobar",
.type = CONFIG_ITEM_STRING, .type = CONFIG_ITEM_STRING,
.default_ = "\"qux\\x01\"" }, .default_ = "\"qux\\x01`\" \"\"`a`" },
{} {}
}; };
@@ -673,6 +675,9 @@ test_config (void)
"top.bar", NULL), invalid, NULL)); "top.bar", NULL), invalid, NULL));
config_item_destroy (invalid); config_item_destroy (invalid);
hard_assert (!strcmp ("qux\001`a",
config_item_get (config.root, "top.foobar", NULL)->value.string.str));
struct str s = str_make (); struct str s = str_make ();
config_item_write (config.root, true, &s); config_item_write (config.root, true, &s);
struct config_item *parsed = config_item_parse (s.str, s.len, false, NULL); struct config_item *parsed = config_item_parse (s.str, s.len, false, NULL);

127
tests/pulse.c Normal file
View File

@@ -0,0 +1,127 @@
/*
* tests/pulse.c
*
* Copyright (c) 2021, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#define PROGRAM_NAME "test"
#define PROGRAM_VERSION "0"
#define LIBERTY_WANT_POLLER
#include "../liberty.c"
#include "../liberty-pulse.c"
// --- Tests -------------------------------------------------------------------
enum
{
EVENT_IO = 1 << 0,
EVENT_TIME = 1 << 1,
EVENT_DEFER = 1 << 2,
EVENT_ALL = (1 << 3) - 1
};
static intptr_t g_events = 0;
static intptr_t g_destroys = 0;
static void
io_event_cb (pa_mainloop_api *a,
pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata)
{
(void) a; (void) e; (void) fd; (void) events;
g_events |= (intptr_t) userdata;
}
static void
io_event_destroy_cb (pa_mainloop_api *a, pa_io_event *e, void *userdata)
{
(void) a; (void) e;
g_destroys += (intptr_t) userdata;
}
static void
time_event_cb (pa_mainloop_api *a,
pa_time_event *e, const struct timeval *tv, void *userdata)
{
(void) a; (void) e; (void) tv;
g_events |= (intptr_t) userdata;
}
static void
time_event_destroy_cb (pa_mainloop_api *a, pa_time_event *e, void *userdata)
{
(void) a; (void) e;
g_destroys += (intptr_t) userdata;
}
static void
defer_event_cb (pa_mainloop_api *a, pa_defer_event *e, void *userdata)
{
(void) a; (void) e;
g_events |= (intptr_t) userdata;
}
static void
defer_event_destroy_cb (pa_mainloop_api *a, pa_defer_event *e, void *userdata)
{
(void) a; (void) e;
g_destroys += (intptr_t) userdata;
}
static void
test_pulse (void)
{
struct poller poller;
poller_init (&poller);
// Let's just get this over with, not aiming for high test coverage here
pa_mainloop_api *api = poller_pa_new (&poller);
pa_io_event *ie = api->io_new (api, STDOUT_FILENO, PA_IO_EVENT_OUTPUT,
io_event_cb, (void *) EVENT_IO);
api->io_set_destroy (ie, io_event_destroy_cb);
const struct timeval tv = poller_pa_get_current_time ();
pa_time_event *te = api->time_new (api, &tv,
time_event_cb, (void *) EVENT_TIME);
api->time_set_destroy (te, time_event_destroy_cb);
api->time_restart (te, &tv);
pa_defer_event *de = api->defer_new (api,
defer_event_cb, (void *) EVENT_DEFER);
api->defer_set_destroy (de, defer_event_destroy_cb);
api->defer_enable (api->defer_new (api,
defer_event_cb, (void *) EVENT_DEFER), false);
alarm (1);
while (g_events != EVENT_ALL)
poller_run (&poller);
poller_pa_destroy (api);
soft_assert (g_destroys == EVENT_ALL);
poller_free (&poller);
}
// --- Main --------------------------------------------------------------------
int
main (int argc, char *argv[])
{
struct test test;
test_init (&test, argc, argv);
test_add_simple (&test, "/pulse", NULL, test_pulse);
return test_run (&test);
}