Compare commits
	
		
			14 Commits
		
	
	
		
			d71c47f8ce
			...
			688c458095
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						688c458095
	
				 | 
					
					
						|||
| 
						
						
							
						
						34460ca715
	
				 | 
					
					
						|||
| 
						
						
							
						
						9883caf849
	
				 | 
					
					
						|||
| 
						
						
							
						
						22a121383f
	
				 | 
					
					
						|||
| 
						
						
							
						
						0e89bb9f46
	
				 | 
					
					
						|||
| 
						
						
							
						
						63aed8f0fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						f545be725d
	
				 | 
					
					
						|||
| 
						
						
							
						
						7e8e085c97
	
				 | 
					
					
						|||
| 
						
						
							
						
						782a9a5977
	
				 | 
					
					
						|||
| 
						
						
							
						
						34f86651f6
	
				 | 
					
					
						|||
| 
						
						
							
						
						5dec46df2c
	
				 | 
					
					
						|||
| 
						
						
							
						
						1b9d89cab3
	
				 | 
					
					
						|||
| 
						
						
							
						
						a3ad5e7751
	
				 | 
					
					
						|||
| 
						
						
							
						
						960420df3e
	
				 | 
					
					
						
							
								
								
									
										32
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.clang-format
									
									
									
									
									
										Normal 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
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,3 +7,5 @@
 | 
				
			|||||||
/liberty.files
 | 
					/liberty.files
 | 
				
			||||||
/liberty.creator*
 | 
					/liberty.creator*
 | 
				
			||||||
/liberty.includes
 | 
					/liberty.includes
 | 
				
			||||||
 | 
					/liberty.cflags
 | 
				
			||||||
 | 
					/liberty.cxxflags
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
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)
 | 
				
			||||||
@@ -36,7 +36,17 @@ 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})
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@@ -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.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -148,7 +148,7 @@ irc_free_message (struct irc_message *msg)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
irc_process_buffer (struct str *buf,
 | 
					irc_process_buffer (struct str *buf,
 | 
				
			||||||
	void (*callback)(const struct irc_message *, const char *, void *),
 | 
						void (*callback) (const struct irc_message *, const char *, void *),
 | 
				
			||||||
	void *user_data)
 | 
						void *user_data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *start = buf->str, *end = start + buf->len;
 | 
						char *start = buf->str, *end = start + buf->len;
 | 
				
			||||||
@@ -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
									
								
							
							
						
						
									
										348
									
								
								liberty-pulse.c
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										90
									
								
								liberty.c
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								liberty.c
									
									
									
									
									
								
							@@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -3247,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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -3272,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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -4413,7 +4417,9 @@ 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]?
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
@@ -4708,13 +4714,15 @@ config_item_write_string (struct str *output, const struct str *s)
 | 
				
			|||||||
	for (size_t i = 0; i < s->len; i++)
 | 
						for (size_t i = 0; i < s->len; i++)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		unsigned char c = s->str[i];
 | 
							unsigned char c = s->str[i];
 | 
				
			||||||
		if      (c == '\n')  str_append        (output, "\\n");
 | 
							if      (c == '\n')  str_append (output, "\\n");
 | 
				
			||||||
		else if (c == '\r')  str_append        (output, "\\r");
 | 
							else if (c == '\r')  str_append (output, "\\r");
 | 
				
			||||||
		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, '"');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -5038,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)) == '"')
 | 
				
			||||||
@@ -5055,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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -5079,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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.
 | 
				
			||||||
