Compare commits

...

37 Commits

Author SHA1 Message Date
1a76b2032e Add a slogan of sorts 2020-08-01 14:03:23 +02:00
722ef65c1f Name change 2020-08-01 14:02:25 +02:00
317dfcb6e2 Improve setjmp safety in config parser 2020-04-19 07:02:13 +02:00
bca7167d03 Fix the SCGI parser and tests 2018-10-18 06:34:16 +02:00
3e4e4e5103 Allow aborting the FastCGI protocol parser 2018-10-18 04:08:47 +02:00
9494e8e2af Add some comments 2018-10-11 21:02:45 +02:00
8ffe20c0e8 Add missing include for "struct iovec" 2018-06-24 06:09:40 +02:00
bb30c7d86e Remove .travis.yml
We don't depend on any proprietary services no longer.  I'll have to
make my own replacements with blackjack and hookers.  Until then,
the file stays in the commit log as an example.
2018-06-21 23:58:24 +02:00
47ef2ae5bd Update README 2018-06-21 23:58:03 +02:00
69800a6afb Relicense to 0BSD, update mail address
I've come to the conclusion that copyright mostly just stands in the way
of software development.  In my jurisdiction I cannot give up my own
copyright and 0BSD seems to be the closest thing to public domain.

The updated mail address, also used in my author/committer lines,
is shorter and looks nicer.  People rarely interact anyway.
2018-06-21 23:57:25 +02:00
fe1035633a Describe syntax of advanced configuration w/ PEG 2018-04-19 00:09:46 +02:00
da75b6f735 siphash: silence fall-through warnings 2017-09-26 19:08:13 +02:00
199c56e141 Little improvements 2017-07-24 03:46:06 +02:00
6e9217e5d0 MPD client: +mpd_client_send_command_raw() 2017-06-26 03:35:05 +02:00
3835b6e499 Improve simple_config_update_from_file()
- considerably shorter
 - catch file read errors as we should
 - better error messages, now including the filename
 - disallow empty keys as they are never used
 - allow whitespace before start of comment

NUL characters stop processing now, though.  If anyone cares.
2017-06-22 20:42:44 +02:00
bf534010cb _init() -> _make() where possible 2017-06-22 20:42:44 +02:00
7b0d7a19e5 Cleanup 2017-06-14 23:28:44 +02:00
1dcd259d05 Make config_item_clone() static 2017-06-12 08:33:59 +02:00
03894cae45 Add VIM syntax highlight for "config" 2017-06-12 02:48:42 +02:00
412100289e Improve read_line()
One less useless boolean variable.
2017-06-12 02:48:42 +02:00
ec128558a4 MPD client: abort pending tasks 2017-06-04 04:27:10 +02:00
7f7606008d Update README 2017-06-04 00:49:15 +02:00
17322a3686 Make socket_io_try_*() actually use read/write
So that they can be used with pipes.
2017-05-07 09:24:03 +02:00
22edb6d489 Add a warning comment to "poller_fd::closed" 2017-05-06 21:15:03 +02:00
9866675bb7 Fix broken toupper_ascii()
Update copyright years.
2017-05-06 10:55:10 +02:00
e25a880883 Add packaging scripts for Meson
It probably doesn't belong here but I don't feel like creating another
repository for this either yet.
2017-04-30 10:45:23 +02:00
9afcb337ad Fix the WebSocket frame parser 2017-02-06 19:45:03 +01:00
daa900e12f Fix and update LICENSE 2017-02-03 23:03:32 +01:00
4a5929b4ef Travis CI: brevify notifications 2017-02-03 23:03:02 +01:00
084e964286 Fixes to the previous batch of commits 2017-01-23 23:14:04 +01:00
0e08055d6d Rename strv_add*() to strv_append*()
Consistency.
2017-01-23 23:07:24 +01:00
6642bdf9cd Rename str_ensure_space() to str_reserve()
Let's not invent our own terminology.
2017-01-23 23:05:42 +01:00
349a0fc3b1 join_strv() -> strv_join(), take a string argument 2017-01-23 23:03:46 +01:00
5552ce1dbe Rename "struct str_vector" to "struct strv"
Short names for things used often.
2017-01-23 23:01:20 +01:00
680980632d Add a library with TUI helpers 2017-01-23 22:55:46 +01:00
973a4b7656 Add ARRAY convenience macros
Because dynamically allocated arrays in C are a pain.
2017-01-23 22:47:39 +01:00
74b00a921a MPD client: fix resource leak 2017-01-18 16:34:25 +01:00
18 changed files with 901 additions and 564 deletions

View File

@@ -1,18 +0,0 @@
language: c
notifications:
irc:
channels: "irc.janouch.name#dev"
use_notice: true
skip_join: true
compiler:
- clang
- gcc
before_install:
- sudo apt-get update -qq
before_script:
- mkdir build
- cd build
script:
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr
- make
- ctest -V

View File

@@ -2,10 +2,11 @@ project (liberty C)
cmake_minimum_required (VERSION 2.8.5)
# Moar warnings
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# -Wunused-function is pretty annoying here, as everything is static
set (CMAKE_C_FLAGS "-std=c99 -Wall -Wextra -Wno-unused-function")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
set (wdisabled "-Wno-unused-function -Wno-implicit-fallthrough")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra ${wdisabled}")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# Dependencies
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
@@ -34,15 +35,8 @@ foreach (extra iconv rt)
endif (extra_lib_${extra})
endforeach (extra)
# Generate a configuration file
# TODO: actualy use the configuration file for something; so far we allow
# for direct inclusion without running this CMakeLists.txt
configure_file (${PROJECT_SOURCE_DIR}/liberty-config.h.in
${PROJECT_BINARY_DIR}/liberty-config.h)
include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})
set (common_sources ${PROJECT_BINARY_DIR}/liberty-config.h)
# Build some unit tests
include_directories (${PROJECT_SOURCE_DIR})
enable_testing ()
foreach (name liberty proto)
add_executable (test-${name} tests/${name}.c ${common_sources})

22
LICENSE
View File

@@ -1,14 +1,12 @@
Copyright (c) 2014 - 2016, Přemysl Janouch <p.janouch@gmail.com>
All rights reserved.
Copyright (c) 2014 - 2018, 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, provided that the above
copyright notice and this permission notice appear in all copies.
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.
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.

View File

