test build

This commit is contained in:
2026-08-27 20:14:05 +02:00
parent 6a007c2cd8
commit 80a413efdc
56 changed files with 24520 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
#include "custom.PRE.h"
#include "application.PRE.h"
#include "audit_log.PRE.h"
#include "auto_moderation.PRE.h"
#include "invite.PRE.h"
#include "channel.PRE.h"
#include "emoji.PRE.h"
#include "guild.PRE.h"
#include "guild_scheduled_event.PRE.h"
#include "guild_template.PRE.h"
#include "stage_instance.PRE.h"
#include "sticker.PRE.h"
#include "user.PRE.h"
#include "voice.PRE.h"
#include "webhook.PRE.h"
#include "gateway.PRE.h"
#include "oauth2.PRE.h"
#include "permissions.PRE.h"
#include "teams.PRE.h"
#include "voice_connections.PRE.h"
#include "application_commands.PRE.h"
#include "message_components.PRE.h"
#include "interactions.PRE.h"
+67
View File
@@ -0,0 +1,67 @@
// MIT License
// Copyright (c) 2022 Anotra
// https://github.com/Anotra/anomap
#pragma once
#ifndef ANOMAP_H
#define ANOMAP_H
#include <stddef.h>
#include <stdbool.h>
#define ANOMAP_DECLARE_COMPARE_FUNCTION(function_name, data_type) \
static int \
function_name(const void *a, const void *b) { \
if (*(data_type *)a == *(data_type *)b) return 0; \
return *(data_type *)a > *(data_type *)b ? 1 : -1; \
}
enum anomap_operation {
anomap_insert = 1 << 0,
anomap_update = 1 << 1,
anomap_upsert = anomap_insert | anomap_update,
anomap_delete = 1 << 2,
anomap_getval = 1 << 3,
};
struct anomap;
struct anomap *anomap_create(size_t key_size, size_t val_size,
int (*cmp)(const void *, const void *));
void anomap_destroy(struct anomap *map);
struct anomap_item_changed {
void *data;
enum anomap_operation op;
void *key;
struct {
void *prev;
void *now;
} val;
};
typedef void anomap_on_item_changed(
struct anomap *map, struct anomap_item_changed *item_changed);
void anomap_set_on_item_changed(
struct anomap *map, anomap_on_item_changed *on_changed, void *data);
size_t anomap_length(struct anomap *map);
void anomap_clear(struct anomap *map);
bool anomap_index_of(struct anomap *map, void *key, size_t *index);
bool anomap_at_index(struct anomap *map, size_t index, void *key, void *val);
enum anomap_operation anomap_do(struct anomap *map,
enum anomap_operation operation,
void *key, void *val);
size_t anomap_copy_range(struct anomap *map,
size_t from_index, size_t to_index,
void *keys, void *vals);
size_t anomap_delete_range(struct anomap *map,
size_t from_index, size_t to_index,
void *keys, void *vals);
#endif // !ANOMAP_H
+256
View File
@@ -0,0 +1,256 @@
/**
* @file application_command.h
* @author Cogmasters
* @brief Application Command public functions and datatypes
* @todo application_id should be cached and used when its input value is `0`
*/
#ifndef DISCORD_APPLICATION_COMMAND_H
#define DISCORD_APPLICATION_COMMAND_H
/** @defgroup DiscordAPIInteractionsApplicationCommand Slash commands
* @ingroup DiscordAPIInteractions
* @brief Receiving and registering slash commands
* @{ */
/**
* @brief Fetch all of the global commands for your application
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @CCORD_ret_obj{ret,application_commands}
* @CCORD_return
*/
CCORDcode discord_get_global_application_commands(
struct discord *client,
u64snowflake application_id,
struct discord_ret_application_commands *ret);
/**
* @brief Create a new global command
* @note New global commands will be available in all guilds after 1 hour
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param params request parameters
* @CCORD_ret_obj{ret,application_command}
* @CCORD_return
*/
CCORDcode discord_create_global_application_command(
struct discord *client,
u64snowflake application_id,
struct discord_create_global_application_command *params,
struct discord_ret_application_command *ret);
/**
* @brief Fetch a global command for your application
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param command_id the registered command id
* @CCORD_ret_obj{ret,application_command}
* @CCORD_return
*/
CCORDcode discord_get_global_application_command(
struct discord *client,
u64snowflake application_id,
u64snowflake command_id,
struct discord_ret_application_command *ret);
/**
* @brief Edit a global command
* @note Updates will be available in all guilds after 1 hour
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param command_id the registered command id
* @param params request parameters
* @CCORD_ret_obj{ret,application_command}
* @CCORD_return
*/
CCORDcode discord_edit_global_application_command(
struct discord *client,
u64snowflake application_id,
u64snowflake command_id,
struct discord_edit_global_application_command *params,
struct discord_ret_application_command *ret);
/**
* @brief Deletes a global command
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param command_id the registered command id
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_global_application_command(
struct discord *client,
u64snowflake application_id,
u64snowflake command_id,
struct discord_ret *ret);
/**
* @brief Overwrite existing global application commands
* @note Updates will be available in all guilds after 1 hour
* @warning Will overwrite all types of application commands: slash
* commands, user commands, and message commands
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param params the request parameters, a list of application commands
* @CCORD_ret_obj{ret,application_commands}
* @CCORD_return
*/
CCORDcode discord_bulk_overwrite_global_application_commands(
struct discord *client,
u64snowflake application_id,
struct discord_application_commands *params,
struct discord_ret_application_commands *ret);
/**
* @brief Fetch all of the guild commands of a given guild
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the commands are located
* @CCORD_ret_obj{ret,application_commands}
* @CCORD_return
*/
CCORDcode discord_get_guild_application_commands(
struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
struct discord_ret_application_commands *ret);
/**
* @brief Create a new guild command
* @note Commands will be available in the guild immediately
* @note Will overwrite any existing guild command with the same name
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the command is located
* @param params request parameters
* @CCORD_ret_obj{ret,application_command}
* @CCORD_return
*/
CCORDcode discord_create_guild_application_command(
struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
struct discord_create_guild_application_command *params,
struct discord_ret_application_command *ret);
/**
* @brief Fetch a guild command for your application
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the command is located
* @param command_id the registered command id
* @CCORD_ret_obj{ret,application_command}
* @CCORD_return
*/
CCORDcode discord_get_guild_application_command(
struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
u64snowflake command_id,
struct discord_ret_application_command *ret);
/**
* @brief Edit a guild command
* @note Updates for guild commands will be available immediately
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the command is located
* @param command_id the registered command id
* @param params request parameters
* @CCORD_ret_obj{ret,application_command}
* @CCORD_return
*/
CCORDcode discord_edit_guild_application_command(
struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
u64snowflake command_id,
struct discord_edit_guild_application_command *params,
struct discord_ret_application_command *ret);
/**
* @brief Deletes a guild command
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the command is located
* @param command_id the registered command id
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_guild_application_command(struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
u64snowflake command_id,
struct discord_ret *ret);
/**
* @brief Overwrite existing guild application commands
* @warning This will overwrite all types of application commands: slash
* commands, user commands, and message commands
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the commands are located
* @param params the request parameters, a list of application commands
* @CCORD_ret_obj{ret,application_commands}
* @CCORD_return
*/
CCORDcode discord_bulk_overwrite_guild_application_commands(
struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
struct discord_bulk_overwrite_guild_application_commands *params,
struct discord_ret_application_commands *ret);
/**
* @brief Fetches command permissions for all commands in a given guild
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the commands are located
* @CCORD_ret_obj{ret,guild_application_command_permissions}
* @CCORD_return
*/
CCORDcode discord_get_guild_application_command_permissions(
struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
struct discord_ret_guild_application_command_permissions *ret);
/**
* @brief Fetches command permissions for a specific command in a given guild
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the parent application
* @param guild_id the guild where the command is located
* @param command_id the registered command id
* @CCORD_ret_obj{ret,application_command_permissions}
* @CCORD_return
*/
CCORDcode discord_get_application_command_permissions(
struct discord *client,
u64snowflake application_id,
u64snowflake guild_id,
u64snowflake command_id,
struct discord_ret_application_command_permission *ret);
/** @example slash-commands.c
* Demonstrates registering and reacting to slash commands */
/** @example slash-commands2.c
* Demonstrates registering and reacting to slash commands from the console */
/** @} DiscordAPIInteractionsApplicationCommand */
#endif /* DISCORD_APPLICATION_COMMAND_H */
+12
View File
@@ -0,0 +1,12 @@
#ifndef ATTRIBUTES_H
#define ATTRIBUTES_H
#if defined(__MINGW32__) \
|| (defined(__GNUC__) && __GNUC__ > 4 ? true : __GNUC_PATCHLEVEL__ >= 4) \
|| defined(__USE_MINGW_ANSI_STDIO)
#define PRINTF_LIKE(a, b) __attribute__((format(gnu_printf, a, b)))
#else
#define PRINTF_LIKE(a, b)
#endif
#endif /* ATTRIBUTES_H */
+37
View File
@@ -0,0 +1,37 @@
/**
* @file audit_log.h
* @author Cogmasters
* @brief Audit Log public functions and datatypes
*/
#ifndef DISCORD_AUDIT_LOG
#define DISCORD_AUDIT_LOG
/** @defgroup DiscordAPIAuditLog Audit Log
* @ingroup DiscordAPI
* @brief Audit Log's public API supported by Concord
* @{ */
/**
* @brief Get audit log for a given guild
*
* @note Requires the 'VIEW_AUDIT_LOG' permission
* @param client the client created with discord_from_token()
* @param guild_id the guild to retrieve the audit log from
* @param params request parameters
* @CCORD_ret_obj{ret,audit_log}
* @CCORD_return
*/
CCORDcode discord_get_guild_audit_log(
struct discord *client,
u64snowflake guild_id,
struct discord_get_guild_audit_log *params,
struct discord_ret_audit_log *ret);
/** @example audit-log.c
* Demonstrates listening to audit-log events and fetching a specific audit-log
*/
/** @} DiscordAPIAuditLog */
#endif /* DISCORD_AUDIT_LOG */
+99
View File
@@ -0,0 +1,99 @@
/**
* @file auto_moderation.h
* @author Cogmasters
* @brief Auto Moderation public functions and datatypes
*/
#ifndef DISCORD_AUTO_MODERATION_H
#define DISCORD_AUTO_MODERATION_H
/** @defgroup DiscordAPIAutoModeration Auto Moderation
* @ingroup DiscordAPI
* @brief Auto Moderation public API supported by Concord
* @{ */
/**
* @brief Get a list of all rules currently configured for the guild
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to fetch the rules from
* @CCORD_ret_obj{ret,auto_moderation_rules}
* @CCORD_return
*/
CCORDcode discord_list_auto_moderation_rules_for_guild(
struct discord *client,
u64snowflake guild_id,
struct discord_ret_auto_moderation_rules *ret);
/**
* @brief Get a single rule
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to fetch the rule from
* @param auto_moderation_rule_id the rule to be fetched
* @CCORD_ret_obj{ret,auto_moderation_rule}
* @CCORD_return
*/
CCORDcode discord_get_auto_moderation_rule(
struct discord *client,
u64snowflake guild_id,
u64snowflake auto_moderation_rule_id,
struct discord_ret_auto_moderation_rule *ret);
/**
* @brief Create a new rule
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to create the rule in
* @param params request parameters
* @CCORD_ret_obj{ret,auto_moderation_rule}
* @CCORD_return
*/
CCORDcode discord_create_auto_moderation_rule(
struct discord *client,
u64snowflake guild_id,
struct discord_create_auto_moderation_rule *params,
struct discord_ret_auto_moderation_rule *ret);
/**
* @brief Modify an existing rule
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild where the rule to be modified is at
* @param auto_moderation_rule_id the rule to be modified
* @param params request parameters
* @CCORD_ret_obj{ret,auto_moderation_rule}
* @CCORD_return
*/
CCORDcode discord_modify_auto_moderation_rule(
struct discord *client,
u64snowflake guild_id,
u64snowflake auto_moderation_rule_id,
struct discord_modify_auto_moderation_rule *params,
struct discord_ret_auto_moderation_rule *ret);
/**
* @brief Delete a rule
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild where the rule to be deleted is at
* @param auto_moderation_rule_id the rule to be deleted
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_auto_moderation_rule(
struct discord *client,
u64snowflake guild_id,
u64snowflake auto_moderation_rule_id,
struct discord_delete_auto_moderation_rule *params,
struct discord_ret *ret);
/** @} DiscordAPIAutoModeration */
#endif /* DISCORD_AUTO_MODERATION_H */
+232
View File
@@ -0,0 +1,232 @@
/* Copyright 2022 Cogmasters */
/*
* C-Ware License
*
* Copyright (c) 2022, C-Ware
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Redistributions of modified source code must append a copyright notice in
* the form of 'Copyright <YEAR> <NAME>' to each modified source file's
* copyright notice, and the standalone license file if one exists.
*
* A 'redistribution' can be constituted as any version of the original source
* code material that is intended to comprise some other derivative work of
* this code. A fork created for the purpose of contributing to any version of
* the source does not constitute a truly 'derivative work' and does not require
* listing.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Modified by Lucas Müller <[email protected]>, 19 Sept 2022
* - __carray_init() should initialize its `size` value
*
* Modified by Lucas Müller <[email protected]>, 15 Feb 2022
* - CARRAY_RESIZE() has a fallback value (+1)
*
* Modified by Lucas Müller <[email protected]>, 06 Feb 2022
* - __carray_init() accept initial length
*
* Modified by Lucas Müller <[email protected]>, 02 Feb 2022
* - remove free(carray) at __carrray_free()
*
* Modified by Lucas Müller <[email protected]>, 01 Feb 2022
* - change CARRAY_INITIAL_SIZE from 5 to 4
* - change CARRAY_RESIZE to doubling arrays to reduce realloc calls
* - remove calloc() from __carray_init(), expect user to allocate it
* - remove pseudo-return from __carray_init()
*
* Modified by Lucas Müller <[email protected]>, 27 Jan 2022
* - rename contents -> array
* - rename logical_size -> size
* - rename physical_size -> realsize
*/
#ifndef CWARE_ARRAY_H
#define CWARE_ARRAY_H
#ifndef CARRAY_INITIAL_SIZE
#define CARRAY_INITIAL_SIZE 4
#endif
#ifndef CARRAY_RESIZE
#define CARRAY_RESIZE(current_size) \
1 + current_size * 2
#endif
/* carray_init */
#ifndef CARRAY_STACKFUL
#define __carray_init(carray, length, _type, _compare, _free) \
do { \
(carray)->realsize = length; \
(carray)->size = 0; \
(carray)->array = calloc(length, sizeof(_type)); \
} while (0)
#define carray_init(carray, settings) \
__carray_init(carray, CARRAY_INITIAL_SIZE, settings)
#else
#define carray_init(carray, length, block) \
do { \
carray.realsize = length; \
carray.size = 0; \
carray.array = block; \
} while (0)
#endif
/* carray_insert */
#ifndef CARRAY_STACKFUL
#define __carray_insert_handle_full(carray, index, value) \
(carray)->realsize = CARRAY_RESIZE((carray)->realsize); \
(carray)->array = realloc((carray)->array, sizeof(*(carray)->array) * (size_t) (carray)->realsize)
#else
#define __carray_insert_handle_full(carray, index, value) \
fprintf(stderr, "carray_insert: attempt to insert value '%s' into full array '%s'. (%s:%i)\n", #value, #carray, __FILE__, __LINE__); \
exit(EXIT_FAILURE)
#endif
#define carray_insert(carray, index, value) \
if((carray)->size == (carray)->realsize) { \
__carray_insert_handle_full(carray, index, value); \
} \
\
if(index < 0 || index > (carray)->size) { \
fprintf(stderr, "carray_insert: attempt to insert at index %i, out of bounds of array '%s'. (%s:%i)\n", index, #carray, __FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
\
memmove((carray)->array + index + 1, (carray)->array + index, sizeof(*(carray)->array) * (size_t) ((carray)->size - index)); \
(carray)->array[index] = value; \
(carray)->size++
/* carray_pop */
#define carray_pop(carray, index, location) \
location; \
\
if(index < 0 || index >= (carray)->size) { \
fprintf(stderr, "carray_pop: attempt to pop index %i, out of bounds of array '%s'. (%s:%i)\n", index, #carray, __FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
\
(carray)->size--; \
(location) = (carray)->array[(index)]; \
memmove((carray)->array + index, (carray)->array + index + 1, sizeof(*(carray)->array) * (size_t) ((carray)->size - index))
/* carray_remove */
#define __carray_remove(carray, value, _type, _compare, _free) \
do { \
int __CARRAY_ITER_INDEX = 0; \
\
for(__CARRAY_ITER_INDEX = 0; __CARRAY_ITER_INDEX < (carray)->size; __CARRAY_ITER_INDEX++) { \
_type __CARRAY_OPERAND_A = (carray)->array[__CARRAY_ITER_INDEX]; \
_type __CARRAY_OPERAND_B = value; \
\
if((_compare) == 0) \
continue; \
\
_free; \
(carray)->size--; \
memmove((carray)->array + __CARRAY_ITER_INDEX, \
(carray)->array + __CARRAY_ITER_INDEX + 1, \
sizeof(*(carray)->array) * (size_t) ((carray)->size - __CARRAY_ITER_INDEX)); \
\
__CARRAY_ITER_INDEX = -1; \
break; \
} \
\
if(__CARRAY_ITER_INDEX != -1) { \
fprintf(stderr, "carray_remove: attempt to remove value '%s' that is not in array '%s'. (%s:%i)\n", #value, #carray, __FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while(0)
#define carray_remove(carray, value, settings) \
__carray_remove(carray, value, settings)
/* carray_find */
#define __carray_find(carray, value, location, _type, _compare, _free) \
-1; \
\
do { \
int __CARRAY_ITER_INDEX = 0; \
location = -1; \
\
for(__CARRAY_ITER_INDEX = 0; __CARRAY_ITER_INDEX < (carray)->size; __CARRAY_ITER_INDEX++) { \
_type __CARRAY_OPERAND_A = (carray)->array[__CARRAY_ITER_INDEX]; \
_type __CARRAY_OPERAND_B = value; \
\
if((_compare) == 0) \
continue; \
\
location = __CARRAY_ITER_INDEX; \
\
break; \
} \
} while(0)
#define carray_find(carray, value, location, settings) \
__carray_find(carray, value, location, settings)
#ifndef CARRAY_STACKFUL
#define __carray_free_array(carray) free((carray)->array);
#else
#define __carray_free_array(carray)
#endif
/* carray_free */
#define __carray_free(carray, _type, _compare, _free) \
do { \
int __CARRAY_ITER_INDEX = 0; \
\
for(__CARRAY_ITER_INDEX = 0; __CARRAY_ITER_INDEX < (carray)->size; __CARRAY_ITER_INDEX++) { \
_type __CARRAY_OPERAND_A = (carray)->array[__CARRAY_ITER_INDEX]; \
(void) __CARRAY_OPERAND_A; \
\
_free; \
} \
\
__carray_free_array(carray); \
} while(0)
#define carray_free(carray, settings) \
__carray_free(carray, settings)
/* carray_append */
#ifndef CARRAY_STACKFUL
#define __carray_append_handle_full(carray, value) \
(carray)->realsize = CARRAY_RESIZE((carray)->realsize); \
(carray)->array = realloc((carray)->array, sizeof(*(carray)->array) * (size_t) (carray)->realsize)
#else
#define __carray_append_handle_full(carray, value) \
fprintf(stderr, "carray_append: attempt to append value '%s' into full array '%s'. (%s:%i)\n", #value, #carray, __FILE__, __LINE__); \
exit(EXIT_FAILURE)
#endif
#define carray_append(carray, value) \
if((carray)->size == (carray)->realsize) { \
__carray_append_handle_full(carray, value); \
} \
\
(carray)->array[(carray)->size] = value; \
(carray)->size++;
#endif
+670
View File
@@ -0,0 +1,670 @@
/**
* @file channel.h
* @author Cogmasters
* @brief Channel public functions and datatypes
*/
#ifndef DISCORD_CHANNEL_H
#define DISCORD_CHANNEL_H
/* forward declaration */
struct discord_ret_users;
/**/
/** @defgroup DiscordAPIChannel Channel
* @ingroup DiscordAPI
* @brief Channel's public API supported by Concord
* @{ */
/**
* @brief Get channel from given id
* @note If the channel is a thread, a thread member object is included in the
* returned result
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to be retrieved
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_get_channel(struct discord *client,
u64snowflake channel_id,
struct discord_ret_channel *ret);
/**
* @brief Update a channel's settings
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to be modified
* @param params request parameters
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_modify_channel(struct discord *client,
u64snowflake channel_id,
struct discord_modify_channel *params,
struct discord_ret_channel *ret);
/**
* @brief Delete a channel, or close a private message
* @note Requires the MANAGE_CHANNELS permission for the guild, or
* MANAGE_THREADS if the channel is a thread
* @note Deleting a category does not delete its child channels; they will have
* their parent_id removed and a `Channel Update Gateway` event will
* fire for each of them
* @note Fires a `Channel Delete` event (or `Thread Delete` if the channel
* was a thread)
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to be deleted
* @param params request parameters
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_delete_channel(struct discord *client,
u64snowflake channel_id,
struct discord_delete_channel *params,
struct discord_ret_channel *ret);
/**
* @brief Get messages for a given channel
* @note If operating on a guild channel, this endpoint requires the
* VIEW_CHANNEL permission to be present on the current user
* @note If the current user is missing the READ_MESSAGE_HISTORY permission
* in the channel then this will return no messages (since they cannot
* read the message history)
* @note The before, after, and around keys are mutually exclusive, only one
* may be passed at a time
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to get messages from
* @param params request parameters
* @CCORD_ret_obj{ret,messages}
* @CCORD_return
*/
CCORDcode discord_get_channel_messages(
struct discord *client,
u64snowflake channel_id,
struct discord_get_channel_messages *params,
struct discord_ret_messages *ret);
/**
* @brief Get a specific message in the channel
* @note If operating on a guild channel, this endpoint requires the
* 'READ_MESSAGE_HISTORY' permission to be present on the current user
* @param client the client created with discord_from_token()
* @param channel_id the channel where the message resides
* @param message_id the message itself
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_get_channel_message(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_ret_message *ret);
/**
* @brief Post a message to a guild text or DM channel
* @note Fires a `Message Create` event
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to send the message at
* @param params request parameters
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_create_message(struct discord *client,
u64snowflake channel_id,
struct discord_create_message *params,
struct discord_ret_message *ret);
/**
* @brief Crosspost a message in a News Channel to following channels
* @note This endpoint requires the 'SEND_MESSAGES' permission, if the current
* user sent the message, or additionally the 'MANAGE_MESSAGES'
* permission, for all other messages, to be present for the current
* user
*
* @param client the client created with discord_from_token()
* @param channel_id the news channel that will crosspost
* @param message_id the message that will crospost
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_crosspost_message(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_ret_message *ret);
/**
* @brief Create a reaction for the message
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message to receive a reaction
* @param emoji_id the emoji id (leave as 0 if not a custom emoji)
* @param emoji_name the emoji name
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_create_reaction(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
u64snowflake emoji_id,
const char emoji_name[],
struct discord_ret *ret);
/**
* @brief Delete a reaction the current user has made for the message
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message to have a reaction deleted
* @param emoji_id the emoji id (leave as 0 if not a custom emoji)
* @param emoji_name the emoji name
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_own_reaction(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
u64snowflake emoji_id,
const char emoji_name[],
struct discord_ret *ret);
/**
* @brief Deletes another user's reaction
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message to have a reaction deleted
* @param user_id the user the reaction belongs to
* @param emoji_id the emoji id (leave as 0 if not a custom emoji)
* @param emoji_name the emoji name
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_user_reaction(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
u64snowflake user_id,
u64snowflake emoji_id,
const char emoji_name[],
struct discord_ret *ret);
/**
* @brief Get a list of users that reacted with given emoji
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message reacted to
* @param emoji_id the emoji id (leave as 0 if not a custom emoji)
* @param emoji_name the emoji name
* @param params request parameters
* @CCORD_ret_obj{ret,users}
* @CCORD_return
*/
CCORDcode discord_get_reactions(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
u64snowflake emoji_id,
const char emoji_name[],
struct discord_get_reactions *params,
struct discord_ret_users *ret);
/**
* @brief Deletes all reactions from message
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message that will be purged of reactions
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_all_reactions(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_ret *ret);
/**
* @brief Deletes all the reactions for a given emoji on message
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message that will be purged of reactions from
* particular emoji
* @param emoji_id the emoji id (leave as 0 if not a custom emoji)
* @param emoji_name the emoji name
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_all_reactions_for_emoji(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
u64snowflake emoji_id,
const char emoji_name[],
struct discord_ret *ret);
/**
* @brief Edit a previously sent message
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message that will be purged of reactions from
* particular emoji
* @param params request parameters
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_edit_message(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_edit_message *params,
struct discord_ret_message *ret);
/**
* @brief Delete a message
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param message_id the message that will be purged of reactions from
* particular emoji
* @param params request parameters
* @CCORD_return
*/
CCORDcode discord_delete_message(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_delete_message *params,
struct discord_ret *ret);
/**
* @brief Delete multiple messages in a single request
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_bulk_delete_messages(
struct discord *client,
u64snowflake channel_id,
struct discord_bulk_delete_messages *params,
struct discord_ret *ret);
/**
* @brief Edit the channel permission overwrites for a user or role in a
* channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param overwrite_id
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_edit_channel_permissions(
struct discord *client,
u64snowflake channel_id,
u64snowflake overwrite_id,
struct discord_edit_channel_permissions *params,
struct discord_ret *ret);
/**
* @brief Get invites (with invite metadata) for the channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @CCORD_ret_obj{ret,invites}
* @CCORD_return
*/
CCORDcode discord_get_channel_invites(struct discord *client,
u64snowflake channel_id,
struct discord_ret_invites *ret);
/**
* @brief Create a new invite for the channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the message belongs to
* @param params request parameters
* @CCORD_ret_obj{ret,invite}
* @CCORD_return
*/
CCORDcode discord_create_channel_invite(
struct discord *client,
u64snowflake channel_id,
struct discord_create_channel_invite *params,
struct discord_ret_invite *ret);
/**
* @brief Delete a channel permission overwrite for a user or role in a
* channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to the permission deleted
* @param overwrite_id the id of the overwritten permission
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_channel_permission(
struct discord *client,
u64snowflake channel_id,
u64snowflake overwrite_id,
struct discord_delete_channel_permission *params,
struct discord_ret *ret);
/**
* @brief Post a typing indicator for the specified channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to post the typing indicator to
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_trigger_typing_indicator(struct discord *client,
u64snowflake channel_id,
struct discord_ret *ret);
/**
* @brief Follow a News Channel to send messages to a target channel
* @note Requires MANAGE_WEBHOOKS permission in the target channel
* MANAGE_WEBHOOKS permission in the target channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to be followed
* @CCORD_ret_obj{ret,followed_channel}
* @CCORD_return
*/
CCORDcode discord_follow_news_channel(
struct discord *client,
u64snowflake channel_id,
struct discord_follow_news_channel *params,
struct discord_ret_followed_channel *ret);
/**
* @brief Get all pinned messages in the channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel where the get pinned messages from
* @CCORD_ret_obj{ret,messages}
* @CCORD_return
*/
CCORDcode discord_get_pinned_messages(struct discord *client,
u64snowflake channel_id,
struct discord_ret_messages *ret);
/**
* @brief Pin a message to a channel
*
* @param client the client created with discord_from_token()
* @param channel_id channel to pin the message on
* @param message_id message to be pinned
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_pin_message(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_pin_message *params,
struct discord_ret *ret);
/**
* @brief Unpin a message from a channel
*
* @param client the client created with discord_from_token()
* @param channel_id channel for the message to be unpinned
* @param message_id message to be unpinned
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_unpin_message(struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_unpin_message *params,
struct discord_ret *ret);
/**
* @brief Adds a recipient to a Group DM using their access token
*
* @param client the client created with discord_from_token()
* @param channel_id group to add the user in
* @param user_id user to be added
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_group_dm_add_recipient(
struct discord *client,
u64snowflake channel_id,
u64snowflake user_id,
struct discord_group_dm_add_recipient *params,
struct discord_ret *ret);
/**
* @brief Removes a recipient from a Group DM
*
* @param client the client created with discord_from_token()
* @param channel_id channel for the user to be removed from
* @param user_id user to be removed
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_group_dm_remove_recipient(struct discord *client,
u64snowflake channel_id,
u64snowflake user_id,
struct discord_ret *ret);
/**
* @brief Creates a new thread from an existing message
* @note Fires a `Thread Create` event
*
* @param client the client created with discord_from_token()
* @param channel_id channel to start a thread on
* @param message_id message to start a thread from
* @param params request parameters
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_start_thread_with_message(
struct discord *client,
u64snowflake channel_id,
u64snowflake message_id,
struct discord_start_thread_with_message *params,
struct discord_ret_channel *ret);
/**
* @brief Creates a new thread that is not connected to an existing message
* @note Fires a `Thread Create` event
*
* @param client the client created with discord_from_token()
* @param channel_id channel to start a thread on
* @param params request parameters
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_start_thread_without_message(
struct discord *client,
u64snowflake channel_id,
struct discord_start_thread_without_message *params,
struct discord_ret_channel *ret);
/**
* @brief Adds the current user to an un-archived thread
* @note Fires a `Thread Members Update` event
*
* @param client the client created with discord_from_token()
* @param channel_id the thread to be joined
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_join_thread(struct discord *client,
u64snowflake channel_id,
struct discord_ret *ret);
/**
* @brief Adds another member to an un-archived thread
* @note Fires a `Thread Members Update` event
*
* @param client the client created with discord_from_token()
* @param channel_id the thread to be joined
* @param user_id user to be added to thread
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_add_thread_member(struct discord *client,
u64snowflake channel_id,
u64snowflake user_id,
struct discord_ret *ret);
/**
* @brief Removes the current user from a un-archived thread
* @note Fires a `Thread Members Update` event
*
* @param client the client created with discord_from_token()
* @param channel_id the thread to be removed from
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_leave_thread(struct discord *client,
u64snowflake channel_id,
struct discord_ret *ret);
/**
* @brief Removes another member from a un-archived thread
* @note Fires a `Thread Members Update` event
* @note Requires `MANAGE_THREADS` permission
*
* @param client the client created with discord_from_token()
* @param channel_id the thread to be removed from
* @param user_id user to be removed
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_remove_thread_member(struct discord *client,
u64snowflake channel_id,
u64snowflake user_id,
struct discord_ret *ret);
/**
* @brief Get members from a given thread channel
* @note Fires a `Thread Members Update` event
* @note Requires `MANAGE_THREADS` permission
*
* @param client the client created with discord_from_token()
* @param channel_id the thread to be joined
* @CCORD_ret_obj{ret,thread_members}
* @CCORD_return
*/
CCORDcode discord_list_thread_members(struct discord *client,
u64snowflake channel_id,
struct discord_ret_thread_members *ret);
/**
* @brief Get public archived threads in a given channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to be searched for threads
* @param before return threads before this timestamp
* @param limit maximum number of threads to return
* @CCORD_ret_obj{ret,thread_response_body}
* @CCORD_return
*/
CCORDcode discord_list_public_archived_threads(
struct discord *client,
u64snowflake channel_id,
u64unix_ms before,
int limit,
struct discord_ret_thread_response_body *ret);
/**
* @brief Get private archived threads in a given channel
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to be searched for threads
* @param before return threads before this timestamp
* @param limit maximum number of threads to return
* @CCORD_ret_obj{ret,thread_response_body}
* @CCORD_return
*/
CCORDcode discord_list_private_archived_threads(
struct discord *client,
u64snowflake channel_id,
u64unix_ms before,
int limit,
struct discord_ret_thread_response_body *ret);
/**
* @brief Get private archived threads that current user has joined
*
* @param client the client created with discord_from_token()
* @param channel_id the channel to be searched for threads
* @param before return threads before this timestamp
* @param limit maximum number of threads to return
* @CCORD_ret_obj{ret,thread_response_body}
* @CCORD_return
*/
CCORDcode discord_list_joined_private_archived_threads(
struct discord *client,
u64snowflake channel_id,
u64unix_ms before,
int limit,
struct discord_ret_thread_response_body *ret);
/** @defgroup DiscordAPIChannelHelper Helper functions
* @brief Custom helper functions
* @{ */
/**
* @brief Get a guild's channel from its given numerical position
*
* @param client the client created with discord_from_token()
* @param guild_id guild the channel belongs to
* @param type the channel type where to take position reference from
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_get_channel_at_pos(struct discord *client,
u64snowflake guild_id,
enum discord_channel_types type,
int position,
struct discord_ret_channel *ret);
/**
* @brief Append to an overwrite list
* @note the list should be freed with `discord_overwrite_list_free()` after
* its no longer being used
*
* @param permission_overwrites list to be appended to
* @param id role or user id
* @param type either 0 (role) or 1 (member)
* @param allow permission bit set
* @param deny permission bit set
*/
void discord_overwrite_append(struct discord_overwrites *permission_overwrites,
u64snowflake id,
int type,
u64bitmask allow,
u64bitmask deny);
/** @} DiscordAPIChannelHelper */
/** @example channel.c
* Demonstrates a couple use cases of the Channel API */
/** @example embed.c
* Demonstrates embed manipulation */
/** @example fetch-messages.c
* Demonstrates fetching user messages */
/** @example manual-dm.c
* Demonstrates sending DMs with your client */
/** @example pin.c
* Demonstrates pinning messages */
/** @example reaction.c
* Demonstrates a couple use cases of the Channel reactions API */
/** @} DiscordAPIChannel */
#endif /* DISCORD_CHANNEL_H */
+510
View File
@@ -0,0 +1,510 @@
/* Copyright 2022 Cogmasters */
/*
* C-Ware License
*
* Copyright (c) 2022, C-Ware
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Redistributions of modified source code must append a copyright notice in
* the form of 'Copyright <YEAR> <NAME>' to each modified source file's
* copyright notice, and the standalone license file if one exists.
*
* A 'redistribution' can be constituted as any version of the original source
* code material that is intended to comprise some other derivative work of
* this code. A fork created for the purpose of contributing to any version of
* the source does not constitute a truly 'derivative work' and does not require
* listing.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Modified by Lucas Müller ([email protected]), 16 May 2022
* - add __chash_init() and __chash_free() as a non-malloc option */
#ifndef CWARE_LIBCHASH_H
#define CWARE_LIBCHASH_H
#define CWARE_LIBCHASH_VERSION "x.0.0"
/* How big heap-allocated hashtables are by default */
#ifndef CHASH_INITIAL_SIZE
#define CHASH_INITIAL_SIZE 10
#elif CHASH_INITIAL_SIZE <= 0
"chash_init: default length must be greater than 0"
#endif
/* Calculates the next size of the hashtable. */
#ifndef CHASH_RESIZE
#define CHASH_RESIZE(size) \
((size) * 1.3)
#endif
/* The threshold that, when passed, will cause a resize */
#ifndef CHASH_LOAD_THRESHOLD
#define CHASH_LOAD_THRESHOLD 0.8
#endif
/* The type that is used for counters; useful for aligning hashtable
* length and capacity fields so type casting warnings do not appear */
#ifndef CHASH_COUNTER_TYPE
#define CHASH_COUNTER_TYPE int
#endif
/* The name of the key field */
#ifndef CHASH_KEY_FIELD
#define CHASH_KEY_FIELD key
#endif
/* The name of the value field */
#ifndef CHASH_VALUE_FIELD
#define CHASH_VALUE_FIELD value
#endif
/* The name of the state field */
#ifndef CHASH_STATE_FIELD
#define CHASH_STATE_FIELD state
#endif
/* The name of the buckets field */
#ifndef CHASH_BUCKETS_FIELD
#define CHASH_BUCKETS_FIELD buckets
#endif
/* The name of the length field */
#ifndef CHASH_LENGTH_FIELD
#define CHASH_LENGTH_FIELD length
#endif
/* The name of the capacity field */
#ifndef CHASH_CAPACITY_FIELD
#define CHASH_CAPACITY_FIELD capacity
#endif
/* State enums */
#define CHASH_UNFILLED 0
#define CHASH_FILLED 1
#define CHASH_TOMBSTONE 2
/* Built-ins */
#define chash_string_hash(key, hash) \
5031; \
do { \
int __CHASH_HINDEX = 0; \
\
for(__CHASH_HINDEX = 0; (key)[__CHASH_HINDEX] != '\0'; \
__CHASH_HINDEX++) { \
(hash) = (((hash) << 1) + (hash)) + (key)[__CHASH_HINDEX]; \
} \
} while(0)
#define chash_string_compare(cmp_a, cmp_b) \
(strcmp((cmp_a), (cmp_b)) == 0)
#define chash_default_init(bucket, _key, _value) \
(bucket).CHASH_KEY_FIELD = (_key); \
(bucket).CHASH_VALUE_FIELD = _value
/* utility macros */
#define __chash_abs(x) \
((x) < 0 ? (x) * - 1 : (x))
#define __chash_hash(mod, _key, namespace) \
__CHASH_HASH = namespace ## _HASH((_key), __CHASH_HASH); \
__CHASH_HASH = __CHASH_HASH % (mod); \
__CHASH_HASH = __chash_abs(__CHASH_HASH);
#define __chash_probe(hashtable, _key, namespace) \
while(__CHASH_INDEX < (hashtable)->CHASH_CAPACITY_FIELD) { \
if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD == \
CHASH_UNFILLED) \
break; \
\
if((namespace ## _COMPARE((_key), \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_KEY_FIELD)) == 1) { \
\
__CHASH_INDEX = -1; \
break; \
} \
\
__CHASH_HASH = (__CHASH_HASH + 1) % (hashtable)->CHASH_CAPACITY_FIELD; \
__CHASH_INDEX++; \
} \
#define __chash_probe_to_unfilled(mod, _key, buffer, namespace) \
while(1) { \
if(buffer[__CHASH_HASH].CHASH_STATE_FIELD != CHASH_FILLED) \
break; \
\
if((namespace ## _COMPARE((_key), buffer[__CHASH_HASH].CHASH_KEY_FIELD)) \
== 1) \
break; \
\
__CHASH_HASH = (__CHASH_HASH + 1) % mod; \
} \
#define __chash_resize(hashtable, namespace) \
do { \
CHASH_COUNTER_TYPE __CHASH_INDEX = 0; \
namespace ## _BUCKET *__CHASH_BUCKETS = NULL; \
CHASH_COUNTER_TYPE __CHASH_NEXT_SIZE = (CHASH_COUNTER_TYPE) \
CHASH_RESIZE((hashtable)->CHASH_CAPACITY_FIELD); \
\
if((namespace ## _HEAP) == 0) { \
if((hashtable)->CHASH_LENGTH_FIELD != \
(hashtable)->CHASH_CAPACITY_FIELD) { \
break; \
} \
\
fprintf(stderr, "__chash_resize: hashtable is full. could not resize" \
" (%s:%i)\n", __FILE__, __LINE__); \
abort(); \
} \
\
if((double) (hashtable)->CHASH_LENGTH_FIELD / \
(double) (hashtable)->CHASH_CAPACITY_FIELD < CHASH_LOAD_THRESHOLD) \
break; \
\
__CHASH_BUCKETS = malloc((size_t) (__CHASH_NEXT_SIZE \
* ((CHASH_COUNTER_TYPE) \
sizeof(namespace ## _BUCKET)))); \
memset(__CHASH_BUCKETS, 0, ((size_t) (__CHASH_NEXT_SIZE \
* ((CHASH_COUNTER_TYPE) \
sizeof(namespace ## _BUCKET))))); \
\
for(__CHASH_INDEX = 0; __CHASH_INDEX < (hashtable)->CHASH_CAPACITY_FIELD; \
__CHASH_INDEX++) { \
namespace ## _BUCKET __CHASH_NEW_KEY_BUCKET; \
memset(&__CHASH_NEW_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
namespace ## _INIT(__CHASH_NEW_KEY_BUCKET, \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_KEY_FIELD, \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_VALUE_FIELD); \
\
if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_STATE_FIELD \
!= CHASH_FILLED) \
continue; \
\
__chash_hash(__CHASH_NEXT_SIZE, __CHASH_NEW_KEY_BUCKET.CHASH_KEY_FIELD, \
namespace); \
__chash_probe_to_unfilled(__CHASH_NEXT_SIZE, \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_KEY_FIELD, \
__CHASH_BUCKETS, namespace) \
\
__CHASH_BUCKETS[__CHASH_HASH] = __CHASH_NEW_KEY_BUCKET; \
__CHASH_BUCKETS[__CHASH_HASH].CHASH_STATE_FIELD = CHASH_FILLED; \
__CHASH_HASH = 0; \
} \
\
free((hashtable)->CHASH_BUCKETS_FIELD); \
(hashtable)->CHASH_BUCKETS_FIELD = __CHASH_BUCKETS; \
(hashtable)->CHASH_CAPACITY_FIELD = __CHASH_NEXT_SIZE; \
__CHASH_HASH = 0; \
} while(0)
#define __chash_assert_nonnull(func, ptr) \
do { \
if((ptr) == NULL) { \
fprintf(stderr, #func ": " #ptr " cannot be null (%s:%i)\n", \
__FILE__, __LINE__); \
abort(); \
} \
} while(0)
/* operations */
#define __chash_init(hashtable, namespace) \
(hashtable)->CHASH_LENGTH_FIELD = 0; \
(hashtable)->CHASH_CAPACITY_FIELD = CHASH_INITIAL_SIZE; \
(hashtable)->CHASH_BUCKETS_FIELD = malloc(CHASH_INITIAL_SIZE \
* sizeof(*((hashtable)->CHASH_BUCKETS_FIELD))); \
memset((hashtable)->CHASH_BUCKETS_FIELD, 0, \
sizeof(*((hashtable)->CHASH_BUCKETS_FIELD)) * CHASH_INITIAL_SIZE)
#define chash_init(hashtable, namespace) \
NULL; \
\
(hashtable) = malloc(sizeof((*(hashtable)))); \
__chash_init(hashtable, namespace)
#define chash_init_stack(hashtable, buffer, _length, namespace) \
(*(hashtable)); \
\
if((_length) <= 0) { \
fprintf(stderr, "chash_init_stack: hashtable cannot have a maximum " \
"length of 0 or less (%s:%i)\n", __FILE__, __LINE__); \
abort(); \
} \
\
__chash_assert_nonnull(chash_init_stack, buffer); \
\
(hashtable)->CHASH_LENGTH_FIELD = 0; \
(hashtable)->CHASH_CAPACITY_FIELD = _length; \
(hashtable)->CHASH_BUCKETS_FIELD = buffer
#define chash_assign(hashtable, _key, _value, namespace) \
do { \
long __CHASH_HASH = 0; \
namespace ## _BUCKET __CHASH_KEY_BUCKET; \
memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
namespace ## _INIT(__CHASH_KEY_BUCKET, _key, _value); \
\
__chash_assert_nonnull(chash_assign, hashtable); \
__chash_assert_nonnull(chash_assign, (hashtable)->CHASH_BUCKETS_FIELD); \
__chash_resize(hashtable, namespace); \
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
__chash_probe_to_unfilled((hashtable)->CHASH_CAPACITY_FIELD, \
(_key), (hashtable)->CHASH_BUCKETS_FIELD, namespace) \
\
if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD == \
CHASH_FILLED) { \
namespace ## _FREE_VALUE( \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD); \
} else { \
(hashtable)->CHASH_LENGTH_FIELD++; \
} \
\
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH] = __CHASH_KEY_BUCKET; \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD = \
CHASH_FILLED; \
} while(0)
#define chash_lookup(hashtable, _key, storage, namespace) \
storage; \
\
do { \
int __CHASH_INDEX = 0; \
long __CHASH_HASH = 0; \
namespace ## _BUCKET __CHASH_KEY_BUCKET; \
memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
namespace ## _INIT(__CHASH_KEY_BUCKET, _key, \
__CHASH_KEY_BUCKET.CHASH_VALUE_FIELD); \
\
(void) __CHASH_KEY_BUCKET; \
\
__chash_assert_nonnull(chash_lookup, hashtable); \
__chash_assert_nonnull(chash_lookup, (hashtable)->CHASH_BUCKETS_FIELD); \
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
__chash_probe(hashtable, _key, namespace) \
\
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
CHASH_FILLED) || __CHASH_INDEX != -1) { \
fprintf(stderr, "chash_lookup: failed to find key in hashtable (%s:%i)" \
"\n", __FILE__, __LINE__); \
abort(); \
} \
\
storage = (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD; \
} while(0)
#define chash_delete(hashtable, _key, namespace) \
do { \
int __CHASH_INDEX = 0; \
long __CHASH_HASH = 0; \
\
__chash_assert_nonnull(chash_delete, hashtable); \
__chash_assert_nonnull(chash_delete, (hashtable)->CHASH_BUCKETS_FIELD); \
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
__chash_probe(hashtable, _key, namespace) \
\
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
CHASH_FILLED) || __CHASH_INDEX != -1) { \
fprintf(stderr, "chash_delete: failed to find key in hashtable (%s:%i)" \
"\n", __FILE__, __LINE__); \
abort(); \
} \
\
namespace ## _FREE_KEY((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH] \
.CHASH_KEY_FIELD); \
namespace ## _FREE_VALUE( \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD); \
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD = \
CHASH_TOMBSTONE; \
(hashtable)->CHASH_LENGTH_FIELD--; \
} while(0)
#define chash_contains(hashtable, _key, storage, namespace) \
1; \
\
do { \
int __CHASH_INDEX = 0; \
long __CHASH_HASH = 0; \
\
__chash_assert_nonnull(chash_contents, hashtable); \
__chash_assert_nonnull(chash_contents, (hashtable)->CHASH_BUCKETS_FIELD); \
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
__chash_probe(hashtable, _key, namespace) \
\
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
CHASH_FILLED) || __CHASH_INDEX != -1) { \
storage = 0; \
} \
} while(0)
#define chash_lookup_bucket(hashtable, _key, storage, namespace) \
storage; \
\
do { \
CHASH_COUNTER_TYPE __CHASH_INDEX = 0; \
long __CHASH_HASH = 0; \
namespace ## _BUCKET __CHASH_KEY_BUCKET; \
memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
namespace ## _INIT(__CHASH_KEY_BUCKET, _key, \
__CHASH_KEY_BUCKET.CHASH_VALUE_FIELD); \
\
(void) __CHASH_KEY_BUCKET; \
\
__chash_assert_nonnull(chash_lookup_bucket, hashtable); \
__chash_assert_nonnull(chash_lookup_bucket, \
(hashtable)->CHASH_BUCKETS_FIELD); \
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
__chash_probe(hashtable, _key, namespace) \
\
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
CHASH_FILLED) || __CHASH_INDEX != -1) { \
fprintf(stderr, "chash_lookup_bucket: failed to find key in hashtable" \
"(%s:%i) \n", __FILE__, __LINE__); \
abort(); \
} \
\
storage = ((hashtable)->CHASH_BUCKETS_FIELD + __CHASH_HASH); \
} while(0)
#define __chash_free(hashtable, namespace) \
do { \
__chash_assert_nonnull(__chash_free, hashtable); \
__chash_assert_nonnull(__chash_free, (hashtable)->CHASH_BUCKETS_FIELD); \
(hashtable)->CHASH_CAPACITY_FIELD--; \
\
while((hashtable)->CHASH_CAPACITY_FIELD != -1) { \
if((hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
.CHASH_STATE_FIELD != CHASH_FILLED) { \
(hashtable)->CHASH_CAPACITY_FIELD--; \
continue; \
} \
\
namespace ##_FREE_KEY( \
(hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
.CHASH_KEY_FIELD); \
namespace ##_FREE_VALUE( \
(hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
.CHASH_VALUE_FIELD); \
(hashtable)->CHASH_CAPACITY_FIELD--; \
(hashtable)->CHASH_LENGTH_FIELD--; \
} \
\
if((namespace ## _HEAP) == 1) { \
free((hashtable)->CHASH_BUCKETS_FIELD); \
} \
} while(0)
#define chash_free(hashtable, namespace) \
do { \
__chash_assert_nonnull(chash_free, hashtable); \
__chash_assert_nonnull(chash_free, (hashtable)->CHASH_BUCKETS_FIELD); \
(hashtable)->CHASH_CAPACITY_FIELD--; \
\
while((hashtable)->CHASH_CAPACITY_FIELD != -1) { \
if((hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
.CHASH_STATE_FIELD != CHASH_FILLED) { \
(hashtable)->CHASH_CAPACITY_FIELD--; \
continue; \
} \
\
namespace ##_FREE_KEY( \
(hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
.CHASH_KEY_FIELD); \
namespace ##_FREE_VALUE( \
(hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
.CHASH_VALUE_FIELD); \
(hashtable)->CHASH_CAPACITY_FIELD--; \
(hashtable)->CHASH_LENGTH_FIELD--; \
} \
\
if((namespace ## _HEAP) == 1) { \
free((hashtable)->CHASH_BUCKETS_FIELD); \
free((hashtable)); \
} \
} while(0)
#define chash_is_full(hashtable, namespace) \
(((hashtable)->CHASH_LENGTH_FIELD) == ((hashtable)->CHASH_CAPACITY_FIELD))
/* Iterator logic */
#define chash_iter(hashtable, index, _key, _value) \
for((index) = 0, (_key) = (hashtable)->CHASH_BUCKETS_FIELD[index]. \
CHASH_KEY_FIELD, \
(_value) = (hashtable)->CHASH_BUCKETS_FIELD[index].CHASH_VALUE_FIELD; \
(index) < (hashtable)->CHASH_CAPACITY_FIELD; \
(index) = ((index) < (hashtable)->CHASH_CAPACITY_FIELD) \
? ((index) + 1) : index, \
(_key) = (hashtable)->CHASH_BUCKETS_FIELD[index].CHASH_KEY_FIELD, \
(_value) = (hashtable)->CHASH_BUCKETS_FIELD[index].CHASH_VALUE_FIELD, \
(index) = (hashtable)->CHASH_CAPACITY_FIELD)
#define chash_skip(hashtable, index) \
if((hashtable)->CHASH_BUCKETS_FIELD[index]. \
CHASH_STATE_FIELD != CHASH_FILLED) \
continue;
#endif
+464
View File
@@ -0,0 +1,464 @@
/* Clocks (v1)
* Portable Snippets - https://github.com/nemequ/portable-snippets
* Created by Evan Nemerson <[email protected]>
*
* To the extent possible under law, the authors have waived all
* copyright and related or neighboring rights to this code. For
* details, see the Creative Commons Zero 1.0 Universal license at
* https://creativecommons.org/publicdomain/zero/1.0/
*/
#if !defined(PSNIP_CLOCK_H)
#define PSNIP_CLOCK_H
/* For maximum portability include the exact-int module from
portable snippets. */
#if !defined(psnip_uint64_t) || !defined(psnip_int32_t) || \
!defined(psnip_uint32_t) || !defined(psnip_int32_t)
# include <stdint.h>
# if !defined(psnip_int64_t)
# define psnip_int64_t int64_t
# endif
# if !defined(psnip_uint64_t)
# define psnip_uint64_t uint64_t
# endif
# if !defined(psnip_int32_t)
# define psnip_int32_t int32_t
# endif
# if !defined(psnip_uint32_t)
# define psnip_uint32_t uint32_t
# endif
#endif
#if !defined(PSNIP_CLOCK_STATIC_INLINE)
# if defined(__GNUC__)
# define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__))
# else
# define PSNIP_CLOCK__COMPILER_ATTRIBUTES
# endif
# define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static
#endif
enum PsnipClockType {
/* This clock provides the current time, in units since 1970-01-01
* 00:00:00 UTC not including leap seconds. In other words, UNIX
* time. Keep in mind that this clock doesn't account for leap
* seconds, and can go backwards (think NTP adjustments). */
PSNIP_CLOCK_TYPE_WALL = 1,
/* The CPU time is a clock which increases only when the current
* process is active (i.e., it doesn't increment while blocking on
* I/O). */
PSNIP_CLOCK_TYPE_CPU = 2,
/* Monotonic time is always running (unlike CPU time), but it only
ever moves forward unless you reboot the system. Things like NTP
adjustments have no effect on this clock. */
PSNIP_CLOCK_TYPE_MONOTONIC = 3
};
struct PsnipClockTimespec {
psnip_uint64_t seconds;
psnip_uint64_t nanoseconds;
};
/* Methods we support: */
#define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1
#define PSNIP_CLOCK_METHOD_TIME 2
#define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3
#define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4
#define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5
#define PSNIP_CLOCK_METHOD_CLOCK 6
#define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7
#define PSNIP_CLOCK_METHOD_GETRUSAGE 8
#define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9
#define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10
#include <assert.h>
#if defined(HEDLEY_UNREACHABLE)
# define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE()
#else
# define PSNIP_CLOCK_UNREACHABLE() assert(0)
#endif
/* Choose an implementation */
/* #undef PSNIP_CLOCK_WALL_METHOD */
/* #undef PSNIP_CLOCK_CPU_METHOD */
/* #undef PSNIP_CLOCK_MONOTONIC_METHOD */
/* We want to be able to detect the libc implementation, so we include
<limits.h> (<features.h> isn't available everywhere). */
#if defined(__unix__) || defined(__unix) || defined(__linux__)
# include <limits.h>
# include <unistd.h>
#endif
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
/* glibc 2.17+ and FreeBSD are known to work without librt. If you
* know of others please let us know so we can add them. */
# if \
(defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \
(defined(__FreeBSD__)) || \
!defined(PSNIP_CLOCK_NO_LIBRT)
/* Even though glibc unconditionally sets _POSIX_TIMERS, it doesn't
actually declare the relevant APIs unless _POSIX_C_SOURCE >=
199309L, and if you compile in standard C mode (e.g., c11 instead
of gnu11) _POSIX_C_SOURCE will be unset by default. */
# if _POSIX_C_SOURCE >= 199309L
# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
# endif
# endif
#endif
#if defined(_WIN32)
# if !defined(PSNIP_CLOCK_CPU_METHOD)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES
# endif
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
# endif
#endif
#if defined(__MACH__) && !defined(__gnu_hurd__)
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
# endif
#endif
#if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME)
# include <time.h>
# if !defined(PSNIP_CLOCK_WALL_METHOD)
# if defined(CLOCK_REALTIME_PRECISE)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE
# elif !defined(__sun)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME
# endif
# endif
# if !defined(PSNIP_CLOCK_CPU_METHOD)
# if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID
# elif defined(CLOCK_VIRTUAL)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL
# endif
# endif
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# if defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC
# endif
# endif
#endif
#if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)
# if !defined(PSNIP_CLOCK_WALL_METHOD)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY
# endif
#endif
#if !defined(PSNIP_CLOCK_WALL_METHOD)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME
#endif
#if !defined(PSNIP_CLOCK_CPU_METHOD)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK
#endif
/* Primarily here for testing. */
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && defined(PSNIP_CLOCK_REQUIRE_MONOTONIC)
# error No monotonic clock found.
#endif
/* Implementations */
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME))
# include <time.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY))
# include <sys/time.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64))
# include <windows.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE))
# include <sys/time.h>
# include <sys/resource.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME))
# include <CoreServices/CoreServices.h>
# include <mach/mach.h>
# include <mach/mach_time.h>
#endif
/*** Implementations ***/
#define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t) (1000000000ULL))
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME))
PSNIP_CLOCK__FUNCTION psnip_uint32_t
psnip_clock__clock_getres (clockid_t clk_id) {
struct timespec res;
int r;
r = clock_getres(clk_id, &res);
if (r != 0)
return 0;
return (psnip_uint32_t) (PSNIP_CLOCK_NSEC_PER_SEC / res.tv_nsec);
}
PSNIP_CLOCK__FUNCTION int
psnip_clock__clock_gettime (clockid_t clk_id, struct PsnipClockTimespec* res) {
struct timespec ts;
if (clock_gettime(clk_id, &ts) != 0)
return -10;
res->seconds = (psnip_uint64_t) (ts.tv_sec);
res->nanoseconds = (psnip_uint64_t) (ts.tv_nsec);
return 0;
}
#endif
PSNIP_CLOCK__FUNCTION psnip_uint32_t
psnip_clock_wall_get_precision (void) {
#if !defined(PSNIP_CLOCK_WALL_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL);
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
return 1000000;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
return 1;
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_wall_get_time (struct PsnipClockTimespec* res) {
(void) res;
#if !defined(PSNIP_CLOCK_WALL_METHOD)
return -2;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res);
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
res->seconds = (uint64_t) time(NULL);
res->nanoseconds = 0;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
return -6;
res->seconds = tv.tv_sec;
res->nanoseconds = tv.tv_usec * 1000;
#else
return -2;
#endif
return 0;
}
PSNIP_CLOCK__FUNCTION psnip_uint32_t
psnip_clock_cpu_get_precision (void) {
#if !defined(PSNIP_CLOCK_CPU_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
return CLOCKS_PER_SEC;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
return PSNIP_CLOCK_NSEC_PER_SEC / 100;
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_cpu_get_time (struct PsnipClockTimespec* res) {
#if !defined(PSNIP_CLOCK_CPU_METHOD)
(void) res;
return -2;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
clock_t t = clock();
if (t == ((clock_t) -1))
return -5;
res->seconds = t / CLOCKS_PER_SEC;
res->nanoseconds = (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
FILETIME CreationTime, ExitTime, KernelTime, UserTime;
LARGE_INTEGER date, adjust;
if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime))
return -7;
/* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */
date.HighPart = UserTime.dwHighDateTime;
date.LowPart = UserTime.dwLowDateTime;
adjust.QuadPart = 11644473600000 * 10000;
date.QuadPart -= adjust.QuadPart;
res->seconds = date.QuadPart / 10000000;
res->nanoseconds = (date.QuadPart % 10000000) * (PSNIP_CLOCK_NSEC_PER_SEC / 100);
#elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage) != 0)
return -8;
res->seconds = usage.ru_utime.tv_sec;
res->nanoseconds = tv.tv_usec * 1000;
#else
(void) res;
return -2;
#endif
return 0;
}
PSNIP_CLOCK__FUNCTION psnip_uint32_t
psnip_clock_monotonic_get_precision (void) {
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
static mach_timebase_info_data_t tbi = { 0, };
if (tbi.denom == 0)
mach_timebase_info(&tbi);
return (psnip_uint32_t) (tbi.numer / tbi.denom);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
return 1000;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
return (psnip_uint32_t) ((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? PSNIP_CLOCK_NSEC_PER_SEC : Frequency.QuadPart);
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_monotonic_get_time (struct PsnipClockTimespec* res) {
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
(void) res;
return -2;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
psnip_uint64_t nsec = mach_absolute_time();
static mach_timebase_info_data_t tbi = { 0, };
if (tbi.denom == 0)
mach_timebase_info(&tbi);
nsec *= ((psnip_uint64_t) tbi.numer) / ((psnip_uint64_t) tbi.denom);
res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC;
res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
LARGE_INTEGER t, f;
if (QueryPerformanceCounter(&t) == 0)
return -12;
QueryPerformanceFrequency(&f);
res->seconds = t.QuadPart / f.QuadPart;
res->nanoseconds = t.QuadPart % f.QuadPart;
if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)
res->nanoseconds /= f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC;
else
res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / f.QuadPart;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
const ULONGLONG msec = GetTickCount64();
res->seconds = msec / 1000;
res->nanoseconds = sec % 1000;
#else
return -2;
#endif
return 0;
}
/* Returns the number of ticks per second for the specified clock.
* For example, a clock with millisecond precision would return 1000,
* and a clock with 1 second (such as the time() function) would
* return 1.
*
* If the requested clock isn't available, it will return 0.
* Hopefully this will be rare, but if it happens to you please let us
* know so we can work on finding a way to support your system.
*
* Note that different clocks on the same system often have a
* different precisions.
*/
PSNIP_CLOCK__FUNCTION psnip_uint32_t
psnip_clock_get_precision (enum PsnipClockType clock_type) {
switch (clock_type) {
case PSNIP_CLOCK_TYPE_MONOTONIC:
return psnip_clock_monotonic_get_precision ();
case PSNIP_CLOCK_TYPE_CPU:
return psnip_clock_cpu_get_precision ();
case PSNIP_CLOCK_TYPE_WALL:
return psnip_clock_wall_get_precision ();
}
PSNIP_CLOCK_UNREACHABLE();
return 0;
}
/* Set the provided timespec to the requested time. Returns 0 on
* success, or a negative value on failure. */
PSNIP_CLOCK__FUNCTION int
psnip_clock_get_time (enum PsnipClockType clock_type, struct PsnipClockTimespec* res) {
assert(res != NULL);
switch (clock_type) {
case PSNIP_CLOCK_TYPE_MONOTONIC:
return psnip_clock_monotonic_get_time (res);
case PSNIP_CLOCK_TYPE_CPU:
return psnip_clock_cpu_get_time (res);
case PSNIP_CLOCK_TYPE_WALL:
return psnip_clock_wall_get_time (res);
}
return -1;
}
#endif /* !defined(PSNIP_CLOCK_H) */
+137
View File
@@ -0,0 +1,137 @@
#ifndef COG_UTILS_H
#define COG_UTILS_H
#include <stdio.h>
#include <stdint.h>
#include "attributes.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @brief Load file contents into a string
*
* @param fp the file to be read
* @param len optional pointer to store the amount of bytes read
* @return the file contents
*/
char *cog_load_whole_file_fp(FILE *fp, size_t *len);
/**
* @brief Load file contents into a string
*
* Wrapper over cog_load_whole_file_fp(), get the file by its
* relative-path.
* @param filename the name of the file to be read
* @param len optional pointer to store the amount of bytes read
* @return the file contents
*/
char *cog_load_whole_file(const char filename[], size_t *len);
/**
* @brief Get the difference between UTC and the latest local standard time, in
* seconds.
* @return difference between UTC and local time in seconds
*/
long cog_timezone(void);
/**
* @brief Convert a iso8601 string to a unix timestamp (milliseconds)
*
* Can be matched to the json_extract() and json_inject() %F specifier
* @param str the iso8601 string timestamp
* @param len the string length
* @param p_value pointer to the `uint64_t` variable to receive the converted
* timestamp
* @return 1 on success, 0 on failure
*/
int cog_iso8601_to_unix_ms(const char str[], size_t len, uint64_t *p_value);
/**
* @brief Convert a unix timestamp (milliseconds) to a iso8601 string
*
* @param timestamp the buffer to receive the converted timestamp
* @param len the size of the buffer
* @param value the unix timestamp to be converted to iso8601
* @return the amount of characters (in bytes) written to the buffer
*/
int cog_unix_ms_to_iso8601(char str[], size_t len, const uint64_t value);
/**
* @brief Convert a numerical string to `uint64_t`
*
* @param str the numerical string
* @param len the string length
* @param p_value pointer to the `uint64_t` variable to receive the converted
* value
* @return 1 on success, 0 on failure
*/
int cog_strtou64(char *str, size_t len, uint64_t *p_value);
/**
* @brief Convert `uint64_t` to a numerical string
*
* @param str the buffer to store the numerical string
* @param len the size of the buffer
* @param p_value the `unsigned long long` value
* @return the amount of characters (in bytes) written to the buffer
*/
int cog_u64tostr(char *str, size_t len, uint64_t *p_value);
/**
* @brief Copies at most `len` bytes of `src` to `*p_dest`.
*
* Analogous to `strndup()`
* @param src the buffer to be copied
* @param len the maximum amount of characters to be copied
* @param p_dest a pointer to the new `src` copy
* @return length of copied string on success, 0 on failure
*/
size_t cog_strndup(const char src[], size_t len, char **p_dest);
/**
* @brief Copies at most `len` bytes of `src` to `*p_dest`.
*
* Analogous to `asprintf()`
* @param strp source to write resulting string to
* @param fmt printf format string
* @param ... variadic arguments to be matched to `fmt` specifiers
* @return length of copied string on success, -1 on failure
*/
size_t cog_asprintf(char **strp, const char fmt[], ...) PRINTF_LIKE(2, 3);
/**
* @brief Sleep for amount of milliseconds
*
* @param tms amount of milliseconds to sleep for
* @return 0 on success, -1 on error with an `errno` set to indicate the error
*/
int cog_sleep_ms(const long tms);
/**
* @brief Sleep for amount of microseconds
*
* @param tms amount of microseconds to sleep for
* @return 0 on success, -1 on error with an `errno` set to indicate the error
*/
int cog_sleep_us(const long tms);
/**
* @brief Get the current timestamp in milliseconds
*
* @return the timestamp on success, 0 on failure
*/
uint64_t cog_timestamp_ms(void);
/**
* @brief Get the current timestamp in microseconds
*
* @return the timestamp on success, 0 on failure
*/
uint64_t cog_timestamp_us(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* COG_UTILS_H */
+69
View File
@@ -0,0 +1,69 @@
/** @file error.h */
#ifndef CONCORD_ERROR_H
#define CONCORD_ERROR_H
/** @defgroup ConcordError Error handling
* @brief Concord error codes and meaning
* @{ */
/** @brief Concord error codes */
typedef int CCORDcode;
/* XXX: As new values are added, ccord_strerror() and ccord_code_as_string()
* should be updated accordingly! */
/** @defgroup CoreError Core error codes
* @brief These codes are used by the core library and should be used by all
* modules
* @
* @{ */
/** most likely a bug in the library, please report it */
#define CCORD_INTERNAL_ERROR -200
/** couldn't encode format */
#define CCORD_BAD_ENCODE -101
/** couldn't decode format */
#define CCORD_BAD_DECODE -100
/** out of memory, something really bad happened! */
#define CCORD_OUT_OF_MEMORY -60
/** check strerror() for more information */
#define CCORD_ERRNO -50
/** curl has been compiled without the --enable-websockets flag */
#define CCORD_CURL_WEBSOCKETS_MISSING -14
/** curl need to be updated to 8.7.1 or greater */
#define CCORD_CURL_OUTDATED_VERSION -13
/** failure when creating request's payload */
#define CCORD_MALFORMED_PAYLOAD -12
/** couldn't enqueue worker thread (queue is full) */
#define CCORD_FULL_WORKER -11
/** couldn't perform action because resource is unavailable */
#define CCORD_RESOURCE_UNAVAILABLE -10
/** couldn't cleanup resource automatically due to being claimed */
#define CCORD_RESOURCE_OWNERSHIP -9
/** attempt to initialize globals more than once */
#define CCORD_GLOBAL_INIT -8
/** curl's multi handle internal error */
#define CCORD_CURLM_INTERNAL -7
/** curl's easy handle internal error */
#define CCORD_CURLE_INTERNAL -6
/** internal failure when encoding or decoding JSON */
#define CCORD_BAD_JSON -5
/** bad value for parameter */
#define CCORD_BAD_PARAMETER -4
/** received a non-standard http code */
#define CCORD_UNUSUAL_HTTP_CODE -3
/** no response came through from curl */
#define CCORD_CURL_NO_RESPONSE -2
/** request wasn't succesful */
#define CCORD_HTTP_CODE -1
/** action was a success */
#define CCORD_OK 0
const char *ccord_code_as_string(CCORDcode code);
const char *ccord_strerror(CCORDcode code);
/** @} CoreError */
/** @} ConcordError */
#endif /* CONCORD_ERROR_H */
+50
View File
@@ -0,0 +1,50 @@
/**
* @file concord-notifier.h
* @author Cogmasters
* @brief Notifier fds listening to pipe, this can be used to propagate events
*/
#ifndef CONCORD_NOTIFIER_H
#define CONCORD_NOTIFIER_H
#include "concord-error.h"
/**
* @brief Open notifier pipe
*
* @param pipe The pipe to open for emitting notifications
* @return CCORDcode on success, CCORD_ERRNO on error
*/
CCORDcode ccord_notifier_open(int pipe[2]);
/**
* @brief Close notifier pipe
*
* @param pipe The pipe to close
*/
void ccord_notifier_close(int pipe[2]);
/**
* @brief Notify fds listening to pipe
*
* @param pipe The pipe to notify
*/
void ccord_notifier_notify(int pipe[2]);
/**
* @brief Whether or not pipe is currently notifying fds
*
* @param pipe The pipe to check
* @return 1 if notifying, 0 if not
*/
_Bool ccord_notifier_is_notifying(int pipe[2]);
/**
* @brief Receive a listener for pipe notifications
*
* @param pipe The pipe to listen to
* @return fd on success, -1 on error
*/
int ccord_notifier_listen(const int pipe[2]);
#endif /* CONCORD_NOTIFIER_H */
+49
View File
@@ -0,0 +1,49 @@
/**
* @file concord-once.h
* @author Cogmasters
* @brief Initialized once
*/
#ifndef CONCORD_ONCE_H
#define CONCORD_ONCE_H
#include "concord-error.h"
/** Callback function type for user initialization */
typedef CCORDcode (*ccord_once_cb)(long flags);
/**
* @brief Register a user callback for initialization
*
* This callback will be executed exactly once during initialization
* after all internal initializations have been performed.
* Multiple callbacks can be registered from different modules, and they will
* be executed in the order they were registered.
*
* @param callback The function to call during initialization
* @param flags Flag to pass to the callback, that will be passed to the
* callback function
* @CCORD_return
*/
CCORDcode ccord_once_set_callback(ccord_once_cb callback, long flags);
/**
* @brief Initialize context once
*
* @param once Pointer to a static boolean flag that will be set to true if the
* initialization was successful. This flag should be used to ensure that
* the initialization is only performed once.
* @CCORD_return
*/
CCORDcode ccord_once(_Bool *once);
/**
* @brief Cleanup once context
*
* This function will be called to cleanup the once context.
* It will be called when the program is exiting, and it will
* cleanup all registered callbacks.
*/
void ccord_once_cleanup(void);
#endif /* CONCORD_ONCE_H */
+51
View File
@@ -0,0 +1,51 @@
/**
* @file discord-cache.h
* @author Cogmasters
* @brief Caching of Discord resources
*/
#ifndef DISCORD_CACHE_H
#define DISCORD_CACHE_H
/** @defgroup DiscordClientCache Caching
* @ingroup DiscordClient
* @brief Caching API supported by Concord
* @{ */
enum discord_cache_options {
DISCORD_CACHE_MESSAGES = 1 << 0,
DISCORD_CACHE_GUILDS = 1 << 1,
};
void discord_cache_enable(struct discord *client,
enum discord_cache_options options);
/**
* @brief Get a message from cache, only if locally available in RAM
* @note When done, discord_unclaim() must be called on the message resource
*
* @param client the client initialized with discord_from_token()
* @param channel_id the channel id the message is in
* @param message_id the id of the message
* @return `NULL` if not found, or a cache'd message
*/
const struct discord_message *discord_cache_get_channel_message(
struct discord *client, u64snowflake channel_id, u64snowflake message_id);
/**
* @brief Get a guild from cache, only if locally available in RAM
* @note When done, discord_unclaim() must be called on the guild resource
*
* @param client the client initialized with discord_from_token()
* @param guild_id the id of the guild
* @return `NULL` if not found, or a cache'd guild
*/
const struct discord_guild *discord_cache_get_guild(struct discord *client,
u64snowflake guild_id);
/** @example cache.c
* Demonstrates cache usage */
/** @} DiscordClientCache */
#endif /* DISCORD_CACHE_H */
+989
View File
@@ -0,0 +1,989 @@
/**
* @file discord-events.h
* @author Cogmasters
* @brief Listen, react and trigger Discord Gateway events
*/
#ifndef DISCORD_EVENTS_H
#define DISCORD_EVENTS_H
/** @defgroup DiscordCommands Commands
* @ingroup DiscordClient
* @brief Requests made by the client to the Gateway socket
* @{ */
/**
* @brief Request all members for a guild or a list of guilds
* @see
* https://discord.com/developers/docs/topics/gateway#request-guild-members
*
* @param client the client created with discord_from_token()
* @param request request guild members information
*/
void discord_request_guild_members(
struct discord *client, struct discord_request_guild_members *request);
/**
* @brief Sent when a client wants to join, move or disconnect from a voice
* channel
*
* @param client the client created with discord_from_token()
* @param update request guild members information
*/
void discord_update_voice_state(struct discord *client,
struct discord_update_voice_state *update);
/**
* @brief Update the client presence status
* @see discord_presence_add_activity()
*
* @param client the client created with discord_from_token()
* @param presence status to update the client's to
*/
void discord_update_presence(struct discord *client,
struct discord_presence_update *presence);
/** @} DiscordCommands */
/** @defgroup DiscordEvents Events
* @ingroup DiscordClient
* @brief Events sent over the Gateway socket to the client
* @{ */
/** @brief Discord Gateway's events */
enum discord_gateway_events {
DISCORD_EV_NONE = 0, /**< missing event */
DISCORD_EV_READY,
DISCORD_EV_RESUMED,
DISCORD_EV_RECONNECT,
DISCORD_EV_INVALID_SESSION,
DISCORD_EV_APPLICATION_COMMAND_PERMISSIONS_UPDATE,
DISCORD_EV_AUTO_MODERATION_RULE_CREATE,
DISCORD_EV_AUTO_MODERATION_RULE_UPDATE,
DISCORD_EV_AUTO_MODERATION_RULE_DELETE,
DISCORD_EV_AUTO_MODERATION_ACTION_EXECUTION,
DISCORD_EV_CHANNEL_CREATE,
DISCORD_EV_CHANNEL_UPDATE,
DISCORD_EV_CHANNEL_DELETE,
DISCORD_EV_CHANNEL_PINS_UPDATE,
DISCORD_EV_THREAD_CREATE,
DISCORD_EV_THREAD_UPDATE,
DISCORD_EV_THREAD_DELETE,
DISCORD_EV_THREAD_LIST_SYNC,
DISCORD_EV_THREAD_MEMBER_UPDATE,
DISCORD_EV_THREAD_MEMBERS_UPDATE,
DISCORD_EV_GUILD_CREATE,
DISCORD_EV_GUILD_UPDATE,
DISCORD_EV_GUILD_DELETE,
DISCORD_EV_GUILD_BAN_ADD,
DISCORD_EV_GUILD_BAN_REMOVE,
DISCORD_EV_GUILD_EMOJIS_UPDATE,
DISCORD_EV_GUILD_STICKERS_UPDATE,
DISCORD_EV_GUILD_INTEGRATIONS_UPDATE,
DISCORD_EV_GUILD_MEMBER_ADD,
DISCORD_EV_GUILD_MEMBER_REMOVE,
DISCORD_EV_GUILD_MEMBER_UPDATE,
DISCORD_EV_GUILD_MEMBERS_CHUNK,
DISCORD_EV_GUILD_ROLE_CREATE,
DISCORD_EV_GUILD_ROLE_UPDATE,
DISCORD_EV_GUILD_ROLE_DELETE,
DISCORD_EV_GUILD_SCHEDULED_EVENT_CREATE,
DISCORD_EV_GUILD_SCHEDULED_EVENT_UPDATE,
DISCORD_EV_GUILD_SCHEDULED_EVENT_DELETE,
DISCORD_EV_GUILD_SCHEDULED_EVENT_USER_ADD,
DISCORD_EV_GUILD_SCHEDULED_EVENT_USER_REMOVE,
DISCORD_EV_INTEGRATION_CREATE,
DISCORD_EV_INTEGRATION_UPDATE,
DISCORD_EV_INTEGRATION_DELETE,
DISCORD_EV_INTERACTION_CREATE,
DISCORD_EV_INVITE_CREATE,
DISCORD_EV_INVITE_DELETE,
DISCORD_EV_MESSAGE_CREATE,
DISCORD_EV_MESSAGE_UPDATE,
DISCORD_EV_MESSAGE_DELETE,
DISCORD_EV_MESSAGE_DELETE_BULK,
DISCORD_EV_MESSAGE_REACTION_ADD,
DISCORD_EV_MESSAGE_REACTION_REMOVE,
DISCORD_EV_MESSAGE_REACTION_REMOVE_ALL,
DISCORD_EV_MESSAGE_REACTION_REMOVE_EMOJI,
DISCORD_EV_PRESENCE_UPDATE,
DISCORD_EV_STAGE_INSTANCE_CREATE,
DISCORD_EV_STAGE_INSTANCE_DELETE,
DISCORD_EV_STAGE_INSTANCE_UPDATE,
DISCORD_EV_TYPING_START,
DISCORD_EV_USER_UPDATE,
DISCORD_EV_VOICE_STATE_UPDATE,
DISCORD_EV_VOICE_SERVER_UPDATE,
DISCORD_EV_WEBHOOKS_UPDATE,
DISCORD_EV_MAX /**< total amount of enumerators */
};
/**
* @brief return value of discord_set_event_scheduler() callback
* @see discord_set_event_scheduler()
*/
typedef enum discord_event_scheduler {
/** this event has been handled */
DISCORD_EVENT_IGNORE,
/** handle this event in main thread */
DISCORD_EVENT_MAIN_THREAD,
/** handle this event in a worker thread */
DISCORD_EVENT_WORKER_THREAD
} discord_event_scheduler_t;
/**
* @brief Event Handling Mode callback
*
* A very important callback that enables the user with a fine-grained control
* of how each event is handled: blocking, non-blocking or ignored
* @see discord_set_event_scheduler(), @ref discord_gateway_events
*/
typedef enum discord_event_scheduler (*discord_ev_scheduler)(
struct discord *client,
const char data[],
size_t size,
enum discord_gateway_events event);
/**
* @brief Provides control over Discord event's callback scheduler
* @see @ref discord_event_scheduler, @ref discord_gateway_events
*
* Allows the user to scan the preliminary raw JSON event payload, and control
* whether it should trigger callbacks
* @param client the client created_with discord_from_token()
* @param fn the function that will be executed
* @warning The user is responsible for providing their own locking mechanism
* to avoid race-condition on sensitive data
*/
void discord_set_event_scheduler(struct discord *client,
discord_ev_scheduler callback);
/**
* @brief Subscribe to Discord Events
*
* @param client the client created with discord_from_token()
* @param code the intents opcode, can be set as a bitmask operation
*/
void discord_add_intents(struct discord *client, uint64_t code);
/**
* @brief Unsubscribe from Discord Events
*
* @param client the client created with discord_from_token()
* @param code the intents opcode, can be set as bitmask operation
* Ex: 1 << 0 | 1 << 1 | 1 << 4
*/
void discord_remove_intents(struct discord *client, uint64_t code);
/**
* @brief Set a mandatory prefix before commands
* @see discord_set_on_command()
*
* Example: If @a 'help' is a command and @a '!' prefix is set, the command
* will only be validated if @a '!help' is sent
* @param client the client created with discord_from_token()
* @param prefix the mandatory command prefix
*/
void discord_set_prefix(struct discord *client, const char prefix[]);
/**
* @brief Set command/callback pair
*
* The callback is triggered when a user types the assigned command in a
* chat visible to the client
* @param client the client created with discord_from_token()
* @param command the command to trigger the callback
* @param callback the callback to be triggered on event
* @note The command and any subjacent empty space is left out of
* the message content
*/
void discord_set_on_command(
struct discord *client,
const char *command,
void (*callback)(struct discord *client,
const struct discord_message *event));
/**
* @brief Set a variadic series of NULL terminated commands to a callback
*
* The callback is triggered when a user types one of the assigned commands in
* a chat visble to the client
* @param client the client created with discord_from_token()
* @param commands array of commands to trigger the callback
* @param amount amount of commands provided
* @param callback the callback to be triggered on event
* @note The command and any subjacent empty space is left out of
* the message content
*/
void discord_set_on_commands(
struct discord *client,
const char *commands[],
int amount,
void (*callback)(struct discord *client,
const struct discord_message *event));
/**
* @brief Triggers when idle
* @note This is a Concord custom event
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_idle(struct discord *client,
void (*callback)(struct discord *client));
/**
* @brief Triggers once per event-loop cycle
* @note This is a Concord custom event
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_cycle(struct discord *client,
void (*callback)(struct discord *client));
/**
* @brief Triggers when the client session is ready
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_ready(struct discord *client,
void (*callback)(struct discord *client,
const struct discord_ready *event));
/**
* @brief Triggers when an application command permission is updated
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_application_command_permissions_update(
struct discord *client,
void (*callback)(
struct discord *client,
const struct discord_application_command_permissions *event));
/**
* @brief Triggers when an auto moderation rule is created
* @note This implicitly sets
* @ref DISCORD_GATEWAY_AUTO_MODERATION_CONFIGURATION intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_auto_moderation_rule_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_auto_moderation_rule *event));
/**
* @brief Triggers when an auto moderation rule is updated
* @note This implicitly sets
* @ref DISCORD_GATEWAY_AUTO_MODERATION_CONFIGURATION intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_auto_moderation_rule_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_auto_moderation_rule *event));
/**
* @brief Triggers when an auto moderation rule is deleted
* @note This implicitly sets
* @ref DISCORD_GATEWAY_AUTO_MODERATION_CONFIGURATION intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_auto_moderation_rule_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_auto_moderation_rule *event));
/**
* @brief Triggers when an auto moderation rule is triggered and an execution
* is executed (e.g a message was blocked)
* @note This implicitly sets @ref DISCORD_GATEWAY_AUTO_MODERATION_EXECUTION
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_auto_moderation_action_execution(
struct discord *client,
void (*callback)(
struct discord *client,
const struct discord_auto_moderation_action_execution *event));
/**
* @brief Triggers when a channel is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_channel_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_channel *event));
/**
* @brief Triggers when a channel is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_channel_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_channel *event));
/**
* @brief Triggers when a channel is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_channel_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_channel *event));
/**
* @brief Triggers when a channel pin is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_channel_pins_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_channel_pins_update *event));
/**
* @brief Triggers when a thread is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_thread_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_channel *event));
/**
* @brief Triggers when a thread is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_thread_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_channel *event));
/**
* @brief Triggers when a thread is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_thread_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_channel *event));
/**
* @brief Triggers when the current user gains access to a channel
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_thread_list_sync(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_thread_list_sync *event));
/**
* @brief Triggers when a thread the bot is in gets updated
* @note For bots, this event largely is just a signal that you are a member of
* the thread
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_thread_member_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_thread_member *event));
/**
* @brief Triggers when someone is added or removed from a thread
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS and
* @ref DISCORD_GATEWAY_GUILD_MEMBERS intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_thread_members_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_thread_members_update *event));
/**
* @brief Triggers when a guild is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild *event));
/**
* @brief Triggers when a guild is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild *event));
/**
* @brief Triggers when a guild is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild *event));
/**
* @brief Triggers when a user is banned from a guild
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_BANS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_ban_add(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_ban_add *event));
/**
* @brief Triggers when a user is unbanned from a guild
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_BANS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_ban_remove(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_ban_remove *event));
/**
* @brief Triggers when a guild emojis are updated
* @note This implicitly sets
* @ref DISCORD_GATEWAY_GUILD_EMOJIS_AND_STICKERS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_emojis_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_emojis_update *event));
/**
* @brief Triggers when a guild stickers are updated
* @note This implicitly sets
* @ref DISCORD_GATEWAY_GUILD_EMOJIS_AND_STICKERS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_stickers_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_stickers_update *event));
/**
* @brief Triggers when a guild integrations are updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_integrations_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_integrations_update *event));
/**
* @brief Triggers when a guild member is added
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MEMBERS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_member_add(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_member *event));
/**
* @brief Triggers when a guild member is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MEMBERS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_member_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_member_update *event));
/**
* @brief Triggers when a guild member is removed
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MEMBERS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_member_remove(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_member_remove *event));
/**
* @brief Triggers in response to discord_request_guild_members()
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_members_chunk(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_members_chunk *event));
/**
* @brief Triggers when a guild role is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_role_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_role_create *event));
/**
* @brief Triggers when a guild role is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_role_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_role_update *event));
/**
* @brief Triggers when a guild role is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_role_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_role_delete *event));
/**
* @brief Triggers when a guild scheduled event is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_scheduled_event_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_scheduled_event *event));
/**
* @brief Triggers when a guild scheduled event is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_scheduled_event_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_scheduled_event *event));
/**
* @brief Triggers when a guild scheduled event is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_scheduled_event_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_guild_scheduled_event *event));
/**
* @brief Triggers when a user subscribes to a guild scheduled event
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_scheduled_event_user_add(
struct discord *client,
void (*callback)(
struct discord *client,
const struct discord_guild_scheduled_event_user_add *event));
/**
* @brief Triggers when a user unsubscribes from a guild scheduled event
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_guild_scheduled_event_user_remove(
struct discord *client,
void (*callback)(
struct discord *client,
const struct discord_guild_scheduled_event_user_remove *event));
/**
* @brief Triggers when a guild integration is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_integration_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_integration *event));
/**
* @brief Triggers when a guild integration is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_integration_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_integration *event));
/**
* @brief Triggers when a guild integration is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_integration_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_integration_delete *event));
/**
* @brief Triggers when user has used an interaction, such as an application
* command
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_interaction_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_interaction *event));
/**
* @brief Triggers when an invite to a channel has been created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INVITES intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_invite_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_invite_create *event));
/**
* @brief Triggers when an invite to a channel has been deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INVITES intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_invite_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_invite_delete *event));
/**
* @brief Triggers when a message is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_message *event));
/**
* @brief Triggers when a message is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_message *event));
/**
* @brief Triggers when a message is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_message_delete *event));
/**
* @brief Triggers when messages are deleted in bulk
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES
* intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_delete_bulk(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_message_delete_bulk *event));
/**
* @brief Triggers when a message reaction is added
* @note This implicitly sets
* @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_reaction_add(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_message_reaction_add *event));
/**
* @brief Triggers when a message reaction is removed
* @note This implicitly sets
* @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_reaction_remove(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_message_reaction_remove *event));
/**
* @brief Triggers when all message reactions are removed
* @note This implicitly sets
* @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_reaction_remove_all(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_message_reaction_remove_all *event));
/** @brief Triggers when all instances of a particular reaction from some
* message is removed */
/**
* @brief Triggers when all instances of a particular reaction is removed from
* a message
* @note This implicitly sets
* @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_message_reaction_remove_emoji(
struct discord *client,
void (*callback)(
struct discord *client,
const struct discord_message_reaction_remove_emoji *event));
/**
* @brief Triggers when user presence is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_PRESENCES intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_presence_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_presence_update *event));
/**
* @brief Triggers when a stage instance is created
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_stage_instance_create(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_stage_instance *event));
/**
* @brief Triggers when a stage instance is updated
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_stage_instance_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_stage_instance *event));
/**
* @brief Triggers when a stage instance is deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_stage_instance_delete(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_stage_instance *event));
/**
* @brief Triggers when user starts typing in a channel
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGE_TYPING and
* @ref DISCORD_GATEWAY_DIRECT_MESSAGE_TYPING intents
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_typing_start(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_typing_start *event));
/**
* @brief Triggers when properties about a user changed
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_user_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_user *event));
/**
* @brief Triggers when a voice state is updated
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_voice_state_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_voice_state *event));
/**
* @brief Triggers when voice server is updated
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_voice_server_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_voice_server_update *event));
/**
* @brief Triggers when guild channel has been created, updated or deleted
* @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_WEBHOOKS intent
*
* @param client the client created with discord_from_token()
* @param callback the callback to be triggered on event
*/
void discord_set_on_webhooks_update(
struct discord *client,
void (*callback)(struct discord *client,
const struct discord_webhooks_update *event));
/** @} DiscordEvents */
#endif /* DISCORD_EVENTS_H */
File diff suppressed because it is too large Load Diff
+101
View File
@@ -0,0 +1,101 @@
/**
* @file discord-request.h
* @ingroup DiscordInternalREST
* @author Cogmasters
* @brief Generic macros for initializing a @ref discord_attributes
*/
#ifndef DISCORD_REQUEST_H
#define DISCORD_REQUEST_H
/* helper typedefs for casting */
typedef void (*cast_done_typed)(struct discord *,
struct discord_response *,
const void *);
typedef void (*cast_init)(void *);
typedef void (*cast_cleanup)(void *);
typedef size_t (*cast_from_json)(const char *, size_t, void *);
/* helper typedef for getting sizeof of `struct discord_ret` common fields */
typedef struct {
DISCORD_RET_DEFAULT_FIELDS;
} discord_ret_default_fields;
#define _RET_COPY_TYPED(dest, src) \
do { \
memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \
(dest).has_type = true; \
(dest).done.typed = (cast_done_typed)(src).done; \
(dest).sync = (src).sync; \
} while (0)
#define _RET_COPY_TYPELESS(dest, src) \
do { \
memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \
(dest).has_type = false; \
(dest).done.typeless = (src).done; \
(dest).sync = (void *)(src).sync; \
} while (0)
/**
* @brief Helper for setting attributes for a specs-generated return struct
*
* @param[out] attr @ref discord_attributes handler to be initialized
* @param[in] type datatype of the struct
* @param[in] ret dispatch attributes
* @param[in] _reason reason for request (if available)
*/
#define DISCORD_ATTR_INIT(attr, type, ret, _reason) \
do { \
(attr).response.size = sizeof(struct type); \
(attr).response.init = (cast_init)type##_init; \
(attr).response.from_json = (cast_from_json)type##_from_json; \
(attr).response.cleanup = (cast_cleanup)type##_cleanup; \
(attr).reason = _reason; \
if (ret) _RET_COPY_TYPED(attr.dispatch, *ret); \
} while (0)
/**
* @brief Helper for setting attributes for a specs-generated list
*
* @param[out] attr @ref discord_attributes handler to be initialized
* @param[in] type datatype of the list
* @param[in] ret dispatch attributes
* @param[in] _reason reason for request (if available)
*/
#define DISCORD_ATTR_LIST_INIT(attr, type, ret, _reason) \
do { \
(attr).response.size = sizeof(struct type); \
(attr).response.from_json = (cast_from_json)type##_from_json; \
(attr).response.cleanup = (cast_cleanup)type##_cleanup; \
(attr).reason = _reason; \
if (ret) _RET_COPY_TYPED(attr.dispatch, *ret); \
} while (0)
/**
* @brief Helper for setting attributes for attruests that doensn't expect a
* response object
*
* @param[out] attr @ref discord_attributes handler to be initialized
* @param[in] ret dispatch attributes
* @param[in] _reason reason for request (if available)
*/
#define DISCORD_ATTR_BLANK_INIT(attr, ret, _reason) \
do { \
(attr).reason = _reason; \
if (ret) _RET_COPY_TYPELESS(attr.dispatch, *ret); \
} while (0)
/**
* @brief Helper for initializing attachments ids
*
* @param[in,out] attchs a @ref discord_attachments to have its IDs initialized
*/
#define DISCORD_ATTACHMENTS_IDS_INIT(attchs) \
do { \
for (int i = 0; i < attchs->size; ++i) { \
attchs->array[i].id = (u64snowflake)i; \
} \
} while (0)
#endif /* DISCORD_REQUEST_H */
+192
View File
@@ -0,0 +1,192 @@
/**
* @file discord-response.h
* @author Cogmasters
* @brief Generic macros for initializing a @ref discord_response and return
* handles
*/
#ifndef DISCORD_RESPONSE_H
#define DISCORD_RESPONSE_H
/** @brief The response for the completed request */
struct discord_response {
/** user arbitrary data provided at @ref discord_ret */
void *data;
/** kept concord's parameter provided at @ref discord_ret */
const void *keep;
/** request completion status @see @ref ConcordError */
CCORDcode code;
/** the JSON object in case of a @ref CCORD_OK, or the JSON error
* object in case of a @ref CCORD_DISCORD_JSON_CODE
*
* @see https://discord.com/developers/docs/reference#error-messages */
struct ccord_szbuf_readonly json;
};
/******************************************************************************
* Templates for generating type-safe return handles for async requests
******************************************************************************/
/**
* @brief Macro containing common fields for `struct discord_ret*` datatypes
* @note this exists for alignment purposes
*/
#define DISCORD_RET_DEFAULT_FIELDS \
/** user arbitrary data to be passed to `done` or `fail` callbacks */ \
void *data; \
/** cleanup method to be called for `data`, once its no longer \
being referenced */ \
void (*cleanup)(struct discord * client, void *data); \
/** Concord callback parameter the client wish to keep reference */ \
const void *keep; \
/** if `true` then request will be prioritized over already enqueued \
requests */ \
bool high_priority; \
/** optional callback to be executed on a failed request */ \
void (*fail)(struct discord * client, struct discord_response * resp)
#define DISCORD_RETURN(_type) \
/** @brief Request's return context */ \
struct discord_ret_##_type { \
DISCORD_RET_DEFAULT_FIELDS; \
/** optional callback to be executed on a successful request */ \
void (*done)(struct discord * client, \
struct discord_response *resp, \
const struct discord_##_type *ret); \
/** if an address is provided, then request will block the thread and \
perform on-spot. \
On success the response object will be written to the address, \
unless enabled with @ref DISCORD_SYNC_FLAG */ \
struct discord_##_type *sync; \
}
/** @brief Request's return context */
struct discord_ret {
DISCORD_RET_DEFAULT_FIELDS;
/** optional callback to be executed on a successful request */
void (*done)(struct discord *client, struct discord_response *resp);
/** if `true`, request will block the thread and perform on-spot */
bool sync;
};
/** @brief flag for enabling `sync` mode without expecting a datatype return */
#define DISCORD_SYNC_FLAG ((void *)-1)
/** @addtogroup DiscordAPIOAuth2
* @{ */
DISCORD_RETURN(application);
DISCORD_RETURN(auth_response);
/** @} DiscordAPIOAuth2 */
/** @addtogroup DiscordAPIAuditLog
* @{ */
DISCORD_RETURN(audit_log);
/** @} DiscordAPIAuditLog */
/** @addtogroup DiscordAPIAutoModeration
* @{ */
DISCORD_RETURN(auto_moderation_rule);
DISCORD_RETURN(auto_moderation_rules);
/** @} DiscordAPIAutoModeration */
/** @addtogroup DiscordAPIChannel
* @{ */
DISCORD_RETURN(channel);
DISCORD_RETURN(channels);
DISCORD_RETURN(message);
DISCORD_RETURN(messages);
DISCORD_RETURN(followed_channel);
DISCORD_RETURN(thread_members);
DISCORD_RETURN(thread_response_body);
/** @} DiscordAPIChannel */
/** @addtogroup DiscordAPIEmoji
* @{ */
DISCORD_RETURN(emoji);
DISCORD_RETURN(emojis);
/** @} DiscordAPIEmoji */
/** @addtogroup DiscordAPIGuild
* @{ */
DISCORD_RETURN(guild);
DISCORD_RETURN(guilds);
DISCORD_RETURN(guild_preview);
DISCORD_RETURN(guild_member);
DISCORD_RETURN(guild_members);
DISCORD_RETURN(guild_widget);
DISCORD_RETURN(guild_widget_settings);
DISCORD_RETURN(ban);
DISCORD_RETURN(bans);
DISCORD_RETURN(role);
DISCORD_RETURN(roles);
DISCORD_RETURN(welcome_screen);
DISCORD_RETURN(integrations);
DISCORD_RETURN(prune_count);
/** @} DiscordAPIGuild */
/** @addtogroup DiscordAPIGuildScheduledEvent
* @{ */
DISCORD_RETURN(guild_scheduled_event);
DISCORD_RETURN(guild_scheduled_events);
DISCORD_RETURN(guild_scheduled_event_users);
/** @} DiscordAPIGuildScheduledEvent */
/** @addtogroup DiscordAPIGuildTemplate
* @{ */
DISCORD_RETURN(guild_template);
DISCORD_RETURN(guild_templates);
/** @} DiscordAPIGuildTemplate */
/** @addtogroup DiscordAPIInvite
* @{ */
DISCORD_RETURN(invite);
DISCORD_RETURN(invites);
/** @} DiscordAPIInvite */
/** @addtogroup DiscordAPIStageInstance
* @{ */
DISCORD_RETURN(stage_instance);
/** @} DiscordAPIStageInstance */
/** @addtogroup DiscordAPISticker
* @{ */
DISCORD_RETURN(sticker);
DISCORD_RETURN(stickers);
DISCORD_RETURN(list_nitro_sticker_packs);
/** @} DiscordAPISticker */
/** @addtogroup DiscordAPIUser
* @{ */
DISCORD_RETURN(user);
DISCORD_RETURN(users);
DISCORD_RETURN(connections);
/** @} DiscordAPIUser */
/** @addtogroup DiscordAPIVoice
* @{ */
DISCORD_RETURN(voice_regions);
/** @} DiscordAPIVoice */
/** @addtogroup DiscordAPIWebhook
* @{ */
DISCORD_RETURN(webhook);
DISCORD_RETURN(webhooks);
/** @} DiscordAPIWebhook */
/** @addtogroup DiscordAPIInteractionsApplicationCommand
* @ingroup DiscordAPIInteractions
* @{ */
DISCORD_RETURN(application_command);
DISCORD_RETURN(application_commands);
DISCORD_RETURN(application_command_permission);
DISCORD_RETURN(application_command_permissions);
DISCORD_RETURN(guild_application_command_permissions);
/** @} DiscordAPIInteractionsApplicationCommand */
/** @addtogroup DiscordAPIInteractionsReact
* @ingroup DiscordAPIInteractions
* @{ */
DISCORD_RETURN(interaction_response);
/** @} DiscordAPIInteractionsReact */
#endif /* DISCORD_RESPONSE_H */
+54
View File
@@ -0,0 +1,54 @@
/**
* @file discord-worker.h
* @author Cogmasters
* @brief Global threadpool
*/
#ifndef DISCORD_WORKER_H
#define DISCORD_WORKER_H
#include "concord-error.h"
/* forward declaration */
struct discord;
/**/
/** @defgroup DiscordInternalWorker Global threadpool
* @ingroup DiscordInternal
* @brief A global threadpool for worker-threads handling
* @{ */
/**
* @brief Initialize global threadpool and priority queue
*
* @param flags unused for now, but reserved for future use
* @CCORD_return
*/
CCORDcode discord_worker_global_init(long flags);
/** @brief Cleanup global threadpool and priority queue */
void discord_worker_global_cleanup(void);
/**
* @brief Run a callback from a worker thread
*
* @param client the client that will be using the worker thread
* @param callback user callback to be executed
* @param data user data to be passed to callback
* @CCORD_return
*/
CCORDcode discord_worker_add(struct discord *client,
void (*callback)(void *data),
void *data);
/**
* @brief Wait until worker-threads being used by `client` have been joined
*
* @param client the client currently using a worker thread
* @CCORD_return
*/
CCORDcode discord_worker_join(struct discord *client);
/** @} DiscordInternalWorker */
#endif /* DISCORD_WORKER_H */
+603
View File
@@ -0,0 +1,603 @@
/**
* @file discord.h
* @author Cogmasters
* @brief Public functions and datatypes
*
* These symbols are organized in a intuitive fashion to be easily
* matched to the official Discord API docs
* @see https://discord.com/developers/docs/intro
*/
#ifndef DISCORD_H
#define DISCORD_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <inttypes.h>
#include <stdbool.h>
#include "concord-error.h"
#include "types.h"
#include "io_poller.h"
#define LOGMOD_HEADER
#include "logmod.h"
#ifndef DISCORD_VERSION
/**
* @brief The Discord API version to use
* @warning only change this if you know what you are doing!
*/
#define DISCORD_VERSION "10"
#endif
#define DISCORD_API_BASE_URL "https://discord.com/api/v" DISCORD_VERSION
#define DISCORD_GATEWAY_URL_SUFFIX "?v=" DISCORD_VERSION "&encoding=json"
/* forward declaration */
struct discord;
/**/
#include "discord_codecs.h"
#include "discord-response.h"
/** @defgroup DiscordClient Client */
/** @defgroup DiscordConstants Constants
* @brief Macros for constants defined by Discord
* @note macros assume the worst-case scenario for strings, where each
* character is 4 bytes long (UTF8)
* @{ */
/** @defgroup DiscordConstantsGeneral General lengths
* @brief Max length for general fields
* @{ */
#define DISCORD_MAX_NAME_LEN 4 * 100 + 1
#define DISCORD_MAX_TOPIC_LEN 4 * 1024 + 1
#define DISCORD_MAX_DESCRIPTION_LEN 4 * 2048 + 1
#define DISCORD_MAX_USERNAME_LEN 4 * 32 + 1
#define DISCORD_MAX_DISCRIMINATOR_LEN 4 + 1
#define DISCORD_MAX_REASON_LEN 4 * 512 + 1
#define DISCORD_MAX_MESSAGE_LEN 4 * 2000 + 1
#define DISCORD_MAX_PAYLOAD_LEN 4 * 4096 + 1
/** @} DiscordConstantsGeneral */
/** @defgroup DiscordConstantsEmbed Embed lengths
* @brief Max length for embed fields
* @{ */
#define DISCORD_EMBED_TITLE_LEN 4 * 256 + 1
#define DISCORD_EMBED_DESCRIPTION_LEN 4 * 4096 + 1
#define DISCORD_EMBED_MAX_FIELDS 25
#define DISCORD_EMBED_FIELD_NAME_LEN 4 * 256 + 1
#define DISCORD_EMBED_FIELD_VALUE_LEN 4 * 1024 + 1
#define DISCORD_EMBED_FOOTER_TEXT_LEN 4 * 2048 + 1
#define DISCORD_EMBED_AUTHOR_NAME_LEN 4 * 256 + 1
/** @} DiscordConstantsEmbed */
/** @defgroup DiscordConstantsWebhook Webhook lengths
* @brief Max length for embed fields
* @{ */
#define DISCORD_WEBHOOK_NAME_LEN 4 * 80 + 1
/** @} DiscordConstantsWebhook */
/** @} DiscordConstants */
/** @addtogroup ConcordError
* @{ */
/* XXX: As new values are added, discord_strerror() and
* discord_code_as_string() should be updated accordingly! */
/** @defgroup DiscordError Discord error codes
* @brief Error codes triggered from Discord
* @{ */
/** Alias for @ref CCORD_OK */
#define CCORD_DISCORD_OK CCORD_OK
/** action is pending (ex: request has been enqueued and will be performed
* later) */
#define CCORD_PENDING 1
/** received a JSON error message */
#define CCORD_DISCORD_JSON_CODE 100
/** bad authentication token */
#define CCORD_DISCORD_BAD_AUTH 101
/** being ratelimited */
#define CCORD_DISCORD_RATELIMIT 102
/** couldn't establish connection to Discord */
#define CCORD_DISCORD_CONNECTION 103
/**
* @brief Return the value of CCORDcode as a string
*
* @param code the CCORDcode value
* @return the enum value as a string
*/
const char *discord_code_as_string(CCORDcode code);
/**
* @brief Return the meaning of CCORDcode
*
* @param code the CCORDcode value
* @param client @note unused parameter
* @return a string containing the code meaning
*/
const char *discord_strerror(CCORDcode code, struct discord *client);
/** @} DiscordError */
/** @} ConcordError */
/** @defgroup DiscordAPI API
* @brief The Discord public API supported by Concord
* @{ */
#include "audit_log.h"
#include "auto_moderation.h"
#include "invite.h"
#include "channel.h"
#include "emoji.h"
#include "guild.h"
#include "guild_scheduled_event.h"
#include "guild_template.h"
#include "stage_instance.h"
#include "sticker.h"
#include "user.h"
#include "voice.h"
#include "webhook.h"
#include "gateway.h"
#include "oauth2.h"
/** @defgroup DiscordAPIInteractions Interactions
* @brief Interactions public API supported by Concord
* @{ */
#include "application_command.h"
#include "interaction.h"
/** @} DiscordAPIInteractions */
/** @} DiscordAPI */
/** @addtogroup DiscordClient
* @brief Client functions and datatypes
* @{ */
/** @struct discord */
#include "discord-cache.h"
#include "discord-events.h"
/**
* @brief Claim ownership of a resource provided by Concord
* @see discord_unclaim()
*
* @param client the client initialized with discord_from_token()
* @param data a resource provided by Concord
* @return pointer to `data` (for one-liners)
*/
#define discord_claim(client, data) (__discord_claim(client, data), data)
void __discord_claim(struct discord *client, const void *data);
/**
* @brief Unclaim ownership of a resource provided by Concord
* @note this will make the resource eligible for cleanup, so this should
* only be called when you no longer plan to use it
* @see discord_claim()
*
* @param client the client initialized with discord_from_token()
* @param data a resource provided by Concord, that has been
* previously claimed with discord_claim()
*/
void discord_unclaim(struct discord *client, const void *data);
/** @deprecated since v3.0.0, keep backwards compatibility */
#define ccord_global_init()
/** @deprecated since v3.0.0, keep backwards compatibility */
#define ccord_global_cleanup()
/**
* @brief Gracefully notify all Discord connections for shutting down
*
* @note this function will not wait before returning, and will
* return immediately. The shutdown process will be handled
* in the background.
*/
void discord_shutdown_all(void);
/**
* @brief Check if all Discord connections shutting down is in progress
*
* @return true if all shutdown is in progress, false otherwise
*/
bool discord_shutdown_all_ongoing(void);
/**
* @brief Creates a Discord Client handle from a token
* @see discord_get_logmod() to configure logging behavior
*
* @param token the bot token
* @return the newly created Discord Client handle
*/
struct discord *discord_from_token(const char token[]);
/**
* @brief Creates a Discord Client handle from a `config.json` file
* @see discord_get_logmod() to configure logging behavior
*
* @param config_file the `config.json` file name
* @return the newly created Discord Client handle
*/
struct discord *discord_from_json(const char config_file[]);
/**
* @brief The Discord configuration handler
*
* This struct is used to store the Discord client configuration
*/
struct discord_config {
/** the bot token */
char *token;
struct {
/** minimum logging level */
enum logmod_levels level;
/** silence terminal logging */
bool quiet;
/** enable color to terminal logging */
bool color;
/** overwrite existing files */
bool overwrite;
/* the trace log file */
FILE *trace;
/* the http log file */
FILE *http;
/* the ws log file */
FILE *ws;
struct {
size_t size;
char **ids;
} disable; /**< list of 'id' that should be ignored */
} log; /**< logging directives */
};
/**
* @brief Creates a Discord Client handle from a
* @ref discord_config structure
* @see discord_get_logmod() to configure logging behavior
*
* @param config the @ref discord_config structure
* @return the newly created Discord Client handle
*/
struct discord *discord_from_config(const struct discord_config *config);
/**
* @brief Backwards compatible alias for discord_from_token()
* @deprecated since v3.0.0
*/
#define discord_init discord_from_token
/**
* @brief Backwards compatible alias for discord_from_json()
* @deprecated since v3.0.0
*/
#define discord_config_init discord_from_json
/**
* @brief Get the contents from the config file field
* @note your bot **MUST** have been initialized with discord_from_json()
*
* @code{.c}
* // Assume the following custom config.json field to be extracted
* // "field": { "foo": "a string", "bar": 1234 }
*
* ...
* struct ccord_szbuf_readonly value;
* char foo[128];
* long bar;
*
* // field.foo
* value = discord_config_get_field(client, (char *[2]){ "field", "foo" }, 2);
* snprintf(foo, sizeof(foo), "%.*s", (int)value.size, value.start);
* // field.bar
* value = discord_config_get_field(client, (char *[2]){ "field", "bar" }, 2);
* bar = strtol(value.start, NULL, 10);
*
* printf("%s %ld", foo, bar); // "a string" 1234
* @endcode
*
* @param client the client created with discord_from_json()
* @param path the JSON key path
* @param depth the path depth
* @return a read-only sized buffer containing the field's contents
*/
struct ccord_szbuf_readonly discord_config_get_field(struct discord *client,
char *const path[],
unsigned depth);
/**
* @brief Clone a discord client
*
* Should be called before entering a thread, to ensure each thread
* has its own client instance with unique buffers, url and headers
* @param orig the original client created with discord_from_token()
* @return the client clone
*/
struct discord *discord_clone(const struct discord *orig);
/**
* @brief Free a Discord Client handle
*
* @param client the client created with discord_from_token()
*/
void discord_cleanup(struct discord *client);
/**
* @brief Get the client's cached user
*
* @param client the client created with discord_from_token()
* @warning the returned structure should NOT be modified
*/
const struct discord_user *discord_get_self(struct discord *client);
/**
* @brief Start a connection to the Discord Gateway
*
* @param client the client created with discord_from_token()
* @CCORD_return
*/
CCORDcode discord_run(struct discord *client);
/**
* @brief Gracefully shutdown an ongoing Discord connection
*
* @param client the client created with discord_from_token()
*/
void discord_shutdown(struct discord *client);
/**
* @brief Gracefully reconnects an ongoing Discord connection
*
* @param client the client created with discord_from_token()
* @param resume true to attempt to resume to previous session,
* false restart a fresh session
*/
void discord_reconnect(struct discord *client, bool resume);
/**
* @brief Store user arbitrary data that can be retrieved by discord_get_data()
*
* @param client the client created with discord_from_token()
* @param data user arbitrary data
* @return pointer to user data
* @warning the user should provide their own locking mechanism to protect
* its data from race conditions
*/
void *discord_set_data(struct discord *client, void *data);
/**
* @brief Receive user arbitrary data stored with discord_set_data()
*
* @param client the client created with discord_from_token()
* @return pointer to user data
* @warning the user should provide their own locking mechanism to protect
* its data from race conditions
*/
void *discord_get_data(struct discord *client);
/**
* @brief Get the client WebSockets ping
* @note Only works after a connection has been established via
* discord_run()
*
* @param client the client created with discord_from_token()
* @return the ping in milliseconds
*/
int discord_get_ping(struct discord *client);
/**
* @brief Get the current timestamp (in milliseconds)
*
* @param client the client created with discord_from_token()
* @return the timestamp in milliseconds
*/
uint64_t discord_timestamp(struct discord *client);
/**
* @brief Get the current timestamp (in microseconds)
*
* @param client the client created with discord_from_token()
* @return the timestamp in microseconds
*/
uint64_t discord_timestamp_us(struct discord *client);
/**
* @brief Retrieve client's logging module for configuration purposes
* @see logmod.h
*
* @param client the client created with discord_from_token()
* @return the client's logging manager
*/
struct logmod *discord_get_logmod(struct discord *client);
/**
* @brief get the io_poller used by the discord client
*
* @param client the client created with discord_from_token()
* @return struct io_poller*
*/
struct io_poller *discord_get_io_poller(struct discord *client);
/** @addtogroup DiscordTimer Timer
* @brief Schedule callbacks to be called in the future
* @{ */
/* forward declaration */
struct discord_timer;
/**/
/** @brief callback to be used with struct discord_timer */
typedef void (*discord_ev_timer)(struct discord *client,
struct discord_timer *ev);
/** @brief flags used to change behaviour of timer */
enum discord_timer_flags {
/** use milliseconds for interval and start_time */
DISCORD_TIMER_MILLISECONDS = 0,
/** use microseconds for interval and start_time */
DISCORD_TIMER_MICROSECONDS = 1 << 0,
/** whether or not timer is marked for deletion */
DISCORD_TIMER_DELETE = 1 << 1,
/** automatically delete a timer once its repeat counter runs out */
DISCORD_TIMER_DELETE_AUTO = 1 << 2,
/** timer has been canceled. user should cleanup only */
DISCORD_TIMER_CANCELED = 1 << 3,
/** flag is set when on_tick callback has been called */
DISCORD_TIMER_TICK = 1 << 4,
/** used in discord_timer_ctl to get the timer's data */
DISCORD_TIMER_GET = 1 << 5,
/** timer should run using a fixed interval based on start time */
DISCORD_TIMER_INTERVAL_FIXED = 1 << 6,
};
/** @brief struct used for modifying, and getting info about a timer */
struct discord_timer {
/** the identifier used for the timer. 0 creates a new timer */
unsigned id;
/** the flags used to manipulate the timer */
enum discord_timer_flags flags;
/** (nullable) the callback that should be called when timer triggers */
discord_ev_timer on_tick;
/** (nullable) the callback for status updates timer->flags
* will have: DISCORD_TIMER_CANCELED, and DISCORD_TIMER_DELETE */
discord_ev_timer on_status_changed;
/** user data */
void *data;
/** delay before timer should start */
int64_t delay;
/** interval that the timer should repeat at. must be >= 0 */
int64_t interval;
/** how many times a timer should repeat (-1 == infinity) */
int64_t repeat;
};
/**
* @brief modifies or creates a timer
*
* @param client the client created with discord_from_token()
* @param timer the timer that should be modified
* @return the id of the timer
*/
unsigned discord_timer_ctl(struct discord *client,
struct discord_timer *timer);
/**
* @brief creates a one shot timer that automatically
* deletes itself upon completion
*
* @param client the client created with discord_from_token()
* @param on_tick_cb (nullable) the callback that should be called when timer
* triggers
* @param on_status_changed_cb (nullable) the callback for status updates
* timer->flags will have: DISCORD_TIMER_CANCELED, and DISCORD_TIMER_DELETE
* @param data user data
* @param delay delay before timer should start in milliseconds
* @return the id of the timer
*/
unsigned discord_timer(struct discord *client,
discord_ev_timer on_tick_cb,
discord_ev_timer on_status_changed_cb,
void *data,
int64_t delay);
/**
* @brief creates a repeating timer that automatically
* deletes itself upon completion
*
* @param client the client created with discord_from_token()
* @param on_tick_cb (nullable) the callback that should be called when timer
* triggers
* @param on_status_changed_cb (nullable) the callback for status updates
* timer->flags will have: DISCORD_TIMER_CANCELED, and DISCORD_TIMER_DELETE
* @param data user data
* @param delay delay before timer should start in milliseconds
* @param interval interval between runs. (-1 == disable repeat)
* @param repeat repetitions (-1 == infinity)
* @return the id of the timer
*/
unsigned discord_timer_interval(struct discord *client,
discord_ev_timer on_tick_cb,
discord_ev_timer on_status_changed_cb,
void *data,
int64_t delay,
int64_t interval,
int64_t repeat);
/**
* @brief get the data associated with the timer
*
* @param client the client created with discord_from_token()
* @param id id of the timer
* @param timer where to copy the timer data to
* @return true on success
*/
bool discord_timer_get(struct discord *client,
unsigned id,
struct discord_timer *timer);
/**
* @brief starts a timer
*
* @param client the client created with discord_from_token()
* @param id id of the timer
* @return true on success
*/
bool discord_timer_start(struct discord *client, unsigned id);
/**
* @brief stops a timer
*
* @param client the client created with discord_from_token()
* @param id id of the timer
* @return true on success
*/
bool discord_timer_stop(struct discord *client, unsigned id);
/**
* @brief cancels a timer,
* this will delete the timer if DISCORD_TIMER_DELETE_AUTO is enabled
*
* @param client the client created with discord_from_token()
* @param id id of the timer
* @return true on success
*/
bool discord_timer_cancel(struct discord *client, unsigned id);
/**
* @brief deletes a timer
*
* @param client the client created with discord_from_token()
* @param id id of the timer
* @return true on success
*/
bool discord_timer_delete(struct discord *client, unsigned id);
/**
* @brief cancels, and deletes a timer
*
* @param client the client created with discord_from_token()
* @param id id of the timer
* @return true on success
*/
bool discord_timer_cancel_and_delete(struct discord *client, unsigned id);
/** @example timers.c
* Demonstrates the Timer API for callback scheduling */
/** @} DiscordTimer */
/** @} DiscordClient */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DISCORD_H */
+74
View File
@@ -0,0 +1,74 @@
#include "gencodecs.h"
#ifdef GENCODECS_HEADER
PP_INCLUDE(<inttypes.h>)
PP_INCLUDE("carray.h")
PP_INCLUDE("cog-utils.h")
PP_INCLUDE("types.h")
PP_INCLUDE("concord-error.h")
#endif
/* Custom JSON encoding macros */
#define GENCODECS_JSON_ENCODER_PTR_json_char(b, buf, size, _var, _type) \
if (0 > (code = jsonb_token_auto(b, buf, size, _var, \
_var ? strlen(_var) : 0))) \
return code
#define GENCODECS_JSON_ENCODER_size_t(b, buf, size, _var, _type) \
{ \
char tok[64]; \
int toklen; \
toklen = sprintf(tok, "%zu", _var); \
if (0 > (code = jsonb_token_auto(b, buf, size, tok, toklen))) \
return code; \
}
#define GENCODECS_JSON_ENCODER_uint64_t(b, buf, size, _var, _type) \
{ \
char tok[64]; \
int toklen; \
toklen = sprintf(tok, "%" PRIu64, _var); \
if (0 > (code = jsonb_string_auto(b, buf, size, tok, toklen))) \
return code; \
}
#define GENCODECS_JSON_ENCODER_u64snowflake GENCODECS_JSON_ENCODER_uint64_t
#define GENCODECS_JSON_ENCODER_u64bitmask GENCODECS_JSON_ENCODER_uint64_t
#define GENCODECS_JSON_ENCODER_u64unix_ms(b, buf, size, _var, _type) \
{ \
char tok[64]; \
int toklen = cog_unix_ms_to_iso8601(tok, sizeof(tok), _var); \
if (0 > (code = jsonb_string_auto(b, buf, size, tok, toklen))) \
return code; \
}
/* Custom JSON decoding macros */
#define GENCODECS_JSON_DECODER_PTR_json_char(_f, _js, _var, _type) \
if (_f) { \
_var = _gc_strndup(js + _f->v->start, _f->v->end - _f->v->start); \
ret += _f->v->end - _f->v->start; \
}
#define GENCODECS_JSON_DECODER_size_t(_f, _js, _var, _type) \
if (_f && _f->v->type == JSMN_PRIMITIVE) \
_var = (size_t)strtoull(_js + _f->v->start, NULL, 10)
#define GENCODECS_JSON_DECODER_uint64_t(_f, _js, _var, _type) \
if (_f) sscanf(_js + _f->v->start, "%" SCNu64, &_var)
#define GENCODECS_JSON_DECODER_u64snowflake GENCODECS_JSON_DECODER_uint64_t
#define GENCODECS_JSON_DECODER_u64bitmask GENCODECS_JSON_DECODER_uint64_t
#define GENCODECS_JSON_DECODER_u64unix_ms(_f, _js, _var, _type) \
if (_f && _f->v->type == JSMN_STRING) \
cog_iso8601_to_unix_ms(_js + _f->v->start, _f->v->end - _f->v->start, &_var)
/* Custom field macros */
#define FIELD_SNOWFLAKE(_name) \
FIELD_PRINTF(_name, u64snowflake, "\"%" PRIu64 "\"", "%" SCNu64)
#define FIELD_BITMASK(_name) \
FIELD_PRINTF(_name, u64bitmask, "\"%" PRIu64 "\"", "%" SCNu64)
#define FIELD_TIMESTAMP(_name) \
FIELD_CUSTOM(_name, #_name, u64unix_ms, DECOR_BLANK, INIT_BLANK, \
CLEANUP_BLANK, GENCODECS_JSON_ENCODER_u64unix_ms, \
GENCODECS_JSON_DECODER_u64unix_ms, (u64unix_ms)0)
/* if GENCODECS_READ is not specified then generate for all files */
#ifndef GENCODECS_READ
#define GENCODECS_READ "all.PRE.h"
#endif
#include "gencodecs-process.PRE.h"
File diff suppressed because it is too large Load Diff
+95
View File
@@ -0,0 +1,95 @@
/**
* @file emoji.h
* @author Cogmasters
* @brief Emoji public functions and datatypes
*/
#ifndef DISCORD_EMOJI_H
#define DISCORD_EMOJI_H
/** @defgroup DiscordAPIEmoji Emoji
* @ingroup DiscordAPI
* @brief Emoji's public API supported by Concord
* @{ */
/**
* @brief Get emojis of a given guild
*
* @param client the client created with discord_from_token()
* @param guild_id guild to get emojis from
* @CCORD_ret_obj{ret,emojis}
* @CCORD_return
*/
CCORDcode discord_list_guild_emojis(struct discord *client,
u64snowflake guild_id,
struct discord_ret_emojis *ret);
/**
* @brief Get a specific emoji from a guild
*
* @param client the client created with discord_from_token()
* @param guild_id guild the emoji belongs to
* @param emoji_id the emoji to be fetched
* @CCORD_ret_obj{ret,emoji}
* @CCORD_return
*/
CCORDcode discord_get_guild_emoji(struct discord *client,
u64snowflake guild_id,
u64snowflake emoji_id,
struct discord_ret_emoji *ret);
/**
* @brief Create a new emoji for the guild
* @note Fires a `Guild Emojis Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id guild to add the new emoji to
* @param params request parameters
* @CCORD_ret_obj{ret,emoji}
* @CCORD_return
*/
CCORDcode discord_create_guild_emoji(struct discord *client,
u64snowflake guild_id,
struct discord_create_guild_emoji *params,
struct discord_ret_emoji *ret);
/**
* @brief Modify the given emoji
* @note Fires a `Guild Emojis Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id guild the emoji belongs to
* @param emoji_id the emoji to be modified
* @param params request parameters
* @CCORD_ret_obj{ret,emoji}
* @CCORD_return
*/
CCORDcode discord_modify_guild_emoji(struct discord *client,
u64snowflake guild_id,
u64snowflake emoji_id,
struct discord_modify_guild_emoji *params,
struct discord_ret_emoji *ret);
/**
* @brief Deletes the given emoji
* @note Fires a `Guild Emojis Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id guild the emoji belongs to
* @param emoji_id the emoji to be deleted
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_guild_emoji(struct discord *client,
u64snowflake guild_id,
u64snowflake emoji_id,
struct discord_delete_guild_emoji *params,
struct discord_ret *ret);
/** @example emoji.c
* Demonstrates a couple use cases of the Emoji API */
/** @} DiscordAPIEmoji */
#endif /* DISCORD_EMOJI_H */
+76
View File
@@ -0,0 +1,76 @@
/**
* @file gateway.h
* @author Cogmasters
* @brief Gateway public functions and datatypes
*/
#ifndef DISCORD_GATEWAY_H
#define DISCORD_GATEWAY_H
/** @defgroup DiscordAPIGateway Gateway
* @ingroup DiscordAPI
* @brief Gateway's public API supported by Concord
* @{ */
/**
* @brief Get a single valid WSS URL, which the client can use for connecting
* @note This route should be cached, and only call the function again if
* unable to properly establishing a connection with the cached version
* @warning This function blocks the running thread
*
* @param client the client created with discord_from_token()
* @param ret if successful, a @ref ccord_szbuf containing the JSON response
* @param ret a sized buffer containing the response JSON
* @CCORD_return
*/
CCORDcode discord_get_gateway(struct discord *client, struct ccord_szbuf *ret);
/**
* @brief Get a single valid WSS URL, and additional metadata that can help
* during the operation of large bots.
* @note This route should not be cached for extended periods of time as the
* value is not guaranteed to be the same per-call, and changes as the
* bot joins/leaves guilds
* @warning This function blocks the running thread
*
* @param client the client created with discord_from_token()
* @param ret if successful, a @ref ccord_szbuf containing the JSON response
* @param ret a sized buffer containing the response JSON
* @CCORD_return
*/
CCORDcode discord_get_gateway_bot(struct discord *client,
struct ccord_szbuf *ret);
/** @defgroup DiscordAPIGatewayHelper Helper functions
* @brief Custom helper functions
* @{ */
/**
* @brief Disconnect a member from voice channel
*
* @param client the client created with discord_from_token()
* @param guild_id the guild the member belongs to
* @param user_id the user to be disconnected
* @param params request parameters
* @CCORD_ret_obj{ret,guild_member}
* @CCORD_return
*/
CCORDcode discord_disconnect_guild_member(
struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_modify_guild_member *params,
struct discord_ret_guild_member *ret);
/**
* @brief Helper function to add presence activities
* @see discord_set_presence()
*/
void discord_presence_add_activity(struct discord_presence_update *presence,
struct discord_activity *activity);
/** @} DiscordAPIGatewayHelper */
/** @} DiscordAPIGateway */
#endif /* DISCORD_GATEWAY_H */
+27
View File
@@ -0,0 +1,27 @@
#ifndef GENCODECS_READ
#error "Missing GENCODECS_READ definition"
#else
#define DATA (1 << 1)
#define JSON_DECODER (1 << 2)
#define JSON_ENCODER (1 << 3)
#define JSON (JSON_DECODER | JSON_ENCODER)
#define GENCODECS_RECIPE DATA
#include "recipes/struct.h"
#undef GENCODECS_RECIPE
#define GENCODECS_RECIPE JSON_DECODER
#include "recipes/json-decoder.h"
#undef GENCODECS_RECIPE
#define GENCODECS_RECIPE JSON_ENCODER
#include "recipes/json-encoder.h"
#undef GENCODECS_RECIPE
#undef DATA
#undef JSON_DECODER
#undef JSON_ENCODER
#undef JSON
#endif /* GENCODECS_READ */
+74
View File
@@ -0,0 +1,74 @@
#ifndef GENCODECS_H
#define GENCODECS_H
/* Allow symbols usage without GENCODECS_ prefix */
#ifndef GENCODECS_USE_PREFIX
# define PP_INCLUDE GENCODECS_PP_INCLUDE
# define PP_DEFINE GENCODECS_PP_DEFINE
# define PP GENCODECS_PP
# define COND_WRITE GENCODECS_COND_WRITE
# define COND_END GENCODECS_COND_END
# define PUB_STRUCT GENCODECS_PUB_STRUCT
# define STRUCT GENCODECS_STRUCT
# define FIELD_CUSTOM GENCODECS_FIELD_CUSTOM
# define FIELD_PRINTF GENCODECS_FIELD_PRINTF
# define FIELD GENCODECS_FIELD
# define FIELD_STRUCT_PTR GENCODECS_FIELD_STRUCT_PTR
# define FIELD_PTR GENCODECS_FIELD_PTR
# define FIELD_ENUM GENCODECS_FIELD_ENUM
# define STRUCT_END GENCODECS_STRUCT_END
# define PUB_LIST GENCODECS_PUB_LIST
# define LIST GENCODECS_LIST
# define LISTTYPE GENCODECS_LISTTYPE
# define LISTTYPE_STRUCT GENCODECS_LISTTYPE_STRUCT
# define LISTTYPE_PTR GENCODECS_LISTTYPE_PTR
# define LIST_END GENCODECS_LIST_END
# define ENUM GENCODECS_ENUM
# define ENUM_END GENCODECS_ENUM_END
# define ENUMERATOR GENCODECS_ENUMERATOR
# define ENUMERATOR_LAST GENCODECS_ENUMERATOR_LAST
# define ENUMERATOR_END GENCODECS_ENUMERATOR_END
#endif /* GENCODECS_USE_PREFIX */
#ifndef GENCODECS_HEADER
# ifdef GENCODECS_DATA
GENCODECS_PP_INCLUDE(<stdio.h>)
GENCODECS_PP_INCLUDE(<stdlib.h>)
GENCODECS_PP_INCLUDE(<string.h>)
# ifdef GENCODECS_INIT
GENCODECS_PP_INCLUDE("carray.h")
# endif
# if defined(GENCODECS_JSON_DECODER) && defined(GENCODECS_FORWARD)
static char *
_gc_strndup(const char *src, size_t len)
{
char *dest = malloc(len + 1);
memcpy(dest, src, len);
dest[len] = '\0';
return dest;
}
# endif /* GENCODECS_JSON_DECODER && GENCODECS_FORWARD */
# endif /* GENCODECS_DATA */
#else
GENCODECS_PP_INCLUDE(<stddef.h>)
GENCODECS_PP_INCLUDE(<stdbool.h>)
# ifdef GENCODECS_JSON_DECODER
GENCODECS_PP_DEFINE(JSMN_STRICT)
GENCODECS_PP_DEFINE(JSMN_HEADER)
GENCODECS_PP_INCLUDE("jsmn.h")
GENCODECS_PP_INCLUDE("jsmn-find.h")
# endif
# ifdef GENCODECS_JSON_ENCODER
GENCODECS_PP_DEFINE(JSONB_HEADER)
GENCODECS_PP_INCLUDE("json-build.h")
# endif
#endif /* GENCODECS_HEADER */
#define GENCODECS_PP(_description)
#define GENCODECS_PP_DEFINE(_description)
#endif /* GENCODECS_H */
+683
View File
@@ -0,0 +1,683 @@
/**
* @file guild.h
* @author Cogmasters
* @brief Guild public functions and datatypes
*/
#ifndef DISCORD_GUILD_H
#define DISCORD_GUILD_H
/** @defgroup DiscordAPIGuild Guild
* @ingroup DiscordAPI
* @brief Guild's public API supported by Concord
* @{ */
/**
* @brief Create a new guild
* @note Fires a `Guild Create` event
*
* @param client the client created with discord_from_token()
* @param params request parameters
* @CCORD_ret_obj{ret,guild}
* @CCORD_return
*/
CCORDcode discord_create_guild(struct discord *client,
struct discord_create_guild *params,
struct discord_ret_guild *ret);
/**
* @brief Get the guild with given id
* @todo missing query parameters
* @note If with_counts is set to true, this endpoint will also return
* approximate_member_count and approximate_presence_count for the
* guild
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to retrieve
* @CCORD_ret_obj{ret,guild}
* @CCORD_return
*/
CCORDcode discord_get_guild(struct discord *client,
u64snowflake guild_id,
struct discord_ret_guild *ret);
/**
* @brief Get the preview for the given guild
* @note If the user is not in the guild, then the guild must be lurkable
*
* @param client the client created with discord_from_token()
* @param guild_id guild to get preview from
* @CCORD_ret_obj{ret,guild_preview}
* @CCORD_return
*/
CCORDcode discord_get_guild_preview(struct discord *client,
u64snowflake guild_id,
struct discord_ret_guild_preview *ret);
/**
* @brief Modify a guild's settings
* @note Requires the MANAGE_GUILD permission
* @note Fires a `Guild Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to modify
* @param params request parameters
* @CCORD_ret_obj{ret,guild}
* @CCORD_return
*/
CCORDcode discord_modify_guild(struct discord *client,
u64snowflake guild_id,
struct discord_modify_guild *params,
struct discord_ret_guild *ret);
/**
* @brief Delete a guild permanently, user must be owner
* @note Fires a `Guild Delete` event
*
* @param client the client created with discord_from_token()
* @param guild_id id of guild to delete
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_guild(struct discord *client,
u64snowflake guild_id,
struct discord_ret *ret);
/**
* @brief Fetch channels from given guild. Does not include threads
*
* @param client the client created with discord_from_token()
* @param guild_id id of guild to fetch channels from
* @CCORD_ret_obj{ret,channels}
* @CCORD_return
*/
CCORDcode discord_get_guild_channels(struct discord *client,
u64snowflake guild_id,
struct discord_ret_channels *ret);
/**
* @brief Create a new guild channel
* @note Requires the MANAGE_CHANNELS permission
* @note If setting permission overwrites, only permissions your
* bot has in the guild can be allowed/denied. Setting MANAGE_ROLES
* permission in channels is only possible for guild administrators
* @note Fires a `Channel Create` event
*
* @param client the client created with discord_from_token()
* @param guild_id id of the guild to create a channel at
* @param params request parameters
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_create_guild_channel(
struct discord *client,
u64snowflake guild_id,
struct discord_create_guild_channel *params,
struct discord_ret_channel *ret);
/**
* @brief Modify guild channel positions
* @note Requires MANAGE_CHANNELS permission
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to change the positions of the
* channels in
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_modify_guild_channel_positions(
struct discord *client,
u64snowflake guild_id,
struct discord_modify_guild_channel_positions *params,
struct discord_ret *ret);
/**
* @brief Get guild member of a guild from given user id
*
* @param client the client created with discord_from_token()
* @param guild_id guild the member belongs to
* @param user_id unique user id of member
* @CCORD_ret_obj{ret,guild_member}
* @CCORD_return
*/
CCORDcode discord_get_guild_member(struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_ret_guild_member *ret);
/**
* @brief Get guild members of a guild
*
* @param client the client created with discord_from_token()
* @param guild_id guild the members belongs to
* @param request parameters
* @CCORD_ret_obj{ret,guild_members}
* @CCORD_return
*/
CCORDcode discord_list_guild_members(struct discord *client,
u64snowflake guild_id,
struct discord_list_guild_members *params,
struct discord_ret_guild_members *ret);
/**
* @brief Get guild members whose username or nickname starts with a provided
* string
*
* @param client the client created with discord_from_token()
* @param guild_id guild the members belongs to
* @param request parameters
* @CCORD_ret_obj{ret,guild_members}
* @CCORD_return
*/
CCORDcode discord_search_guild_members(
struct discord *client,
u64snowflake guild_id,
struct discord_search_guild_members *params,
struct discord_ret_guild_members *ret);
/**
* @brief Adds a user to the guild
* @note Requires valid oauth2 access token for the user with `guilds.join`
* scope
* @note Fires a `Guild Member Add` event
* @note The bot must be a member of the guild with CREATE_INSTANT_INVITE
* permission
*
* @param client the client created with discord_from_token()
* @param guild_id guild to add the member to
* @param user_id the user to be added
* @param request parameters
* @CCORD_ret_obj{ret,guild_member}
* @CCORD_return
*/
CCORDcode discord_add_guild_member(struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_add_guild_member *params,
struct discord_ret_guild_member *ret);
/**
* @brief Modify retibutes of a guild member
* @note Fires a `Guild Member Update` event
* @see discord_disconnect_guild_member()
*
* @param client the client created with discord_from_token()
* @param guild_id guild the member belongs to
* @param user_id the user id of member
* @param request parameters
* @CCORD_ret_obj{ret,guild_member}
* @CCORD_return
*/
CCORDcode discord_modify_guild_member(
struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_modify_guild_member *params,
struct discord_ret_guild_member *ret);
/**
* @brief Modifies the current member in the guild
* @note Fires a `Guild Member Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild where the member exists
* @param params request parameters
* @CCORD_ret_obj{ret,guild_member}
* @CCORD_return
*/
CCORDcode discord_modify_current_member(
struct discord *client,
u64snowflake guild_id,
struct discord_modify_current_member *params,
struct discord_ret_guild_member *ret);
/**
* @brief Adds a role to a guild member
* @note Fires a `Guild Member Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild where the member exists
* @param user_id the unique id of the user
* @param role_id the unique id of the role to be added
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_add_guild_member_role(
struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
u64snowflake role_id,
struct discord_add_guild_member_role *params,
struct discord_ret *ret);
/**
* @brief Removes a role from a guild member
* @note Requires the MANAGE_ROLES permission
* @note Fires a `Guild Member Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild where the member exists
* @param user_id the unique id of the user
* @param role_id the unique id of the role to be removed
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_remove_guild_member_role(
struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
u64snowflake role_id,
struct discord_remove_guild_member_role *params,
struct discord_ret *ret);
/**
* @brief Remove a member from a guild
* @note Requires the KICK_MEMBERS permission
* @note Fires a `Guild Member Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to remove the member from
* @param user_id the user to be removed
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_remove_guild_member(
struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_remove_guild_member *params,
struct discord_ret *ret);
/**
* @brief Fetch banned users for given guild
* @note Requires the BAN_MEMBERS permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to get the list from
* @CCORD_ret_obj{ret,bans}
* @CCORD_return
*/
CCORDcode discord_get_guild_bans(struct discord *client,
u64snowflake guild_id,
struct discord_ret_bans *ret);
/**
* @brief Fetch banned user from given guild
* @note Requires the BAN_MEMBERS permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to return the ban from
* @param user_id the user that is banned
* @CCORD_ret_obj{ret,ban}
* @CCORD_return
*/
CCORDcode discord_get_guild_ban(struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_ret_ban *ret);
/**
* @brief Bans user from a given guild
* @note Requires the BAN_MEMBERS permission
* @note Fires a `Guild Ban Add` event
*
* @param client the client created with discord_from_token()
* @param guild_id guild the user belongs to
* @param user_id the user to be banned
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_create_guild_ban(struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_create_guild_ban *params,
struct discord_ret *ret);
/**
* @brief Remove the ban for a user
* @note Requires the BAN_MEMBERS permission
* @note Fires a `Guild Ban Remove` event
*
* @param client the client created with discord_from_token()
* @param guild_id guild the user belonged to
* @param user_id the user to have its ban revoked
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_remove_guild_ban(struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_remove_guild_ban *params,
struct discord_ret *ret);
/**
* @brief Get guild roles
*
* @param client the client created with discord_from_token()
* @param guild_id guild to get roles from
* @CCORD_ret_obj{ret,roles}
* @CCORD_return
*/
CCORDcode discord_get_guild_roles(struct discord *client,
u64snowflake guild_id,
struct discord_ret_roles *ret);
/**
* @brief Create a new guild role
* @note Requires MANAGE_ROLES permission
* @note Fires a `Guild Role Create` event
*
* @param client the client created with discord_from_token()
* @param guild_id guild to add a role to
* @param params request parameters
* @CCORD_ret_obj{ret,role}
* @CCORD_return
*/
CCORDcode discord_create_guild_role(struct discord *client,
u64snowflake guild_id,
struct discord_create_guild_role *params,
struct discord_ret_role *ret);
/**
* @brief Returns the number of members that would be removed in a prune
* operation
* @note Requires the KICK_MEMBERS permission
* @note By default will not remove users with roles. You can include specific
* roles in your prune by providing the `params.include_roles` value
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to be checked
* @param params request parameters
* @CCORD_ret_obj{ret,prune_count}
* @CCORD_return
*/
CCORDcode discord_get_guild_prune_count(
struct discord *client,
u64snowflake guild_id,
struct discord_get_guild_prune_count *params,
struct discord_ret_prune_count *ret);
/**
* @brief Begin guild prune operation
* @note Discord recommends for larger servers to set "compute_prune_count" to
* false
* @note Requires the KICK_MEMBERS permission
* @note Fires multiple `Guild Member Remove` events
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to start the prune
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_begin_guild_prune(struct discord *client,
u64snowflake guild_id,
struct discord_begin_guild_prune *params,
struct discord_ret *ret);
/**
* @brief Get voice regions (includes VIP servers when the guild is
* VIP-enabled)
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get voice regions from
* @CCORD_ret_obj{ret,voice_regions}
* @CCORD_return
*/
CCORDcode discord_get_guild_voice_regions(
struct discord *client,
u64snowflake guild_id,
struct discord_ret_voice_regions *ret);
/**
* @brief Get guild invites
* @note requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get invites from
* @CCORD_ret_obj{ret,invites}
* @CCORD_return
*/
CCORDcode discord_get_guild_invites(struct discord *client,
u64snowflake guild_id,
struct discord_ret_invites *ret);
/**
* @brief Get guild integrations
* @note requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get integrations from
* @CCORD_ret_obj{ret,integrations}
* @CCORD_return
*/
CCORDcode discord_get_guild_integrations(struct discord *client,
u64snowflake guild_id,
struct discord_ret_integrations *ret);
/**
* @brief Deletes the integration for the guild. It will also delete any
* associated webhooks and bots
* @note Requires the MANAGE_GUILD permission
* @note Fires a `Guild Integrations Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to delete the integrations from
* @param integration_id the id of the integration to delete
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_guild_integrations(
struct discord *client,
u64snowflake guild_id,
u64snowflake integration_id,
struct discord_delete_guild_integrations *params,
struct discord_ret *ret);
/**
* @brief Get a guild widget settings
* @note requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get widget settings from
* @CCORD_ret_obj{ret,guild_widget_settings}
* @CCORD_return
*/
CCORDcode discord_get_guild_widget_settings(
struct discord *client,
u64snowflake guild_id,
struct discord_ret_guild_widget_settings *ret);
/**
* @brief Modify a guild widget settings
* @note requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to modify the widget settings
* from
* @param param request parameters
* @CCORD_ret_obj{ret,guild_widget_settings}
* @CCORD_return
*/
CCORDcode discord_modify_guild_widget(
struct discord *client,
u64snowflake guild_id,
struct discord_guild_widget_settings *params,
struct discord_ret_guild_widget_settings *ret);
/**
* @brief Get the widget for the guild
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get the widget from
* @CCORD_ret_obj{ret,guild_widget}
* @CCORD_return
*/
CCORDcode discord_get_guild_widget(struct discord *client,
u64snowflake guild_id,
struct discord_ret_guild_widget *ret);
/**
* @brief Get invite from a given guild
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get vanity url from
* @CCORD_ret_obj{ret,invite}
* @CCORD_return
*/
CCORDcode discord_get_guild_vanity_url(struct discord *client,
u64snowflake guild_id,
struct discord_ret_invite *ret);
/* TODO: handle ContentType: image/png and add 'struct discord_png' */
#if 0
/**
* @brief Get a PNG image widget for the guild
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get a PNG widget image from
* @param params request parameters
* @CCORD_ret_obj{ret,png}
* @CCORD_return
*/
CCORDcode discord_get_guild_widget_image(
struct discord *client,
u64snowflake guild_id,
struct discord_get_guild_widget_image *params,
struct discord_ret_png *ret);
#endif
/**
* @brief Get the Welcome Screen for the guild
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get welcome screen of
* @CCORD_ret_obj{ret,welcome_screen}
* @CCORD_return
*/
CCORDcode discord_get_guild_welcome_screen(
struct discord *client,
u64snowflake guild_id,
struct discord_ret_welcome_screen *ret);
/**
* @brief Modify the Welcome Screen for the guild
* @note requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to modify welcome screen of
* @param params request parameters
* @CCORD_ret_obj{ret,welcome_screen}
* @CCORD_return
*/
CCORDcode discord_modify_guild_welcome_screen(
struct discord *client,
u64snowflake guild_id,
struct discord_modify_guild_welcome_screen *params,
struct discord_ret_welcome_screen *ret);
/**
* @brief Updates the current user's voice state
* @see Caveats
* https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state-caveats
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to modify the current user's
* voice state
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_modify_current_user_voice_state(
struct discord *client,
u64snowflake guild_id,
struct discord_modify_current_user_voice_state *params,
struct discord_ret *ret);
/**
* @brief Updates user's voice state
* @see Caveats
* https://discord.com/developers/docs/resources/guild#modify-user-voice-state-caveats
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to modify the user's voice state
* @param user_id the unique id of user to have its voice state modified
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_modify_user_voice_state(
struct discord *client,
u64snowflake guild_id,
u64snowflake user_id,
struct discord_modify_user_voice_state *params,
struct discord_ret *ret);
/**
* @brief Modify the positions of a given role list for the guild
* @note Requires the MANAGE_ROLES permission
* @note Fires multiple `Guild Role Update` events
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild to get welcome screen of
* @param params request parameters
* @CCORD_ret_obj{ret,roles}
* @CCORD_return
*/
CCORDcode discord_modify_guild_role_positions(
struct discord *client,
u64snowflake guild_id,
struct discord_modify_guild_role_positions *params,
struct discord_ret_roles *ret);
/**
* @brief Modify a guild role
* @note Requires the MANAGE_ROLES permission
* @note Fires a `Guild Role Update` event
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild that the role belongs to
* @param role_id the unique id of the role to modify
* @param params request parameters
* @CCORD_ret_obj{ret,role}
* @CCORD_return
*/
CCORDcode discord_modify_guild_role(struct discord *client,
u64snowflake guild_id,
u64snowflake role_id,
struct discord_modify_guild_role *params,
struct discord_ret_role *ret);
/**
* @brief Delete a guild role
* @note Requires the MANAGE_ROLES permission
* @note Fires a `Guild Role Delete` event
*
* @param client the client created with discord_from_token()
* @param guild_id the unique id of the guild that the role belongs to
* @param role_id the unique id of the role to delete
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_guild_role(struct discord *client,
u64snowflake guild_id,
u64snowflake role_id,
struct discord_delete_guild_role *params,
struct discord_ret *ret);
/** @example guild.c
* Demonstrates a couple use cases of the Guild API */
/** @example ban.c
* Demonstrates banning and unbanning members */
/** @} DiscordAPIGuild */
#endif /* DISCORD_GUILD_H */
+118
View File
@@ -0,0 +1,118 @@
/**
* @file guild_scheduled_event.h
* @author Cogmasters
* @brief Guild Scheduled Event public functions and datatypes
*/
#ifndef DISCORD_GUILD_SCHEDULED_EVENT_H
#define DISCORD_GUILD_SCHEDULED_EVENT_H
/** @defgroup DiscordAPIGuildScheduledEvent Guild Scheduled Event
* @ingroup DiscordAPI
* @brief Guild Scheduled Event's public API supported by Concord
* @{ */
/**
* @brief Get a list of scheduled events for the guild
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to fetch the scheduled events from
* @param params request parameters
* @CCORD_ret_obj{ret,guild_scheduled_events}
* @CCORD_return
*/
CCORDcode discord_list_guild_scheduled_events(
struct discord *client,
u64snowflake guild_id,
struct discord_list_guild_scheduled_events *params,
struct discord_ret_guild_scheduled_events *ret);
/**
* @brief Create a guild scheduled event
* @note A guild can have a maximum of 100 events with `SCHEDULED` or `ACTIVE`
* status at any time
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to create the scheduled event at
* @param params request parameters
* @CCORD_ret_obj{ret,guild_scheduled_event}
* @CCORD_return
*/
CCORDcode discord_create_guild_scheduled_event(
struct discord *client,
u64snowflake guild_id,
struct discord_create_guild_scheduled_event *params,
struct discord_ret_guild_scheduled_event *ret);
/**
* @brief Get a guild scheduled event
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to fetch the scheduled event from
* @param guild_scheduled_event_id the scheduled event to be fetched
* @param params request parameters
* @CCORD_ret_obj{ret,guild_scheduled_event}
* @CCORD_return
*/
CCORDcode discord_get_guild_scheduled_event(
struct discord *client,
u64snowflake guild_id,
u64snowflake guild_scheduled_event_id,
struct discord_get_guild_scheduled_event *params,
struct discord_ret_guild_scheduled_event *ret);
/**
* @brief Modify a guild scheduled event
* @note Silently discards `entity_metadata` for non-`EXTERNAL` events
*
* @param client the client created with discord_from_token()
* @param guild_id the guild where the scheduled event to be modified is at
* @param guild_scheduled_event_id the scheduled event to be modified
* @param params request parameters
* @CCORD_ret_obj{ret,guild_scheduled_event}
* @CCORD_return
*/
CCORDcode discord_modify_guild_scheduled_event(
struct discord *client,
u64snowflake guild_id,
u64snowflake guild_scheduled_event_id,
struct discord_modify_guild_scheduled_event *params,
struct discord_ret_guild_scheduled_event *ret);
/**
* @brief Delete a guild scheduled event
*
* @param client the client created with discord_from_token()
* @param guild_id the guild where the scheduled event to be deleted is at
* @param guild_scheduled_event_id the scheduled event to be deleted
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_guild_scheduled_event(
struct discord *client,
u64snowflake guild_id,
u64snowflake guild_scheduled_event_id,
struct discord_ret *ret);
/**
* @brief Get a list of members subscribed to a guild scheduled event
* @note Guild member data, if it exists, is included if the
* `params.with_member` value is set
*
* @param client the client created with discord_from_token()
* @param guild_id the guild with the scheduled event belongs to
* @param guild_scheduled_event_id the scheduled event
* @param params request parameters
* @CCORD_ret_obj{ret,guild_scheduled_event_users}
* @CCORD_return
*/
CCORDcode discord_get_guild_scheduled_event_users(
struct discord *client,
u64snowflake guild_id,
u64snowflake guild_scheduled_event_id,
struct discord_get_guild_scheduled_event_users *params,
struct discord_ret_guild_scheduled_event_users *ret);
/** @} DiscordAPIGuildScheduledEvent */
#endif /* DISCORD_GUILD_SCHEDULED_EVENT_H */
+126
View File
@@ -0,0 +1,126 @@
/**
* @file guild_template.h
* @author Cogmasters
* @brief Guild Template public functions and datatypes
*/
#ifndef DISCORD_GUILD_TEMPLATE_H
#define DISCORD_GUILD_TEMPLATE_H
/** @defgroup DiscordAPIGuildTemplate Guild Template
* @ingroup DiscordAPI
* @brief Guild Template's public API supported by Concord
* @{ */
/**
* @brief Get a guild template for the given code
*
* @param client the client created with discord_from_token()
* @param template_code the guild template code
* @CCORD_ret_obj{ret,guild_template}
* @CCORD_return
*/
CCORDcode discord_get_guild_template(struct discord *client,
const char template_code[],
struct discord_ret_guild_template *ret);
/**
* @brief Create a new guild based on a template
* @note This endpoint can be used only by bots in less than 10 guilds
*
* @param client the client created with discord_from_token()
* @param template_code the guild template code
* @param params the request parameters
* @CCORD_ret_obj{ret,guild}
* @CCORD_return
*/
CCORDcode discord_create_guild_from_guild_template(
struct discord *client,
const char template_code[],
struct discord_create_guild_from_guild_template *params,
struct discord_ret_guild *ret);
/**
* @brief Returns @ref discord_guild_templates from a guild
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to fetch the templates from
* @CCORD_ret_obj{ret,guild_templates}
* @CCORD_return
*/
CCORDcode discord_get_guild_templates(struct discord *client,
u64snowflake guild_id,
struct discord_ret_guild_templates *ret);
/**
* @brief Creates a template for the guild
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to create a template from
* @param params the request parameters
* @CCORD_ret_obj{ret,guild_template}
* @CCORD_return
*/
CCORDcode discord_create_guild_template(
struct discord *client,
u64snowflake guild_id,
struct discord_create_guild_template *params,
struct discord_ret_guild_template *ret);
/**
* @brief Syncs the template to the guild's current state
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to sync the template from
* @param template_code the guild template code
* @CCORD_ret_obj{ret,guild_template}
* @CCORD_return
*/
CCORDcode discord_sync_guild_template(struct discord *client,
u64snowflake guild_id,
const char template_code[],
struct discord_ret_guild_template *ret);
/**
* @brief Modifies the template's metadata
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to modify the template at
* @param template_code the guild template code
* @param params the request parameters
* @CCORD_ret_obj{ret,guild_template}
* @CCORD_return
*/
CCORDcode discord_modify_guild_template(
struct discord *client,
u64snowflake guild_id,
const char template_code[],
struct discord_modify_guild_template *params,
struct discord_ret_guild_template *ret);
/**
* @brief Deletes the guild template
* @note Requires the `MANAGE_GUILD` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild to delete the template at
* @param template_code the guild template code
* @CCORD_ret_obj{ret,guild_template}
* @CCORD_return
*/
CCORDcode discord_delete_guild_template(
struct discord *client,
u64snowflake guild_id,
const char template_code[],
struct discord_ret_guild_template *ret);
/** @example guild-template.c
* Demonstrates a couple use cases of the Guild Template API */
/** @} DiscordAPIGuildTemplate */
#endif /* DISCORD_GUILD_TEMPLATE_H */
+152
View File
@@ -0,0 +1,152 @@
/**
* @file interaction.h
* @author Cogmasters
* @brief Interaciton public functions and datatypes
*/
#ifndef DISCORD_INTERACTION_H
#define DISCORD_INTERACTION_H
/** @defgroup DiscordAPIInteractionsReact Receiving and sending
* @ingroup DiscordAPIInteractions
* @brief Receiving and sending interactions
* @{ */
/**
* @brief Create a response to an Interaction from the gateway
*
* @param client the client created with discord_from_token()
* @param interaction_id the unique id of the interaction
* @param interaction_token the unique token of the interaction
* @param params the request parameters
* @CCORD_ret_obj{ret,interaction_response}
* @CCORD_return
*/
CCORDcode discord_create_interaction_response(
struct discord *client,
u64snowflake interaction_id,
const char interaction_token[],
struct discord_interaction_response *params,
struct discord_ret_interaction_response *ret);
/**
* @brief Get the initial Interaction response
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the application
* @param interaction_token the unique token of the interaction
* @CCORD_ret_obj{ret,interaction_response}
* @CCORD_return
*/
CCORDcode discord_get_original_interaction_response(
struct discord *client,
u64snowflake application_id,
const char interaction_token[],
struct discord_ret_interaction_response *ret);
/**
* @brief Edit the initial Interaction response
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the application
* @param interaction_token the unique token of the interaction
* @param params request parameters
* @CCORD_ret_obj{ret,interaction_response}
* @CCORD_return
*/
CCORDcode discord_edit_original_interaction_response(
struct discord *client,
u64snowflake application_id,
const char interaction_token[],
struct discord_edit_original_interaction_response *params,
struct discord_ret_interaction_response *ret);
/**
* @brief Delete the initial Interaction response
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the application
* @param interaction_token the unique token of the interaction
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_original_interaction_response(
struct discord *client,
u64snowflake application_id,
const char interaction_token[],
struct discord_ret *ret);
/**
* @brief Create a followup message for an Interaction
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the application
* @param interaction_token the unique token of the interaction
* @param params request parameters
* @CCORD_ret_obj{ret,webhook}
* @CCORD_return
*/
CCORDcode discord_create_followup_message(
struct discord *client,
u64snowflake application_id,
const char interaction_token[],
struct discord_create_followup_message *params,
struct discord_ret_webhook *ret);
/**
* @brief Get a followup message for an interaction
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the application
* @param interaction_token the unique token of the interaction
* @param message_id the unique id of the message
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_get_followup_message(struct discord *client,
u64snowflake application_id,
const char interaction_token[],
u64snowflake message_id,
struct discord_ret_message *ret);
/**
* @brief Edits a followup message for an interaction
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the application
* @param interaction_token the unique token of the interaction
* @param message_id the unique id of the message
* @param params request parameters
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_edit_followup_message(
struct discord *client,
u64snowflake application_id,
const char interaction_token[],
u64snowflake message_id,
struct discord_edit_followup_message *params,
struct discord_ret_message *ret);
/**
* @brief Edits a followup message for an interaction
*
* @param client the client created with discord_from_token()
* @param application_id the unique id of the application
* @param interaction_token the unique token of the interaction
* @param message_id the unique id of the message
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_followup_message(struct discord *client,
u64snowflake application_id,
const char interaction_token[],
u64snowflake message_id,
struct discord_ret *ret);
/** @example components.c
* Demonstrates a couple use cases of the Message Components API */
/** @} DiscordAPIInteractionsReact */
#endif /* DISCORD_INTERACTION_H */
+51
View File
@@ -0,0 +1,51 @@
/**
* @file invite.h
* @author Cogmasters
* @brief Invite public functions and datatypes
*/
#ifndef DISCORD_INVITE_H
#define DISCORD_INVITE_H
/** @defgroup DiscordAPIInvite Invite
* @ingroup DiscordAPI
* @brief Invite's public API supported by Concord
* @{ */
/**
* @brief Get an invite for the given code
*
* @param client the client created with discord_from_token()
* @param invite_code the invite code
* @param params request parameters
* @CCORD_ret_obj{ret,invite}
* @CCORD_return
*/
CCORDcode discord_get_invite(struct discord *client,
char *invite_code,
struct discord_get_invite *params,
struct discord_ret_invite *ret);
/**
* @brief Delete an invite
* @note Requires the MANAGE_CHANNELS permission on the channel this invite
* belongs to, or MANAGE_GUILD to remove any invite across the guild.
* @note Fires a `Invite Delete` event
*
* @param client the client created with discord_from_token()
* @param invite_code the invite code
* @param params request parameters
* @CCORD_ret_obj{ret,invite}
* @CCORD_return
*/
CCORDcode discord_delete_invite(struct discord *client,
char *invite_code,
struct discord_delete_invite *params,
struct discord_ret_invite *ret);
/** @example invite.c
* Demonstrates a couple use cases of the Invite API */
/** @} DiscordAPIInvite */
#endif /* DISCORD_INVITE_H */
+118
View File
@@ -0,0 +1,118 @@
#ifndef CONCORD_IO_POLLER_H
#define CONCORD_IO_POLLER_H
#include <stdbool.h>
#include <curl/curl.h>
/**
* @brief The flags to poll for
*/
enum io_poller_events {
IO_POLLER_IN = 1 << 0,
IO_POLLER_OUT = 1 << 1,
};
/**
* @brief a socket or file descriptor
*/
typedef int io_poller_socket;
/**
* @brief handle for watching file descriptors, sockets, and curl multis
*/
struct io_poller;
/**
* @brief callback for when an event is triggered by the socket
*/
typedef void (*io_poller_cb)(struct io_poller *io,
enum io_poller_events events,
void *user_data);
struct io_poller *io_poller_create(void);
void io_poller_destroy(struct io_poller *io);
/**
* @brief wakeup the thread listening to this io_poller
*
* @param io the io_poller to wake up
*/
void
io_poller_wakeup(struct io_poller *io);
/**
* @brief wait for events to be triggered
* @param io the io_poller to poll on
* @param milliseconds -1 for infinity, or ms to poll for
* @return -1 for error, or number of sockets that have events waiting
*/
int io_poller_poll(struct io_poller *io, int milliseconds);
/**
* @brief performs any actions needed and clears events set by io_poller_poll
* @param io the io_poller to perform on
* @return 0 on success
*/
int io_poller_perform(struct io_poller *io);
/**
* @brief adds or modifies a socket or file descriptor to watch list
* @param io the io_poller to add socket to
* @param sock the file descriptor or socket to handle
* @param events the events to watch for
* @param cb the callback for when any event is triggered
* @param user_data custom user data
* @return true on success
*/
bool io_poller_socket_add(struct io_poller *io,
io_poller_socket sock,
enum io_poller_events events,
io_poller_cb cb,
void *user_data);
/**
* @brief removes a socket or file descriptor from watch list
* @param io the io_poller to remove the socket from
* @param sock the file descriptor or socket to remove
* @return true on success
*/
bool io_poller_socket_del(struct io_poller *io, io_poller_socket sock);
/**
* @brief callback for when curl multi should be performed on
*/
typedef int (*io_poller_curl_cb)(struct io_poller *io,
CURLM *multi,
void *user_data);
/**
* @brief add or modifies a curl multi to watch list
* @param io the io_poller to add curl multi to
* @param multi the curl multi to add or modify
* @param cb the callback for when curl multi should be performed on
* @param user_data custom user data
* @return true on success
*/
bool io_poller_curlm_add(struct io_poller *io,
CURLM *multi,
io_poller_curl_cb cb,
void *user_data);
/**
* @brief remove curl multi from watch list
* @param io the io_poller to remove curl multi from
* @param multi the curl multi to remove
* @return true on success
*/
bool io_poller_curlm_del(struct io_poller *io, CURLM *multi);
/**
* @brief this multi should be performed on next cycle
* causing poll to return immediately
* @param io the io_poller to enable perform on
* @param multi the multi that should be performed
* @return true on success
*/
bool io_poller_curlm_enable_perform(struct io_poller *io, CURLM *multi);
#endif // CONCORD_IO_POLLER_H
+683
View File
@@ -0,0 +1,683 @@
#ifndef JSMN_FIND_H
#define JSMN_FIND_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef JSMN_H
#error "jsmn-find.h should be included after jsmn.h"
#else
#define OA_HASH_HEADER
#include "oa_hash.h"
#undef OA_HASH_HEADER
#define JSMNF_PAIR_ATTRS_const \
/** JSON object or array pair attributes */ \
OA_HASH_ATTRS(const); \
/** JSON object or array fields */ \
const struct jsmnf_pair *const fields; \
/** key attributes */ \
const jsmntok_t *const k; \
/** value attribute */ \
const jsmntok_t *const v
#define JSMNF_PAIR_ATTRS_mut \
/** JSON object or array pair attributes */ \
OA_HASH_ATTRS(mut); \
/** JSON object or array fields */ \
struct jsmnf_pair *fields; \
/** key attributes */ \
jsmntok_t *k; \
/** value attribute */ \
jsmntok_t *v
#define JSMNF_PAIR_ATTRS(_qualifier) JSMNF_PAIR_ATTRS_##_qualifier
typedef struct jsmnf_pair {
JSMNF_PAIR_ATTRS(const);
} jsmnf_pair;
/** @brief Bucket @ref jsmnf_pair loader, keeps track of pair array
* position */
typedef struct jsmnf_loader {
/** jsmnf_loader can be cast to jsmn_parser */
jsmn_parser parser;
/** next pair to allocate */
unsigned pairnext;
/** root pair */
const jsmnf_pair *root;
} jsmnf_loader;
/** @brief JSON table, not supposed to be accessed by user */
typedef struct jsmnf_table {
/** @private */
const struct jsmntok _;
const struct jsmnf_pair __;
const struct oa_hash_entry ___;
} jsmnf_table;
/**
* @brief Initialize a @ref jsmnf_loader
*
* @param[out] loader jsmnf_loader to be initialized
*/
JSMN_API void jsmnf_init(jsmnf_loader *loader);
/**
* @brief Populate the @ref jsmnf_pair pairs from jsmn tokens
*
* @param[in,out] loader the @ref jsmnf_loader initialized with jsmnf_init()
* @param[in] js the JSON data string
* @param[in] len the raw JSON string length
* @param[out] tokens jsmn tokens
* @param[out] table jsmnf_table pairs array
* @param[in] table_len maximum amount of pairs provided
* @attention must not be less than the amount of tokens
* @return a `enum jsmnerr` value for error or the amount of `pairs` used
*/
JSMN_API long jsmnf_load(jsmnf_loader *loader,
const char js[],
const size_t len,
jsmnf_table table[],
const size_t table_len);
/**
* @brief Find a @ref jsmnf_pair token by its associated key
*
* @param[in] head a @ref jsmnf_pair object or array loaded at jsmnf_init()
* @param[in] key the key too be matched
* @param[in] length length of the key too be matched
* @return the @ref jsmnf_pair `head`'s field matched to `key`, or NULL if
* not encountered
*/
JSMN_API const jsmnf_pair *jsmnf_find(const jsmnf_pair *const head,
const char key[],
const size_t length);
/**
* @brief Find a @ref jsmnf_pair token by its full key path
*
* @param[in] head a @ref jsmnf_pair object or array loaded at jsmnf_init()
* @param[in] path an array of key path strings, from least to highest depth
* @param[in] depth the depth level of the last `path` key
* @return the @ref jsmnf_pair `head`'s field matched to `path`, or NULL if
* not encountered
*/
JSMN_API const jsmnf_pair *jsmnf_find_path(const jsmnf_pair *const head,
char *const path[],
unsigned depth);
/**
* @brief Populate and automatically allocate the @ref jsmnf_pair pairs from
* jsmn tokens
* @brief jsmnf_load() counterpart that automatically allocates the necessary
* amount of pairs necessary for sorting the JSON tokens
*
* @param[in,out] loader the @ref jsmnf_loader initialized with jsmnf_init()
* @param[in] js the JSON data string
* @param[in] len the raw JSON string length
* @param[out] p_table pointer to @ref jsmnf_table to be dynamically increased
* @note must be `free()`'d once done being used
* @param[in,out] table_len maximum amount of pairs provided
* @return a `enum jsmnerr` value for error or the amount of `pairs` used
*/
JSMN_API long jsmnf_load_auto(jsmnf_loader *loader,
const char js[],
const size_t len,
jsmnf_table **p_table,
size_t *num_pairs);
/**
* @brief `jsmn_parse()` counterpart that automatically allocates the necessary
* amount of tokens necessary for parsing the JSON string
*
* @param[in,out] parser the `jsmn_parser` initialized with `jsmn_init()`
* @param[in] js the JSON data string
* @param[in] len the raw JSON string length
* @param[out] p_tokens pointer to `jsmntok_t` to be dynamically increased
* @note must be `free()`'d once done being used
* @param[in,out] num_tokens amount of tokens
* @return a `enum jsmnerr` value for error or the amount of `tokens` used
*/
JSMN_API long jsmn_parse_auto(jsmn_parser *parser,
const char js[],
const size_t len,
jsmntok_t **p_tokens,
unsigned *num_tokens);
/**
* @brief Utility function for unescaping a Unicode string
*
* @param[out] buf destination buffer
* @param[in] bufsize destination buffer size
* @param[in] src source string to be unescaped
* @param[in] length source string length
* @return length of unescaped string if successful or a negative jsmn error
* code on failure
*/
JSMN_API long jsmnf_unescape(char buf[],
size_t bufsize,
const char src[],
size_t length);
#ifndef JSMN_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OA_HASH_STATIC
#include "oa_hash.h"
#undef OA_HASH_STATIC
struct _jsmnf_pair_mut {
JSMNF_PAIR_ATTRS(mut);
};
JSMN_API void
jsmnf_init(jsmnf_loader *loader)
{
jsmn_init(&loader->parser);
loader->pairnext = 0;
}
static long
_jsmnf_load_pairs(struct jsmnf_loader *loader,
const char js[],
struct _jsmnf_pair_mut *curr,
const size_t num_tokens,
struct _jsmnf_pair_mut pairs[],
struct oa_hash_entry buckets[],
const size_t table_len)
{
int offset = 0;
if (!num_tokens) return 0;
switch (curr->v->type) {
case JSMN_STRING:
case JSMN_PRIMITIVE:
break;
case JSMN_OBJECT:
case JSMN_ARRAY: {
const unsigned value_size = (unsigned)curr->v->size,
top_idx = loader->pairnext + (1 + value_size),
bottom_idx = loader->pairnext;
int ret;
if (value_size > (table_len - bottom_idx)
|| top_idx > (table_len - bottom_idx))
{
return JSMN_ERROR_NOMEM;
}
loader->pairnext = top_idx;
oa_hash_init((struct oa_hash *)curr, &buckets[bottom_idx],
top_idx - bottom_idx);
if (curr == NULL) {
abort();
}
if (JSMN_OBJECT == curr->v->type) {
while (curr->length < value_size) {
struct _jsmnf_pair_mut *fields = pairs + bottom_idx,
*element = fields + curr->length;
element->k = curr->v + 1 + (offset++);
if (element->k->size > 0) {
element->v = curr->v + 1 + offset;
oa_hash_set((struct oa_hash *)curr, js + element->k->start,
element->k->end - element->k->start, element);
if ((ret = _jsmnf_load_pairs(loader, js, element,
num_tokens - offset, pairs,
buckets, table_len))
< 0)
{
return ret;
}
curr->fields = (struct jsmnf_pair *)fields;
offset += ret;
}
else {
oa_hash_set((struct oa_hash *)curr, js + element->k->start,
element->k->end - element->k->start, NULL);
}
}
}
else if (JSMN_ARRAY == curr->v->type) {
for (; curr->length < value_size; ++curr->length) {
static jsmntok_t empty_key = { 0 };
struct oa_hash_entry *entry = curr->buckets + curr->length;
struct _jsmnf_pair_mut *fields = pairs + bottom_idx,
*element = fields + curr->length;
entry->state = OA_HASH_ENTRY_OCCUPIED;
entry->value = element;
element->v = curr->v + 1 + offset;
element->k = &empty_key;
if ((ret = _jsmnf_load_pairs(loader, js, element,
num_tokens - offset, pairs,
buckets, table_len))
< 0)
{
return ret;
}
curr->fields = (struct jsmnf_pair *)fields;
offset += ret;
}
}
break;
}
default:
case JSMN_UNDEFINED:
return JSMN_ERROR_INVAL;
}
return offset + 1;
}
JSMN_API long
jsmnf_load(struct jsmnf_loader *loader,
const char js[],
const size_t len,
struct jsmnf_table table[],
const size_t table_len)
{
struct jsmntok *tokens = (struct jsmntok *)table;
struct _jsmnf_pair_mut
*pairs = (struct _jsmnf_pair_mut *)(((char *)tokens)
+ (table_len * sizeof *tokens)),
*mut_root = &pairs[0];
struct oa_hash_entry *buckets =
(struct oa_hash_entry *)(((char *)pairs)
+ (table_len * sizeof *pairs));
long ret;
if (loader->pairnext == 0) { /* first run, initialize pairs */
/* initialize tokens if not already initialized */
if (loader->parser.toknext == 0) {
memset(tokens, 0, table_len * sizeof *tokens);
if ((ret = jsmn_parse(&loader->parser, js, len, tokens, table_len))
< 0)
{
return jsmn_init(&loader->parser), ret;
}
}
memset(pairs, 0, table_len * sizeof *pairs);
memset(buckets, 0, table_len * sizeof *buckets);
mut_root->v = tokens + loader->pairnext++;
loader->root = (struct jsmnf_pair *)mut_root;
}
if ((ret = _jsmnf_load_pairs(loader, js, mut_root, loader->parser.toknext,
pairs, buckets, table_len))
< 0)
{
/* TODO: rather than reseting pairnext keep the last 'bucket' ptr
* stored, so it can continue from there in the next try */
loader->pairnext = 0;
loader->root = NULL;
}
return ret;
}
JSMN_API const struct jsmnf_pair *
jsmnf_find(const struct jsmnf_pair *head,
const char key[],
const size_t length)
{
if (!head || !head->v) return NULL;
if (!key && !length) return head;
if (JSMN_OBJECT == head->v->type) {
return oa_hash_get((struct oa_hash *)head, key, length);
}
if (JSMN_ARRAY == head->v->type) {
char *endptr;
const unsigned idx = (unsigned)strtoul(key, &endptr, 10);
if (endptr != key && (idx < head->length)
&& head->buckets[idx].state == OA_HASH_ENTRY_OCCUPIED)
{
return head->buckets[idx].value;
}
}
return NULL;
}
JSMN_API const struct jsmnf_pair *
jsmnf_find_path(const struct jsmnf_pair *head,
char *const path[],
unsigned depth)
{
const struct jsmnf_pair *iter = head, *found = NULL;
unsigned i;
for (i = 0; i < depth; ++i) {
if (!iter || !(found = jsmnf_find(iter, path[i], strlen(path[i]))))
break;
iter = found;
}
return found;
}
#define RECALLOC_OR_ERROR(ptr, prev_size) \
do { \
const unsigned new_size = *(prev_size) * 2; \
void *tmp = realloc((ptr), new_size * sizeof *(ptr)); \
if (!tmp) return JSMN_ERROR_NOMEM; \
(ptr) = tmp; \
memset((ptr) + *(prev_size), 0, \
(new_size - *(prev_size)) * sizeof *(ptr)); \
*(prev_size) = new_size; \
} while (0)
JSMN_API long
jsmn_parse_auto(struct jsmn_parser *parser,
const char js[],
const size_t len,
struct jsmntok **p_tokens,
unsigned *num_tokens)
{
int ret;
if (NULL == *p_tokens || 0 == *num_tokens) {
*p_tokens = calloc(1, sizeof **p_tokens);
*num_tokens = 1;
}
while ((ret = jsmn_parse(parser, js, len, *p_tokens, *num_tokens))
== JSMN_ERROR_NOMEM)
{
RECALLOC_OR_ERROR(*p_tokens, num_tokens);
}
return ret;
}
JSMN_API long
jsmnf_load_auto(struct jsmnf_loader *loader,
const char js[],
const size_t len,
struct jsmnf_table **p_table,
size_t *table_len)
{
int ret;
if (NULL == *p_table || 0 == *table_len) {
if (!(*p_table = calloc(1, sizeof **p_table))) {
return JSMN_ERROR_NOMEM;
}
*table_len = 1;
}
while ((ret = jsmnf_load(loader, js, len, *p_table, *table_len))
== JSMN_ERROR_NOMEM)
{
RECALLOC_OR_ERROR(*p_table, table_len);
}
return ret;
}
#undef RECALLOC_OR_ERROR
static int
_jsmnf_read_4_digits(char *s, const char *end, unsigned *p_hex)
{
char buf[5] = { 0 };
int i;
if (end - s < 4) return JSMN_ERROR_PART;
for (i = 0; i < 4; i++) {
buf[i] = s[i];
if (('0' <= s[i] && s[i] <= '9') || ('A' <= s[i] && s[i] <= 'F')
|| ('a' <= s[i] && s[i] <= 'f'))
{
continue;
}
return JSMN_ERROR_INVAL;
}
*p_hex = (unsigned)strtoul(buf, NULL, 16);
return 4;
}
#define _JSMNF_UTF16_IS_FIRST_SURROGATE(c) \
(0xD800 <= (unsigned)c && (unsigned)c <= 0xDBFF)
#define _JSMNF_UTF16_IS_SECOND_SURROGATE(c) \
(0xDC00 <= (unsigned)c && (unsigned)c <= 0xDFFF)
#define _JSMNF_UTF16_JOIN_SURROGATE(c1, c2) \
(((((unsigned long)c1 & 0x3FF) << 10) | ((unsigned)c2 & 0x3FF)) + 0x10000)
#define _JSMNF_UTF8_IS_VALID(c) \
(((unsigned long)c <= 0x10FFFF) \
&& ((unsigned long)c < 0xD800 || (unsigned long)c > 0xDFFF))
#define _JSMNF_UTF8_IS_TRAIL(c) (((unsigned char)c & 0xC0) == 0x80)
#define _JSMNF_UTF_ILLEGAL 0xFFFFFFFFu
static int
_jsmnf_utf8_trail_length(unsigned char c)
{
if (c < 128) return 0;
if (c < 194) return -1;
if (c < 224) return 1;
if (c < 240) return 2;
if (c <= 244) return 3;
return -1;
}
static int
_jsmnf_utf8_width(unsigned long value)
{
if (value <= 0x7F) return 1;
if (value <= 0x7FF) return 2;
if (value <= 0xFFFF) return 3;
return 4;
}
/* See RFC 3629
Based on: http://www.w3.org/International/questions/qa-forms-utf-8 */
static unsigned long
_jsmnf_utf8_next(char **p, const char *end)
{
unsigned char lead, tmp;
int trail_size;
unsigned long c;
if (*p == end) return _JSMNF_UTF_ILLEGAL;
lead = **p;
(*p)++;
/* First byte is fully validated here */
trail_size = _jsmnf_utf8_trail_length(lead);
if (trail_size < 0) return _JSMNF_UTF_ILLEGAL;
/* Ok as only ASCII may be of size = 0 also optimize for ASCII text */
if (trail_size == 0) return lead;
c = lead & ((1 << (6 - trail_size)) - 1);
/* Read the rest */
switch (trail_size) {
case 3:
if (*p == end) return _JSMNF_UTF_ILLEGAL;
tmp = **p;
(*p)++;
if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL;
c = (c << 6) | (tmp & 0x3F);
/* fall-through */
case 2:
if (*p == end) return _JSMNF_UTF_ILLEGAL;
tmp = **p;
(*p)++;
if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL;
c = (c << 6) | (tmp & 0x3F);
/* fall-through */
case 1:
if (*p == end) return _JSMNF_UTF_ILLEGAL;
tmp = **p;
(*p)++;
if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL;
c = (c << 6) | (tmp & 0x3F);
}
/* Check code point validity: no surrogates and valid range */
if (!_JSMNF_UTF8_IS_VALID(c)) return _JSMNF_UTF_ILLEGAL;
/* make sure it is the most compact representation */
if (_jsmnf_utf8_width(c) != trail_size + 1) return _JSMNF_UTF_ILLEGAL;
return c;
}
static long
_jsmnf_utf8_validate(char *p, const char *end)
{
const char *start = p;
while (p != end) {
if (_jsmnf_utf8_next(&p, end) == _JSMNF_UTF_ILLEGAL)
return JSMN_ERROR_INVAL;
}
return (long)(end - start);
}
static unsigned
_jsmnf_utf8_encode(unsigned long value, char utf8_seq[4])
{
if (value <= 0x7F) {
utf8_seq[0] = value;
return 1;
}
if (value <= 0x7FF) {
utf8_seq[0] = (value >> 6) | 0xC0;
utf8_seq[1] = (value & 0x3F) | 0x80;
return 2;
}
if (value <= 0xFFFF) {
utf8_seq[0] = (value >> 12) | 0xE0;
utf8_seq[1] = ((value >> 6) & 0x3F) | 0x80;
utf8_seq[2] = (value & 0x3F) | 0x80;
return 3;
}
utf8_seq[0] = (value >> 18) | 0xF0;
utf8_seq[1] = ((value >> 12) & 0x3F) | 0x80;
utf8_seq[2] = ((value >> 6) & 0x3F) | 0x80;
utf8_seq[3] = (value & 0x3F) | 0x80;
return 4;
}
static int
_jsmnf_utf8_append(unsigned long hex, char *buf_tok, const char *buf_end)
{
char utf8_seq[4];
unsigned utf8_seqlen = _jsmnf_utf8_encode(hex, utf8_seq);
unsigned i;
if ((buf_tok + utf8_seqlen) >= buf_end) return JSMN_ERROR_NOMEM;
for (i = 0; i < utf8_seqlen; ++i)
buf_tok[i] = utf8_seq[i];
return utf8_seqlen;
}
#define BUF_PUSH(buf_tok, c, buf_end) \
do { \
if (buf_tok >= buf_end) return JSMN_ERROR_NOMEM; \
*buf_tok++ = c; \
} while (0)
JSMN_API long
jsmnf_unescape(char buf[], size_t bufsize, const char src[], size_t len)
{
char *src_tok = (char *)src, *const src_end = src_tok + len;
char *buf_tok = buf, *const buf_end = buf + bufsize;
int second_surrogate_expected = 0;
unsigned first_surrogate = 0;
while (*src_tok && src_tok < src_end) {
char c = *src_tok++;
if (0 <= c && c <= 0x1F) return JSMN_ERROR_INVAL;
if (c != '\\') {
if (second_surrogate_expected) return JSMN_ERROR_INVAL;
BUF_PUSH(buf_tok, c, buf_end);
continue;
}
/* expects escaping but src is a well-formed string */
if (!*src_tok || src_tok >= src_end) return JSMN_ERROR_PART;
c = *src_tok++;
if (second_surrogate_expected && c != 'u') return JSMN_ERROR_INVAL;
switch (c) {
case '"':
case '\\':
case '/':
BUF_PUSH(buf_tok, c, buf_end);
break;
case 'b':
BUF_PUSH(buf_tok, '\b', buf_end);
break;
case 'f':
BUF_PUSH(buf_tok, '\f', buf_end);
break;
case 'n':
BUF_PUSH(buf_tok, '\n', buf_end);
break;
case 'r':
BUF_PUSH(buf_tok, '\r', buf_end);
break;
case 't':
BUF_PUSH(buf_tok, '\t', buf_end);
break;
case 'u': {
unsigned hex;
int ret = _jsmnf_read_4_digits(src_tok, src_end, &hex);
if (ret != 4) return ret;
src_tok += ret;
if (second_surrogate_expected) {
if (!_JSMNF_UTF16_IS_SECOND_SURROGATE(hex))
return JSMN_ERROR_INVAL;
ret = _jsmnf_utf8_append(
_JSMNF_UTF16_JOIN_SURROGATE(first_surrogate, hex), buf_tok,
buf_end);
if (ret < 0) return ret;
buf_tok += ret;
second_surrogate_expected = 0;
}
else if (_JSMNF_UTF16_IS_FIRST_SURROGATE(hex)) {
second_surrogate_expected = 1;
first_surrogate = hex;
}
else {
ret = _jsmnf_utf8_append(hex, buf_tok, buf_end);
if (ret < 0) return ret;
buf_tok += ret;
}
} break;
default:
return JSMN_ERROR_INVAL;
}
}
return _jsmnf_utf8_validate(buf, buf_tok);
}
#undef BUF_PUSH
#endif /* JSMN_HEADER */
#endif /* JSMN_H */
#undef JSMNF_PAIR_ATTRS_const
#undef JSMNF_PAIR_ATTRS_mut
#undef JSMNF_PAIR_ATTRS
#ifdef __cplusplus
}
#endif
#endif /* JSMN_FIND_H */
+471
View File
@@ -0,0 +1,471 @@
/*
* MIT License
*
* Copyright (c) 2010 Serge Zaitsev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef JSMN_H
#define JSMN_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef JSMN_STATIC
#define JSMN_API static
#else
#define JSMN_API extern
#endif
/**
* JSON type identifier. Basic types are:
* o Object
* o Array
* o String
* o Other primitive: number, boolean (true/false) or null
*/
typedef enum {
JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1,
JSMN_ARRAY = 2,
JSMN_STRING = 3,
JSMN_PRIMITIVE = 4
} jsmntype_t;
enum jsmnerr {
/* Not enough tokens were provided */
JSMN_ERROR_NOMEM = -1,
/* Invalid character inside JSON string */
JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3
};
/**
* JSON token description.
* type type (object, array, string etc.)
* start start position in JSON data string
* end end position in JSON data string
*/
typedef struct jsmntok {
jsmntype_t type;
int start;
int end;
int size;
#ifdef JSMN_PARENT_LINKS
int parent;
#endif
} jsmntok_t;
/**
* JSON parser. Contains an array of token blocks available. Also stores
* the string being parsed now and current position in that string.
*/
typedef struct jsmn_parser {
unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g. parent object or array */
} jsmn_parser;
/**
* Create JSON parser over an array of tokens
*/
JSMN_API void jsmn_init(jsmn_parser *parser);
/**
* Run JSON parser. It parses a JSON data string into and array of tokens, each
* describing
* a single JSON object.
*/
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens);
#ifndef JSMN_HEADER
/**
* Allocates a fresh unused token from the token pool.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *tok;
if (parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
tok->start = tok->end = -1;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
#endif
return tok;
}
/**
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
const int start, const int end) {
token->type = type;
token->start = start;
token->end = end;
token->size = 0;
}
/**
* Fills next available token with JSON primitive.
*/
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start;
start = parser->pos;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
switch (js[parser->pos]) {
#ifndef JSMN_STRICT
/* In strict mode primitive must be followed by "," or "}" or "]" */
case ':':
#endif
case '\t':
case '\r':
case '\n':
case ' ':
case ',':
case ']':
case '}':
goto found;
default:
/* to quiet a warning from gcc*/
break;
}
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
#ifdef JSMN_STRICT
/* In strict mode primitive must be followed by a comma/object/array */
parser->pos = start;
return JSMN_ERROR_PART;
#endif
found:
if (tokens == NULL) {
parser->pos--;
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
parser->pos--;
return 0;
}
/**
* Fills next token with JSON string.
*/
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start = parser->pos;
parser->pos++;
/* Skip starting quote */
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c = js[parser->pos];
/* Quote: end of string */
if (c == '\"') {
if (tokens == NULL) {
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
return 0;
}
/* Backslash: Quoted symbol expected */
if (c == '\\' && parser->pos + 1 < len) {
int i;
parser->pos++;
switch (js[parser->pos]) {
/* Allowed escaped symbols */
case '\"':
case '/':
case '\\':
case 'b':
case 'f':
case 'r':
case 'n':
case 't':
break;
/* Allows escaped symbol \uXXXX */
case 'u':
parser->pos++;
for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
i++) {
/* If it isn't a hex character we have an error */
if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
parser->pos = start;
return JSMN_ERROR_INVAL;
}
parser->pos++;
}
parser->pos--;
break;
/* Unexpected symbol */
default:
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
}
parser->pos = start;
return JSMN_ERROR_PART;
}
/**
* Parse JSON string and fill tokens.
*/
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens) {
int r;
int i;
jsmntok_t *token;
int count = parser->toknext;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c;
jsmntype_t type;
c = js[parser->pos];
switch (c) {
case '{':
case '[':
count++;
if (tokens == NULL) {
break;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
return JSMN_ERROR_NOMEM;
}
if (parser->toksuper != -1) {
jsmntok_t *t = &tokens[parser->toksuper];
#ifdef JSMN_STRICT
/* In strict mode an object or array can't become a key */
if (t->type == JSMN_OBJECT) {
return JSMN_ERROR_INVAL;
}
#endif
t->size++;
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
}
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = parser->pos;
parser->toksuper = parser->toknext - 1;
break;
case '}':
case ']':
if (tokens == NULL) {
break;
}
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
#ifdef JSMN_PARENT_LINKS
if (parser->toknext < 1) {
return JSMN_ERROR_INVAL;
}
token = &tokens[parser->toknext - 1];
for (;;) {
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
}
if (token->parent == -1) {
if (token->type != type || parser->toksuper == -1) {
return JSMN_ERROR_INVAL;
}
break;
}
token = &tokens[token->parent];
}
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
parser->toksuper = -1;
token->end = parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1) {
return JSMN_ERROR_INVAL;
}
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
parser->toksuper = i;
break;
}
}
#endif
break;
case '\"':
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
case '\t':
case '\r':
case '\n':
case ' ':
break;
case ':':
parser->toksuper = parser->toknext - 1;
break;
case ',':
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
parser->toksuper = tokens[parser->toksuper].parent;
#else
for (i = parser->toknext - 1; i >= 0; i--) {
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = i;
break;
}
}
}
#endif
}
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 't':
case 'f':
case 'n':
/* And they must not be keys of the object */
if (tokens != NULL && parser->toksuper != -1) {
const jsmntok_t *t = &tokens[parser->toksuper];
if (t->type == JSMN_OBJECT ||
(t->type == JSMN_STRING && t->size != 0)) {
return JSMN_ERROR_INVAL;
}
}
#else
/* In non-strict mode every unquoted value is a primitive */
default:
#endif
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
#ifdef JSMN_STRICT
/* Unexpected char in strict mode */
default:
return JSMN_ERROR_INVAL;
#endif
}
}
if (tokens != NULL) {
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
if (tokens[i].start != -1 && tokens[i].end == -1) {
return JSMN_ERROR_PART;
}
}
}
return count;
}
/**
* Creates a new parser based over a given buffer with an array of tokens
* available.
*/
JSMN_API void jsmn_init(jsmn_parser *parser) {
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
}
#endif /* JSMN_HEADER */
#ifdef __cplusplus
}
#endif
#endif /* JSMN_H */
+957
View File
@@ -0,0 +1,957 @@
/*
* Special thanks to Christopher Wellons (aka skeeto) for giving valuable
* feedback that helped improve this lib.
*
* See: https://www.reddit.com/r/C_Programming/comments/sf95m3/comment/huojrjn
*/
#ifndef JSON_BUILD_H
#define JSON_BUILD_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef JSONB_STATIC
#define JSONB_API static
#else
#define JSONB_API extern
#endif
#ifndef JSONB_MAX_DEPTH
/**
* Maximum JSON nesting depth, if default value is unwanted then it should be
* defined before json-build.h is included:
*
* #define JSONB_MAX_DEPTH 256
* #include "json-build.h"
*/
#define JSONB_MAX_DEPTH 128
#endif /* JSONB_MAX_DEPTH */
/** @brief json-builder return codes */
typedef enum jsonbcode {
/** no error, operation was a success */
JSONB_OK = 0,
/** string is complete, expects no more inputs */
JSONB_END,
/** not enough tokens were provided */
JSONB_ERROR_NOMEM = -1,
/** token doesn't match expected value */
JSONB_ERROR_INPUT = -2,
/** operation would lead to out of boundaries access */
JSONB_ERROR_STACK = -3,
/** buffer overflow */
JSONB_ERROR_OVERFLOW = -4
} jsonbcode;
/** @brief json-builder serializing state */
enum jsonbstate {
JSONB_INIT = 0,
JSONB_ARRAY_OR_OBJECT_OR_VALUE = JSONB_INIT,
JSONB_OBJECT_KEY_OR_CLOSE,
JSONB_OBJECT_VALUE,
JSONB_OBJECT_NEXT_KEY_OR_CLOSE,
JSONB_ARRAY_VALUE_OR_CLOSE,
JSONB_ARRAY_NEXT_VALUE_OR_CLOSE,
JSONB_ERROR,
JSONB_DONE
};
/** @brief Handle for building a JSON string */
typedef struct jsonb {
/** state stack to keep track and enforce next inputs */
enum jsonbstate stack[JSONB_MAX_DEPTH + 1];
/** pointer to stack top */
enum jsonbstate *top;
/** offset in the JSON buffer (current length) */
size_t pos;
} jsonb;
/**
* @brief Reset a jsonb handle buffer's position tracker
* (for streaming purposes)
* @note Should be used in conjunction with @ref JSONB_ERROR_NOMEM if the
* buffer is meant to be used as a stream
*
* @param builder pointer to the @ref jsonb handle
*/
#define jsonb_reset(builder) ((builder)->pos = 0)
/**
* @brief Initialize a jsonb handle
*
* @param builder the handle to be initialized
*/
JSONB_API void jsonb_init(jsonb *builder);
/**
* @brief Push an object to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_object(jsonb *builder, char buf[], size_t bufsize);
/**
* @brief @ref jsonb_object() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_object_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize);
/**
* @brief Pop an object from the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_object_pop(jsonb *builder,
char buf[],
size_t bufsize);
/**
* @brief @ref jsonb_object_pop() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_object_pop_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize);
/**
* @brief Push a key to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @param key the key to be inserted
* @param len the key length
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_key(
jsonb *builder, char buf[], size_t bufsize, const char key[], size_t len);
/**
* @brief @ref jsonb_key() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @param key the key to be inserted
* @param len the key length
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_key_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize,
const char key[],
size_t len);
/**
* @brief Push an array to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_array(jsonb *builder, char buf[], size_t bufsize);
/**
* @brief @ref jsonb_array() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_array_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize);
/**
* @brief Pop an array from the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_array_pop(jsonb *builder,
char buf[],
size_t bufsize);
/**
* @brief @ref jsonb_array_pop() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_array_pop_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize);
/**
* @brief Push a raw JSON token to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @param token the token to be inserted
* @param len the token length
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_token(jsonb *builder,
char buf[],
size_t bufsize,
const char token[],
size_t len);
/**
* @brief @ref jsonb_token() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @param token the token to be inserted
* @param len the token length
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_token_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize,
const char token[],
size_t len);
/**
* @brief Push a boolean token to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @param boolean the boolean to be inserted
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_bool(jsonb *builder,
char buf[],
size_t bufsize,
int boolean);
/**
* @brief @ref jsonb_bool() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @param boolean the boolean to be inserted
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_bool_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize,
int boolean);
/**
* @brief Push a null token to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_null(jsonb *builder, char buf[], size_t bufsize);
/**
* @brief @ref jsonb_null() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_null_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize);
/**
* @brief Push a string token to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @param str the string to be inserted
* @param len the string length
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_string(
jsonb *builder, char buf[], size_t bufsize, const char str[], size_t len);
/**
* @brief @ref jsonb_string() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @param str the string to be inserted
* @param len the string length
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_string_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize,
const char str[],
size_t len);
/**
* @brief Push a number token to the builder
*
* @param builder the builder initialized with jsonb_init()
* @param buf the JSON buffer
* @param bufsize the JSON buffer size
* @param number the number to be inserted
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_number(jsonb *builder,
char buf[],
size_t bufsize,
double number);
/**
* @brief @ref jsonb_number() with dynamic buffer
*
* @param builder the builder initialized with jsonb_init()
* @param p_buf pointer to the JSON buffer
* @param p_bufsize pointer to the JSON buffer size
* @param number the number to be inserted
* @return @ref jsonbcode value
*/
JSONB_API jsonbcode jsonb_number_auto(jsonb *builder,
char *p_buf[],
size_t *p_bufsize,
double number);
#ifndef JSONB_HEADER
#include <stdio.h>
#include <stdlib.h>
#ifndef JSONB_DEBUG
#define TRACE(prev, next) next
#define DECORATOR(a)
#else
static const char *
_jsonb_eval_state(enum jsonbstate state)
{
switch (state) {
case JSONB_ARRAY_OR_OBJECT_OR_VALUE: return "array or object or value";
case JSONB_OBJECT_KEY_OR_CLOSE: return "object key or close";
case JSONB_OBJECT_NEXT_KEY_OR_CLOSE: return "object next key or close";
case JSONB_OBJECT_VALUE: return "object value";
case JSONB_ARRAY_VALUE_OR_CLOSE: return "array value or close";
case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: return "array next value or close";
case JSONB_ERROR: return "error";
case JSONB_DONE: return "done";
default: return "unknown";
}
}
#define TRACE(prev, next) \
do { \
enum jsonbstate _prev = prev, _next = next; \
fprintf(stderr, "%s():L%d | %s -> %s\n", __func__, __LINE__, \
_jsonb_eval_state(_prev), _jsonb_eval_state(_next)); \
} while (0)
#define DECORATOR(d) d
#endif /* JSONB_DEBUG */
#define STACK_HEAD(b, state) *(b)->top = (state)
#define STACK_PUSH(b, state) TRACE(*(b)->top, *++(b)->top = (state))
#define STACK_POP(b) TRACE(*(b)->top, DECORATOR(*)--(b)->top)
#define BUFFER_COPY_CHAR_STATIC(b, c, _pos, buf, bufsize) \
do { \
if ((b)->pos + (_pos) + 1 + 1 > (bufsize)) { \
(buf)[(b)->pos] = '\0'; \
return JSONB_ERROR_NOMEM; \
} \
(buf)[(b)->pos + (_pos)++] = (c); \
(buf)[(b)->pos + (_pos)] = '\0'; \
} while (0)
#define BUFFER_COPY_STATIC(b, value, len, _pos, buf, bufsize) \
do { \
size_t i; \
if ((b)->pos + (_pos) + (len) + 1 > (bufsize)) { \
(buf)[(b)->pos] = '\0'; \
return JSONB_ERROR_NOMEM; \
} \
for (i = 0; i < (len); ++i) \
(buf)[(b)->pos + (_pos) + i] = (value)[i]; \
(_pos) += (len); \
(buf)[(b)->pos + (_pos)] = '\0'; \
} while (0)
#define BUFFER_COPY_CHAR_REALLOC(b, c, _pos, p_buf, p_bufsize) \
do { \
if ((b)->pos + (_pos) + 1 + 1 > *p_bufsize) { \
char *new_buf = NULL; \
const size_t needed = (b)->pos + (_pos) + 1 + 1; \
size_t new_size = *p_bufsize + (*p_bufsize >> 1); /* 1.5x */ \
if (new_size < needed) new_size = needed; \
if (new_size < *p_bufsize) return JSONB_ERROR_OVERFLOW; \
new_buf = realloc(*p_buf, new_size); \
if (!new_buf) return JSONB_ERROR_NOMEM; \
*p_buf = new_buf; \
*p_bufsize = new_size; \
} \
(*p_buf)[(b)->pos + (_pos)++] = (c); \
(*p_buf)[(b)->pos + (_pos)] = '\0'; \
} while (0)
#define BUFFER_COPY_REALLOC(b, value, len, _pos, p_buf, p_bufsize) \
do { \
size_t i; \
if ((b)->pos + (_pos) + (len) + 1 > *p_bufsize) { \
char *new_buf = NULL; \
const size_t needed = (b)->pos + (_pos) + (len) + 1; \
size_t new_size = *p_bufsize + (*p_bufsize >> 1); /* 1.5x */ \
if (new_size < needed) new_size = needed; \
if (new_size < *p_bufsize) return JSONB_ERROR_OVERFLOW; \
new_buf = realloc(*p_buf, new_size); \
if (!new_buf) return JSONB_ERROR_NOMEM; \
*p_buf = new_buf; \
*p_bufsize = new_size; \
} \
for (i = 0; i < (len); ++i) \
(*p_buf)[(b)->pos + (_pos) + i] = (value)[i]; \
(_pos) += (len); \
(*p_buf)[(b)->pos + (_pos)] = '\0'; \
} while (0)
JSONB_API void
jsonb_init(jsonb *b)
{
static jsonb empty_builder;
*b = empty_builder;
b->top = b->stack;
}
#define JSONB_OBJECT_EXEC(_type, buf, bufsize) \
enum jsonbstate new_state; \
size_t pos = 0; \
if (b->top - b->stack >= JSONB_MAX_DEPTH) return JSONB_ERROR_STACK; \
switch (*b->top) { \
case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \
BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \
/* fall-through */ \
case JSONB_ARRAY_VALUE_OR_CLOSE: \
new_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \
break; \
case JSONB_OBJECT_VALUE: \
new_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \
break; \
case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \
new_state = JSONB_DONE; \
break; \
default: \
STACK_HEAD(b, JSONB_ERROR); \
/* fall-through */ \
case JSONB_DONE: \
case JSONB_ERROR: \
return JSONB_ERROR_INPUT; \
} \
BUFFER_COPY_CHAR_##_type(b, '{', pos, buf, bufsize); \
STACK_HEAD(b, new_state); \
STACK_PUSH(b, JSONB_OBJECT_KEY_OR_CLOSE); \
b->pos += pos; \
return JSONB_OK
JSONB_API jsonbcode
jsonb_object(jsonb *b, char buf[], size_t bufsize)
{
JSONB_OBJECT_EXEC(STATIC, buf, bufsize);
}
JSONB_API jsonbcode
jsonb_object_auto(jsonb *b, char *p_buf[], size_t *p_bufsize)
{
JSONB_OBJECT_EXEC(REALLOC, p_buf, p_bufsize);
}
#define JSONB_OBJECT_POP_EXEC(_type, buf, bufsize) \
enum jsonbcode code; \
size_t pos = 0; \
switch (*b->top) { \
case JSONB_OBJECT_KEY_OR_CLOSE: \
case JSONB_OBJECT_NEXT_KEY_OR_CLOSE: \
code = b->stack == b->top - 1 ? JSONB_END : JSONB_OK; \
break; \
default: \
STACK_HEAD(b, JSONB_ERROR); \
/* fall-through */ \
case JSONB_DONE: \
case JSONB_ERROR: \
return JSONB_ERROR_INPUT; \
} \
BUFFER_COPY_CHAR_##_type(b, '}', pos, buf, bufsize); \
STACK_POP(b); \
b->pos += pos; \
return code
JSONB_API jsonbcode
jsonb_object_pop(jsonb *b, char buf[], size_t bufsize)
{
JSONB_OBJECT_POP_EXEC(STATIC, buf, bufsize);
}
JSONB_API jsonbcode
jsonb_object_pop_auto(jsonb *b, char *p_buf[], size_t *p_bufsize)
{
JSONB_OBJECT_POP_EXEC(REALLOC, p_buf, p_bufsize);
}
static jsonbcode
_jsonb_escape_STATIC(size_t *pos,
char buf[],
size_t bufsize,
unsigned offset,
const char str[],
size_t len)
{
char *esc_tok = NULL, _esc_tok[8] = "\\u00";
char *esc_buf = NULL;
int extra_bytes = 0;
size_t i;
buf += offset;
bufsize -= offset;
second_iter:
/* 1st iteration, esc_buf is NULL and count extra_bytes needed for escaping
* 2st iteration, esc_buf is not NULL, and does escaping. */
for (i = 0; i < len; ++i) {
unsigned char c = str[i];
esc_tok = NULL;
switch (c) {
case 0x22: esc_tok = "\\\""; break;
case 0x5C: esc_tok = "\\\\"; break;
case '\b': esc_tok = "\\b"; break;
case '\f': esc_tok = "\\f"; break;
case '\n': esc_tok = "\\n"; break;
case '\r': esc_tok = "\\r"; break;
case '\t': esc_tok = "\\t"; break;
default: if (c <= 0x1F) {
static const char tohex[] = "0123456789abcdef";
_esc_tok[4] = tohex[c >> 4];
_esc_tok[5] = tohex[c & 0xF];
_esc_tok[6] = 0;
esc_tok = _esc_tok;
}
}
if (esc_tok) {
int j;
for (j = 0; esc_tok[j]; j++) {
if (!esc_buf) /* count how many extra bytes are needed */
continue;
*esc_buf++ = esc_tok[j];
}
extra_bytes += j - 1;
}
else if (esc_buf) {
*esc_buf++ = c;
}
}
if (*pos + len + extra_bytes > bufsize) {
*buf = '\0';
return JSONB_ERROR_NOMEM;
}
if (esc_buf) {
*pos += len + extra_bytes;
return JSONB_OK;
}
if (!extra_bytes) {
size_t j;
for (j = 0; j < len; ++j)
buf[*pos + j] = str[j];
*pos += len;
return JSONB_OK;
}
esc_buf = buf + *pos;
extra_bytes = 0;
goto second_iter;
}
static jsonbcode
_jsonb_escape_REALLOC(size_t *pos,
char *p_buf[],
size_t *p_bufsize,
unsigned offset,
const char str[],
size_t len)
{
char *esc_tok = NULL, _esc_tok[8] = "\\u00";
char *esc_buf = NULL;
int extra_bytes = 0;
size_t i;
int second_pass = 0;
ptrdiff_t esc_buf_offset = 0;
char *buf;
size_t bufsize;
restart:
buf = *p_buf + offset;
bufsize = *p_bufsize - offset;
if (second_pass && esc_buf) esc_buf = buf + esc_buf_offset;
second_iter:
/* 1st iteration, esc_buf is NULL and count extra_bytes needed for escaping
* 2st iteration, esc_buf is not NULL, and does escaping. */
for (i = 0; i < len; ++i) {
unsigned char c = str[i];
esc_tok = NULL;
switch (c) { case 0x22: esc_tok = "\\\""; break;
case 0x5C: esc_tok = "\\\\"; break;
case '\b': esc_tok = "\\b"; break;
case '\f': esc_tok = "\\f"; break;
case '\n': esc_tok = "\\n"; break;
case '\r': esc_tok = "\\r"; break;
case '\t': esc_tok = "\\t"; break;
default: if (c <= 0x1F) {
static const char tohex[] = "0123456789abcdef";
_esc_tok[4] = tohex[c >> 4];
_esc_tok[5] = tohex[c & 0xF];
_esc_tok[6] = 0;
esc_tok = _esc_tok;
}
}
if (esc_tok) {
int j;
for (j = 0; esc_tok[j]; j++) {
if (!esc_buf) /* count how many extra bytes are needed */
continue;
*esc_buf++ = esc_tok[j];
}
extra_bytes += j - 1;
}
else if (esc_buf) {
*esc_buf++ = c;
}
}
if (*pos + len + extra_bytes + 1 > bufsize) {
char *new_buf = NULL;
const size_t needed = *pos + len + extra_bytes + 1;
size_t new_size = *p_bufsize + (*p_bufsize >> 1); /* 1.5x */
if (new_size < needed) new_size = needed;
if (new_size < *p_bufsize) return JSONB_ERROR_OVERFLOW;
new_buf = realloc(*p_buf, new_size);
if (!new_buf) return JSONB_ERROR_NOMEM;
if (esc_buf) esc_buf_offset = esc_buf - buf;
*p_buf = new_buf;
*p_bufsize = new_size;
second_pass = 1;
goto restart;
}
if (esc_buf) {
*pos += len + extra_bytes;
return JSONB_OK;
}
if (!extra_bytes) {
size_t j;
for (j = 0; j < len; ++j)
buf[*pos + j] = str[j];
*pos += len;
return JSONB_OK;
}
esc_buf = buf + *pos;
extra_bytes = 0;
goto second_iter;
}
#define JSONB_KEY_EXEC(_type, buf, bufsize, key, len) \
size_t pos = 0; \
switch (*b->top) { \
case JSONB_OBJECT_NEXT_KEY_OR_CLOSE: \
BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \
/* fall-through */ \
case JSONB_OBJECT_KEY_OR_CLOSE: { \
enum jsonbcode ret; \
BUFFER_COPY_CHAR_##_type(b, '"', pos, buf, bufsize); \
ret = _jsonb_escape_##_type(&pos, buf, bufsize, b->pos, key, len); \
if (ret != JSONB_OK) return ret; \
BUFFER_COPY_##_type(b, "\":", 2, pos, buf, bufsize); \
STACK_HEAD(b, JSONB_OBJECT_VALUE); \
} break; \
default: \
STACK_HEAD(b, JSONB_ERROR); \
/* fall-through */ \
case JSONB_DONE: \
return JSONB_ERROR_INPUT; \
} \
b->pos += pos; \
return JSONB_OK
JSONB_API jsonbcode
jsonb_key(jsonb *b, char buf[], size_t bufsize, const char key[], size_t len)
{
JSONB_KEY_EXEC(STATIC, buf, bufsize, key, len);
}
JSONB_API jsonbcode
jsonb_key_auto(
jsonb *b, char *p_buf[], size_t *p_bufsize, const char key[], size_t len)
{
JSONB_KEY_EXEC(REALLOC, p_buf, p_bufsize, key, len);
}
#define JSONB_ARRAY_EXEC(_type, buf, bufsize) \
enum jsonbstate new_state; \
size_t pos = 0; \
if (b->top - b->stack >= JSONB_MAX_DEPTH) return JSONB_ERROR_STACK; \
switch (*b->top) { \
case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \
BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \
/* fall-through */ \
case JSONB_ARRAY_VALUE_OR_CLOSE: \
new_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \
break; \
case JSONB_OBJECT_VALUE: \
new_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \
break; \
case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \
new_state = JSONB_DONE; \
break; \
default: \
STACK_HEAD(b, JSONB_ERROR); \
/* fall-through */ \
case JSONB_DONE: \
case JSONB_ERROR: \
return JSONB_ERROR_INPUT; \
} \
BUFFER_COPY_CHAR_##_type(b, '[', pos, buf, bufsize); \
STACK_HEAD(b, new_state); \
STACK_PUSH(b, JSONB_ARRAY_VALUE_OR_CLOSE); \
b->pos += pos; \
return JSONB_OK
JSONB_API jsonbcode
jsonb_array(jsonb *b, char buf[], size_t bufsize)
{
JSONB_ARRAY_EXEC(STATIC, buf, bufsize);
}
JSONB_API jsonbcode
jsonb_array_auto(jsonb *b, char *p_buf[], size_t *p_bufsize)
{
JSONB_ARRAY_EXEC(REALLOC, p_buf, p_bufsize);
}
#define JSONB_ARRAY_POP_EXEC(_type, buf, bufsize) \
enum jsonbcode code; \
size_t pos = 0; \
switch (*b->top) { \
case JSONB_ARRAY_VALUE_OR_CLOSE: \
case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \
code = b->stack == b->top - 1 ? JSONB_END : JSONB_OK; \
break; \
default: \
STACK_HEAD(b, JSONB_ERROR); \
/* fall-through */ \
case JSONB_DONE: \
case JSONB_ERROR: \
return JSONB_ERROR_INPUT; \
} \
BUFFER_COPY_CHAR_##_type(b, ']', pos, buf, bufsize); \
STACK_POP(b); \
b->pos += pos; \
return code
JSONB_API jsonbcode
jsonb_array_pop(jsonb *b, char buf[], size_t bufsize)
{
JSONB_ARRAY_POP_EXEC(STATIC, buf, bufsize);
}
JSONB_API jsonbcode
jsonb_array_pop_auto(jsonb *b, char *p_buf[], size_t *p_bufsize)
{
JSONB_ARRAY_POP_EXEC(REALLOC, p_buf, p_bufsize);
}
#define JSONB_TOKEN_EXEC(_type, buf, bufsize, token, len) \
enum jsonbstate next_state; \
enum jsonbcode code; \
size_t pos = 0; \
switch (*b->top) { \
case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \
next_state = JSONB_DONE; \
code = JSONB_END; \
break; \
case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \
BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \
/* fall-through */ \
case JSONB_ARRAY_VALUE_OR_CLOSE: \
next_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \
code = JSONB_OK; \
break; \
case JSONB_OBJECT_VALUE: \
next_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \
code = JSONB_OK; \
break; \
default: \
STACK_HEAD(b, JSONB_ERROR); \
/* fall-through */ \
case JSONB_DONE: \
case JSONB_ERROR: \
return JSONB_ERROR_INPUT; \
} \
BUFFER_COPY_##_type(b, token, len, pos, buf, bufsize); \
STACK_HEAD(b, next_state); \
b->pos += pos; \
return code
JSONB_API jsonbcode
jsonb_token(
jsonb *b, char buf[], size_t bufsize, const char token[], size_t len)
{
JSONB_TOKEN_EXEC(STATIC, buf, bufsize, token, len);
}
JSONB_API jsonbcode
jsonb_token_auto(
jsonb *b, char *p_buf[], size_t *p_bufsize, const char token[], size_t len)
{
JSONB_TOKEN_EXEC(REALLOC, p_buf, p_bufsize, token, len);
}
JSONB_API jsonbcode
jsonb_bool(jsonb *b, char buf[], size_t bufsize, int boolean)
{
return boolean ? jsonb_token(b, buf, bufsize, "true", 4)
: jsonb_token(b, buf, bufsize, "false", 5);
}
JSONB_API jsonbcode
jsonb_bool_auto(jsonb *b, char *p_buf[], size_t *p_bufsize, int boolean)
{
return boolean ? jsonb_token_auto(b, p_buf, p_bufsize, "true", 4)
: jsonb_token_auto(b, p_buf, p_bufsize, "false", 5);
}
JSONB_API jsonbcode
jsonb_null(jsonb *b, char buf[], size_t bufsize)
{
return jsonb_token(b, buf, bufsize, "null", 4);
}
JSONB_API jsonbcode
jsonb_null_auto(jsonb *b, char *p_buf[], size_t *p_bufsize)
{
return jsonb_token_auto(b, p_buf, p_bufsize, "null", 4);
}
#define JSONB_STRING_EXEC(_type, buf, bufsize, str, len) \
enum jsonbstate next_state; \
enum jsonbcode code, ret; \
size_t pos = 0; \
switch (*b->top) { \
case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \
next_state = JSONB_DONE; \
code = JSONB_END; \
break; \
case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \
BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \
/* fall-through */ \
case JSONB_ARRAY_VALUE_OR_CLOSE: \
next_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \
code = JSONB_OK; \
break; \
case JSONB_OBJECT_VALUE: \
next_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \
code = JSONB_OK; \
break; \
default: \
STACK_HEAD(b, JSONB_ERROR); \
/* fall-through */ \
case JSONB_DONE: \
case JSONB_ERROR: \
return JSONB_ERROR_INPUT; \
} \
BUFFER_COPY_CHAR_##_type(b, '"', pos, buf, bufsize); \
ret = _jsonb_escape_##_type(&pos, buf, bufsize, b->pos, str, len); \
if (ret != JSONB_OK) return ret; \
BUFFER_COPY_CHAR_##_type(b, '"', pos, buf, bufsize); \
STACK_HEAD(b, next_state); \
b->pos += pos; \
return code
JSONB_API jsonbcode
jsonb_string(
jsonb *b, char buf[], size_t bufsize, const char str[], size_t len)
{
JSONB_STRING_EXEC(STATIC, buf, bufsize, str, len);
}
JSONB_API jsonbcode
jsonb_string_auto(
jsonb *b, char *p_buf[], size_t *p_bufsize, const char str[], size_t len)
{
JSONB_STRING_EXEC(REALLOC, p_buf, p_bufsize, str, len);
}
JSONB_API jsonbcode
jsonb_number(jsonb *b, char buf[], size_t bufsize, double number)
{
char token[32];
const long len = sprintf(token, "%.17G", number);
return (len < 0) ? JSONB_ERROR_INPUT
: jsonb_token(b, buf, bufsize, token, len);
}
JSONB_API jsonbcode
jsonb_number_auto(jsonb *b, char *p_buf[], size_t *p_bufsize, double number)
{
char token[32];
const long len = sprintf(token, "%.17G", number);
return (len < 0) ? JSONB_ERROR_INPUT
: jsonb_token_auto(b, p_buf, p_bufsize, token, len);
}
#undef TRACE
#undef DECORATOR
#undef STACK_HEAD
#undef STACK_PUSH
#undef STACK_POP
#undef BUFFER_COPY_CHAR_STATIC
#undef BUFFER_COPY_STATIC
#undef BUFFER_COPY_CHAR_REALLOC
#undef BUFFER_COPY_REALLOC
#undef JSONB_OBJECT_EXEC
#undef JSONB_OBJECT_POP_EXEC
#undef JSONB_KEY_EXEC
#undef JSONB_ARRAY_EXEC
#undef JSONB_ARRAY_POP_EXEC
#undef JSONB_TOKEN_EXEC
#undef JSONB_STRING_EXEC
#endif /* JSONB_HEADER */
#ifdef __cplusplus
}
#endif
#endif /* JSON_BUILD_H */
+46
View File
@@ -0,0 +1,46 @@
/**
* @file log.h
* @author Cogmasters
* @brief Maintain support for log.c deprecated functions using logmod.h as a
* wrapper
* @attention This file is deprecated and will be removed in future releases
* @deprecated since v3.0.0
*/
#ifndef LOG_DEPRECATED_SUPPORT_H
#define LOG_DEPRECATED_SUPPORT_H
#include "logmod.h"
/**
* @brief Backwards compatible alias for logmod_log()
* @deprecated since v3.0.0
*/
#define log_trace(...) logmod_log(TRACE, NULL, __VA_ARGS__)
/**
* @brief Backwards compatible alias for logmod_log()
* @deprecated since v3.0.0
*/
#define log_debug(...) logmod_log(DEBUG, NULL, __VA_ARGS__)
/**
* @brief Backwards compatible alias for logmod_log()
* @deprecated since v3.0.0
*/
#define log_info(...) logmod_log(INFO, NULL, __VA_ARGS__)
/**
* @brief Backwards compatible alias for logmod_log()
* @deprecated since v3.0.0
*/
#define log_warn(...) logmod_log(WARN, NULL, __VA_ARGS__)
/**
* @brief Backwards compatible alias for logmod_log()
* @deprecated since v3.0.0
*/
#define log_error(...) logmod_log(ERROR, NULL, __VA_ARGS__)
/**
* @brief Backwards compatible alias for logmod_log()
* @deprecated since v3.0.0
*/
#define log_fatal(...) logmod_log(FATAL, NULL, __VA_ARGS__)
#endif /* LOG_DEPRECATED_SUPPORT_H */
File diff suppressed because it is too large Load Diff
+345
View File
@@ -0,0 +1,345 @@
#ifndef OA_HASH_H
#define OA_HASH_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef OA_HASH_STATIC
#define OA_HASH_API static
#else
#define OA_HASH_API extern
#endif /* OA_HASH_STATIC */
#include <stddef.h>
/** @brief Hash table entry state */
enum oa_hash_entry_state {
OA_HASH_ENTRY_EMPTY = 0, /**< empty entry */
OA_HASH_ENTRY_OCCUPIED, /**< occupied entry */
OA_HASH_ENTRY_DELETED /**< deleted entry */
};
/** @brief Entry holding key-value pair in hash table */
struct oa_hash_entry {
enum oa_hash_entry_state state; /**< entry state */
struct {
const char *buf; /**< key buffer */
size_t length; /**< key length */
} key;
void *value; /**< value pointer */
};
#define __OA_HASH_ATTRS_const \
const size_t length; /**< amount of entries */ \
const size_t capacity; /**< total buckets capacity */ \
const struct oa_hash_entry *buckets /**< entries array */
#define __OA_HASH_ATTRS_mut \
size_t length; /**< amount of entries */ \
size_t capacity; /**< total buckets capacity */ \
struct oa_hash_entry *buckets /**< entries array */
/** @brief can be used to cast to struct oa_hash */
#define OA_HASH_ATTRS(_qualifier) __OA_HASH_ATTRS_##_qualifier
/** @brief Open addressing hash table */
struct oa_hash {
OA_HASH_ATTRS(mut);
};
/**
* @brief Initialize hash table with given buckets array
*
* @param[out] ht the hash table to be initialized
* @param[out] buckets pre-allocated array of entries
* @param[in] capacity amount of buckets
*/
OA_HASH_API void oa_hash_init(struct oa_hash *ht,
struct oa_hash_entry buckets[],
const size_t capacity);
/**
* @brief Clean up hash table entries and struct
*
* @param[out] ht the hash table to be cleaned
*/
OA_HASH_API void oa_hash_cleanup(struct oa_hash *ht);
/**
* @brief Retrieve entry by key
*
* @param[in] ht the hash table
* @param[in] key the key to search for
* @param[in] len the key length
* @return entry if found, NULL otherwise
*/
OA_HASH_API const struct oa_hash_entry *oa_hash_get_entry(
const struct oa_hash *ht, const char key[], const size_t len);
/**
* @brief Retrieve value by key (wrapper around oa_hash_get_entry)
*
* @param[in] ht the hash table
* @param[in] key the key to search for
* @param[in] len the key length
* @return value if found, NULL otherwise
*/
OA_HASH_API void *oa_hash_get(const struct oa_hash *ht,
const char key[],
const size_t len);
/**
* @brief Insert or update entry
*
* @param[in,out] ht the hash table
* @param[in] key the key to insert/update
* @param[in] len the key length
* @param[in] value the value to be assigned
* @return entry if successful, or NULL if no space left, in which case
* oa_hash_rehash() should be called
*/
OA_HASH_API const struct oa_hash_entry *oa_hash_set_entry(struct oa_hash *ht,
const char key[],
const size_t len,
void *value);
/**
* @brief Insert or update entry (wrapper around oa_hash_set_entry)
*
* @param[in,out] ht the hash table
* @param[in] key the key to insert/update
* @param[in] len the key length
* @param[in] value the value to be assigned
* @return value if successful, or NULL if no space left, in which case
* oa_hash_rehash() should be called
*/
OA_HASH_API void *oa_hash_set(struct oa_hash *ht,
const char key[],
const size_t len,
void *value);
/**
* @brief Remove entry by key
*
* @param[in,out] ht the hash table
* @param[in] key the key to be removed
* @param[in] len the key length
* @return 1 if found and removed, 0 otherwise
*/
OA_HASH_API int oa_hash_remove(struct oa_hash *ht,
const char key[],
const size_t len);
/**
* @brief Rehash table to new buckets array
*
* @param[in,out] ht the hash table
* @param[in,out] new_buckets the new buckets array
* @param[in] new_capacity the new buckets capacity
* @return pointer to old (now unused) bucket if successful, or NULL otherwise
*/
OA_HASH_API struct oa_hash_entry *oa_hash_rehash(
struct oa_hash *ht,
struct oa_hash_entry new_buckets[],
const size_t new_capacity);
#ifndef OA_HASH_HEADER
#include <string.h>
#include <stdint.h>
OA_HASH_API void
oa_hash_init(struct oa_hash *ht,
struct oa_hash_entry buckets[],
const size_t capacity)
{
memset(buckets, 0, sizeof(struct oa_hash_entry) * capacity);
ht->buckets = buckets;
ht->length = 0;
ht->capacity = capacity;
}
OA_HASH_API void
oa_hash_cleanup(struct oa_hash *ht)
{
if (!ht) return;
ht->length = 0;
ht->capacity = 0;
ht->buckets = NULL;
}
static size_t
_oa_hash_genhash(const char key[], size_t len, const size_t capacity)
{
const unsigned char *str = (const unsigned char *)key;
unsigned long hash = 5381; /* DJB2 initial value */
if (!key || !capacity) return 0;
while (len--) {
hash = ((hash & 0x7fffffff) << 5) + hash + *str++;
}
return hash % capacity;
}
OA_HASH_API const struct oa_hash_entry *
oa_hash_get_entry(const struct oa_hash *ht, const char key[], const size_t len)
{
const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity);
size_t slot = start_slot;
if (!len || !ht->capacity) return NULL;
do {
struct oa_hash_entry *entry = &ht->buckets[slot];
if (entry->state == OA_HASH_ENTRY_EMPTY) {
return NULL;
}
if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length
&& 0 == memcmp(entry->key.buf, key, len))
{
return entry;
}
slot = (slot + 1) % ht->capacity;
} while (slot != start_slot);
return NULL;
}
OA_HASH_API void *
oa_hash_get(const struct oa_hash *ht, const char key[], const size_t len)
{
const struct oa_hash_entry *entry = oa_hash_get_entry(ht, key, len);
return entry ? entry->value : NULL;
}
OA_HASH_API const struct oa_hash_entry *
oa_hash_set_entry(struct oa_hash *ht,
const char key[],
const size_t len,
void *value)
{
const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity);
size_t slot = start_slot;
size_t first_deleted = SIZE_MAX;
if (!len || !ht->capacity) return NULL;
do {
struct oa_hash_entry *entry = &ht->buckets[slot];
if (entry->state != OA_HASH_ENTRY_OCCUPIED) {
if (first_deleted == SIZE_MAX
&& entry->state == OA_HASH_ENTRY_DELETED)
{
first_deleted = slot;
}
if (entry->state == OA_HASH_ENTRY_EMPTY) {
slot = (first_deleted != SIZE_MAX) ? first_deleted : slot;
entry = &ht->buckets[slot];
entry->key.buf = (char *)key;
entry->key.length = len;
entry->value = value;
entry->state = OA_HASH_ENTRY_OCCUPIED;
ht->length++;
return entry;
}
}
if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length
&& 0 == memcmp(entry->key.buf, key, len))
{
entry->value = value;
return entry;
}
slot = (slot + 1) % ht->capacity;
} while (slot != start_slot);
return NULL;
}
OA_HASH_API void *
oa_hash_set(struct oa_hash *ht,
const char key[],
const size_t len,
void *value)
{
const struct oa_hash_entry *entry = oa_hash_set_entry(ht, key, len, value);
return entry ? entry->value : NULL;
}
OA_HASH_API int
oa_hash_remove(struct oa_hash *ht, const char key[], const size_t len)
{
const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity);
size_t slot = start_slot;
if (!len || !ht->capacity) return 0;
do {
struct oa_hash_entry *entry = &ht->buckets[slot];
if (entry->state == OA_HASH_ENTRY_EMPTY) {
return 0;
}
if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length
&& 0 == memcmp(entry->key.buf, key, len))
{
entry->state = OA_HASH_ENTRY_DELETED;
ht->length--;
return 1;
}
slot = (slot + 1) % ht->capacity;
} while (slot != start_slot);
return 0;
}
OA_HASH_API struct oa_hash_entry *
oa_hash_rehash(struct oa_hash *ht,
struct oa_hash_entry new_buckets[],
const size_t new_capacity)
{
struct oa_hash_entry *old_buckets = ht->buckets;
const size_t old_capacity = ht->capacity;
const size_t old_length = ht->length;
size_t i;
if (!new_buckets || new_capacity <= old_capacity) return 0;
memset(new_buckets, 0, sizeof(struct oa_hash_entry) * new_capacity);
/* temporarily switch to new buckets */
ht->buckets = new_buckets;
ht->capacity = new_capacity;
ht->length = 0;
for (i = 0; i < old_capacity; ++i) {
if (old_buckets[i].state == OA_HASH_ENTRY_OCCUPIED
&& !oa_hash_set_entry(ht, old_buckets[i].key.buf,
old_buckets[i].key.length,
old_buckets[i].value))
{
/* restore original state on failure */
ht->buckets = old_buckets;
ht->capacity = old_capacity;
ht->length = old_length;
return NULL;
}
}
return old_buckets;
}
#endif /* OA_HASH_HEADER */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* OA_HASH_H */
+38
View File
@@ -0,0 +1,38 @@
/**
* @file oauth2.h
* @author Cogmasters
* @brief OAuth2 public functions and datatypes
*/
#ifndef DISCORD_OAUTH2_H
#define DISCORD_OAUTH2_H
/** @defgroup DiscordAPIOAuth2 OAuth2
* @ingroup DiscordAPI
* @brief OAuth2's public API supported by Concord
* @{ */
/**
* @brief Returns the bot's application object
*
* @param client the client created with discord_from_token()
* @CCORD_ret_obj{ret,application}
* @CCORD_return
*/
CCORDcode discord_get_current_bot_application_information(
struct discord *client, struct discord_ret_application *ret);
/**
* @brief Returns info about the current authorization
* @note Requires authentication with a bearer token
*
* @param client the client created with discord_from_token()
* @CCORD_ret_obj{ret,auth_response}
* @CCORD_return
*/
CCORDcode discord_get_current_authorization_information(
struct discord *client, struct discord_ret_auth_response *ret);
/** @} DiscordAPIOAuth2 */
#endif /* DISCORD_OAUTH2_H */
+319
View File
@@ -0,0 +1,319 @@
#ifndef OSNAME_H
#define OSNAME_H 1
/*
* HackerSmacker's "Detect-It-All" OS Detector
*/
enum OSClass {
UNIX,
WINDOWS,
DOS,
OS2,
S370,
DEC,
MACINTOSH,
AMIGA,
OTHER
};
#ifdef _AIX
#define OSNAME "AIX"
#define OSCLASS UNIX
#endif
#ifdef __ANDROID__
#define OSNAME "Android"
#define OSCLASS UNIX
#endif
#ifdef UTS
#define OSNAME "UTS"
#define OSCLASS UNIX
#endif
#ifdef aegis
#define OSNAME "Aegis"
#define OSCLASS UNIX
#endif
#ifdef __BEOS__
#define OSNAME "BeOS"
#define OSCLASS OTHER
#endif
#ifdef __FreeBSD__
#define OSNAME "FreeBSD"
#define OSCLASS UNIX
#endif
#ifdef __NetBSD__
#define OSNAME "NetBSD"
#define OSCLASS UNIX
#endif
#ifdef __OpenBSD__
#define OSNAME "OpenBSD"
#define OSCLASS UNIX
#endif
#ifdef __bsdi__
#define OSNAME "BSD/OS"
#define OSCLASS UNIX
#endif
#ifdef __DragonFly__
#define OSNAME "DragonFly BSD"
#define OSCLASS UNIX
#endif
#ifdef __convex__
#define OSNAME "ConvexOS"
#define OSCLASS UNIX
#endif
#ifdef __CYGWIN__
#define OSNAME "Windows NT (Cygwin)"
#define OSCLASS UNIX
#endif
#if defined __DGUX__ || DGUX
#define OSNAME "DG/UX"
#define OSCLASS UNIX
#endif
#if defined __SEQUENT__ || sequent
#define OSNAME "DYNIX/ptx"
#define OSCLASS UNIX
#endif
#ifdef __ECOS
#define OSNAME "eCos"
#define OSCLASS OTHER
#endif
#ifdef __EMX__
#define OSNAME "OS/2 (EMX)"
#define OSCLASS UNIX
#endif
#ifdef __gnu_hurd__
#define OSNAME "GNU/Hurd"
#define OSCLASS UNIX
#endif
#if defined __gnu_linux__ || defined __linux__ || defined linux
#define OSNAME "GNU/Linux"
#define OSCLASS UNIX
#endif
#if defined _hpux || defined hpux || defined __hpux
#define OSNAME "HP-UX"
#define OSCLASS UNIX
#endif
#ifdef __OS400__
#define OSNAME "OS/400"
#define OSCLASS OTHER
#endif
#if defined __sgi || defined sgi
#define OSNAME "IRIX"
#define OSCLASS UNIX
#endif
#ifdef __INTEGRITY
#define OSNAME "INTEGRITY"
#define OSCLASS OTHER
#endif
#ifdef __Lynx__
#define OSNAME "LynxOS"
#define OSCLASS OTHER
#endif
#if defined macintosh || defined Macintosh
#define OSNAME "Classic Mac OS"
#define OSTYPE MACINTOSH
#endif
#ifdef __APPLE__
#ifdef __MACH
#define OSNAME "Mac OS X"
#define OSCLASS UNIX
#endif
#endif
#if defined __OS9000 || defined _OSK
#define OSNAME "OS-9"
#define OSCLASS OTHER
#endif
#ifdef __MORPHOS__
#define OSNAME "MorphOS"
#define OSCLASS AMIGA
#endif
#if defined AMIGA || defined __amigaos__
#define OSNAME "AmigaOS"
#define OSCLASS AMIGA
#endif
#if defined mpeix || defined __mpexl
#define OSNAME "MPE/iX"
#define OSCLASS OTHER
#endif
#if defined MSDOS || defined __MSDOS__ || defined _MSDOS || defined __DOS__
#define OSNAME "MS-DOS"
#define OSCLASS DOS
#endif
#ifdef __TANDEM
#define OSNAME "NonStop OS"
#define OSCLASS OTHER
#endif
#if defined OS2 || defined _OS2 || defined __OS2__ || defined __TOS_OS2__
#define OSNAME "OS/2"
#define OSCLASS OS2
#endif
#ifdef EPLAN9
#define OSNAME "Plan 9"
#define OSCLASS OTHER
#endif
#if defined __QNX__ || defined __QNXNTO__
#define OSNAME "QNX"
#define OSCLASS UNIX
#endif
#ifdef M_I386
#define OSNAME "SCO UNIX"
#define OSCLASS UNIX
#endif
#if defined sun || defined __sun
#if defined __SVR4 || defined __svr4
#define OSNAME "Solaris"
#define OSCLASS UNIX
#endif
#define OSNAME "SunOS"
#define OSCLASS UNIX
#endif
#ifdef __VOS__
#define OSNAME "VOS"
#define OSCLASS OTHER
#endif
#if defined __osf__ || defined __osf
#define OSNAME "OSF/1"
#define OSCLASS UNIX
#endif
#if defined ultrix || defined __ultrix || defined __ultrix__ || __SYSTYPE_BSD
#define OSNAME "ULTRIX"
#define OSCLASS UNIX
#endif
#if defined sco || defined _UNIXWARE7
#define OSNAME "UnixWare"
#define OSCLASS UNIX
#endif
#if defined VMS || defined __VMS
#define OSNAME "VMS"
#define OSCLASS VMS
#endif
#ifdef __VM__
#define OSNAME "VM/CMS"
#define OSCLASS S370
#endif
#ifdef __MVS__
#define OSNAME "MVS"
#define OSCLASS S370
#endif
#ifdef __EDC_LE
#ifndef __VM__
#define OSNAME "VSE"
#define OSCLASS S370
#endif
#ifndef __MVS__
#define OSNAME "VSE"
#define OSCLASS S370
#endif
#endif
#if defined __MCP__
#define OSNAME "MCP"
#define OSCLASS OTHER
#endif
#if defined _NETWARE_ || defined __NETWARE__
#define OSNAME "NetWare"
#define OSCLASS OTHER
#endif
#ifdef __MACH__
#ifndef __APPLE__
#define OSNAME "NeXTSTEP"
#define OSCLASS UNIX
#endif
#endif
#ifdef pyr
#define OSNAME "DC/OSx"
#define OSCLASS UNIX
#endif
#if defined sinux || defined sinix
#define OSNAME "Reliant UNIX"
#define OSCLASS UNIX
#endif
#ifdef _UNICOS
#define OSNAME "UNICOS"
#define OSCLASS UNIX
#endif
#if defined _CRAY || defined _crayx1
#define OSNAME "UNICOS/mp"
#define OSCLASS UNIX
#endif
#ifdef _UWIN
#define OSNAME "Windows NT (U/Win)"
#define OSCLASS WINDOWS
#endif
#if defined __VXWORKS__ || defined __vxworks
#define OSNAME "VxWorks"
#define OSCLASS OTHER
#endif
#ifdef _WIN32_WCE
#define OSNAME "Windows CE"
#define OSCLASS WINDOWS
#endif
#if defined _WIN32 | defined _WIN64 | defined __WIN32__
#define OSNAME "Windows NT"
#define OSCLASS WINDOWS
#endif
#ifdef _WIN16
#define OSNAME "Windows 3.x"
#define OSCLASS WINDOWS
#endif
#ifndef OSNAME
#define OSNAME "POSIX"
#define OSCLASS UNIX
#endif
#endif
+59
View File
@@ -0,0 +1,59 @@
// MIT License
// Copyright (c) 2022 Anotra
// https://github.com/Anotra/priority_queue
#pragma once
#ifndef PRIORITY_QUEUE_H
#define PRIORITY_QUEUE_H
#include <stdlib.h>
typedef struct priority_queue priority_queue;
typedef unsigned priority_queue_id;
typedef enum {
priority_queue_min = 0,
priority_queue_max = 1,
} priority_queue_flags;
priority_queue *priority_queue_create(
size_t key_size, size_t val_size,
int(*cmp)(const void *a, const void *b),
priority_queue_flags flags);
void priority_queue_destroy(priority_queue *queue);
size_t priority_queue_length(priority_queue *queue);
void priority_queue_set_max_capacity(
priority_queue *queue,
size_t capacity);
priority_queue_id priority_queue_push(
priority_queue *queue,
void *key, void *val);
priority_queue_id priority_queue_peek(
priority_queue *queue,
void *key, void *val);
priority_queue_id priority_queue_pop(
priority_queue *queue,
void *key, void *val);
priority_queue_id priority_queue_get(
priority_queue *queue,
priority_queue_id id,
void *key, void *val);
int priority_queue_del(
priority_queue *queue,
priority_queue_id id);
int priority_queue_update(priority_queue *queue,
priority_queue_id id,
void *key, void *val);
#endif //! PRIORITY_QUEUE_H
+30
View File
@@ -0,0 +1,30 @@
#ifndef QUERIEC_H
#define QUERIEC_H
#define QUERIEC_ADDITIONAL_LETTERS_SIZE 2
#define QUERIEC_ERROR_NOMEM -1
#define QUERIEC_OK 0
#include "attributes.h"
struct queriec {
int state;
size_t size;
size_t offset;
};
void
queriec_init(struct queriec *queriec, size_t size);
int queriec_snprintf_add(struct queriec *queriec, char *query,
const char key[], size_t keySize,
char buffer[], size_t bufferLen,
const char *format, ...) PRINTF_LIKE(7, 8);
int
queriec_add(struct queriec *queriec, char *query, char key[],
size_t keySize, char value[], size_t valueSize);
#endif
+111
View File
@@ -0,0 +1,111 @@
/* Copyright (c) 2013, Ben Noordhuis <[email protected]>
*
* 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.
*
* 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.
*/
#ifndef QUEUE_H_
#define QUEUE_H_
#include <stddef.h>
typedef void *QUEUE[2];
/* Improve readability by letting user specify underlying type. */
#define QUEUE(type) QUEUE
/* Private macros. */
#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q)))
#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q)))
/* Public macros. */
#define QUEUE_DATA(ptr, type, field) \
((type *) ((char *) (ptr) - offsetof(type, field)))
/* Important note: mutating the list while QUEUE_FOREACH is
* iterating over its elements results in undefined behavior.
*/
#define QUEUE_FOREACH(q, h) \
for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
#define QUEUE_EMPTY(q) \
((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
#define QUEUE_HEAD(q) \
(QUEUE_NEXT(q))
#define QUEUE_INIT(q) \
do { \
QUEUE_NEXT(q) = (q); \
QUEUE_PREV(q) = (q); \
} \
while (0)
#define QUEUE_ADD(h, n) \
do { \
QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \
QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \
QUEUE_PREV(h) = QUEUE_PREV(n); \
QUEUE_PREV_NEXT(h) = (h); \
} \
while (0)
#define QUEUE_SPLIT(h, q, n) \
do { \
QUEUE_PREV(n) = QUEUE_PREV(h); \
QUEUE_PREV_NEXT(n) = (n); \
QUEUE_NEXT(n) = (q); \
QUEUE_PREV(h) = QUEUE_PREV(q); \
QUEUE_PREV_NEXT(h) = (h); \
QUEUE_PREV(q) = (n); \
} \
while (0)
#define QUEUE_MOVE(h, n) \
do { \
if (QUEUE_EMPTY(h)) \
QUEUE_INIT(n); \
else { \
QUEUE* q = QUEUE_HEAD(h); \
QUEUE_SPLIT(h, q, n); \
} \
} \
while (0)
#define QUEUE_INSERT_HEAD(h, q) \
do { \
QUEUE_NEXT(q) = QUEUE_NEXT(h); \
QUEUE_PREV(q) = (h); \
QUEUE_NEXT_PREV(q) = (q); \
QUEUE_NEXT(h) = (q); \
} \
while (0)
#define QUEUE_INSERT_TAIL(h, q) \
do { \
QUEUE_NEXT(q) = (h); \
QUEUE_PREV(q) = QUEUE_PREV(h); \
QUEUE_PREV_NEXT(q) = (q); \
QUEUE_PREV(h) = (q); \
} \
while (0)
#define QUEUE_REMOVE(q) \
do { \
QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \
QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \
} \
while (0)
#endif /* QUEUE_H_ */
+75
View File
@@ -0,0 +1,75 @@
/**
* @file stage_instance.h
* @author Cogmasters
* @brief Stage Instance public functions and datatypes
*/
#ifndef DISCORD_STAGE_INSTANCE_H
#define DISCORD_STAGE_INSTANCE_H
/** @defgroup DiscordAPIStageInstance Stage Instance
* @ingroup DiscordAPI
* @brief Stage Instance's public API supported by Concord
* @{ */
/**
* @brief Creates a new Stage Instance associated to a Stage channel
* @note requires the user to be a moderator of the Stage channel
*
* @param client the client created with discord_from_token()
* @param params the request parameters
* @CCORD_ret_obj{ret,stage_instance}
* @CCORD_return
*/
CCORDcode discord_create_stage_instance(
struct discord *client,
struct discord_create_stage_instance *params,
struct discord_ret_stage_instance *ret);
/**
* @brief Gets the stage instance associated with the Stage channel, if it
* exists
*
* @param client the client created with discord_from_token()
* @param channel_id the stage channel id
* @CCORD_ret_obj{ret,stage_instance}
* @CCORD_return
*/
CCORDcode discord_get_stage_instance(struct discord *client,
u64snowflake channel_id,
struct discord_ret_stage_instance *ret);
/**
* @brief Updates fields of an existing Stage instance
* @note requires the user to be a moderator of the Stage channel
*
* @param client the client created with discord_from_token()
* @param channel_id the stage channel id
* @param params the request parameters
* @CCORD_ret_obj{ret,stage_instance}
* @CCORD_return
*/
CCORDcode discord_modify_stage_instance(
struct discord *client,
u64snowflake channel_id,
struct discord_modify_stage_instance *params,
struct discord_ret_stage_instance *ret);
/**
* @brief Deletes the Stage instance
* @note requires the user to be a moderator of the Stage channel
*
* @param client the client created with discord_from_token()
* @param channel_id the stage channel to be deleted
* @param params the request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_stage_instance(struct discord *client,
u64snowflake channel_id,
struct discord_delete_stage_instance *params,
struct discord_ret *ret);
/** @} DiscordAPIStageInstance */
#endif /* DISCORD_STAGE_INSTANCE_H */
+104
View File
@@ -0,0 +1,104 @@
/**
* @file sticker.h
* @author Cogmasters
* @brief Sticker public functions and datatypes
*/
#ifndef DISCORD_STICKER_H
#define DISCORD_STICKER_H
/** @defgroup DiscordAPISticker Sticker
* @ingroup DiscordAPI
* @brief Sticker's public API supported by Concord
* @{ */
/**
* @brief Get a sticker from a given ID
*
* @param client the client created with discord_from_token()
* @param sticker_id the sticker to be fetched
* @CCORD_ret_obj{ret,sticker}
* @CCORD_return
*/
CCORDcode discord_get_sticker(struct discord *client,
u64snowflake sticker_id,
struct discord_ret_sticker *ret);
/**
* @brief Get a list of sticker packs available to Nitro subscribers
*
* @param client the client created with discord_from_token()
* @CCORD_ret_obj{ret,list_nitro_sticker_packs}
* @CCORD_return
*/
CCORDcode discord_list_nitro_sticker_packs(
struct discord *client, struct discord_ret_list_nitro_sticker_packs *ret);
/**
* @brief Get stickers for the given guild
* @note includes `user` fields if the bot has the `MANAGE_EMOJIS_AND_STICKERS`
* permission
*
* @param client the client created with discord_from_token()
* @param guild_id guild to fetch the stickers from
* @CCORD_ret_obj{ret,stickers}
* @CCORD_return
*/
CCORDcode discord_list_guild_stickers(struct discord *client,
u64snowflake guild_id,
struct discord_ret_stickers *ret);
/**
* @brief Get a sticker for the given guild and sticker ID
* @note includes the `user` field if the bot has the
* `MANAGE_EMOJIS_AND_STICKERS` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild where the sticker belongs to
* @param sticker_id the sticker to be fetched
* @CCORD_ret_obj{ret,sticker}
* @CCORD_return
*/
CCORDcode discord_get_guild_sticker(struct discord *client,
u64snowflake guild_id,
u64snowflake sticker_id,
struct discord_ret_sticker *ret);
/**
* @brief Modify the given sticker
* @note requires the `MANAGE_EMOJIS_AND_STICKERS` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild where the sticker belongs to
* @param sticker_id the sticker to be modified
* @param params the request parameters
* @CCORD_ret_obj{ret,sticker}
* @CCORD_return
*/
CCORDcode discord_modify_guild_sticker(
struct discord *client,
u64snowflake guild_id,
u64snowflake sticker_id,
struct discord_modify_guild_sticker *params,
struct discord_ret_sticker *ret);
/**
* @brief Delete the given sticker
* @note requires the `MANAGE_EMOJIS_AND_STICKERS` permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild where the sticker belongs to
* @param sticker_id the sticker to be deleted
* @param params the request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_guild_sticker(struct discord *client,
u64snowflake guild_id,
u64snowflake sticker_id,
struct discord_delete_guild_sticker *params,
struct discord_ret *ret);
/** @} DiscordAPISticker */
#endif /* DISCORD_STICKER_H */
+101
View File
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2016, Mathias Brossard <[email protected]>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _THREADPOOL_H_
#define _THREADPOOL_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file threadpool.h
* @brief Threadpool Header File
*/
/**
* Increase this constants at your own risk
* Large values might slow down your system
*/
#define MAX_THREADS 64
#define MAX_QUEUE 65536
typedef struct threadpool_t threadpool_t;
typedef enum {
threadpool_invalid = -1,
threadpool_lock_failure = -2,
threadpool_queue_full = -3,
threadpool_shutdown = -4,
threadpool_thread_failure = -5
} threadpool_error_t;
typedef enum {
threadpool_graceful = 1
} threadpool_destroy_flags_t;
/**
* @function threadpool_create
* @brief Creates a threadpool_t object.
* @param thread_count Number of worker threads.
* @param queue_size Size of the queue.
* @param flags Unused parameter.
* @return a newly created thread pool or NULL
*/
threadpool_t *threadpool_create(int thread_count, int queue_size, int flags);
/**
* @function threadpool_add
* @brief add a new task in the queue of a thread pool
* @param pool Thread pool to which add the task.
* @param function Pointer to the function that will perform the task.
* @param argument Argument to be passed to the function.
* @param flags Unused parameter.
* @return 0 if all goes well, negative values in case of error (@see
* threadpool_error_t for codes).
*/
int threadpool_add(threadpool_t *pool, void (*routine)(void *),
void *arg, int flags);
/**
* @function threadpool_destroy
* @brief Stops and destroys a thread pool.
* @param pool Thread pool to destroy.
* @param flags Flags for shutdown
*
* Known values for flags are 0 (default) and threadpool_graceful in
* which case the thread pool doesn't accept any new tasks but
* processes all pending tasks before shutdown.
*/
int threadpool_destroy(threadpool_t *pool, int flags);
#ifdef __cplusplus
}
#endif
#endif /* _THREADPOOL_H_ */
+66
View File
@@ -0,0 +1,66 @@
/** @file types.h */
#ifndef CONCORD_TYPES_H
#define CONCORD_TYPES_H
#include <stddef.h>
#include <stdint.h>
/** @defgroup ConcordTypes Primitives
* @brief Commonly used datatypes
*
* @note these datatypes are typedefs of C primitives,
* its purpose is to facilitate identification
* and "intent of use".
* @{ */
/**
* @brief Unix time in milliseconds
*
* Commonly used for fields that may store timestamps
*/
typedef uint64_t u64unix_ms;
/**
* @brief Snowflake datatype
*
* Used in APIs such as Twitter and Discord for their unique IDs
*/
typedef uint64_t u64snowflake;
/**
* @brief Bitmask primitive
*
* Used for fields that may store values of, or perform bitwise operations
*/
typedef uint64_t u64bitmask;
/**
* @brief Raw JSON string
*
* Used for fields that have dynamic or unreliable types. A string made out of
* `json_char` should be used to keep a raw JSON, which can then be
* parsed with the assistance of a JSON library.
*/
typedef char json_char;
/** @brief Generic sized buffer */
struct ccord_szbuf {
/** the buffer's start */
char *start;
/** the buffer's size in bytes */
size_t size;
/** true if buffer is static (else is dynamic and shall be freed) */
bool is_static;
};
/** @brief Read-only generic sized buffer */
struct ccord_szbuf_readonly {
/** the buffer's start */
const char *start;
/** the buffer's size in bytes */
size_t size;
};
/** @} ConcordTypes */
#endif /* CONCORD_TYPES_H */
+400
View File
@@ -0,0 +1,400 @@
/** @file user-agent.h */
#ifndef USER_AGENT_H
#define USER_AGENT_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <curl/curl.h>
/** @brief HTTP methods */
enum http_method {
HTTP_INVALID = -1,
HTTP_DELETE,
HTTP_GET,
HTTP_POST,
HTTP_MIMEPOST,
HTTP_PATCH,
HTTP_PUT
};
/**
* @brief Get the HTTP method name string
*
* @param method the HTTP method
* @return the HTTP method name
*/
const char *http_method_print(enum http_method method);
/**
* @brief Get the HTTP method enumerator from a string
*
* @param method the HTTP method string
* @return the HTTP method enumerator
*/
enum http_method http_method_eval(char method[]);
/** @defgroup HttpStatusCode
* @see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
* @{ */
#define HTTP_OK 200
#define HTTP_CREATED 201
#define HTTP_ACCEPTED 202
#define HTTP_NON_AUTHORITATIVE_INFO 203
#define HTTP_NO_CONTENT 204
#define HTTP_RESET_CONTENT 205
#define HTTP_PARTIAL_CONTENT 206
#define HTTP_MULTI_STATUS 207
#define HTTP_ALREADY_REPORTED 208
#define HTTP_IM_USED 226
#define HTTP_MULTIPLE_CHOICES 300
#define HTTP_MOVED_PERMANENTLY 301
#define HTTP_FOUND 302
#define HTTP_SEE_OTHER 303
#define HTTP_NOT_MODIFIED 304
#define HTTP_USE_PROXY 305
#define HTTP_TEMPORARY_REDIRECT 307
#define HTTP_PERMANENT_REDIRECT 308
#define HTTP_BAD_REQUEST 400
#define HTTP_UNAUTHORIZED 401
#define HTTP_PAYMENT_REQUIRED 402
#define HTTP_FORBIDDEN 403
#define HTTP_NOT_FOUND 404
#define HTTP_METHOD_NOT_ALLOWED 405
#define HTTP_NOT_ACCEPTABLE 406
#define HTTP_PROXY_AUTHENTICATION 407
#define HTTP_REQUEST_TIMEOUT 408
#define HTTP_CONFLICT 409
#define HTTP_GONE 410
#define HTTP_LENGTH_REQUIRED 411
#define HTTP_PRECONDITION_FAILED 412
#define HTTP_PAYLOAD_TOO_LARGE 413
#define HTTP_URI_TOO_LONG 414
#define HTTP_UNSUPPORTED_MEDIA_TYPE 415
#define HTTP_RANGE_NOT_SATISFIABLE 416
#define HTTP_EXPECTATION_FAILED 417
#define HTTP_IM_A_TEAPOT 418
#define HTTP_MISDIRECTED_REQUEST 421
#define HTTP_UNPROCESSABLE_ENTITY 422
#define HTTP_LOCKED 423
#define HTTP_FAILED_DEPENDENCY 424
#define HTTP_TOO_EARLY 425
#define HTTP_UPGRADE_REQUIRED 426
#define HTTP_PRECONDITION_REQUIRED 428
#define HTTP_TOO_MANY_REQUESTS 429
#define HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE 431
#define HTTP_UNAVAILABLE_FOR_LEGAL_REASONS 451
#define HTTP_INTERNAL_SERVER_ERROR 500
#define HTTP_NOT_IMPLEMENTED 501
#define HTTP_BAD_GATEWAY 502
#define HTTP_SERVICE_UNAVAILABLE 503
#define HTTP_GATEWAY_TIMEOUT 504
#define HTTP_VERSION_NOT_SUPPORTED 505
#define HTTP_VARIANT_ALSO_NEGOTIATES 506
#define HTTP_INSUFFICIENT_STORAGE 507
#define HTTP_LOOP_DETECTED 508
#define HTTP_NOT_EXTENDED 510
#define HTTP_NETWORK_AUTHENTICATION_REQUIRED 511
#define HTTP_INVALID_HTTP_CODE 999
/** @} */
/**
* @brief Get the HTTP status code name string
*
* @param httpcode the HTTP status code
* @return the HTTP status code name
*/
const char *http_code_print(int httpcode);
/**
* @brief Get the HTTP status code reason string
*
* @param httpcode the HTTP status code
* @return the HTTP status code reason
*/
const char *http_reason_print(int httpcode);
/**
* @struct user_agent
* @brief Opaque User-Agent handle
*
* @see ua_init(), ua_cleanup(), ua_set_url(), ua_get_url(), ua_set_opt()
*/
struct user_agent;
/* forward declaration */
struct logmod;
/**/
/**
* @struct ua_conn
* @brief Opaque connection handle
*
* @see ua_conn_start(), ua_conn_setup(), ua_conn_reset(), ua_conn_stop(),
* ua_conn_easy_perform(), ua_conn_add_header(), ua_conn_print_header(),
* ua_conn_set_mime(), ua_conn_get_easy_handle()
*/
struct ua_conn;
/** @brief Read-only generic sized buffer */
struct ua_szbuf_readonly {
/** the buffer's start */
const char *start;
/** the buffer's size in bytes */
size_t size;
};
/** @brief header fields to have its contents hidden when logging */
struct ua_log_filter {
/** list of headers */
struct ua_szbuf_readonly *headers;
/** amount of headers to be filtered */
size_t length;
};
/** @brief Connection attributes */
struct ua_conn_attr {
/** the HTTP method of this transfer (GET, POST, ...) */
enum http_method method;
/** the optional request body, can be NULL */
char *body;
/** the request body size */
size_t body_size;
/** the endpoint to be appended to the base URL */
char *endpoint;
/** optional base_url to override ua_set_url(), can be NULL */
char *base_url;
/** @brief header fields to have its contents filtered when logging */
struct ua_log_filter log_filter;
};
/** Maximum amount of header pairs */
#define UA_MAX_HEADER_PAIRS 100 + 1
/** @brief Structure for storing the request's response header */
struct ua_resp_header {
/** response header buffer */
char *buf;
/** response header string length */
size_t len;
/** real size occupied in memory by buffer */
size_t bufsize;
/** array of header field/value pairs */
struct {
struct {
/** offset index of 'buf' for the start of field or value */
size_t idx;
/** length of individual field or value */
size_t size;
} field, value;
} pairs[UA_MAX_HEADER_PAIRS];
/** amount of pairs initialized */
int n_pairs;
};
/** @brief Structure for storing the request's response body */
struct ua_resp_body {
/** response body buffer */
char *buf;
/** response body string length */
size_t len;
/** real size occupied in memory by buffer */
size_t bufsize;
};
/** @brief Informational handle received on request's completion */
struct ua_info {
/** the HTTP response code */
long httpcode;
/** @privatesection */
/** the response header */
struct ua_resp_header header;
/** the response body */
struct ua_resp_body body;
};
/**
* @brief Callback to be called on each libcurl's easy handle initialization
*
* @param ua the User-Handle created with ua_init()
* @param data user data to be passed along to `callback`
* @param callback the user callback
*/
void ua_set_opt(struct user_agent *ua,
void *data,
void (*callback)(struct ua_conn *conn, void *data));
/**
* @brief Initialize User-Agent handle
*
* @param logmod optional pre-initialized logging module
* @param fp file pointer for writing HTTP traces to
* @return the user agent handle
*/
struct user_agent *ua_init(struct logmod *logmod, FILE *fp);
/**
* @brief Cleanup User-Agent handle resources
*
* @param ua the User-Agent handle created with ua_init()
*/
void ua_cleanup(struct user_agent *ua);
/**
* @brief Set the request url
*
* @param ua the User-Agent handle created with ua_init()
* @param base_url the base request url
*/
void ua_set_url(struct user_agent *ua, const char base_url[]);
/**
* @brief Get the request url
*
* @param ua the User-Agent handle created with ua_init()
* @return the request url set with ua_set_url()
*/
const char *ua_get_url(struct user_agent *ua);
/** @brief Callback for object to be loaded by api response */
typedef void (*ua_load_obj_cb)(char *str, size_t len, void *p_obj);
/** @brief User callback to be called on request completion */
struct ua_resp_handle {
/** callback called when a successful transfer occurs */
ua_load_obj_cb ok_cb;
/** the pointer to be passed to ok_cb */
void *ok_obj;
/** callback called when a failed transfer occurs */
ua_load_obj_cb err_cb;
/** the pointer to be passed to err_cb */
void *err_obj;
};
/**
* @brief Get a connection handle and mark it as running
*
* @param conn the User-Agent handle created with ua_init()
* @return a connection handle
*/
struct ua_conn *ua_conn_start(struct user_agent *ua);
/**
* @brief Add a field/value pair to the request header
*
* @param conn the connection handle
* @param field header's field to be added
* @param value field's value
* @return CURLE_OK on success, otherwise an error code
*/
CURLcode ua_conn_add_header(struct ua_conn *conn,
const char field[],
const char value[]);
/**
* @brief Remove a header field
*
* @param conn the connection handle
* @param field header's field to be removed
* @return CURLE_OK on success, otherwise an error code
*/
CURLcode ua_conn_remove_header(struct ua_conn *conn, const char field[]);
/**
* @brief Fill a buffer with the request header
*
* @param conn the connection handle
* @param buf the user buffer to be filled
* @param bufsize the user buffer size in bytes
* @param log_filter headers to have its contents hidden when logging
* @return the user buffer
*/
char *ua_conn_print_header(struct ua_conn *conn,
char *buf,
size_t bufsize,
struct ua_log_filter *log_filter);
/**
* @brief Multipart creation callback for `conn`
* @see https://curl.se/libcurl/c/smtp-mime.html
*
* @param conn the connection handle to send multipart body
* @param data user data to be passed along to `callback`
* @param callback the user callback
*/
void ua_conn_set_mime(struct ua_conn *conn,
void *data,
void (*callback)(curl_mime *mime, void *data));
/**
* @brief Reset a connection handle fields
*
* @param conn connection handle to be reset
* @warning this won't deactivate the handle, for that purpose check
* ua_conn_stop()
*/
void ua_conn_reset(struct ua_conn *conn);
/**
* @brief Stop a connection handle and mark it as idle
*
* @param conn connection handle to be deactivated
*/
void ua_conn_stop(struct ua_conn *conn);
/**
* @brief Setup transfer attributes
*
* @param conn the connection handle
* @param attr attributes to be set for transfer
* @return CURLE_OK on success, otherwise an error code
*/
CURLcode ua_conn_setup(struct ua_conn *conn, struct ua_conn_attr *attr);
/**
* @brief Get libcurl's easy handle assigned to `conn`
*
* @param conn the connection handle
* @return the libcurl's easy handle
*/
CURL *ua_conn_get_easy_handle(struct ua_conn *conn);
/**
* @brief Extract information from `conn` previous request
*
* @param conn the connection handle
* @param info handle to store information on previous request
*/
void ua_info_extract(struct ua_conn *conn, struct ua_info *info);
/**
* @brief Cleanup informational handle
*
* @param info handle containing information on previous request
*/
void ua_info_cleanup(struct ua_info *info);
/**
* @brief Get a value's from the response header
*
* @param info handle containing information on previous request
* @param field the header field to fetch the value
* @return a @ref ua_szbuf_readonly containing the field's value
*/
struct ua_szbuf_readonly ua_info_get_header(struct ua_info *info,
char field[]);
/**
* @brief Get the response body
*
* @param info handle containing information on previous request
* @return a @ref ua_szbuf_readonly containing the response body
*/
struct ua_szbuf_readonly ua_info_get_body(struct ua_info *info);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* USER_AGENT_H */
+115
View File
@@ -0,0 +1,115 @@
/**
* @file user.h
* @author Cogmasters
* @brief User public functions and datatypes
*/
#ifndef DISCORD_USER_H
#define DISCORD_USER_H
/** @defgroup DiscordAPIUser User
* @ingroup DiscordAPI
* @brief User's public API supported by Concord
* @{ */
/**
* @brief Get client's user
*
* @param client the client created with discord_from_token()
* @CCORD_ret_obj{ret,user}
* @CCORD_return
*/
CCORDcode discord_get_current_user(struct discord *client,
struct discord_ret_user *ret);
/**
* @brief Get user for a given id
*
* @param client the client created with discord_from_token()
* @param user_id user to be fetched
* @CCORD_ret_obj{ret,user}
* @CCORD_return
*/
CCORDcode discord_get_user(struct discord *client,
u64snowflake user_id,
struct discord_ret_user *ret);
/**
* @brief Modify client's user account settings
*
* @param client the client created with discord_from_token()
* @param params request parameters
* @CCORD_ret_obj{ret,user}
* @CCORD_return
*/
CCORDcode discord_modify_current_user(
struct discord *client,
struct discord_modify_current_user *params,
struct discord_ret_user *ret);
/**
* @brief Get guilds client is a member of
* @note Requires the `guilds` oauth2 scope
*
* @param client the client created with discord_from_token()
* @CCORD_ret_obj{ret,guilds}
* @CCORD_return
*/
CCORDcode discord_get_current_user_guilds(struct discord *client,
struct discord_ret_guilds *ret);
/**
* @brief Leave a guild
*
* @param client the client created with discord_from_token()
* @param guild_id guild to exit from
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_leave_guild(struct discord *client,
u64snowflake guild_id,
struct discord_ret *ret);
/**
* @brief Create a new DM channel with a given user
* @warning DMs should generally be initiated by a user action. If you open a
* significant amount of DMs too quickly, your bot may be rate limited
* or blocked from opening new ones
*
* @param client the client created with discord_from_token()
* @param params the request parameters
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_create_dm(struct discord *client,
struct discord_create_dm *params,
struct discord_ret_channel *ret);
/**
* @brief Create a new group DM channel with multiple users
* @note DMs created with this function will not be shown in the Discord client
* @note Limited to 10 active group DMs
*
* @param client the client created with discord_from_token()
* @param params the request parameters
* @CCORD_ret_obj{ret,channel}
* @CCORD_return
*/
CCORDcode discord_create_group_dm(struct discord *client,
struct discord_create_group_dm *params,
struct discord_ret_channel *ret);
/**
* @brief Get a list of connection objects
* @note Requires the `connections` oauth2 scope
*
* @param client the client created with discord_from_token()
* @CCORD_ret_obj{ret,connections}
* @CCORD_return
*/
CCORDcode discord_get_user_connections(struct discord *client,
struct discord_ret_connections *ret);
/** @} DiscordAPIUser */
#endif /* DISCORD_USER_H */
+31
View File
@@ -0,0 +1,31 @@
/**
* @file voice.h
* @author Cogmasters
* @brief Voice public functions and datatypes
*/
#ifndef DISCORD_VOICE_H
#define DISCORD_VOICE_H
/** @defgroup DiscordAPIVoice Voice
* @ingroup DiscordAPI
* @brief Voice's public API supported by Concord
* @{ */
/**
* @brief Get voice regions that can be used when setting a
* voice or stage channel's `rtc_region`
*
* @param client the client created with discord_from_token()
* @CCORD_ret_obj{ret,voice_regions}
* @CCORD_return
*/
CCORDcode discord_list_voice_regions(struct discord *client,
struct discord_ret_voice_regions *ret);
/** @example voice.c
* Demonstrates a couple use cases of the Voice API */
/** @} DiscordAPIVoice */
#endif /* DISCORD_VOICE_H */
+211
View File
@@ -0,0 +1,211 @@
/**
* @file webhook.h
* @author Cogmasters
* @brief Webhook public functions and datatypes
*/
#ifndef DISCORD_WEBHOOK_H
#define DISCORD_WEBHOOK_H
/** @defgroup DiscordAPIWebhook Webhook
* @ingroup DiscordAPI
* @brief Webhook's public API supported by Concord
* @{ */
/**
* @brief Create a new webhook
* @note Requires the MANAGE_WEBHOOKS permission
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the webhook belongs to
* @param params request parameters
* @CCORD_ret_obj{ret,webhook}
* @CCORD_return
*/
CCORDcode discord_create_webhook(struct discord *client,
u64snowflake channel_id,
struct discord_create_webhook *params,
struct discord_ret_webhook *ret);
/**
* @brief Get webhooks from a given channel
* @note Requires the MANAGE_WEBHOOKS permission
*
* @param client the client created with discord_from_token()
* @param channel_id the channel that the webhooks belongs to
* @CCORD_ret_obj{ret,webhooks}
* @CCORD_return
*/
CCORDcode discord_get_channel_webhooks(struct discord *client,
u64snowflake channel_id,
struct discord_ret_webhooks *ret);
/**
* @brief Get webhooks from a given guild webhook objects
* @note Requires the MANAGE_WEBHOOKS permission
*
* @param client the client created with discord_from_token()
* @param guild_id the guild that the webhooks belongs to
* @CCORD_ret_obj{ret,webhooks}
* @CCORD_return
*/
CCORDcode discord_get_guild_webhooks(struct discord *client,
u64snowflake guild_id,
struct discord_ret_webhooks *ret);
/**
* @brief Get the new webhook object for the given id
*
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @CCORD_ret_obj{ret,webhook}
* @CCORD_return
*/
CCORDcode discord_get_webhook(struct discord *client,
u64snowflake webhook_id,
struct discord_ret_webhook *ret);
/**
* Same as discord_get_webhook(), except this call does not require
* authentication and returns no user in the webhook object
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param webhook_token the webhook token
* @CCORD_ret_obj{ret,webhook}
* @CCORD_return
*/
CCORDcode discord_get_webhook_with_token(struct discord *client,
u64snowflake webhook_id,
const char webhook_token[],
struct discord_ret_webhook *ret);
/**
* @brief Modify a webhook
* @note Requires the MANAGE_WEBHOOKS permission
*
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param params request parameters
* @CCORD_ret_obj{ret,webhook}
* @CCORD_return
*/
CCORDcode discord_modify_webhook(struct discord *client,
u64snowflake webhook_id,
struct discord_modify_webhook *params,
struct discord_ret_webhook *ret);
/**
* Same discord_modify_webhook(), except this call does not require
* authentication and returns no user in the webhook object
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param webhook_token the webhook token
* @param params request parameters
* @CCORD_ret_obj{ret,webhook}
* @CCORD_return
*/
CCORDcode discord_modify_webhook_with_token(
struct discord *client,
u64snowflake webhook_id,
const char webhook_token[],
struct discord_modify_webhook_with_token *params,
struct discord_ret_webhook *ret);
/**
* Delete a webhook permanently. Requires the MANAGE_WEBHOOKS permission
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_webhook(struct discord *client,
u64snowflake webhook_id,
struct discord_delete_webhook *params,
struct discord_ret *ret);
/**
* Same discord_delete_webhook(), except this call does not require
* authentication
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param webhook_token the webhook token
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_webhook_with_token(struct discord *client,
u64snowflake webhook_id,
const char webhook_token[],
struct discord_ret *ret);
/**
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param webhook_token the webhook token
* @param params request parameters
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_execute_webhook(struct discord *client,
u64snowflake webhook_id,
const char webhook_token[],
struct discord_execute_webhook *params,
struct discord_ret *ret);
/**
* @brief Get previously-sent webhook message from the same token
*
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param webhook_token the webhook token
* @param message_id the message the webhook belongs to
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_get_webhook_message(struct discord *client,
u64snowflake webhook_id,
const char webhook_token[],
u64snowflake message_id,
struct discord_ret_message *ret);
/**
* @brief Edits a previously-sent webhook message from the same token
*
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param webhook_token the webhook token
* @param message_id the message the webhook belongs to
* @param params request parameters
* @CCORD_ret_obj{ret,message}
* @CCORD_return
*/
CCORDcode discord_edit_webhook_message(
struct discord *client,
u64snowflake webhook_id,
const char webhook_token[],
u64snowflake message_id,
struct discord_edit_webhook_message *params,
struct discord_ret_message *ret);
/**
* @brief Deletes a message that was created by the webhook
*
* @param client the client created with discord_from_token()
* @param webhook_id the webhook itself
* @param webhook_token the webhook token
* @param message_id the message the webhook belongs to
* @CCORD_ret{ret}
* @CCORD_return
*/
CCORDcode discord_delete_webhook_message(struct discord *client,
u64snowflake webhook_id,
const char webhook_token[],
u64snowflake message_id,
struct discord_ret *ret);
/** @example webhook.c
* Demonstrates a couple use cases of the Webhook API */
/** @} DiscordAPIWebhook */
#endif /* DISCORD_WEBHOOK_H */
+326
View File
@@ -0,0 +1,326 @@
/**
* @file websockets.h
*/
#ifndef WEBSOCKETS_H
#define WEBSOCKETS_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <stdint.h>
#include <curl/curl.h>
/**
* @struct websockets
* @brief Opaque handler for WebSockets
*
* @see ws_init(), ws_cleanup()
*/
struct websockets;
/* forward declaration */
struct logmod;
/**/
/**
* @brief The WebSockets client status
*
* @see ws_get_status()
*/
enum ws_status {
/** client disconnected from ws */
WS_DISCONNECTED = 0,
/** client connected to ws */
WS_CONNECTED,
/** client in the process of disconnecting from ws */
WS_DISCONNECTING,
/** client in the process of connecting to ws */
WS_CONNECTING,
};
/**
* @brief WebSockets CLOSE opcodes
* @see ws_close_opcode_print()
* @see https://tools.ietf.org/html/rfc6455#section-7.4.1
*/
enum ws_close_reason {
WS_CLOSE_REASON_NORMAL = 1000,
WS_CLOSE_REASON_GOING_AWAY = 1001,
WS_CLOSE_REASON_PROTOCOL_ERROR = 1002,
WS_CLOSE_REASON_UNEXPECTED_DATA = 1003,
WS_CLOSE_REASON_NO_REASON = 1005,
WS_CLOSE_REASON_ABRUPTLY = 1006,
WS_CLOSE_REASON_INCONSISTENT_DATA = 1007,
WS_CLOSE_REASON_POLICY_VIOLATION = 1008,
WS_CLOSE_REASON_TOO_BIG = 1009,
WS_CLOSE_REASON_MISSING_EXTENSION = 1010,
WS_CLOSE_REASON_SERVER_ERROR = 1011,
WS_CLOSE_REASON_IANA_REGISTRY_START = 3000,
WS_CLOSE_REASON_IANA_REGISTRY_END = 3999,
WS_CLOSE_REASON_PRIVATE_START = 4000,
WS_CLOSE_REASON_PRIVATE_END = 4999
};
/** @brief WebSockets callbacks */
struct ws_callbacks {
/**
* @brief Called upon connection
*/
void (*on_connect)(void *data, struct websockets *ws);
/**
* @brief Reports UTF-8 text messages.
*
* @note it's guaranteed to be NULL (\0) terminated, but the UTF-8 is
* not validated. If it's invalid, consider closing the connection
* with WS_CLOSE_REASON_INCONSISTENT_DATA.
*/
void (*on_text)(void *data,
struct websockets *ws,
const char *text,
size_t len);
/** @brief reports binary data. */
void (*on_binary)(void *data,
struct websockets *ws,
const void *mem,
size_t len);
/**
* @brief reports PING.
*
* @note if provided you should reply with ws_pong(). If not
* provided, pong is sent with the same message payload.
*/
void (*on_ping)(void *data,
struct websockets *ws,
const char *reason,
size_t len);
/** @brief reports PONG. */
void (*on_pong)(void *data,
struct websockets *ws,
const char *reason,
size_t len);
/**
* @brief reports server closed the connection with the given reason.
*
* Clients should not transmit any more data after the server is
* closed
*/
void (*on_close)(void *data,
struct websockets *ws,
enum ws_close_reason wscode,
const char *reason,
size_t len);
/** @brief user arbitrary data to be passed around callbacks */
void *data;
};
/**
* @brief Check if a WebSockets connection is alive
*
* This will only return true if the connection status is
* different than WS_DISCONNECTED
* @param ws the WebSockets handle created with ws_init()
* @return `true` if WebSockets status is different than
* WS_DISCONNECTED, `false` otherwise.
*/
#define ws_is_alive(ws) (ws_get_status(ws) != WS_DISCONNECTED)
/**
* @brief Check if WebSockets connection is functional
*
* This will only return true if the connection status is
* WS_CONNECTED
* @param ws the WebSockets handle created with ws_init()
* @return `true` if is functional, `false` otherwise
*/
#define ws_is_functional(ws) (ws_get_status(ws) == WS_CONNECTED)
/**
* @brief Create a new (CURL-based) WebSockets handle
*
* @param cbs set of functions to call back when server report events.
* @param mhandle user-owned curl_multi handle for performing non-blocking
* transfers
* @param logmod optional pre-initialized logging handler
* @param fp file pointer for writing WebSockets traces to
* @return newly created WebSockets handle, free with ws_cleanup()
*/
struct websockets *ws_init(struct ws_callbacks *cbs,
CURLM *mhandle,
struct logmod *logmod,
FILE *fp);
/**
* @brief Free a WebSockets handle created with ws_init()
*
* @param ws the WebSockets handle created with ws_init()
*/
void ws_cleanup(struct websockets *ws);
/**
* @brief Set the URL for the WebSockets handle to connect
*
* @param ws the WebSockets handle created with ws_init()
* @param base_url the URL to connect, such as ws://echo.websockets.org
*/
void ws_set_url(struct websockets *ws, const char base_url[]);
/**
* @brief Send a binary message of given size.
*
* Binary messages do not need to include the null terminator (\0), they
* will be read up to @a msglen.
*
* @param ws the WebSockets handle created with ws_init()
* @param msg the pointer to memory (linear) to send.
* @param msglen the length in bytes of @a msg.
* @return true if sent, false on errors.
*/
_Bool ws_send_binary(struct websockets *ws, const char msg[], size_t msglen);
/**
* @brief Send a text message of given size.
*
* Text messages do not need to include the null terminator (\0), they
* will be read up to @a len.
*
* @param ws the WebSockets handle created with ws_init()
* @param text the pointer to memory (linear) to send.
* @param len the length in bytes of @a text.
* @return true if sent, false on errors.
*/
_Bool ws_send_text(struct websockets *ws, const char text[], size_t len);
/**
* @brief Send a PING (opcode 0x9) frame with @a reason as payload.
*
* @param ws the WebSockets handle created with ws_init()
* @param reason NULL or some UTF-8 string null ('\0') terminated.
* @param len the length of @a reason in bytes. If SIZE_MAX, uses
* strlen() on @a reason if it's not NULL.
* @return true if sent, false on errors.
*/
_Bool ws_ping(struct websockets *ws, const char reason[], size_t len);
/**
* @brief Send a PONG (opcode 0xA) frame with @a reason as payload.
*
* Note that pong is sent automatically if no "on_ping" callback is
* defined. If one is defined you must send pong manually.
*
* @param ws the WebSockets handle created with ws_init()
* @param reason NULL or some UTF-8 string null ('\0') terminated.
* @param len the length of @a reason in bytes. If SIZE_MAX, uses
* strlen() on @a reason if it's not NULL.
* @return true if sent, false on errors.
*/
_Bool ws_pong(struct websockets *ws, const char reason[], size_t len);
/**
* @brief Signals connecting state before entering the WebSockets event loop
*
* @param ws the WebSockets handle created with ws_init()
* @return the WebSockets easy_handle that is free'd at ws_end()
*/
CURL *ws_start(struct websockets *ws);
/**
* @brief Cleanup and reset `ws` connection resources
*
* @param ws the WebSockets handle created with ws_init()
*/
void ws_end(struct websockets *ws);
/**
* @brief Reads/Write available data from WebSockets
* @note Helper over curl_multi_wait()
*
* @param ws the WebSockets handle created with ws_init()
* @param wait_ms limit amount in milliseconds to wait for until activity
* @param tstamp get current timestamp for this iteration
* @return `true` if connection is still alive, `false` otherwise
* @note This is an easy, yet highly abstracted way of performing transfers.
* If a higher control is necessary, users are better of using
* ws_multi_socket_run()
*/
_Bool ws_easy_run(struct websockets *ws, uint64_t wait_ms, uint64_t *tstamp);
/**
* @brief Reads/Write available data from WebSockets
* @note I/O is driven by io_poller per-socket-event via curl_multi_socket_action()
*
* @param ws the WebSockets handle created with ws_init()
* @param tstamp get current timestamp for this iteration
* @return `true` if connection is still alive, `false` otherwise
*/
_Bool ws_multi_socket_run(struct websockets *ws, uint64_t *tstamp);
/**
* @brief Returns the WebSockets handle connection status
*
* @param ws the WebSockets handle created with ws_init()
* @return a ws_status opcode
*/
enum ws_status ws_get_status(struct websockets *ws);
/**
* @brief Returns a enum ws_close_reason opcode in a string format
*
* @param opcode the opcode to be converted to string
* @return a read-only string literal of the opcode
*/
const char *ws_close_opcode_print(enum ws_close_reason opcode);
/**
* @brief The WebSockets event-loop concept of "now"
*
* @param ws the WebSockets handle created with ws_init()
* @return the timestamp in milliseconds from when ws_timestamp_update() was
* last called
* @note the timestamp is updated at the start of each event-loop iteration
*/
uint64_t ws_timestamp(struct websockets *ws);
/**
* @brief Update the WebSockets event-loop concept of "now"
*
* @param ws the WebSockets handle created with ws_init()
* @return the timestamp in milliseconds
*/
uint64_t ws_timestamp_update(struct websockets *ws);
/**
* @brief Thread-safe way to stop websockets connection
*
* This will activate a internal WS_USER_CMD_EXIT flag that will
* force disconnect when the next iteration begins.
* @note it will create a copy of the reason string
* @param ws the WebSockets handle created with ws_init()
* @param code the WebSockets CLOSE opcode
* @param reason the close reason
* @param len the reason length
*/
void ws_close(struct websockets *ws,
const enum ws_close_reason code,
const char reason[],
const size_t len);
/**
* @brief Add a header field/value pair
*
* @param ws the WebSockets handle created with ws_init()
* @param field the header field
* @param value the header value
*/
void ws_add_header(struct websockets *ws,
const char field[],
const char value[]);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* WEBSOCKETS_H */