@@ -649,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`" },
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -675,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
									
								
							
							
						
						
									
										127
									
								
								tests/pulse.c
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										237
									
								
								tools/asciiman.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								tools/asciiman.awk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,237 @@
 | 
				
			|||||||
 | 
					# asciiman.awk: stupid AsciiDoc to manual page converter
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: 0BSD
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This is not intended to produce great output, merely useful output.
 | 
				
			||||||
 | 
					# As such, input documents should restrict themselves as follows:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#  - Attributes cannot be passed on the command line.
 | 
				
			||||||
 | 
					#  - In-line formatting sequences must not overlap,
 | 
				
			||||||
 | 
					#    cannot be escaped, and cannot span lines.
 | 
				
			||||||
 | 
					#  - Heading underlines must match in byte length exactly.
 | 
				
			||||||
 | 
					#  - Only a small subset of syntax is supported overall.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Also beware that the output has only been tested with GNU troff.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function fatal(message) {
 | 
				
			||||||
 | 
						print ".\\\" " FILENAME ":" FNR ": fatal error: " message
 | 
				
			||||||
 | 
						print FILENAME ":" FNR ": fatal error: " message > "/dev/stderr"
 | 
				
			||||||
 | 
						exit 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function expand(s,   attr) {
 | 
				
			||||||
 | 
						# TODO: This should not expand unknown attribute names.
 | 
				
			||||||
 | 
						while (match(s, /[{][^{}]*[}]/)) {
 | 
				
			||||||
 | 
							attr = substr(s, RSTART + 1, RLENGTH - 2)
 | 
				
			||||||
 | 
							s = substr(s, 1, RSTART - 1) Attrs[attr] substr(s, RSTART + RLENGTH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function escape(s) {
 | 
				
			||||||
 | 
						gsub(/\\/, "\\\\", s)
 | 
				
			||||||
 | 
						gsub(/-/, "\\-", s)
 | 
				
			||||||
 | 
						sub(/^[.']/, "\\\\\\&&", s)
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function readattribute(line,    attrname, attrvalue) {
 | 
				
			||||||
 | 
						if (match(line, /^:[^:]*: /)) {
 | 
				
			||||||
 | 
							attrname = substr(line, RSTART + 1, RLENGTH - 3)
 | 
				
			||||||
 | 
							attrvalue = substr(line, RSTART + RLENGTH)
 | 
				
			||||||
 | 
							Attrs[attrname] = expand(attrvalue)
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NR == 1 {
 | 
				
			||||||
 | 
						nameline = $0
 | 
				
			||||||
 | 
						if (match(nameline, /[(][[:digit:]][)]$/)) {
 | 
				
			||||||
 | 
							name = substr(nameline, 1, RSTART - 1)
 | 
				
			||||||
 | 
							section = substr(nameline, RSTART + 1, RLENGTH - 2)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fatal("invalid header line")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getline
 | 
				
			||||||
 | 
						if (length(nameline) != length($0) || /[^=]/)
 | 
				
			||||||
 | 
							fatal("invalid header underline")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getline
 | 
				
			||||||
 | 
						while (readattribute($0))
 | 
				
			||||||
 | 
							getline
 | 
				
			||||||
 | 
						if ($0)
 | 
				
			||||||
 | 
							fatal("expected an empty line after the header")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Requesting tbl(1), even though we currently do not support tables.
 | 
				
			||||||
 | 
						print "'\\\" t"
 | 
				
			||||||
 | 
						print ".TH \"" toupper(name) "\" \"" section "\""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Hyphenation is indeed rather annoying, in particular with long links.
 | 
				
			||||||
 | 
						print ".nh"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function inline(line) {
 | 
				
			||||||
 | 
						if (!line) {
 | 
				
			||||||
 | 
							print ".sp"
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						line = escape(expand(line))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Strip empty URL descriptions, otherwise useful for demarking the end.
 | 
				
			||||||
 | 
						while (match(line, /[^[:space:]]+\[\]/)) {
 | 
				
			||||||
 | 
							line = substr(line, 1, RSTART + RLENGTH - 3) \
 | 
				
			||||||
 | 
								 substr(line, RSTART + RLENGTH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Pass-through, otherwise useful for hacks, is a lie here.
 | 
				
			||||||
 | 
						while (match(line, /[+][+][+][^+]+[+][+][+]/)) {
 | 
				
			||||||
 | 
							line = substr(line, 1, RSTART - 1) \
 | 
				
			||||||
 | 
								 substr(line, RSTART + 3, RLENGTH - 6) \
 | 
				
			||||||
 | 
								 substr(line, RSTART + RLENGTH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Italic and bold formatting doesn't respect any word boundaries.
 | 
				
			||||||
 | 
						while (match(line, /__[^_]+__/)) {
 | 
				
			||||||
 | 
							line = substr(line, 1, RSTART - 1) \
 | 
				
			||||||
 | 
								 "\\fI" substr(line, RSTART + 2, RLENGTH - 4) "\\fP" \
 | 
				
			||||||
 | 
								 substr(line, RSTART + RLENGTH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						while (match(line, /_[^_]+_/)) {
 | 
				
			||||||
 | 
							line = substr(line, 1, RSTART - 1) \
 | 
				
			||||||
 | 
								 "\\fI" substr(line, RSTART + 1, RLENGTH - 2) "\\fP" \
 | 
				
			||||||
 | 
								 substr(line, RSTART + RLENGTH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						while (match(line, /[*][*][^*]+[*][*]/)) {
 | 
				
			||||||
 | 
							line = substr(line, 1, RSTART - 1) \
 | 
				
			||||||
 | 
								 "\\fB" substr(line, RSTART + 2, RLENGTH - 4) "\\fP" \
 | 
				
			||||||
 | 
								 substr(line, RSTART + RLENGTH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						while (match(line, /[*][^*]+[*]/)) {
 | 
				
			||||||
 | 
							line = substr(line, 1, RSTART - 1) \
 | 
				
			||||||
 | 
								 "\\fB" substr(line, RSTART + 1, RLENGTH - 2) "\\fP" \
 | 
				
			||||||
 | 
								 substr(line, RSTART + RLENGTH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Enable double-spacing after the end of a sentence.
 | 
				
			||||||
 | 
						gsub(/[.][[:space:]]+/, ".\n", line)
 | 
				
			||||||
 | 
						gsub(/[!][[:space:]]+/, "!\n", line)
 | 
				
			||||||
 | 
						gsub(/[?][[:space:]]+/, "?\n", line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Quote commands resulting from that, as well as from expand().
 | 
				
			||||||
 | 
						gsub(/\n[.]/, "\n\\\\\\&.", line)
 | 
				
			||||||
 | 
						gsub(/\n[']/, "\n\\\\\\&'", line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sub(/[[:space:]]+[+]$/, "\n.br", line)
 | 
				
			||||||
 | 
						print line
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Returns 1 iff the left-over $0 should be processed further.
 | 
				
			||||||
 | 
					function process(firstline) {
 | 
				
			||||||
 | 
						if (readattribute(firstline))
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						if (getline <= 0) {
 | 
				
			||||||
 | 
							inline(firstline)
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (length(firstline) == length($0) && /^-+$/) {
 | 
				
			||||||
 | 
							print ".SH \"" escape(toupper(expand(firstline))) "\""
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (length(firstline) == length($0) && /^~+$/) {
 | 
				
			||||||
 | 
							print ".SS \"" escape(expand(firstline)) "\""
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (firstline ~ /^(-{4,}|[.]{4,})$/) {
 | 
				
			||||||
 | 
							print ".if n .RS 4"
 | 
				
			||||||
 | 
							print ".nf"
 | 
				
			||||||
 | 
							print ".fam C"
 | 
				
			||||||
 | 
							do {
 | 
				
			||||||
 | 
								print escape($0)
 | 
				
			||||||
 | 
							} while (getline > 0 && $0 != firstline)
 | 
				
			||||||
 | 
							print ".fam"
 | 
				
			||||||
 | 
							print ".fi"
 | 
				
			||||||
 | 
							print ".if n .RE"
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (firstline ~ /^\/{4,}$/) {
 | 
				
			||||||
 | 
							do {
 | 
				
			||||||
 | 
								print ".\\\" " $0
 | 
				
			||||||
 | 
							} while (getline > 0 && $0 != firstline)
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (match(firstline, /^\/\//)) {
 | 
				
			||||||
 | 
							print ".\\\" " firstline
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# We generally assume these block end with a blank line.
 | 
				
			||||||
 | 
						if (match(firstline, /^[[:space:]]*[*][[:space:]]+/)) {
 | 
				
			||||||
 | 
							# Bullet magic copied over from AsciiDoc/Asciidoctor generators.
 | 
				
			||||||
 | 
							print ".RS 4"
 | 
				
			||||||
 | 
							print ".ie n \\{\\"
 | 
				
			||||||
 | 
							print "\\h'-04'\\(bu\\h'+03'\\c"
 | 
				
			||||||
 | 
							print ".\\}"
 | 
				
			||||||
 | 
							print ".el \\{\\"
 | 
				
			||||||
 | 
							print ".sp -1"
 | 
				
			||||||
 | 
							print ".IP \\(bu 2.3"
 | 
				
			||||||
 | 
							print ".\\}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							inline(substr(firstline, RSTART + RLENGTH))
 | 
				
			||||||
 | 
							while ($0) {
 | 
				
			||||||
 | 
								sub(/^[[:space:]]+/, "")
 | 
				
			||||||
 | 
								sub(/^[+]$/, "")
 | 
				
			||||||
 | 
								if (!process($0) && getline <= 0)
 | 
				
			||||||
 | 
									fatal("unexpected EOF")
 | 
				
			||||||
 | 
								if (match($0, /^[[:space:]]*[*][[:space:]]+/))
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							print ".RE"
 | 
				
			||||||
 | 
							print ".sp"
 | 
				
			||||||
 | 
							return !!$0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (match(firstline, /^[[:space:]]+/)) {
 | 
				
			||||||
 | 
							print ".if n .RS 4"
 | 
				
			||||||
 | 
							print ".nf"
 | 
				
			||||||
 | 
							print ".fam C"
 | 
				
			||||||
 | 
							do {
 | 
				
			||||||
 | 
								print escape(substr(firstline, RLENGTH + 1))
 | 
				
			||||||
 | 
								firstline = $0
 | 
				
			||||||
 | 
							} while ($0 && getline > 0)
 | 
				
			||||||
 | 
							print ".fam"
 | 
				
			||||||
 | 
							print ".fi"
 | 
				
			||||||
 | 
							print ".if n .RE"
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (match(firstline, /::$/)) {
 | 
				
			||||||
 | 
							inline(substr(firstline, 1, RSTART - 1))
 | 
				
			||||||
 | 
							while (match($0, /::$/)) {
 | 
				
			||||||
 | 
								print ".br"
 | 
				
			||||||
 | 
								inline(substr($0, 1, RSTART - 1))
 | 
				
			||||||
 | 
								if (getline <= 0)
 | 
				
			||||||
 | 
									fatal("unexpected EOF")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							print ".RS 4"
 | 
				
			||||||
 | 
							while ($0) {
 | 
				
			||||||
 | 
								sub(/^[[:space:]]+/, "")
 | 
				
			||||||
 | 
								sub(/^[+]$/, "")
 | 
				
			||||||
 | 
								if (!process($0) && getline <= 0)
 | 
				
			||||||
 | 
									fatal("unexpected EOF")
 | 
				
			||||||
 | 
								if (match($0, /::$/))
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							print ".RE"
 | 
				
			||||||
 | 
							print ".sp"
 | 
				
			||||||
 | 
							return !!$0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						inline(firstline)
 | 
				
			||||||
 | 
						return 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						while (process($0)) {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user