@@ -19,16 +19,13 @@ I'm not testing them at all, with the exception of OpenBSD.
Contributing and Support
------------------------
Use this project's GitHub to report any bugs, request features, or submit pull
requests. If you want to discuss this project, or maybe just hang out with
the developer, feel free to join me at irc://irc.janouch.name, channel #dev.
Use https://git.janouch.name/p/liberty to report any bugs, request features,
or submit pull requests. `git send-email` is tolerated. If you want to discuss
the project, feel free to join me at ircs://irc.janouch.name, channel #dev.
Bitcoin donations are accepted at: 12r5uEWEgcHC46xd64tt3hHt9EUvYYDHe9
License
-------
'liberty' is written by Přemysl Janouch <p.janouch@gmail.com>.
You may use the software under the terms of the ISC license, the text of which
is included within the package, or, at your option, you may relicense the work
under the MIT or the Modified BSD License, as listed at the following site:
http://www.gnu.org/licenses/license-list.html
This software is released under the terms of the 0BSD license, the text of which
is included within the package along with the list of authors.

17
cmake/FindNcursesw.cmake Normal file
View File

@@ -0,0 +1,17 @@
# Public Domain
find_package (PkgConfig REQUIRED)
pkg_check_modules (NCURSESW QUIET ncursesw)
# OpenBSD doesn't provide a pkg-config file
set (required_vars NCURSESW_LIBRARIES)
if (NOT NCURSESW_FOUND)
find_library (NCURSESW_LIBRARIES NAMES ncursesw)
find_path (NCURSESW_INCLUDE_DIRS ncurses.h)
list (APPEND required_vars NCURSESW_INCLUDE_DIRS)
endif (NOT NCURSESW_FOUND)
include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (NCURSESW DEFAULT_MSG ${required_vars})
mark_as_advanced (NCURSESW_LIBRARIES NCURSESW_INCLUDE_DIRS)

10
cmake/FindUnistring.cmake Normal file
View File

@@ -0,0 +1,10 @@
# Public Domain
find_path (UNISTRING_INCLUDE_DIRS unistr.h)
find_library (UNISTRING_LIBRARIES NAMES unistring libunistring)
include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (UNISTRING DEFAULT_MSG
UNISTRING_INCLUDE_DIRS UNISTRING_LIBRARIES)
mark_as_advanced (UNISTRING_LIBRARIES UNISTRING_INCLUDE_DIRS)

View File

View File

