test build
This commit is contained in:
@@ -411,6 +411,14 @@ else()
|
||||
LOG_OUTPUT_ON_FAILURE TRUE
|
||||
)
|
||||
|
||||
ExternalProject_Add_Step(concord_external mirror_headers
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CONCORD_INCLUDE_DIR}/concord"
|
||||
"${PROJECT_SOURCE_DIR}/include/concord"
|
||||
DEPENDEES install
|
||||
COMMENT "Mirroring Concord headers into include/concord"
|
||||
)
|
||||
|
||||
add_library(concord::concord STATIC IMPORTED GLOBAL)
|
||||
set_target_properties(concord::concord PROPERTIES
|
||||
IMPORTED_LOCATION "${CONCORD_LIBRARY}"
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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) */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Mathias Brossard <mathias@brossard.org>.
|
||||
* 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_ */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,872 @@
|
||||
/*
|
||||
vector.h - Vectors for C
|
||||
Version: 1.0.0
|
||||
|
||||
Description:
|
||||
Header-only library for vectors in C. Memory safe against the mistakes it can
|
||||
detect, but not thread-safe (you must implement your own synchronization
|
||||
mechanisms).
|
||||
Designed for C23 onwards, but should be compatible with older standards (tested
|
||||
from C99 onwards).
|
||||
|
||||
Guarantees:
|
||||
- A vector_t must never be duplicated by copying the struct. Two vector_t values
|
||||
sharing one data pointer both believe they own it, and whichever is destroyed
|
||||
or moved from first leaves the other holding freed memory. Copying the pointer
|
||||
(vector_t* b = a;) is fine - that is one vector with two names, and it must be
|
||||
destroyed exactly once.
|
||||
- To hand a vector's data to another vector, use vector_move(). The source is
|
||||
emptied, freed and its pointer set to NULL, so there is only ever one owner.
|
||||
- To get a second, independent vector with the same contents, use
|
||||
vector_deep_copy(), which copies the data as well as the vector itself. Deep
|
||||
copies are only available for vectors without an element destructor, as a
|
||||
byte-wise copy of owning elements would result in a double free.
|
||||
- No gaps in the raw data array.
|
||||
- When a vector is resized, the data is reallocated to a new memory location,
|
||||
so any pointers into the old data are invalidated. There is no way to
|
||||
preserve them; re-fetch with vector_get() after the resize.
|
||||
- Order of elements is preserved.
|
||||
- It is safe to pass a pointer into a vector's own data as the source element
|
||||
of vector_push_back(), vector_insert() and vector_set(). Each of them handles
|
||||
the aliasing in whichever way suits it, so the value survives the reallocation
|
||||
or the shifting of elements. On a vector with an element destructor this is
|
||||
rejected with -1 instead, as a byte-wise copy would leave two slots owning the
|
||||
same memory. To duplicate an owning element, copy what it owns yourself and
|
||||
push that.
|
||||
- vector_take_at() and vector_take_back() transfer ownership of the element to the
|
||||
caller, and do not call the destructor on it. It is the caller's responsibility
|
||||
to free whatever the element owns.
|
||||
|
||||
Element destructors:
|
||||
A vector may be given an element destructor with vector_set_destructor(). It is
|
||||
called for every element that leaves the vector, i.e. by vector_pop_back(),
|
||||
vector_pop_at(), vector_set() (on the element being overwritten), vector_clear()
|
||||
and vector_destroy().
|
||||
|
||||
The destructor is NOT called by vector_take_back() or vector_take_at(), as these
|
||||
functions transfer ownership of the element to the caller. It is the caller's
|
||||
responsibility to free whatever the element owns.
|
||||
|
||||
The destructor receives a pointer to the element's slot inside the vector's data
|
||||
array - NOT a pointer that was returned by malloc(). It must free whatever the
|
||||
element owns, and must never free the pointer it was handed, as that memory
|
||||
belongs to the vector. For a vector of char* this means:
|
||||
|
||||
void free_str(void* element) { free(*(char**)element); }
|
||||
|
||||
vector_t* vec = vector_create(sizeof(char*));
|
||||
vector_set_destructor(vec, free_str);
|
||||
// ... use the vector ...
|
||||
vector_destroy(&vec); // Takes the address of your pointer, and NULLs it
|
||||
|
||||
A warning regarding misuse:
|
||||
The vector manages its own buffer correctly: no leaks, no double frees of the data
|
||||
array, no use of stale pointers internally. It cannot reason about what your
|
||||
elements own — that is what the destructor is for, and it is your responsibility
|
||||
to set one and to avoid duplicating owned pointers between slots.
|
||||
|
||||
License:
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 DcruBro
|
||||
|
||||
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 VECTOR_H
|
||||
#define VECTOR_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void (*vector_destructor_t)(void* element); // Function pointer type for element destructor
|
||||
|
||||
/*
|
||||
A vector. Do not construct, copy or modify this struct directly - the fields are
|
||||
visible only because the functions below are inline. Create vectors with
|
||||
vector_create() and use the accessors. A vector_t that did not come from
|
||||
vector_create() is not a valid vector, and the functions will reject it where
|
||||
they can and misbehave where they cannot.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t size; // Number of elements in the vector
|
||||
size_t capacity; // Allocated capacity of the vector
|
||||
size_t element_size; // Size of each element in the vector
|
||||
void *data; // Pointer to the raw data array
|
||||
vector_destructor_t destructor; // Function pointer to the element destructor
|
||||
} vector_t;
|
||||
|
||||
/* Forward declarations */
|
||||
static inline int vector_is_aliased(const vector_t* vec, const void* ptr);
|
||||
static inline vector_t* vector_create(size_t element_size);
|
||||
static inline int vector_set_destructor(vector_t* vec, vector_destructor_t destructor);
|
||||
static inline vector_destructor_t vector_get_destructor(const vector_t* vec);
|
||||
static inline int vector_reserve(vector_t* vec, size_t new_capacity);
|
||||
static inline int vector_grow(vector_t* vec);
|
||||
static inline int vector_prune(vector_t* vec);
|
||||
static inline int vector_push_back(vector_t* vec, const void* element);
|
||||
static inline int vector_pop_back(vector_t* vec);
|
||||
static inline int vector_pop_at(vector_t* vec, size_t index);
|
||||
static inline int vector_take_at(vector_t* vec, size_t index, void* out);
|
||||
static inline int vector_take_back(vector_t* vec, void* out);
|
||||
static inline int vector_insert(vector_t* vec, size_t index, const void* element);
|
||||
static inline int vector_clear(vector_t* vec);
|
||||
static inline int vector_set(vector_t* vec, size_t index, const void* element);
|
||||
static inline int vector_is_empty(const vector_t* vec);
|
||||
static inline void* vector_front(vector_t* vec);
|
||||
static inline const void* vector_front_const(const vector_t* vec);
|
||||
static inline void* vector_back(vector_t* vec);
|
||||
static inline const void* vector_back_const(const vector_t* vec);
|
||||
static inline void* vector_get(vector_t* vec, size_t index);
|
||||
static inline const void* vector_get_const(const vector_t* vec, size_t index);
|
||||
static inline size_t vector_size(const vector_t* vec);
|
||||
static inline size_t vector_capacity(const vector_t* vec);
|
||||
static inline size_t vector_element_size(const vector_t* vec);
|
||||
static inline const void* vector_as_c_array(const vector_t* vec);
|
||||
static inline void* vector_as_c_array_mutable(vector_t* vec);
|
||||
static inline int vector_move(vector_t* dest, vector_t** src);
|
||||
static inline vector_t* vector_deep_copy(const vector_t* vec);
|
||||
static inline int vector_destroy(vector_t** vec);
|
||||
|
||||
/*
|
||||
@brief Checks whether a pointer points inside the vector's own data array. Used internally to make the write functions safe against self-referential input.
|
||||
@param vec A pointer to the vector to check against.
|
||||
@param ptr The pointer to check.
|
||||
@return 1 if the pointer lies within the vector's allocated buffer, 0 otherwise.
|
||||
@attention This is an internal helper. You are not expected to call it directly, but it is harmless if you do.
|
||||
*/
|
||||
static inline int vector_is_aliased(const vector_t* vec, const void* ptr) {
|
||||
if (!vec || !ptr || !vec->data) {
|
||||
return 0; // Nothing to alias
|
||||
}
|
||||
|
||||
// Compared as integers rather than pointers, as comparing pointers into
|
||||
// different objects is not well defined.
|
||||
uintptr_t base = (uintptr_t)vec->data;
|
||||
uintptr_t end = base + (vec->capacity * vec->element_size);
|
||||
uintptr_t target = (uintptr_t)ptr;
|
||||
|
||||
return target >= base && target < end;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Creates a new vector with the specified element size.
|
||||
@param element_size The size of each element in the vector. Call with sizeof(type)
|
||||
@return A pointer to the newly created vector, or NULL if allocation fails. The vector lives on the heap.
|
||||
@attention The vector must be destroyed with vector_destroy() to free its memory. Failing to do so will result in a memory leak.
|
||||
@attention The vector's initial capacity is set to 10. If you want to change the initial capacity globally, set the DLIBC_VECTOR_INITIAL_CAPACITY macro before including this header file. The initial capacity must be greater than 0.
|
||||
@attention The vector's element size must be greater than 0. If you pass 0, the function will return NULL.
|
||||
@attention The vector is created without an element destructor. If your elements own memory of their own, set one with vector_set_destructor().
|
||||
*/
|
||||
static inline vector_t* vector_create(size_t element_size) {
|
||||
if (element_size == 0) {
|
||||
return NULL; // Invalid element size
|
||||
}
|
||||
|
||||
#ifndef DLIBC_VECTOR_INITIAL_CAPACITY
|
||||
size_t initial_capacity = 10; // Default initial capacity
|
||||
#else
|
||||
#if DLIBC_VECTOR_INITIAL_CAPACITY <= 0
|
||||
#error "DLIBC_VECTOR_INITIAL_CAPACITY must be greater than 0"
|
||||
#endif
|
||||
size_t initial_capacity = DLIBC_VECTOR_INITIAL_CAPACITY; // Use the macro if defined
|
||||
#endif
|
||||
|
||||
if (SIZE_MAX / element_size < initial_capacity) {
|
||||
return NULL; // Prevent overflow
|
||||
}
|
||||
|
||||
vector_t* vec = (vector_t*)malloc(sizeof(vector_t));
|
||||
if (!vec) {
|
||||
return NULL; // Allocation failed
|
||||
}
|
||||
|
||||
vec->size = 0;
|
||||
vec->capacity = initial_capacity;
|
||||
vec->element_size = element_size;
|
||||
vec->destructor = NULL; // Initialize destructor to NULL
|
||||
vec->data = malloc(vec->capacity * vec->element_size);
|
||||
if (!vec->data) {
|
||||
free(vec);
|
||||
return NULL; // Allocation failed
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Sets the destructor function for the vector's elements. This function will be called on each element as it leaves the vector, allowing for custom cleanup of dynamically allocated memory within the elements.
|
||||
@param vec A pointer to the vector for which to set the destructor.
|
||||
@param destructor A function pointer to the destructor function, or NULL to remove the current one. The function should take a single void* parameter, which will be a pointer to the element to be destroyed.
|
||||
@return 0 on success, -1 if the vector is NULL.
|
||||
@attention If you do not set a destructor function, the vector will not automatically free any dynamically allocated memory within its elements when it is destroyed. You must ensure that you free any such memory manually before destroying the vector to avoid memory leaks.
|
||||
@attention The destructor is passed a pointer to the element's slot inside the vector's data array, not a pointer returned by malloc(). It must free what the element owns and must never free the pointer it is given. For a vector of char*, the destructor body is free(*(char**)element);
|
||||
@attention This assumes that your destructor function is valid and safe to call. If it isn't, bad things will happen and it will not be nice to watch.
|
||||
@attention Set the destructor before adding any elements. Setting it on a vector that already holds elements is allowed, but elements that were removed beforehand will not have been destroyed.
|
||||
*/
|
||||
static inline int vector_set_destructor(vector_t* vec, vector_destructor_t destructor) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid vector
|
||||
}
|
||||
|
||||
vec->destructor = destructor;
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets the destructor function currently set on the vector.
|
||||
@param vec A pointer to the vector whose destructor is to be retrieved.
|
||||
@return The vector's destructor function pointer, or NULL if the vector is NULL or has no destructor set.
|
||||
*/
|
||||
static inline vector_destructor_t vector_get_destructor(const vector_t* vec) {
|
||||
if (!vec) {
|
||||
return NULL; // Invalid vector
|
||||
}
|
||||
|
||||
return vec->destructor;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Ensures the vector has room for at least the specified number of elements.
|
||||
@param vec A pointer to the vector for which to reserve space.
|
||||
@param new_capacity The minimum capacity the vector should have.
|
||||
@return 0 on success, -1 if the vector is NULL, its element size is zero, or allocation fails.
|
||||
@attention Capacity never decreases. If the vector already has room, this is a no-op and reports success.
|
||||
@attention If the buffer does grow, it is reallocated, so any pointers into the vector's data are invalidated. Use vector_prune() to release unused memory.
|
||||
*/
|
||||
static inline int vector_reserve(vector_t* vec, size_t new_capacity) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid vector
|
||||
}
|
||||
|
||||
if (vec->element_size == 0) {
|
||||
return -1; // Not a usable vector; also guards the division below
|
||||
}
|
||||
|
||||
if (new_capacity <= vec->capacity) {
|
||||
return 0; // Already have the room. Never shrink
|
||||
}
|
||||
|
||||
if (new_capacity > SIZE_MAX / vec->element_size) {
|
||||
return -1; // Prevent overflow
|
||||
}
|
||||
|
||||
void* new_data = realloc(vec->data, new_capacity * vec->element_size);
|
||||
if (!new_data) {
|
||||
return -1; // Allocation failed
|
||||
}
|
||||
|
||||
vec->data = new_data;
|
||||
vec->capacity = new_capacity;
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Grows the vector's capacity to make room for at least one more element. Used internally by the functions that add elements.
|
||||
@param vec A pointer to the vector to grow.
|
||||
@return 0 on success, -1 if the vector is NULL or allocation fails.
|
||||
@attention This is an internal helper. You are not expected to call it directly.
|
||||
*/
|
||||
static inline int vector_grow(vector_t* vec) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid vector
|
||||
}
|
||||
|
||||
if (vec->capacity > SIZE_MAX / 2) {
|
||||
return -1; // Prevent overflow
|
||||
}
|
||||
|
||||
size_t new_capacity = vec->capacity > 0 ? vec->capacity * 2 : 1; // Double the capacity, or set to 1 if it was 0
|
||||
return vector_reserve(vec, new_capacity);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Prunes the vector to free unused memory. If the vector's size is less than its capacity, this function will reallocate the vector's data array to match its size, freeing any unused memory.
|
||||
@param vec A pointer to the vector to be pruned.
|
||||
@return 0 on success, -1 if the vector is NULL or allocation fails.
|
||||
@attention After calling this function, the vector's capacity will be equal to its size. Any pointers to the old data will be invalidated.
|
||||
@attention Capacity may never drop below 1, even if the vector is empty.
|
||||
*/
|
||||
static inline int vector_prune(vector_t* vec) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid vector
|
||||
}
|
||||
|
||||
if (vec->element_size == 0) {
|
||||
return -1; // Not a usable vector; also guards the division below
|
||||
}
|
||||
|
||||
if (vec->size < vec->capacity) {
|
||||
size_t new_capacity = vec->size > 0 ? vec->size : 1; // Ensure capacity is at least 1
|
||||
|
||||
if (new_capacity > SIZE_MAX / vec->element_size) {
|
||||
return -1; // Prevent overflow
|
||||
}
|
||||
|
||||
void* new_data = realloc(vec->data, new_capacity * vec->element_size);
|
||||
if (!new_data) {
|
||||
return -1; // Allocation failed
|
||||
}
|
||||
|
||||
vec->data = new_data;
|
||||
vec->capacity = new_capacity;
|
||||
}
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Adds an element to the end of the vector.
|
||||
@param vec A pointer to the vector to which the element will be added.
|
||||
@param element A pointer to the element to be added. The element will be copied into the vector's data array.
|
||||
@return 0 on success, -1 if the vector is NULL, the element is NULL, if the element is an owning element that is aliased in the vector and a destructor is set, or if reservation fails.
|
||||
@attention The element must be a pointer to a valid memory location containing data of the same type as the vector's element type. The vector will make a copy of the data, so the original element can be modified or freed after this function returns.
|
||||
@attention The element may point into the vector's own data array. If growing the vector moves the data, the pointer is followed to its new location automatically.
|
||||
*/
|
||||
static inline int vector_push_back(vector_t* vec, const void* element) {
|
||||
if (!vec || !element) {
|
||||
return -1; // Invalid vector or element
|
||||
}
|
||||
|
||||
if (vec->destructor && vector_is_aliased(vec, element)) {
|
||||
return -1; // Refuse to push an owning element that lives inside the vector
|
||||
}
|
||||
|
||||
if (vec->size >= vec->capacity) {
|
||||
// If the element lives inside our own data array, remember where it sits
|
||||
// so that we can find it again after the data has been reallocated.
|
||||
// This must happen before the growth, while the old buffer is still valid
|
||||
size_t offset = 0;
|
||||
int aliased = vector_is_aliased(vec, element);
|
||||
if (aliased) {
|
||||
offset = (size_t)((const char*)element - (const char*)vec->data);
|
||||
}
|
||||
|
||||
if (vector_grow(vec) != 0) {
|
||||
return -1; // Failed to grow the vector
|
||||
}
|
||||
|
||||
if (aliased) {
|
||||
element = (const char*)vec->data + offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the new element into the vector's data array
|
||||
memmove((char*)vec->data + (vec->size * vec->element_size), element, vec->element_size);
|
||||
vec->size++;
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Removes the last element from the vector.
|
||||
@param vec A pointer to the vector from which the element will be removed.
|
||||
@return 0 on success, -1 if the vector is NULL or empty.
|
||||
@attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set (in which case, the destructor will be called on the element being removed if it is set). Use vector_take_back() to retrieve the last element, remove it from the vector, and NOT call the destructor on it (hands ownership to the caller).
|
||||
*/
|
||||
static inline int vector_pop_back(vector_t* vec) {
|
||||
if (!vec || vec->size == 0) {
|
||||
return -1; // Invalid vector or empty vector
|
||||
}
|
||||
|
||||
if (vec->destructor) {
|
||||
void* element = (char*)vec->data + ((vec->size - 1) * vec->element_size);
|
||||
vec->destructor(element); // Call the destructor for the last element
|
||||
}
|
||||
|
||||
vec->size--;
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Removes the element at the specified index from the vector.
|
||||
@param vec A pointer to the vector from which the element will be removed.
|
||||
@param index The index of the element to be removed.
|
||||
@return 0 on success, -1 if the vector is NULL or the index is out of bounds.
|
||||
@attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set (in which case, the destructor will be called on the element being removed if it is set). Use vector_take_at() to retrieve the element at the specified index, remove it from the vector, and NOT call the destructor on it (hands ownership to the caller).
|
||||
*/
|
||||
static inline int vector_pop_at(vector_t* vec, size_t index) {
|
||||
if (!vec || index >= vec->size) {
|
||||
return -1; // Invalid vector or index out of bounds
|
||||
}
|
||||
|
||||
if (vec->destructor) {
|
||||
void* element = (char*)vec->data + (index * vec->element_size);
|
||||
vec->destructor(element); // Call the destructor for the element to be removed
|
||||
}
|
||||
|
||||
// Move elements after the index one position to the left
|
||||
if (index + 1 < vec->size) {
|
||||
memmove((char*)vec->data + (index * vec->element_size),
|
||||
(char*)vec->data + ((index + 1) * vec->element_size),
|
||||
(vec->size - index - 1) * vec->element_size);
|
||||
}
|
||||
|
||||
vec->size--;
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Removes the element at the specified index and transfers ownership of it to the caller.
|
||||
@param vec A pointer to the vector from which the element will be taken.
|
||||
@param index The index of the element to take.
|
||||
@param out A pointer to a buffer of at least vector_element_size(vec) bytes, which the element is copied into.
|
||||
@return 0 on success, -1 if the vector is NULL, out is NULL, out points into the vector's own data, or the index is out of bounds.
|
||||
@attention The element destructor is deliberately NOT called. Whatever the element owns becomes the caller's responsibility to free.
|
||||
@attention out must not point into the vector's own data array. Doing so would leave two slots owning the same memory and is rejected with -1.
|
||||
*/
|
||||
static inline int vector_take_at(vector_t* vec, size_t index, void* out) {
|
||||
if (!vec || index >= vec->size || !out) {
|
||||
return -1; // Invalid vector, index out of bounds, or output pointer is NULL
|
||||
}
|
||||
|
||||
if (vector_is_aliased(vec, out)) {
|
||||
return -1; // Refuse to take an element into a pointer that lives inside the vector
|
||||
}
|
||||
|
||||
void* slot = (char*)vec->data + (index * vec->element_size);
|
||||
memcpy(out, slot, vec->element_size); // Copy the element to the output pointer
|
||||
|
||||
// Move elements after the index one position to the left
|
||||
if (index + 1 < vec->size) {
|
||||
memmove(slot,
|
||||
(char*)vec->data + ((index + 1) * vec->element_size),
|
||||
(vec->size - index - 1) * vec->element_size);
|
||||
}
|
||||
|
||||
vec->size--;
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Removes the last element from the vector and transfers ownership of it to the caller.
|
||||
@param vec A pointer to the vector from which the element will be taken.
|
||||
@param out A pointer to a buffer of at least vector_element_size(vec) bytes, which the element is copied into.
|
||||
@return 0 on success, -1 if the vector is NULL, empty, if out aliases the vector's data, or out is NULL.
|
||||
@attention The element destructor is deliberately NOT called. Whatever the element owns becomes the caller's responsibility to free.
|
||||
*/
|
||||
static inline int vector_take_back(vector_t* vec, void* out) {
|
||||
if (!vec || vec->size == 0 || !out) {
|
||||
return -1; // Invalid vector, empty vector, or output pointer is NULL
|
||||
}
|
||||
|
||||
return vector_take_at(vec, vec->size - 1, out); // Take the last element
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Inserts an element at the specified index in the vector.
|
||||
@param vec A pointer to the vector into which the element will be inserted.
|
||||
@param index The index at which to insert the element.
|
||||
@param element A pointer to the element to be inserted. The element will be copied into the vector's data array.
|
||||
@return 0 on success, -1 if the vector is NULL, the element is NULL, the index is out of bounds, if the element is an owning element that is aliased in the vector and a destructor is set, or if reservation fails.
|
||||
@attention After calling this function, the vector's size will be increased by one and every element from the index onwards will have moved one position to the right. Any pointers into the vector are invalidated.
|
||||
@attention The element may point into the vector's own data array. Its value is copied aside first, so that neither the reallocation nor the shifting of elements can corrupt it.
|
||||
*/
|
||||
static inline int vector_insert(vector_t* vec, size_t index, const void* element) {
|
||||
if (!vec || !element || index > vec->size) {
|
||||
return -1; // Invalid vector, element, or index out of bounds
|
||||
}
|
||||
|
||||
if (vec->destructor && vector_is_aliased(vec, element)) {
|
||||
return -1; // Refuse to insert an owning element that lives inside the vector
|
||||
}
|
||||
|
||||
// If the element lives inside our own data array, take a copy of it now. Both
|
||||
// the reservation below and the shifting of elements would otherwise destroy
|
||||
// the value before it can be read
|
||||
void* temp = NULL;
|
||||
if (vector_is_aliased(vec, element)) {
|
||||
temp = malloc(vec->element_size);
|
||||
if (!temp) {
|
||||
return -1; // Allocation failed
|
||||
}
|
||||
|
||||
memcpy(temp, element, vec->element_size);
|
||||
element = temp;
|
||||
}
|
||||
|
||||
if (vec->size >= vec->capacity) {
|
||||
if (vector_grow(vec) != 0) {
|
||||
free(temp);
|
||||
return -1; // Failed to grow the vector
|
||||
}
|
||||
}
|
||||
|
||||
// Move elements after the index one position to the right
|
||||
memmove((char*)vec->data + ((index + 1) * vec->element_size),
|
||||
(char*)vec->data + (index * vec->element_size),
|
||||
(vec->size - index) * vec->element_size);
|
||||
|
||||
// Copy the new element into the vector's data array at the specified index
|
||||
memcpy((char*)vec->data + (index * vec->element_size), element, vec->element_size);
|
||||
vec->size++;
|
||||
|
||||
free(temp); // Harmless if no copy was needed, as temp is NULL in that case
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Clears all elements from the vector.
|
||||
@param vec A pointer to the vector to be cleared.
|
||||
@return 0 on success, -1 if the vector is NULL.
|
||||
@attention After calling this function, the vector's size will be zero. The memory occupied by the elements will not be freed automatically unless a destructor is set.
|
||||
@attention The vector's capacity is left untouched, so the vector may be refilled without reallocating. Call vector_prune() afterwards if you want the memory back.
|
||||
*/
|
||||
static inline int vector_clear(vector_t* vec) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid vector
|
||||
}
|
||||
|
||||
if (vec->destructor) {
|
||||
for (size_t i = 0; i < vec->size; ++i) {
|
||||
void* element = (char*)vec->data + (i * vec->element_size);
|
||||
vec->destructor(element); // Call the destructor for each element
|
||||
}
|
||||
}
|
||||
|
||||
vec->size = 0;
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Sets the element at the specified index in the vector, overwriting whatever was there before.
|
||||
@param vec A pointer to the vector in which to set the element.
|
||||
@param index The index of the element to set.
|
||||
@param element A pointer to the element to set. The element will be copied into the vector's data array.
|
||||
@return 0 on success, -1 if the vector is NULL, the element is NULL, if the element is an owning element that is aliased in the vector and a destructor is set, or if the index is out of bounds.
|
||||
@attention This never grows the vector. The index must be below the current size.
|
||||
@attention If a destructor is set, it is called on the element being overwritten before the new value is copied in.
|
||||
@attention Setting an element to itself is a no-op and reports success.
|
||||
@attention On a vector without a destructor, the element may point into the
|
||||
vector's own data array; the copy handles any overlap. On a vector with a
|
||||
destructor this is refused with -1, as it would leave two slots owning the
|
||||
same memory.
|
||||
*/
|
||||
static inline int vector_set(vector_t* vec, size_t index, const void* element) {
|
||||
if (!vec || !element || index >= vec->size) {
|
||||
return -1; // Invalid vector, element, or index out of bounds
|
||||
}
|
||||
|
||||
void* slot = (char*)vec->data + (index * vec->element_size);
|
||||
if (slot == element) {
|
||||
return 0; // Setting the element to itself, no action needed
|
||||
}
|
||||
|
||||
if (vec->destructor && vector_is_aliased(vec, element)) {
|
||||
return -1; // Refuse to set an owning element that lives inside the vector
|
||||
}
|
||||
|
||||
if (vec->destructor) {
|
||||
vec->destructor(slot); // Call the destructor for the element being overwritten
|
||||
}
|
||||
|
||||
memmove(slot, element, vec->element_size); // memmove, as the element may overlap the slot
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Checks if the vector is empty.
|
||||
@param vec A pointer to the vector to check.
|
||||
@return 1 if the vector is empty, 0 if it is not empty.
|
||||
@attention If the vector is NULL, this function will return 1 (is empty) to indicate that the vector is invalid.
|
||||
*/
|
||||
static inline int vector_is_empty(const vector_t* vec) {
|
||||
if (!vec) {
|
||||
return 1; // Invalid vector
|
||||
}
|
||||
|
||||
return vec->size == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a pointer to the first element in the vector.
|
||||
@param vec A pointer to the vector from which to get the element.
|
||||
@return A pointer to the first element in the vector, or NULL if the vector is NULL or empty.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
*/
|
||||
static inline void* vector_front(vector_t* vec) {
|
||||
if (!vec || vec->size == 0) {
|
||||
return NULL; // Invalid vector or empty vector
|
||||
}
|
||||
|
||||
return vec->data;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a const pointer to the first element in the vector. Cannot be used to modify the element.
|
||||
@param vec A pointer to the vector from which to get the element.
|
||||
@return A constant pointer to the first element in the vector, or NULL if the vector is NULL or empty.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
*/
|
||||
static inline const void* vector_front_const(const vector_t* vec) {
|
||||
if (!vec || vec->size == 0) {
|
||||
return NULL; // Invalid vector or empty vector
|
||||
}
|
||||
|
||||
return vec->data;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a pointer to the last element in the vector.
|
||||
@param vec A pointer to the vector from which to get the element.
|
||||
@return A pointer to the last element in the vector, or NULL if the vector is NULL or empty.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
*/
|
||||
static inline void* vector_back(vector_t* vec) {
|
||||
if (!vec || vec->size == 0) {
|
||||
return NULL; // Invalid vector or empty vector
|
||||
}
|
||||
|
||||
return (char*)vec->data + ((vec->size - 1) * vec->element_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a const pointer to the last element in the vector. Cannot be used to modify the element.
|
||||
@param vec A pointer to the vector from which to get the element.
|
||||
@return A constant pointer to the last element in the vector, or NULL if the vector is NULL or empty.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
*/
|
||||
static inline const void* vector_back_const(const vector_t* vec) {
|
||||
if (!vec || vec->size == 0) {
|
||||
return NULL; // Invalid vector or empty vector
|
||||
}
|
||||
|
||||
return (const char*)vec->data + ((vec->size - 1) * vec->element_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a pointer to the element at the specified index in the vector.
|
||||
@param vec A pointer to the vector from which to get the element.
|
||||
@param index The index of the element to get.
|
||||
@return A pointer to the element at the specified index, or NULL if the vector is NULL or the index is out of bounds.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
@attention For most read-only operations, consider using vector_get_const() instead, which returns a const pointer to the element.
|
||||
*/
|
||||
static inline void* vector_get(vector_t* vec, size_t index) {
|
||||
if (!vec || index >= vec->size) {
|
||||
return NULL; // Invalid vector or index out of bounds
|
||||
}
|
||||
|
||||
return (char*)vec->data + (index * vec->element_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a constant pointer to the element at the specified index in the vector. Cannot be used to modify the element.
|
||||
@param vec A pointer to the vector from which to get the element.
|
||||
@param index The index of the element to get.
|
||||
@return A constant pointer to the element at the specified index, or NULL if the vector is NULL or the index is out of bounds.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
*/
|
||||
static inline const void* vector_get_const(const vector_t* vec, size_t index) {
|
||||
if (!vec || index >= vec->size) {
|
||||
return NULL; // Invalid vector or index out of bounds
|
||||
}
|
||||
|
||||
return (const char*)vec->data + (index * vec->element_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets the size of the vector.
|
||||
@param vec A pointer to the vector whose size is to be retrieved.
|
||||
@return The size of the vector, or 0 if the vector is NULL.
|
||||
*/
|
||||
static inline size_t vector_size(const vector_t* vec) {
|
||||
if (!vec) {
|
||||
return 0; // Invalid vector
|
||||
}
|
||||
|
||||
return vec->size;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets the capacity of the vector.
|
||||
@param vec A pointer to the vector whose capacity is to be retrieved.
|
||||
@return The capacity of the vector, or 0 if the vector is NULL.
|
||||
*/
|
||||
static inline size_t vector_capacity(const vector_t* vec) {
|
||||
if (!vec) {
|
||||
return 0; // Invalid vector
|
||||
}
|
||||
|
||||
return vec->capacity;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets the size of each element in the vector.
|
||||
@param vec A pointer to the vector whose element size is to be retrieved.
|
||||
@return The size of each element in the vector, or 0 if the vector is NULL.
|
||||
*/
|
||||
static inline size_t vector_element_size(const vector_t* vec) {
|
||||
if (!vec) {
|
||||
return 0; // Invalid vector
|
||||
}
|
||||
|
||||
return vec->element_size;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a pointer to the underlying C array of the vector. Cannot modify the vector through this pointer.
|
||||
@param vec A pointer to the vector whose underlying array is to be retrieved.
|
||||
@return A pointer to the underlying C array, or NULL if the vector is NULL.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
*/
|
||||
static inline const void* vector_as_c_array(const vector_t* vec) {
|
||||
if (!vec) {
|
||||
return NULL; // Invalid vector
|
||||
}
|
||||
|
||||
return vec->data;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Gets a pointer to the underlying C array of the vector. Can be used to modify the vector's elements.
|
||||
@param vec A pointer to the vector whose underlying array is to be retrieved.
|
||||
@return A pointer to the underlying C array, or NULL if the vector is NULL.
|
||||
@attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed.
|
||||
*/
|
||||
static inline void* vector_as_c_array_mutable(vector_t* vec) {
|
||||
if (!vec) {
|
||||
return NULL; // Invalid vector
|
||||
}
|
||||
|
||||
return vec->data;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Moves a vector to another vector, transferring ownership of the data. The destination vector will take ownership of the source vector's data, its element size and its destructor.
|
||||
@param dest A pointer to the destination vector.
|
||||
@param src A pointer to the pointer holding the source vector. It will be set to NULL.
|
||||
@return 0 on success, -1 if the destination vector is NULL, the source pointer is NULL, the source vector is NULL, or both vectors share the same data pointer.
|
||||
@attention After calling this function, the source vector will be freed (excluding data) and the source pointer will be set to NULL, so it cannot be used again.
|
||||
@attention The destination vector's existing elements will be destroyed (using the destination's own destructor, if it has one) and its data freed. Ensure that you do not need the existing data before calling this function.
|
||||
@attention Moving a vector onto itself is a no-op and reports success, leaving the vector untouched.
|
||||
@attention Two vectors can only share a data pointer if a vector_t was duplicated by copying the struct, which is never valid. The move is refused rather than freeing a buffer the source still points at.
|
||||
*/
|
||||
static inline int vector_move(vector_t* dest, vector_t** src) {
|
||||
if (!dest || !src || !*src) {
|
||||
return -1; // Invalid vectors
|
||||
}
|
||||
|
||||
if (dest == *src) {
|
||||
return 0; // Moving to itself, no action needed
|
||||
}
|
||||
|
||||
if (dest->data == (*src)->data) {
|
||||
return -1; // Refuse to move a vector onto another vector that shares its data
|
||||
// Realistically, this is unreachable via legal use, but... safety.
|
||||
// Or something. You WILL trigger this if you do shallow copies. DO NOT!
|
||||
}
|
||||
|
||||
// Destroy the destination vector's existing elements with its own destructor,
|
||||
// then free its data, as it is about to be replaced
|
||||
vector_clear(dest);
|
||||
free(dest->data);
|
||||
|
||||
// Transfer ownership of the source vector's data to the destination vector
|
||||
dest->size = (*src)->size;
|
||||
dest->capacity = (*src)->capacity;
|
||||
dest->element_size = (*src)->element_size;
|
||||
dest->data = (*src)->data;
|
||||
dest->destructor = (*src)->destructor; // The elements keep the cleanup they came with
|
||||
|
||||
// Reset the source vector to an empty state
|
||||
(*src)->size = 0;
|
||||
(*src)->capacity = 0;
|
||||
(*src)->element_size = 0;
|
||||
(*src)->data = NULL;
|
||||
(*src)->destructor = NULL;
|
||||
|
||||
free(*src); // Free the source vector structure, but not its data (ownership transferred)
|
||||
*src = NULL;
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Creates a deep copy of the vector, including its data.
|
||||
@param vec A pointer to the vector to be copied.
|
||||
@return A pointer to the newly created deep copy of the vector, or NULL if allocation fails, if the input vector is NULL, or if the input vector has a destructor set.
|
||||
@attention The returned vector must be destroyed with vector_destroy() to free its memory. Failing to do so will result in a memory leak.
|
||||
@attention Vectors with a destructor cannot be deep copied and this function will return NULL for them. The elements are copied byte for byte, so any memory they own would end up owned by both vectors and freed twice. If you need to copy such a vector, do it by hand: create a new vector and push copies of the elements into it yourself.
|
||||
*/
|
||||
static inline vector_t* vector_deep_copy(const vector_t* vec) {
|
||||
if (!vec) {
|
||||
return NULL; // Invalid vector
|
||||
}
|
||||
|
||||
if (vec->destructor) {
|
||||
return NULL; // Refuse to byte-copy elements that own memory
|
||||
}
|
||||
|
||||
vector_t* new_vec = vector_create(vec->element_size);
|
||||
if (!new_vec) {
|
||||
return NULL; // Allocation failed
|
||||
}
|
||||
|
||||
int r = vector_reserve(new_vec, vec->capacity);
|
||||
if (r != 0) {
|
||||
vector_destroy(&new_vec);
|
||||
return NULL; // Allocation failed
|
||||
}
|
||||
|
||||
if (vec->size > 0) {
|
||||
memcpy(new_vec->data, vec->data, vec->size * vec->element_size);
|
||||
new_vec->size = vec->size;
|
||||
} // vector_create() already sets size to 0 by default
|
||||
|
||||
return new_vec;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Destroys the vector and frees its memory.
|
||||
@param vec A pointer to the pointer holding the vector to be destroyed. It will be set to NULL.
|
||||
@return 0 on success, or if the vector was already NULL. -1 only if the pointer itself is NULL.
|
||||
@attention After calling this function, the vector pointer should not be used again (It will be set to NULL). Accessing it after destruction will lead to undefined behavior.
|
||||
@attention Only the pointer you pass in is set to NULL. Copies of that pointer held elsewhere are left dangling and must not be used.
|
||||
@attention If stored elements own memory of their own, set a destructor with vector_set_destructor() to have it cleaned up here. Otherwise, the vector will only free the memory allocated for the data array and the vector structure itself, but not any dynamically allocated memory within the elements.
|
||||
*/
|
||||
static inline int vector_destroy(vector_t** vec) {
|
||||
if (!vec) {
|
||||
return -1; // Invalid pointer
|
||||
}
|
||||
|
||||
if (!(*vec)) {
|
||||
return 0; // Already NULL, nothing to destroy
|
||||
}
|
||||
|
||||
vector_t* target = *vec;
|
||||
|
||||
if (target->destructor) {
|
||||
for (size_t i = 0; i < target->size; ++i) {
|
||||
void* element = (char*)target->data + (i * target->element_size);
|
||||
target->destructor(element); // Call the destructor for each element
|
||||
}
|
||||
}
|
||||
|
||||
free(target->data);
|
||||
free(target);
|
||||
*vec = NULL; // Set the pointer to NULL to avoid dangling references
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
#endif // VECTOR_H
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user