@@ -1,12 +1,10 @@
/*
* liberty-proto.c: the ultimate C unlibrary: protocols
*
* Copyright (c) 2014 - 2016, Přemysl Janouch <p.janouch@gmail.com>
* All rights reserved.
* Copyright (c) 2014 - 2016, 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, provided that the above
* copyright notice and this permission notice appear in all copies.
* 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
@@ -29,15 +27,13 @@ struct irc_message
struct str_map tags; ///< IRC 3.2 message tags
char *prefix; ///< Message prefix
char *command; ///< IRC command
struct str_vector params; ///< Command parameters
struct strv params; ///< Command parameters
};
static char *
irc_unescape_message_tag (const char *value)
{
struct str s;
str_init (&s);
struct str s = str_make ();
bool escape = false;
for (const char *p = value; *p; p++)
{
@@ -64,8 +60,7 @@ irc_unescape_message_tag (const char *value)
static void
irc_parse_message_tags (const char *tags, struct str_map *out)
{
struct str_vector v;
str_vector_init (&v);
struct strv v = strv_make ();
cstr_split (tags, ";", true, &v);
for (size_t i = 0; i < v.len; i++)
@@ -79,19 +74,16 @@ irc_parse_message_tags (const char *tags, struct str_map *out)
else
str_map_set (out, key, xstrdup (""));
}
str_vector_free (&v);
strv_free (&v);
}
static void
irc_parse_message (struct irc_message *msg, const char *line)
{
str_map_init (&msg->tags);
msg->tags.free = free;
msg->tags = str_map_make (free);
msg->prefix = NULL;
msg->command = NULL;
str_vector_init (&msg->params);
msg->params = strv_make ();
// IRC 3.2 message tags
if (*line == '@')
@@ -132,7 +124,7 @@ irc_parse_message (struct irc_message *msg, const char *line)
if (*line == ':')
{
str_vector_add (&msg->params, ++line);
strv_append (&msg->params, ++line);
break;
}
@@ -140,7 +132,7 @@ irc_parse_message (struct irc_message *msg, const char *line)
if (!param_len)
break;
str_vector_add_owned (&msg->params, xstrndup (line, param_len));
strv_append_owned (&msg->params, xstrndup (line, param_len));
line += param_len;
}
}
@@ -151,7 +143,7 @@ irc_free_message (struct irc_message *msg)
str_map_free (&msg->tags);
free (msg->prefix);
free (msg->command);
str_vector_free (&msg->params);
strv_free (&msg->params);
}
static void
@@ -221,6 +213,7 @@ irc_fnmatch (const char *pattern, const char *string)
char x_pattern[pattern_size], x_string[string_size];
irc_strxfrm (x_pattern, pattern, pattern_size);
irc_strxfrm (x_string, string, string_size);
// FIXME: this supports [], which is not mentioned in RFC 2812
return fnmatch (x_pattern, x_string, 0);
}
@@ -289,14 +282,15 @@ struct http_tokenizer
struct str string; ///< "token" / "quoted-string" content
};
static void
http_tokenizer_init (struct http_tokenizer *self, const char *input, size_t len)
static struct http_tokenizer
http_tokenizer_make (const char *input, size_t len)
{
memset (self, 0, sizeof *self);
self->input = (const unsigned char *) input;
self->input_len = len;
str_init (&self->string);
return (struct http_tokenizer)
{
.input = (const unsigned char *) input,
.input_len = len,
.string = str_make (),
};
}
static void
@@ -429,8 +423,8 @@ http_parse_media_type (const char *media_type,
char **type, char **subtype, struct str_map *parameters)
{
bool result = false;
struct http_tokenizer t;
http_tokenizer_init (&t, media_type, strlen (media_type));
struct http_tokenizer t =
http_tokenizer_make (media_type, strlen (media_type));
if (http_tokenizer_next (&t, true) != HTTP_T_TOKEN)
goto end;
@@ -491,8 +485,7 @@ http_parse_upgrade (const char *upgrade, struct http_protocol **out)
struct http_protocol *list = NULL;
struct http_protocol *tail = NULL;
struct http_tokenizer t;
http_tokenizer_init (&t, upgrade, strlen (upgrade));
struct http_tokenizer t = http_tokenizer_make (upgrade, strlen (upgrade));
enum {
STATE_PROTOCOL_NAME,
@@ -619,16 +612,16 @@ struct scgi_parser
void *user_data; ///< User data passed to callbacks
};
static void
scgi_parser_init (struct scgi_parser *self)
static struct scgi_parser
scgi_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
str_map_init (&self->headers);
self->headers.free = free;
str_init (&self->name);
str_init (&self->value);
return (struct scgi_parser)
{
.input = str_make (),
.headers = str_map_make (free),
.name = str_make (),
.value = str_make (),
};
}
static void
@@ -671,10 +664,11 @@ scgi_parser_push (struct scgi_parser *self,
if (digit == ':')
{
self->state = SCGI_READING_NAME;
str_remove_slice (&self->input, 0, 1);
break;
}
if (digit < '0' || digit >= '9')
if (digit < '0' || digit > '9')
return error_set (e, "invalid header netstring");
size_t new_len = self->headers_len * 10 + (digit - '0');
@@ -707,6 +701,7 @@ scgi_parser_push (struct scgi_parser *self,
self->state = SCGI_READING_VALUE;
str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break;
}
case SCGI_READING_VALUE:
@@ -729,12 +724,13 @@ scgi_parser_push (struct scgi_parser *self,
self->name.str, str_steal (&self->value));
str_reset (&self->name);
str_init (&self->value);
self->value = str_make ();
self->state = SCGI_READING_NAME;
}
str_remove_slice (&self->input, 0, 1);
self->headers_len--;
break;
}
case SCGI_READING_CONTENT:
@@ -799,7 +795,8 @@ enum fcgi_protocol_status
struct fcgi_parser;
typedef void (*fcgi_message_fn)
/// Message handler, returns false if further processing should be stopped
typedef bool (*fcgi_message_fn)
(const struct fcgi_parser *parser, void *user_data);
enum fcgi_parser_state
@@ -828,12 +825,11 @@ struct fcgi_parser
void *user_data; ///< User data
};
static void
fcgi_parser_init (struct fcgi_parser *self)
static struct fcgi_parser
fcgi_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
str_init (&self->content);
return (struct fcgi_parser)
{ .input = str_make (), .content = str_make () };
}
static void
@@ -846,8 +842,8 @@ fcgi_parser_free (struct fcgi_parser *self)
static void
fcgi_parser_unpack_header (struct fcgi_parser *self)
{
struct msg_unpacker unpacker;
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
struct msg_unpacker unpacker =
msg_unpacker_make (self->input.str, self->input.len);
bool success = true;
uint8_t reserved;
@@ -862,7 +858,7 @@ fcgi_parser_unpack_header (struct fcgi_parser *self)
str_remove_slice (&self->input, 0, unpacker.offset);
}
static void
static bool
fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{
// This could be made considerably faster for high-throughput applications
@@ -874,14 +870,14 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
{
case FCGI_READING_HEADER:
if (self->input.len < FCGI_HEADER_LEN)
return;
return true;
fcgi_parser_unpack_header (self);
self->state = FCGI_READING_CONTENT;
break;
case FCGI_READING_CONTENT:
if (self->input.len < self->content_length)
return;
return true;
// Move an appropriate part of the input buffer to the content buffer
str_reset (&self->content);
@@ -891,10 +887,11 @@ fcgi_parser_push (struct fcgi_parser *self, const void *data, size_t len)
break;
case FCGI_READING_PADDING:
if (self->input.len < self->padding_length)
return;
return true;
// Call the callback to further process the message
self->on_message (self, self->user_data);
if (!self->on_message (self, self->user_data))
return false;
// Remove the padding from the input buffer
str_remove_slice (&self->input, 0, self->padding_length);
@@ -929,11 +926,10 @@ struct fcgi_nv_parser
char *value; ///< The current value, 0-terminated
};
static void
fcgi_nv_parser_init (struct fcgi_nv_parser *self)
static struct fcgi_nv_parser
fcgi_nv_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
return (struct fcgi_nv_parser) { .input = str_make () };
}
static void
@@ -952,8 +948,8 @@ fcgi_nv_parser_push (struct fcgi_nv_parser *self, const void *data, size_t len)
while (true)
{
struct msg_unpacker unpacker;
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
struct msg_unpacker unpacker =
msg_unpacker_make (self->input.str, self->input.len);
switch (self->state)
{
@@ -1050,8 +1046,7 @@ fcgi_nv_convert_len (size_t len, struct str *output)
static void
fcgi_nv_convert (struct str_map *map, struct str *output)
{
struct str_map_iter iter;
str_map_iter_init (&iter, map);
struct str_map_iter iter = str_map_iter_make (map);
while (str_map_iter_next (&iter))
{
const char *name = iter.link->key;
@@ -1090,8 +1085,7 @@ ws_encode_response_key (const char *key)
SHA1 ((unsigned char *) response_key, strlen (response_key), hash);
free (response_key);
struct str base64;
str_init (&base64);
struct str base64 = str_make ();
base64_encode (hash, sizeof hash, &base64);
return str_steal (&base64);
}
@@ -1169,11 +1163,10 @@ struct ws_parser
void *user_data; ///< User data for callbacks
};
static void
ws_parser_init (struct ws_parser *self)
static struct ws_parser
ws_parser_make (void)
{
memset (self, 0, sizeof *self);
str_init (&self->input);
return (struct ws_parser) { .input = str_make () };
}
static void
@@ -1201,8 +1194,10 @@ ws_parser_unmask (char *payload, uint64_t len, uint32_t mask)
{
case 3:
payload[end + 2] ^= (mask >> 8) & 0xFF;
// Fall-through
case 2:
payload[end + 1] ^= (mask >> 16) & 0xFF;
// Fall-through
case 1:
payload[end ] ^= (mask >> 24) & 0xFF;
}
@@ -1214,8 +1209,8 @@ ws_parser_push (struct ws_parser *self, const void *data, size_t len)
bool success = false;
str_append_data (&self->input, data, len);
struct msg_unpacker unpacker;
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
struct msg_unpacker unpacker =
msg_unpacker_make (self->input.str, self->input.len);
while (true)
switch (self->state)
@@ -1276,10 +1271,7 @@ ws_parser_push (struct ws_parser *self, const void *data, size_t len)
case WS_PARSER_PAYLOAD:
// Move the buffer so that payload data is at the front
str_remove_slice (&self->input, 0, unpacker.offset);
// And continue unpacking frames past the payload
msg_unpacker_init (&unpacker, self->input.str, self->input.len);
unpacker.offset = self->payload_len;
unpacker = msg_unpacker_make (self->input.str, self->input.len);
if (self->input.len < self->payload_len)
goto need_data;
@@ -1288,6 +1280,8 @@ ws_parser_push (struct ws_parser *self, const void *data, size_t len)
if (!self->on_frame (self->user_data, self))
goto fail;
// And continue unpacking frames past the payload
unpacker.offset = self->payload_len;
self->state = WS_PARSER_FIXED;
break;
}
@@ -1358,9 +1352,9 @@ struct mpd_response
char *message_text; ///< Error message
};
/// Task completion callback
/// Task completion callback; on connection abortion most fields are 0
typedef void (*mpd_client_task_cb) (const struct mpd_response *response,
const struct str_vector *data, void *user_data);
const struct strv *data, void *user_data);
struct mpd_client_task
{
@@ -1396,7 +1390,7 @@ struct mpd_client
struct mpd_client_task *tasks; ///< Task queue
struct mpd_client_task *tasks_tail; ///< Tail of task queue
struct str_vector data; ///< Data from last command
struct strv data; ///< Data from last command
// User configuration:
@@ -1420,21 +1414,19 @@ struct mpd_client
static void mpd_client_reset (struct mpd_client *self);
static void mpd_client_destroy_connector (struct mpd_client *self);
static void
mpd_client_init (struct mpd_client *self, struct poller *poller)
static struct mpd_client
mpd_client_make (struct poller *poller)
{
memset (self, 0, sizeof *self);
self->poller = poller;
self->socket = -1;
str_init (&self->read_buffer);
str_init (&self->write_buffer);
str_vector_init (&self->data);
poller_fd_init (&self->socket_event, poller, -1);
poller_timer_init (&self->timeout_timer, poller);
return (struct mpd_client)
{
.poller = poller,
.socket = -1,
.read_buffer = str_make (),
.write_buffer = str_make (),
.data = strv_make (),
.socket_event = poller_fd_make (poller, -1),
.timeout_timer = poller_timer_make (poller),
};
}
static void
@@ -1446,15 +1438,36 @@ mpd_client_free (struct mpd_client *self)
str_free (&self->read_buffer);
str_free (&self->write_buffer);
str_vector_free (&self->data);
strv_free (&self->data);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
mpd_client_dispatch (struct mpd_client *self, struct mpd_response *response)
{
struct mpd_client_task *task;
if (!(task = self->tasks))
return;
if (task->callback)
task->callback (response, &self->data, task->user_data);
strv_reset (&self->data);
LIST_UNLINK_WITH_TAIL (self->tasks, self->tasks_tail, task);
free (task);
}
/// Reinitialize the interface so that you can reconnect anew
static void
mpd_client_reset (struct mpd_client *self)
{
// Get rid of all pending tasks to release resources etc.
strv_reset (&self->data);
struct mpd_response aborted = { .message_text = "Disconnected" };
while (self->tasks)
mpd_client_dispatch (self, &aborted);
if (self->state == MPD_CONNECTING)
mpd_client_destroy_connector (self);
@@ -1462,6 +1475,7 @@ mpd_client_reset (struct mpd_client *self)
xclose (self->socket);
self->socket = -1;
// FIXME: this is not robust wrt. forking
self->socket_event.closed = true;
poller_fd_reset (&self->socket_event);
poller_timer_reset (&self->timeout_timer);
@@ -1469,17 +1483,11 @@ mpd_client_reset (struct mpd_client *self)
str_reset (&self->read_buffer);
str_reset (&self->write_buffer);
str_vector_reset (&self->data);
self->got_hello = false;
self->idling = false;
self->idling_subsystems = 0;
self->in_list = false;
LIST_FOR_EACH (struct mpd_client_task, iter, self->tasks)
free (iter);
self->tasks = self->tasks_tail = NULL;
self->state = MPD_DISCONNECTED;
}
@@ -1530,21 +1538,6 @@ mpd_client_parse_response (const char *p, struct mpd_response *response)
return true;
}
static void
mpd_client_dispatch (struct mpd_client *self, struct mpd_response *response)
{
struct mpd_client_task *task;
if (!(task = self->tasks))
return;
if (task->callback)
task->callback (response, &self->data, task->user_data);
str_vector_reset (&self->data);
LIST_UNLINK_WITH_TAIL (self->tasks, self->tasks_tail, task);
free (task);
}
static bool
mpd_client_parse_hello (struct mpd_client *self, const char *line)
{
@@ -1572,7 +1565,7 @@ mpd_client_parse_line (struct mpd_client *self, const char *line)
struct mpd_response response;
memset (&response, 0, sizeof response);
if (!strcmp (line, "list_OK"))
str_vector_add_owned (&self->data, NULL);
strv_append_owned (&self->data, NULL);
else if (mpd_client_parse_response (line, &response))
{
mpd_client_dispatch (self, &response);
@@ -1580,7 +1573,7 @@ mpd_client_parse_line (struct mpd_client *self, const char *line)
free (response.message_text);
}
else
str_vector_add (&self->data, line);
strv_append (&self->data, line);
return true;
}
@@ -1695,8 +1688,9 @@ mpd_client_add_task
static void mpd_client_send_command
(struct mpd_client *self, const char *command, ...) ATTRIBUTE_SENTINEL;
/// Avoid calling this method directly if you don't want things to explode
static void
mpd_client_send_commandv (struct mpd_client *self, char **commands)
mpd_client_send_command_raw (struct mpd_client *self, const char *raw)
{
// Automatically interrupt idle mode
if (self->idling)
@@ -1708,48 +1702,52 @@ mpd_client_send_commandv (struct mpd_client *self, char **commands)
mpd_client_send_command (self, "noidle", NULL);
}
struct str line;
str_init (&line);
for (; *commands; commands++)
{
if (line.len)
str_append_c (&line, ' ');
if (mpd_client_must_quote (*commands))
mpd_client_quote (*commands, &line);
else
str_append (&line, *commands);
}
if (self->on_io_hook)
self->on_io_hook (self->user_data, true, line.str);
self->on_io_hook (self->user_data, true, raw);
str_append_c (&line, '\n');
str_append_str (&self->write_buffer, &line);
str_free (&line);
str_append (&self->write_buffer, raw);
str_append_c (&self->write_buffer, '\n');
mpd_client_update_poller (self);
}
static void
mpd_client_send_commandv (struct mpd_client *self, char **fields)
{
struct str line = str_make ();
for (; *fields; fields++)
{
if (line.len)
str_append_c (&line, ' ');
if (mpd_client_must_quote (*fields))
mpd_client_quote (*fields, &line);
else
str_append (&line, *fields);
}
mpd_client_send_command_raw (self, line.str);
str_free (&line);
}
static void
mpd_client_send_command (struct mpd_client *self, const char *command, ...)
{
struct str_vector v;
str_vector_init (&v);
struct strv v = strv_make ();
va_list ap;
va_start (ap, command);
for (; command; command = va_arg (ap, const char *))
str_vector_add (&v, command);
strv_append (&v, command);
va_end (ap);
mpd_client_send_commandv (self, v.vector);
str_vector_free (&v);
strv_free (&v);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// "On success for all commands, OK is returned. If a command fails, no more
/// commands are executed and the appropriate ACK error is returned"
static void
mpd_client_list_begin (struct mpd_client *self)
{
@@ -1793,7 +1791,7 @@ mpd_resolve_subsystem (const char *name, unsigned *output)
static void
mpd_client_on_idle_return (const struct mpd_response *response,
const struct str_vector *data, void *user_data)
const struct strv *data, void *user_data)
{
(void) response;
@@ -1836,16 +1834,14 @@ mpd_client_idle (struct mpd_client *self, unsigned subsystems)
{
hard_assert (!self->in_list);
struct str_vector v;
str_vector_init (&v);
str_vector_add (&v, "idle");
struct strv v = strv_make ();
strv_append (&v, "idle");
for (size_t i = 0; i < N_ELEMENTS (mpd_subsystem_names); i++)
if (subsystems & (1 << i))
str_vector_add (&v, mpd_subsystem_names[i]);
strv_append (&v, mpd_subsystem_names[i]);
mpd_client_send_commandv (self, v.vector);
str_vector_free (&v);
strv_free (&v);
self->timeout_timer.dispatcher = mpd_client_on_timeout;
self->timeout_timer.user_data = self;
@@ -1865,7 +1861,7 @@ mpd_client_finish_connection (struct mpd_client *self, int socket)
self->socket = socket;
self->state = MPD_CONNECTED;
poller_fd_init (&self->socket_event, self->poller, self->socket);
self->socket_event = poller_fd_make (self->poller, self->socket);
self->socket_event.dispatcher = mpd_client_on_ready;
self->socket_event.user_data = self;
@@ -1925,7 +1921,11 @@ mpd_client_connect_unix (struct mpd_client *self, const char *address,
free (expanded);
if (connect (fd, (struct sockaddr *) &sun, sizeof sun))
return error_set (e, "%s: %s", "connect", strerror (errno));
{
error_set (e, "%s: %s", "connect", strerror (errno));
xclose (fd);
return false;
}
mpd_client_finish_connection (self, fd);
return true;

270
liberty-tui.c Normal file
View File

@@ -0,0 +1,270 @@
/*
* liberty-tui.c: the ultimate C unlibrary: TUI
*
* Copyright (c) 2016 - 2017, 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.
*
*/
// This file includes some common stuff to build TUI applications with
#include <ncurses.h>
// It is surprisingly hard to find a good library to handle Unicode shenanigans,
// and there's enough of those for it to be impractical to reimplement them.
//
// GLib ICU libunistring utf8proc
// Decently sized . . x x
// Grapheme breaks . x . x
// Character width x . x x
// Locale handling . . x .
// Liberal license . x . x
//
// Also note that the ICU API is icky and uses UTF-16 for its primary encoding.
//
// Currently we're chugging along with libunistring but utf8proc seems viable.
// Non-Unicode locales can mostly be handled with simple iconv like in sdtui.
// Similarly grapheme breaks can be guessed at using character width (a basic
// test here is Zalgo text).
//
// None of this is ever going to work too reliably anyway because terminals
// and Unicode don't go awfully well together. In particular, character cell
// devices have some problems with double-wide characters.
#include <unistr.h>
#include <uniwidth.h>
#include <uniconv.h>
#include <unicase.h>
// --- Configurable display attributes -----------------------------------------
struct attrs
{
short fg; ///< Foreground colour index
short bg; ///< Background colour index
chtype attrs; ///< Other attributes
};
/// Decode attributes in the value using a subset of the git config format,
/// ignoring all errors since it doesn't affect functionality
static struct attrs
attrs_decode (const char *value)
{
struct strv v = strv_make ();
cstr_split (value, " ", true, &v);
int colors = 0;
struct attrs attrs = { -1, -1, 0 };
for (char **it = v.vector; *it; it++)
{
char *end = NULL;
long n = strtol (*it, &end, 10);
if (*it != end && !*end && n >= SHRT_MIN && n <= SHRT_MAX)
{
if (colors == 0) attrs.fg = n;
if (colors == 1) attrs.bg = n;
colors++;
}
else if (!strcmp (*it, "bold")) attrs.attrs |= A_BOLD;
else if (!strcmp (*it, "dim")) attrs.attrs |= A_DIM;
else if (!strcmp (*it, "ul")) attrs.attrs |= A_UNDERLINE;
else if (!strcmp (*it, "blink")) attrs.attrs |= A_BLINK;
else if (!strcmp (*it, "reverse")) attrs.attrs |= A_REVERSE;
#ifdef A_ITALIC
else if (!strcmp (*it, "italic")) attrs.attrs |= A_ITALIC;
#endif // A_ITALIC
}
strv_free (&v);
return attrs;
}
// --- Terminal output ---------------------------------------------------------
// Necessary abstraction to simplify aligned, formatted character output
// This callback you need to implement in the application
static bool app_is_character_in_locale (ucs4_t ch);
struct row_char
{
ucs4_t c; ///< Unicode codepoint
chtype attrs; ///< Special attributes
int width; ///< How many cells this takes
};
struct row_buffer
{
ARRAY (struct row_char, chars) ///< Characters
int total_width; ///< Total width of all characters
};
static struct row_buffer
row_buffer_make (void)
{
struct row_buffer self = {};
ARRAY_INIT_SIZED (self.chars, 256);
return self;
}
static void
row_buffer_free (struct row_buffer *self)
{
free (self->chars);
}
/// Replace invalid chars and push all codepoints to the array w/ attributes.
static void
row_buffer_append (struct row_buffer *self, const char *str, chtype attrs)
{
// The encoding is only really used internally for some corner cases
const char *encoding = locale_charset ();
// Note that this function is a hotspot, try to keep it decently fast
struct row_char current = { .attrs = attrs };
struct row_char invalid = { .attrs = attrs, .c = '?', .width = 1 };
const uint8_t *next = (const uint8_t *) str;
while ((next = u8_next (&current.c, next)))
{
current.width = uc_width (current.c, encoding);
if (current.width < 0 || !app_is_character_in_locale (current.c))
current = invalid;
ARRAY_RESERVE (self->chars, 1);
self->chars[self->chars_len++] = current;
self->total_width += current.width;
}
}
static void
row_buffer_append_args (struct row_buffer *self, const char *s, ...)
ATTRIBUTE_SENTINEL;
static void
row_buffer_append_args (struct row_buffer *self, const char *s, ...)
{
va_list ap;
va_start (ap, s);
while (s)
{
row_buffer_append (self, s, va_arg (ap, chtype));
s = va_arg (ap, const char *);
}
va_end (ap);
}
static void
row_buffer_append_buffer (struct row_buffer *self, const struct row_buffer *rb)
{
ARRAY_RESERVE (self->chars, rb->chars_len);
memcpy (self->chars + self->chars_len, rb->chars,
rb->chars_len * sizeof *rb->chars);
self->chars_len += rb->chars_len;
self->total_width += rb->total_width;
}
/// Pop as many codepoints as needed to free up "space" character cells.
/// Given the suffix nature of combining marks, this should work pretty fine.
static int
row_buffer_pop_cells (struct row_buffer *self, int space)
{
int made = 0;
while (self->chars_len && made < space)
made += self->chars[--self->chars_len].width;
self->total_width -= made;
return made;
}
static void
row_buffer_space (struct row_buffer *self, int width, chtype attrs)
{
if (width < 0)
return;
ARRAY_RESERVE (self->chars, (size_t) width);
struct row_char space = { .attrs = attrs, .c = ' ', .width = 1 };
self->total_width += width;
while (width-- > 0)
self->chars[self->chars_len++] = space;
}
static void
row_buffer_ellipsis (struct row_buffer *self, int target)
{
if (self->total_width <= target
|| !row_buffer_pop_cells (self, self->total_width - target))
return;
// We use attributes from the last character we've removed,
// assuming that we don't shrink the array (and there's no real need)
ucs4_t ellipsis = 0x2026; // …
if (app_is_character_in_locale (ellipsis))
{
if (self->total_width >= target)
row_buffer_pop_cells (self, 1);
if (self->total_width + 1 <= target)
row_buffer_append (self, "", self->chars[self->chars_len].attrs);
}
else if (target >= 3)
{
if (self->total_width >= target)
row_buffer_pop_cells (self, 3);
if (self->total_width + 3 <= target)
row_buffer_append (self, "...", self->chars[self->chars_len].attrs);
}
}
static void
row_buffer_align (struct row_buffer *self, int target, chtype attrs)
{
row_buffer_ellipsis (self, target);
row_buffer_space (self, target - self->total_width, attrs);
}
static void
row_buffer_print (uint32_t *ucs4, chtype attrs)
{
// This assumes that we can reset the attribute set without consequences
char *str = u32_strconv_to_locale (ucs4);
if (str)
{
attrset (attrs);
addstr (str);
attrset (0);
free (str);
}
}
static void
row_buffer_flush (struct row_buffer *self)
{
if (!self->chars_len)
return;
// We only NUL-terminate the chunks because of the libunistring API
uint32_t chunk[self->chars_len + 1], *insertion_point = chunk;
for (size_t i = 0; i < self->chars_len; i++)
{
struct row_char *iter = self->chars + i;
if (i && iter[0].attrs != iter[-1].attrs)
{
row_buffer_print (chunk, iter[-1].attrs);
insertion_point = chunk;
}
*insertion_point++ = iter->c;
*insertion_point = 0;
}
row_buffer_print (chunk, self->chars[self->chars_len - 1].attrs);
}

575
liberty.c

File diff suppressed because it is too large Load Diff

26
libertyconf.vim Normal file
View File

@@ -0,0 +1,26 @@
" Since the liberty configuration format is nearly indistinguishable,
" this syntax highlight definition needs to be loaded with `set ft=libertyconf`
if exists("b:current_syntax")
finish
endif
syn match libertyconfError "[^_[:alnum:][:space:]]\+"
syn match libertyconfComment "#.*"
syn match libertyconfSpecial "{\|}\|="
syn match libertyconfNumber "[+-]\=\<\d\+\>"
syn match libertyconfBoolean "\c\<\(true\|yes\|on\|false\|no\|off\)\>"
syn match libertyconfNull "null"
syn match libertyconfEscape display "\\\([xX]\x\{1,2}\|\o\{1,3}\|.\|$\)"
\ contained
syn region libertyconfString start=+"+ skip=+\\\\\|\\"+ end=+"+
\ contains=libertyconfEscape
let b:current_syntax = "libertyconf"
hi def link libertyconfError Error
hi def link libertyconfComment Comment
hi def link libertyconfSpecial Special
hi def link libertyconfNumber Number
hi def link libertyconfBoolean Boolean
hi def link libertyconfNull Constant
hi def link libertyconfEscape SpecialChar
hi def link libertyconfString String

28
meson/packaging/make-deb.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/sh -e
cd "$MESON_BUILD_ROOT"
. "$MESON_SUBDIR/meta"
wd="`pwd`/`mktemp -d deb.XXXXXX`"
trap "rm -rf '$wd'" INT QUIT TERM EXIT
[ "$arch" = x86 ] && arch=i386
[ "$arch" = x86_64 ] && arch=amd64
target="$name-$version-$system-$arch.deb"
echo 2.0 > "$wd/debian-binary"
cat > "$wd/control" <<-EOF
Package: $name
Version: $version
Section: misc
Priority: optional
Architecture: $arch
Maintainer: $author
Description: $summary
EOF
fakeroot sh -e <<-EOF
DESTDIR="$wd/pkg" ninja install
cd "$wd/pkg" && tar cJf ../data.tar.xz .
EOF
(cd "$wd" && tar czf control.tar.gz ./control)
ar rc "$target" "$wd/debian-binary" "$wd/control.tar.gz" "$wd/data.tar.xz"
echo Written $target

22
meson/packaging/make-pacman.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh -e
cd "$MESON_BUILD_ROOT"
. "$MESON_SUBDIR/meta"
wd="`pwd`/`mktemp -d pacman.XXXXXX`"
trap "rm -rf '$wd'" INT QUIT TERM EXIT
target="$name-$version-$arch.tar.xz"
fakeroot sh -e <<-EOF
DESTDIR="$wd" ninja install
cat > "$wd/.PKGINFO" <<END
pkgname = $name
pkgver = $version-1
pkgdesc = $summary
url = $url
builddate = \`date -u +%s\`
packager = $author
size = \`du -sb | cut -f1\`
arch = $arch
END
cd "$wd" && tar cJf "../$target" .PKGINFO *
echo Written $target
EOF

View File

@@ -0,0 +1,11 @@
# You need to prepare a configuration object with the required metadata
packaging.set ('arch', target_machine.cpu_family ())
packaging.set ('system', target_machine.system ())
configure_file (input: 'meta.in', output: 'meta', configuration: packaging)
# RPM is awful and I've given up on both manual generation (we'd have to either
# include rpmrc data or generate fake noarch packages) and rpmbuild (just no)
run_target ('deb',
command: [join_paths (meson.current_source_dir (), 'make-deb.sh')])
run_target ('pacman',
command: [join_paths (meson.current_source_dir (), 'make-pacman.sh')])

8
meson/packaging/meta.in Normal file
View File

@@ -0,0 +1,8 @@
define() { [ -z "$2" ] && { echo $1 is undefined; exit 1; } || eval "$1='$2'"; }
define name "@name@"
define version "@version@"
define summary "@summary@"
define author "@author@"
define arch "@arch@"
define system "@system@"

View File

@@ -61,13 +61,13 @@ siphash (const unsigned char key[16], const unsigned char *m, size_t len)
switch (len - blocks)
{
case 7: last7 |= (uint64_t) m[i + 6] << 48;
case 6: last7 |= (uint64_t) m[i + 5] << 40;
case 5: last7 |= (uint64_t) m[i + 4] << 32;
case 4: last7 |= (uint64_t) m[i + 3] << 24;
case 3: last7 |= (uint64_t) m[i + 2] << 16;
case 2: last7 |= (uint64_t) m[i + 1] << 8;
case 1: last7 |= (uint64_t) m[i + 0] ;
case 7: last7 |= (uint64_t) m[i + 6] << 48; // Fall-through
case 6: last7 |= (uint64_t) m[i + 5] << 40; // Fall-through
case 5: last7 |= (uint64_t) m[i + 4] << 32; // Fall-through
case 4: last7 |= (uint64_t) m[i + 3] << 24; // Fall-through
case 3: last7 |= (uint64_t) m[i + 2] << 16; // Fall-through
case 2: last7 |= (uint64_t) m[i + 1] << 8; // Fall-through
case 1: last7 |= (uint64_t) m[i + 0] ; // Fall-through
default:;
};
v3 ^= last7;

View File

@@ -1,12 +1,10 @@
/*
* tests/liberty.c
*
* Copyright (c) 2015 - 2016, Přemysl Janouch <p.janouch@gmail.com>
* All rights reserved.
* Copyright (c) 2015 - 2016, 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, provided that the above
* copyright notice and this permission notice appear in all copies.
* 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
@@ -157,31 +155,28 @@ test_list_with_tail (void)
// --- Strings -----------------------------------------------------------------
static void
test_str_vector (void)
test_strv (void)
{
struct str_vector v;
str_vector_init (&v);
str_vector_add_owned (&v, xstrdup ("xkcd"));
str_vector_reset (&v);
struct strv v = strv_make ();
strv_append_owned (&v, xstrdup ("xkcd"));
strv_reset (&v);
const char *a[] =
{ "123", "456", "a", "bc", "def", "ghij", "klmno", "pqrstu" };
// Add the first two items via another vector
struct str_vector w;
str_vector_init (&w);
str_vector_add_args (&w, a[0], a[1], NULL);
str_vector_add_vector (&v, w.vector);
str_vector_free (&w);
struct strv w = strv_make ();
strv_append_args (&w, a[0], a[1], NULL);
strv_append_vector (&v, w.vector);
strv_free (&w);
// Add an item and delete it right after
str_vector_add (&v, "test");
str_vector_remove (&v, v.len - 1);
strv_append (&v, "test");
strv_remove (&v, v.len - 1);
// Add the rest of the list properly
for (int i = 2; i < (int) N_ELEMENTS (a); i++)
str_vector_add (&v, a[i]);
strv_append (&v, a[i]);
// Check the contents
soft_assert (v.len == N_ELEMENTS (a));
@@ -189,7 +184,7 @@ test_str_vector (void)
soft_assert (!strcmp (v.vector[i], a[i]));
soft_assert (v.vector[v.len] == NULL);
str_vector_free (&v);
strv_free (&v);
}
static void
@@ -197,15 +192,13 @@ test_str (void)
{
uint8_t x[] = { 0x12, 0x34, 0x56, 0x78, 0x11, 0x22, 0x33, 0x44 };
struct str s;
str_init (&s);
str_ensure_space (&s, MEGA);
struct str s = str_make ();
str_reserve (&s, MEGA);
str_append_data (&s, x, sizeof x);
str_remove_slice (&s, 4, 4);
soft_assert (s.len == 4);
struct str t;
str_init (&t);
struct str t = str_make ();
str_append_str (&t, &s);
str_append (&t, "abc");
str_append_c (&t, 'd');
@@ -266,10 +259,8 @@ static void
test_str_map (void)
{
// Put two reference counted objects in the map under case-insensitive keys
struct str_map m;
str_map_init (&m);
struct str_map m = str_map_make (free_counter);
m.key_xfrm = tolower_ascii_strxfrm;
m.free = free_counter;
int *a = make_counter ();
int *b = make_counter ();
@@ -283,8 +274,7 @@ test_str_map (void)
soft_assert (str_map_find (&m, "DEFghi") == b);
// Check that we can iterate over both of them
struct str_map_iter iter;
str_map_iter_init (&iter, &m);
struct str_map_iter iter = str_map_iter_make (&m);
bool met_a = false;
bool met_b = false;
@@ -311,8 +301,7 @@ test_str_map (void)
free_counter (b);
// Iterator test with a high number of items
str_map_init (&m);
m.free = free;
m = str_map_make (free);
for (size_t i = 0; i < 100 * 100; i++)
{
@@ -320,8 +309,7 @@ test_str_map (void)
str_map_set (&m, x, x);
}
struct str_map_unset_iter unset_iter;
str_map_unset_iter_init (&unset_iter, &m);
struct str_map_unset_iter unset_iter = str_map_unset_iter_make (&m);
while ((str_map_unset_iter_next (&unset_iter)))
{
unsigned long x;
@@ -343,9 +331,7 @@ test_utf8 (void)
soft_assert ( utf8_validate (valid, sizeof valid));
soft_assert (!utf8_validate (invalid, sizeof invalid));
struct utf8_iter iter;
utf8_iter_init (&iter, "fóọ");
struct utf8_iter iter = utf8_iter_make ("fóọ");
size_t ch_len;
hard_assert (utf8_iter_next (&iter, &ch_len) == 'f' && ch_len == 1);
hard_assert (utf8_iter_next (&iter, &ch_len) == 0x00F3 && ch_len == 2);
@@ -359,8 +345,8 @@ test_base64 (void)
for (size_t i = 0; i < N_ELEMENTS (data); i++)
data[i] = i;
struct str encoded; str_init (&encoded);
struct str decoded; str_init (&decoded);
struct str encoded = str_make ();
struct str decoded = str_make ();
base64_encode (data, sizeof data, &encoded);
soft_assert (base64_decode (encoded.str, false, &decoded));
@@ -431,9 +417,9 @@ test_async (void)
{
struct test_async_data data;
memset (&data, 0, sizeof data);
async_manager_init (&data.manager);
data.manager = async_manager_make ();
async_init (&data.busyloop, &data.manager);
data.busyloop = async_make (&data.manager);
data.busyloop.execute = on_busyloop_execute;
data.busyloop.destroy = on_busyloop_destroy;
async_run (&data.busyloop);
@@ -543,7 +529,7 @@ test_connector_fixture_init
// Make it so that we immediately accept all connections
poller_init (&self->poller);
poller_fd_init (&self->listening_event, &self->poller, self->listening_fd);
self->listening_event = poller_fd_make (&self->poller, self->listening_fd);
self->listening_event.dispatcher = test_connector_on_client;
self->listening_event.user_data = (poller_fd_fn) self;
poller_fd_set (&self->listening_event, POLLIN);
@@ -629,7 +615,7 @@ main (int argc, char *argv[])
test_add_simple (&test, "/memory", NULL, test_memory);
test_add_simple (&test, "/list", NULL, test_list);
test_add_simple (&test, "/list-with-tail", NULL, test_list_with_tail);
test_add_simple (&test, "/str-vector", NULL, test_str_vector);
test_add_simple (&test, "/strv", NULL, test_strv);
test_add_simple (&test, "/str", NULL, test_str);
test_add_simple (&test, "/error", NULL, test_error);
test_add_simple (&test, "/str-map", NULL, test_str_map);

View File

@@ -1,12 +1,10 @@
/*
* tests/proto.c
*
* Copyright (c) 2015, Přemysl Janouch <p.janouch@gmail.com>
* All rights reserved.
* Copyright (c) 2015, 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, provided that the above
* copyright notice and this permission notice appear in all copies.
* 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
@@ -44,8 +42,7 @@ test_irc (void)
irc_parse_message (&msg, "@first=a\\:\\s\\r\\n\\\\;2nd "
":srv hi there :good m8 :how are you?");
struct str_map_iter iter;
str_map_iter_init (&iter, &msg.tags);
struct str_map_iter iter = str_map_iter_make (&msg.tags);
soft_assert (msg.tags.len == 2);
char *value;
@@ -80,8 +77,7 @@ test_irc (void)
static void
test_http_parser (void)
{
struct str_map parameters;
str_map_init (&parameters);
struct str_map parameters = str_map_make (NULL);
parameters.key_xfrm = tolower_ascii_strxfrm;
char *type = NULL;
@@ -109,10 +105,20 @@ test_http_parser (void)
http_protocol_destroy (iter);
}
struct scgi_fixture
{
struct scgi_parser parser;
bool seen_headers;
bool seen_content;
};
static bool
test_scgi_parser_on_headers_read (void *user_data)
{
struct scgi_parser *parser = user_data;
struct scgi_fixture *fixture = user_data;
struct scgi_parser *parser = &fixture->parser;
fixture->seen_headers = true;
soft_assert (parser->headers.len == 4);
soft_assert (!strcmp (str_map_find (&parser->headers,
"CONTENT_LENGTH"), "27"));
@@ -128,7 +134,9 @@ test_scgi_parser_on_headers_read (void *user_data)
static bool
test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
{
(void) user_data;
struct scgi_fixture *fixture = user_data;
fixture->seen_content = true;
soft_assert (!strncmp (data, "What is the answer to life?", len));
return true;
}
@@ -136,11 +144,12 @@ test_scgi_parser_on_content (void *user_data, const void *data, size_t len)
static void
test_scgi_parser (void)
{
struct scgi_parser parser;
scgi_parser_init (&parser);
parser.on_headers_read = test_scgi_parser_on_headers_read;
parser.on_content = test_scgi_parser_on_content;
parser.user_data = &parser;
struct scgi_fixture fixture = { scgi_parser_make(), false, false };
struct scgi_parser *parser = &fixture.parser;
parser->on_headers_read = test_scgi_parser_on_headers_read;
parser->on_content = test_scgi_parser_on_content;
parser->user_data = &fixture;
// This is an example straight from the specification
const char example[] =
@@ -152,8 +161,9 @@ test_scgi_parser (void)
","
"What is the answer to life?";
soft_assert (scgi_parser_push (&parser, example, sizeof example, NULL));
scgi_parser_free (&parser);
soft_assert (scgi_parser_push (parser, example, sizeof example, NULL));
soft_assert (fixture.seen_headers && fixture.seen_content);
scgi_parser_free (parser);
}
static bool
@@ -182,8 +192,7 @@ test_websockets (void)
soft_assert (!strcmp (accept, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="));
free (accept);
struct ws_parser parser;
ws_parser_init (&parser);
struct ws_parser parser = ws_parser_make ();
parser.on_frame_header = test_websockets_on_frame_header;
parser.on_frame = test_websockets_on_frame;
parser.user_data = &parser;